commit 913a8d75da43e8003789ab52de29a392430119cf Author: hang <872297557@qq.com> Date: Mon Sep 11 17:48:41 2023 +0800 注册证init diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e8bd25d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,175 @@ +[*.cs] + +# CS8618: 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 +dotnet_diagnostic.CS8618.severity = silent +csharp_space_around_binary_operators = before_and_after +csharp_indent_labels = no_change +csharp_using_directive_placement = outside_namespace:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_elsewhere = false:silent + +[*.{cs,vb}] +end_of_line = crlf +tab_width = 4 +indent_size = 4 +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_code_quality_unused_parameters = all:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +[*.cs] +#### 命名样式 #### + +# 命名规则 + +dotnet_naming_rule.接口_should_be_以_i_开始.severity = suggestion +dotnet_naming_rule.接口_should_be_以_i_开始.symbols = 接口 +dotnet_naming_rule.接口_should_be_以_i_开始.style = 以_i_开始 + +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型 +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 + +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员 +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 + +# 符号规范 + +dotnet_naming_symbols.接口.applicable_kinds = interface +dotnet_naming_symbols.接口.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.接口.required_modifiers = + +dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.类型.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.类型.required_modifiers = + +dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method +dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.非字段成员.required_modifiers = + +# 命名样式 + +dotnet_naming_style.以_i_开始.required_prefix = I +dotnet_naming_style.以_i_开始.required_suffix = +dotnet_naming_style.以_i_开始.word_separator = +dotnet_naming_style.以_i_开始.capitalization = pascal_case + +dotnet_naming_style.帕斯卡拼写法.required_prefix = +dotnet_naming_style.帕斯卡拼写法.required_suffix = +dotnet_naming_style.帕斯卡拼写法.word_separator = +dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case + +dotnet_naming_style.帕斯卡拼写法.required_prefix = +dotnet_naming_style.帕斯卡拼写法.required_suffix = +dotnet_naming_style.帕斯卡拼写法.word_separator = +dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_prefer_static_local_function = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +[*.vb] +#### 命名样式 #### + +# 命名规则 + +dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion +dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface +dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始 + +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型 +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 + +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员 +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 + +# 符号规范 + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected +dotnet_naming_symbols.类型.required_modifiers = + +dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method +dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected +dotnet_naming_symbols.非字段成员.required_modifiers = + +# 命名样式 + +dotnet_naming_style.以_i_开始.required_prefix = I +dotnet_naming_style.以_i_开始.required_suffix = +dotnet_naming_style.以_i_开始.word_separator = +dotnet_naming_style.以_i_开始.capitalization = pascal_case + +dotnet_naming_style.帕斯卡拼写法.required_prefix = +dotnet_naming_style.帕斯卡拼写法.required_suffix = +dotnet_naming_style.帕斯卡拼写法.word_separator = +dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case + +dotnet_naming_style.帕斯卡拼写法.required_prefix = +dotnet_naming_style.帕斯卡拼写法.required_suffix = +dotnet_naming_style.帕斯卡拼写法.word_separator = +dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case 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..94b54cc --- /dev/null +++ b/.gitignore @@ -0,0 +1,373 @@ +## 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 +/UploadFile/Check +/TrialData/281bdb09-f792-4faa-de18-08da0e042672/50d0cf71-ee4d-4e54-af06-490f8be1bff1/72b8ef0b-4adc-465e-fa25-08da122bbd9c/eb53c5e2-6307-42f3-d710-08da122bbde1/Dicom +/TrialData/4067198c-f9c4-47de-705a-08da12df3a17/6dedb49c-874c-4f7c-5c0d-08d947774c19 +/TrialData/2a864970-1832-4cf9-f3ab-08da0cd341a0/01a4d468-caa4-41a2-ab36-c42ba30130ac/3210c828-cf32-4a70-fa40-08da0cd72d16/9a3b5449-ac01-4df1-bf24-08da0cd72d2a/Dicom/d287dde0-b9d1-9b37-28bb-371b6a445cf1 +TrialData +/IRaCIS.Core.Application/IRaCIS.Core.Application.xml + + +IRaCISData +UploadFile diff --git a/IRaCIS.Core.API.sln b/IRaCIS.Core.API.sln new file mode 100644 index 0000000..ca6b0f7 --- /dev/null +++ b/IRaCIS.Core.API.sln @@ -0,0 +1,185 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.API", "IRaCIS.Core.API\IRaCIS.Core.API.csproj", "{F15CE209-6039-46A6-AE7F-E81ADA795F28}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Domain", "IRaCIS.Core.Domain\IRaCIS.Core.Domain.csproj", "{D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Application", "IRaCIS.Core.Application\IRaCIS.Core.Application.csproj", "{037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Domain.Share", "IRaCIS.Core.Domain.Share\IRaCIS.Core.Domain.Share.csproj", "{7CBC76F5-3817-46B7-8D9D-79253A89B578}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Test", "IRaCIS.Core.Test\IRaCIS.Core.Test.csproj", "{3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infra.EFCore", "IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj", "{6D8115E5-84D6-424B-8F8D-0C2D40347A8C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure", "IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj", "{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EI_Image_Viewer_Installer", "Start\EI_Image_Viewer_Installer.csproj", "{D96F4B52-359C-43C9-8110-BAD1437F9280}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Install", "Install\Install.csproj", "{F5820DF0-DE23-4F4A-8D49-7E22F67B784D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnInstall", "UnInstall\UnInstall.csproj", "{60B9AC72-5744-4517-93A5-A1ECD573A529}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EI_Image_Viewer_Activation", "EI_Image_Viewer_Activation\EI_Image_Viewer_Activation.csproj", "{1D8AC441-D578-4B38-BCEE-686BD5D59E5A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EI_TestProject", "EI_TestProject\EI_TestProject.csproj", "{47F99CA7-E55B-4A0E-A511-7EDF34C57A20}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Debug|x64.ActiveCfg = Debug|x64 + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Debug|x64.Build.0 = Debug|x64 + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Debug|x86.ActiveCfg = Debug|Any CPU + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Debug|x86.Build.0 = Debug|Any CPU + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Release|Any CPU.Build.0 = Release|Any CPU + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Release|x64.ActiveCfg = Release|x64 + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Release|x64.Build.0 = Release|x64 + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Release|x86.ActiveCfg = Release|Any CPU + {F15CE209-6039-46A6-AE7F-E81ADA795F28}.Release|x86.Build.0 = Release|Any CPU + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Debug|x64.ActiveCfg = Debug|x64 + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Debug|x64.Build.0 = Debug|x64 + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Debug|x86.ActiveCfg = Debug|Any CPU + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Debug|x86.Build.0 = Debug|Any CPU + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Release|Any CPU.Build.0 = Release|Any CPU + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Release|x64.ActiveCfg = Release|x64 + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Release|x64.Build.0 = Release|x64 + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Release|x86.ActiveCfg = Release|Any CPU + {D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Release|x86.Build.0 = Release|Any CPU + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Debug|x64.ActiveCfg = Debug|x64 + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Debug|x64.Build.0 = Debug|x64 + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Debug|x86.ActiveCfg = Debug|Any CPU + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Debug|x86.Build.0 = Debug|Any CPU + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Release|Any CPU.Build.0 = Release|Any CPU + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Release|x64.ActiveCfg = Release|x64 + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Release|x64.Build.0 = Release|x64 + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Release|x86.ActiveCfg = Release|Any CPU + {037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Release|x86.Build.0 = Release|Any CPU + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Debug|x64.ActiveCfg = Debug|x64 + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Debug|x64.Build.0 = Debug|x64 + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Debug|x86.ActiveCfg = Debug|Any CPU + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Debug|x86.Build.0 = Debug|Any CPU + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Release|Any CPU.Build.0 = Release|Any CPU + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Release|x64.ActiveCfg = Release|x64 + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Release|x64.Build.0 = Release|x64 + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Release|x86.ActiveCfg = Release|Any CPU + {7CBC76F5-3817-46B7-8D9D-79253A89B578}.Release|x86.Build.0 = Release|Any CPU + {3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}.Debug|x64.ActiveCfg = Debug|x64 + {3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}.Debug|x86.ActiveCfg = Debug|Any CPU + {3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}.Release|x64.ActiveCfg = Release|x64 + {3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}.Release|x86.ActiveCfg = Release|Any CPU + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Debug|x64.ActiveCfg = Debug|x64 + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Debug|x64.Build.0 = Debug|x64 + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Debug|x86.ActiveCfg = Debug|Any CPU + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Debug|x86.Build.0 = Debug|Any CPU + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Release|Any CPU.Build.0 = Release|Any CPU + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Release|x64.ActiveCfg = Release|x64 + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Release|x64.Build.0 = Release|x64 + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Release|x86.ActiveCfg = Release|Any CPU + {6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Release|x86.Build.0 = Release|Any CPU + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|x64.ActiveCfg = Debug|x64 + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|x64.Build.0 = Debug|x64 + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|x86.ActiveCfg = Debug|Any CPU + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|x86.Build.0 = Debug|Any CPU + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.Build.0 = Release|Any CPU + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|x64.ActiveCfg = Release|x64 + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|x64.Build.0 = Release|x64 + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|x86.ActiveCfg = Release|Any CPU + {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|x86.Build.0 = Release|Any CPU + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Debug|x64.ActiveCfg = Debug|x64 + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Debug|x64.Build.0 = Debug|x64 + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Debug|x86.ActiveCfg = Debug|Any CPU + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Debug|x86.Build.0 = Debug|Any CPU + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Release|Any CPU.Build.0 = Release|Any CPU + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Release|x64.ActiveCfg = Release|x64 + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Release|x64.Build.0 = Release|x64 + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Release|x86.ActiveCfg = Release|Any CPU + {D96F4B52-359C-43C9-8110-BAD1437F9280}.Release|x86.Build.0 = Release|Any CPU + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Debug|x64.ActiveCfg = Debug|x64 + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Debug|x64.Build.0 = Debug|x64 + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Debug|x86.ActiveCfg = Debug|Any CPU + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Debug|x86.Build.0 = Debug|Any CPU + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Release|Any CPU.Build.0 = Release|Any CPU + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Release|x64.ActiveCfg = Release|x64 + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Release|x64.Build.0 = Release|x64 + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Release|x86.ActiveCfg = Release|Any CPU + {F5820DF0-DE23-4F4A-8D49-7E22F67B784D}.Release|x86.Build.0 = Release|Any CPU + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Debug|x64.ActiveCfg = Debug|x64 + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Debug|x64.Build.0 = Debug|x64 + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Debug|x86.ActiveCfg = Debug|Any CPU + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Debug|x86.Build.0 = Debug|Any CPU + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Release|Any CPU.Build.0 = Release|Any CPU + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Release|x64.ActiveCfg = Release|x64 + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Release|x64.Build.0 = Release|x64 + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Release|x86.ActiveCfg = Release|Any CPU + {60B9AC72-5744-4517-93A5-A1ECD573A529}.Release|x86.Build.0 = Release|Any CPU + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Debug|x64.ActiveCfg = Debug|x64 + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Debug|x64.Build.0 = Debug|x64 + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Debug|x86.ActiveCfg = Debug|Any CPU + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Debug|x86.Build.0 = Debug|Any CPU + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Release|Any CPU.Build.0 = Release|Any CPU + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Release|x64.ActiveCfg = Release|x64 + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Release|x64.Build.0 = Release|x64 + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Release|x86.ActiveCfg = Release|Any CPU + {1D8AC441-D578-4B38-BCEE-686BD5D59E5A}.Release|x86.Build.0 = Release|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Debug|x64.ActiveCfg = Debug|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Debug|x64.Build.0 = Debug|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Debug|x86.ActiveCfg = Debug|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Debug|x86.Build.0 = Debug|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Release|Any CPU.Build.0 = Release|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Release|x64.ActiveCfg = Release|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Release|x64.Build.0 = Release|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Release|x86.ActiveCfg = Release|Any CPU + {47F99CA7-E55B-4A0E-A511-7EDF34C57A20}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BCC2EB19-3914-489B-B1D7-B7303E0218A3} + EndGlobalSection +EndGlobal diff --git a/IRaCIS.Core.API/.config/dotnet-tools.json b/IRaCIS.Core.API/.config/dotnet-tools.json new file mode 100644 index 0000000..06b8682 --- /dev/null +++ b/IRaCIS.Core.API/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "5.0.9", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.API/.preview.jpg b/IRaCIS.Core.API/.preview.jpg new file mode 100644 index 0000000..e69de29 diff --git a/IRaCIS.Core.API/2Program.cs b/IRaCIS.Core.API/2Program.cs new file mode 100644 index 0000000..e7649cb --- /dev/null +++ b/IRaCIS.Core.API/2Program.cs @@ -0,0 +1,231 @@ +//using Autofac; +//using Autofac.Extensions.DependencyInjection; +//using IRaCIS.Core.API; +//using IRaCIS.Core.Application.Filter; +//using IRaCIS.Core.Application.MediatR.Handlers; +//using LogDashboard; +//using MassTransit; +//using MassTransit.NewIdProviders; +//using MediatR; +//using Microsoft.AspNetCore.Builder; +//using Microsoft.AspNetCore.Http.Features; +//using Microsoft.AspNetCore.HttpOverrides; +//using Microsoft.AspNetCore.SignalR; +//using Microsoft.Extensions.Configuration; +//using Microsoft.Extensions.DependencyInjection; +//using Microsoft.Extensions.Hosting; +//using Serilog; +//using System; + +//var builder = WebApplication.CreateBuilder(args); + + +// //ļΪ׼ urlȡֵ(дݲļ˾ͲҪݻ) +// var config = new ConfigurationBuilder() +// .AddEnvironmentVariables() +// .Build(); + +// var enviromentName = config["ASPNETCORE_ENVIRONMENT"]; + +// if (string.IsNullOrWhiteSpace(enviromentName)) +// { + +// var index = Array.IndexOf(args, "--env"); +// enviromentName = index > -1 +// ? args[index + 1] +// : "Development"; +// } + + +// NewId.SetProcessIdProvider(new CurrentProcessIdProvider()); + + + + +//builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()) +// .ConfigureContainer(containerBuilder => +// { +// containerBuilder.RegisterModule(); +// }) +// .UseWindowsService().UseSerilog(); + + + +//// Add services to the container. + +////ػ +//builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources"); + +//// 쳣ͳһ֤JsonлáַͳһTrim() +//builder.Services.AddControllers(options => +//{ +// //options.Filters.Add(); +// options.Filters.Add(); +// options.Filters.Add(); +// //options.Filters.Add(); + +// //if (_configuration.GetSection("BasicSystemConfig").GetValue("OpenLoginLimit")) +// //{ +// // options.Filters.Add(); + +// //} + + +//}).AddNewtonsoftJsonSetup(); // NewtonsoftJson л + +////̬WebApi + UnifiedApiResultFilter ʡ +//builder.Services.AddDynamicWebApiSetup(); +////AutoMapper +//builder.Services.AddAutoMapperSetup(); +////EF ORM QueryWithNoLock +//builder.Services.AddEFSetup(builder.Configuration); +////Http Ӧѹ +//builder.Services.AddResponseCompressionSetup(); +////Swagger Api ĵ +//builder.Services.AddSwaggerSetup(); +////JWT Token ֤ +//builder.Services.AddJWTAuthSetup(builder.Configuration); +//// MediatR Ϣ ¼ ӳ עhandlerӦϵ +//builder.Services.AddMediatR(typeof(ConsistencyVerificationHandler).Assembly); +//// EasyCaching +//builder.Services.AddEasyCachingSetup(); + +////services.AddDistributedMemoryCache(); + +////// hangfire ʱ н棬Ѻ~ +//builder.Services.AddhangfireSetup(builder.Configuration); +////// QuartZ ʱ ʹhangfire ʱãҪԴ򿪣Ѿ +////builder.Services.AddQuartZSetup(_configuration); + +//// ϴļ +////services.AddStaticFileAuthorizationSetup(); + + +//////HttpReports ʱ +////services.AddHttpReports().AddHttpTransport(); +////Serilog ־ӻ LogDashboard־ +//builder.Services.AddLogDashboardSetup(); +////ϴ +//builder.Services.Configure(options => +//{ +// options.MultipartBodyLengthLimit = int.MaxValue; +// options.ValueCountLimit = int.MaxValue; +// options.ValueLengthLimit = int.MaxValue; +//}); +////IP ð ߺ +////services.AddIpPolicyRateLimitSetup(_configuration); +////û Ȩ +//builder.Services.AddAuthorizationPolicySetup(builder.Configuration); + +//builder.Services.AddJsonConfigSetup(builder.Configuration); + +////תͷ ȡʵIP +//builder.Services.Configure(options => +//{ +// options.ForwardedHeaders = +// ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; +//}); +////DicomӰȾͼƬ ƽ̨ +//builder.Services.AddDicomSetup(); + +//// ʵʱӦ +//builder.Services.AddSignalR(); + + +//builder.Services.AddSingleton(); + +//builder.Services.AddControllers(); +//// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +////builder.Services.AddEndpointsApiExplorer(); +////builder.Services.AddSwaggerGen(); + +////SerilogExtension.AddSerilogSetup(enviromentName, builder.Host.confi); + + +//var app = builder.Build(); + +//// Configure the HTTP request pipeline. + +////ػ +//app.UseLocalization(); + +//app.UseForwardedHeaders(); + + +////Ҫ token ʵľ̬ļ wwwroot css, JavaScript, and images don't require authentication. +//app.UseStaticFiles(); + +//app.UseIRacisHostStaticFileStore(app.Environment); + +////LogDashboard +//app.UseLogDashboard("/LogDashboard"); + +////hangfire +////app.UseHangfireConfig(app.Environment); + +//////ʱ +////app.UseHttpReports(); + +////// м +////app.UseIpRateLimiting(); + +////Ӧѹ +//app.UseResponseCompression(); + +//if (app.Environment.IsDevelopment()) +//{ +// app.UseDeveloperExceptionPage(); +//} +//else +//{ + +// //app.UseHsts(); +//} + +//SwaggerSetup.Configure(app, app.Environment); + +//Console.WriteLine("ǰ " + builder.Environment.EnvironmentName); + +////app.UseMiddleware(); + +//// 쳣 404 +//app.UseStatusCodePagesWithReExecute("/Error/{0}"); + + + + +//////serilog ¼ûϢ +//app.UseSerilogConfig(app.Environment); + +//app.UseRouting(); + +//app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); + + +//app.UseAuthentication(); +////app.UseJwtBearerQueryString(); +//app.UseAuthorization(); + +//////ļŷ Token +//////app.UseIRacisHostStaticFileStore(env); + + + +////app.UseEndpoints(endpoints => +////{ + + +//// endpoints.MapControllers(); + +//// endpoints.MapHub("/UploadHub")/*.RequireCors(t=>t.WithOrigins(new string[] {"null"}).AllowAnyMethod().AllowAnyHeader().AllowCredentials())*/; +////}); + + + +//app.MapControllers(); +//app.MapHub("/UploadHub")/*.RequireCors(t=>t.WithOrigins(new string[] {"null"}).AllowAnyMethod().AllowAnyHeader().AllowCredentials())*/; + + +//app.Run(); + +//ͬ diff --git a/IRaCIS.Core.API/Controllers/ErrorController.cs b/IRaCIS.Core.API/Controllers/ErrorController.cs new file mode 100644 index 0000000..c273d3e --- /dev/null +++ b/IRaCIS.Core.API/Controllers/ErrorController.cs @@ -0,0 +1,39 @@ +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; + +namespace EasyCaching.Demo.Interceptors.Controllers +{ + + [NonDynamicWebApi] + public class ErrorController : ControllerBase + { + /// + /// 主要处理 前端404等错误 全局业务异常已统一处理了,非业务错误会来到这里 + /// + /// + /// + [Route("error/{code:int}")] + [HttpGet] + public IResponseOutput Error(int code) + { + + if (code < 500) + { + //LogDashboard 要求返回码必须是401不能覆盖,否则 认证有问题 + if (code == 401) + { + ControllerContext.HttpContext.Response.StatusCode = 401; + } + + return ResponseOutput.NotOk($"Client error, actual request error status code({code})"); + } + else + { + + return ResponseOutput.NotOk($"Server error , actual request error status code({code})"); + } + + } + } +} diff --git a/IRaCIS.Core.API/Controllers/ExtraController.cs b/IRaCIS.Core.API/Controllers/ExtraController.cs new file mode 100644 index 0000000..dc5fcea --- /dev/null +++ b/IRaCIS.Core.API/Controllers/ExtraController.cs @@ -0,0 +1,278 @@ +using System; +using System.Net.Http; +using EasyCaching.Core; +using gRPC.ZHiZHUN.AuthServer.protos; +using Grpc.Net.Client; +using Grpc.Net.Client.Configuration; +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Auth; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; +using System.Text; +using Microsoft.AspNetCore.Http; +using IRaCIS.Core.Application.Interfaces; +using System.Threading.Tasks; +using IRaCIS.Application.Services; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure; +using System.Linq; +using Microsoft.Extensions.Logging; + +namespace IRaCIS.Api.Controllers +{ + /// + /// 医生基本信息 、工作信息 专业信息、审核状态 + /// + [ApiController, ApiExplorerSettings(GroupName = "Reviewer")] + public class ExtraController : ControllerBase + { + + + /// + /// 获取医生详情 + /// + /// + /// + /// + /// + /// + /// + /// + /// + [HttpGet, Route("doctor/getDetail/{doctorId:guid}")] + + public async Task> GetDoctorDetail([FromServices] IAttachmentService attachmentService, [FromServices] IDoctorService _doctorService, + [FromServices] IEducationService _educationService, [FromServices] ITrialExperienceService _trialExperienceService, + [FromServices] IResearchPublicationService _researchPublicationService, [FromServices] IVacationService _vacationService, Guid doctorId) + { + var education = await _educationService.GetEducation(doctorId); + + var sowList = _doctorService.GetDoctorSowList(doctorId); + var ackSowList = _doctorService.GetDoctorAckSowList(doctorId); + + var doctorDetail = new DoctorDetailDTO + { + AuditView =await _doctorService.GetAuditState(doctorId), + BasicInfoView = await _doctorService.GetBasicInfo(doctorId), + EmploymentView = await _doctorService.GetEmploymentInfo(doctorId), + AttachmentList = await attachmentService.GetAttachments(doctorId), + + EducationList = education.EducationList, + PostgraduateList = education.PostgraduateList, + + TrialExperienceView = await _trialExperienceService.GetTrialExperience(doctorId), + ResearchPublicationView = await _researchPublicationService.GetResearchPublication(doctorId), + + SpecialtyView =await _doctorService.GetSpecialtyInfo(doctorId), + InHoliday = (await _vacationService.OnVacation(doctorId)).IsSuccess, + IntoGroupInfo = _doctorService.GetDoctorIntoGroupInfo(doctorId), + SowList = sowList, + AckSowList = ackSowList + }; + + return ResponseOutput.Ok(doctorDetail); + } + + + + + + + + /// 系统用户登录接口[New] + [HttpPost, Route("user/login")] + [AllowAnonymous] + public async Task> Login(UserLoginDTO loginUser, [FromServices] IEasyCachingProvider provider, [FromServices] IUserService _userService, + [FromServices] ITokenService _tokenService, [FromServices] IConfiguration configuration) + { + + var returnModel = await _userService.Login(loginUser.UserName, loginUser.Password); + + if (returnModel.IsSuccess) + { + #region GRPC 调用鉴权中心,因为服务器IIS问题 http/2 故而没法使用 + + ////重试策略 + //var defaultMethodConfig = new MethodConfig + //{ + // Names = { MethodName.Default }, + // RetryPolicy = new RetryPolicy + // { + // MaxAttempts = 3, + // InitialBackoff = TimeSpan.FromSeconds(1), + // MaxBackoff = TimeSpan.FromSeconds(5), + // BackoffMultiplier = 1.5, + // RetryableStatusCodes = { Grpc.Core.StatusCode.Unavailable } + // } + //}; + + //#region unable to trust the certificate then the gRPC client can be configured to ignore the invalid certificate + + //var httpHandler = new HttpClientHandler(); + //// Return `true` to allow certificates that are untrusted/invalid + //httpHandler.ServerCertificateCustomValidationCallback = + // HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; + + + //////这一句是让grpc支持本地 http 如果本地访问部署在服务器上,那么是访问不成功的 + //AppContext.SetSwitch( + // "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); + + //#endregion + + + + //var grpcAdress = configuration.GetValue("GrpcAddress"); + ////var grpcAdress = "http://localhost:7200"; + + //var channel = GrpcChannel.ForAddress(grpcAdress, new GrpcChannelOptions + //{ + // HttpHandler = httpHandler, + // ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } } + + //}); + ////var channel = GrpcChannel.ForAddress(grpcAdress); + //var grpcClient = new TokenGrpcService.TokenGrpcServiceClient(channel); + + //var userInfo = returnModel.Data.BasicInfo; + + //var tokenResponse = grpcClient.GetUserToken(new GetTokenReuqest() + //{ + // Id = userInfo.Id.ToString(), + // ReviewerCode = userInfo.ReviewerCode, + // IsAdmin = userInfo.IsAdmin, + // RealName = userInfo.RealName, + // UserTypeEnumInt = (int)userInfo.UserTypeEnum, + // UserTypeShortName = userInfo.UserTypeShortName, + // UserName = userInfo.UserName + //}); + + //returnModel.Data.JWTStr = tokenResponse.Token; + + #endregion + + returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo)); + + // 创建一个 CookieOptions 对象,用于设置 Cookie 的属性 + var option = new CookieOptions + { + Expires = DateTime.Now.AddMonths(1), // 设置过期时间为 30 分钟之后 + HttpOnly = false, // 确保 cookie 只能通过 HTTP 访问 + SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, // 设置 SameSite 属性 + Secure = false // 确保 cookie 只能通过 HTTPS 访问 + }; + + HttpContext.Response.Cookies.Append("access_token", returnModel.Data.JWTStr, option); + + } + + var userId = returnModel.Data.BasicInfo.Id.ToString(); + //provider.Set(userId, userId, TimeSpan.FromMinutes(AppSettings.LoginExpiredTimeSpan)); + + await provider.SetAsync(userId.ToString(), returnModel.Data.JWTStr, TimeSpan.FromDays(7)); + return returnModel; + } + + + + [HttpGet, Route("imageShare/ShareImage")] + [AllowAnonymous] + public IResponseOutput ShareImage([FromServices] ITokenService _tokenService) + { + var token = _tokenService.GetToken(IRaCISClaims.Create(new UserBasicInfo() + { + Id = Guid.Empty, + IsReviewer = false, + IsAdmin = false, + RealName = "Share001", + UserName = "Share001", + Sex = 0, + //UserType = "ShareType", + UserTypeEnum = UserTypeEnum.ShareImage, + Code = "ShareCode001", + })); + return ResponseOutput.Ok("/showdicom?studyId=f7b67793-8155-0223-2f15-118f2642efb8&type=Share&token=" + token); + } + + + + + + [HttpGet("User/UserRedirect")] + [AllowAnonymous] + public async Task UserRedirect([FromServices] IRepository _userRepository, string url ,[FromServices]ILogger _logger) + { + + var decodeUrl = System.Web.HttpUtility.UrlDecode(url); + + var userId = decodeUrl.Substring(decodeUrl.IndexOf("UserId=") + "UserId=".Length , 36) ; + + var token = decodeUrl.Substring(decodeUrl.IndexOf("access_token=") + "access_token=".Length); + + var domainStrList = decodeUrl.Split("/").ToList().Take(3).ToList(); + + var errorUrl = domainStrList[0]+"//"+ domainStrList[2]+ "/error"; + + + if (!await _userRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd)) + { + decodeUrl = errorUrl+ $"?ErrorMessage={System.Web.HttpUtility.UrlEncode("您的初始化链接已过期")} "; + } + + return Redirect(decodeUrl); + } + + + + + + [HttpGet, Route("ip")] + [AllowAnonymous] + public IResponseOutput Get([FromServices] IHttpContextAccessor _context/*, [FromServices] IUserService _userService*/) + { + + StringBuilder sb = new StringBuilder(); + sb.AppendLine($"RemoteIpAddress:{_context.HttpContext.Connection.RemoteIpAddress}"); + + if (Request.Headers.ContainsKey("X-Real-IP")) + { + sb.AppendLine($"X-Real-IP:{Request.Headers["X-Real-IP"].ToString()}"); + } + + if (Request.Headers.ContainsKey("X-Forwarded-For")) + { + sb.AppendLine($"X-Forwarded-For:{Request.Headers["X-Forwarded-For"].ToString()}"); + } + return ResponseOutput.Ok(sb.ToString()); + } + + + [HttpGet, Route("ip2")] + [AllowAnonymous] + public IResponseOutput Get2([FromServices] IHttpContextAccessor _context, [FromServices] IRepository _userService) + { + + StringBuilder sb = new StringBuilder(); + sb.AppendLine($"RemoteIpAddress:{_context.HttpContext.Connection.RemoteIpAddress}"); + + if (Request.Headers.ContainsKey("X-Real-IP")) + { + sb.AppendLine($"X-Real-IP:{Request.Headers["X-Real-IP"].ToString()}"); + } + + if (Request.Headers.ContainsKey("X-Forwarded-For")) + { + sb.AppendLine($"X-Forwarded-For:{Request.Headers["X-Forwarded-For"].ToString()}"); + } + return ResponseOutput.Ok(sb.ToString()); + } + + } +} diff --git a/IRaCIS.Core.API/Controllers/FinancialChangeController.cs b/IRaCIS.Core.API/Controllers/FinancialChangeController.cs new file mode 100644 index 0000000..cea063c --- /dev/null +++ b/IRaCIS.Core.API/Controllers/FinancialChangeController.cs @@ -0,0 +1,323 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Authorization; +using System.Threading.Tasks; +using IRaCIS.Application.Services; +using IRaCIS.Core.Application.Service.Inspection.DTO; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Application.Auth; + +namespace IRaCIS.Core.API.Controllers.Special +{ + //谨慎修改 涉及到财务模块 + + [ApiController, Authorize, ApiExplorerSettings(GroupName = "Financial")] + public class FinancialChangeController : ControllerBase + { + private readonly ITrialService _trialService; + private readonly ICalculateService _calculateService; + + public FinancialChangeController(ITrialService trialService, ICalculateService calculateService + ) + { + _trialService = trialService; + _calculateService = calculateService; + } + + + ////[TrialAudit(AuditType.TrialAudit, AuditOptType.AddOrUpdateTrial)] + + ///// 添加实验项目-返回新增Id[AUTH] + ///// 新记录Id + //[HttpPost, Route("Inspection/trial/addOrUpdateTrial")] + //[UnitOfWork] + + //public async Task AddOrUpdateTrialInspection(DataInspectionDto opt) + //{ + // var fun =await AddOrUpdateTrial(opt.Data); + + // return fun; + //} + + + /// 添加实验项目-返回新增Id[AUTH] + /// + /// 新记录Id + [HttpPost, Route("trial/addOrUpdateTrial")] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AddOrUpdateTrial", "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + public async Task> AddOrUpdateTrial(TrialCommand param, [FromServices] ITrialConfigService _ITrialConfigService) + { + var userId = Guid.Parse(User.FindFirst("id").Value); + var result = await _trialService.AddOrUpdateTrial(param, _ITrialConfigService); + + if (_trialService.TrialExpeditedChange) + { + var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(param.Id.Value); + var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty); + + calcList.ForEach(t => + { + if (needCalReviewerIds.Contains(t.DoctorId)) + { + _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO() + { + NeedCalculateReviewers = new List() + { + t.DoctorId + }, + CalculateMonth = DateTime.Parse(t.YearMonth) + }, User.FindFirst("id").Value); + + } + }); + } + + return result; + } + + + /// + /// 添加或更新工作量[AUTH] + /// + /// + /// + /// + + [HttpPost, Route("doctorWorkload/workLoadAddOrUpdate")] + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task WorkLoadAddOrUpdate([FromServices] IDoctorWorkloadService _trialWorkloadService, WorkloadCommand workLoadAddOrUpdateModel) + { + var userId = Guid.Parse(User.FindFirst("id").Value); + var result = await _trialWorkloadService.AddOrUpdateWorkload(workLoadAddOrUpdateModel, userId); + if (result.IsSuccess && workLoadAddOrUpdateModel.DataFrom == 2) + { + await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO() + { + NeedCalculateReviewers = new List() + { + workLoadAddOrUpdateModel.DoctorId + }, + CalculateMonth = workLoadAddOrUpdateModel.WorkTime + }, User.FindFirst("id").Value); + } + return result; + } + + + + [HttpDelete, Route("doctorWorkload/deleteWorkLoad/{id:guid}/{trialId:guid}")] + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task DeleteWorkLoad([FromServices] IDoctorWorkloadService _trialWorkloadService, Guid id) + { + //先判断该工作量的费用是否被锁定,如果被锁定,则不能删除 + var workload = await _trialWorkloadService.GetWorkloadDetailById(id); + var yearMonth = workload.WorkTime.ToString("yyyy-MM"); + var isLock = await _calculateService.IsLock(workload.DoctorId, yearMonth); + + if (isLock) + { + return ResponseOutput.NotOk("Expenses have been settled and workload can not be reset."); + } + + var deleteResult = await _trialWorkloadService.DeleteWorkload(id); + if (workload.DataFrom == (int)Domain.Share.WorkLoadFromStatus.FinalConfirm) + { + await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO() + { + NeedCalculateReviewers = new List() + { + workload.DoctorId + }, + CalculateMonth = workload.WorkTime + }, User.FindFirst("id").Value); + } + return deleteResult; + } + + + /// + /// 添加或更新汇率(会触发没有对锁定的费用计算) + /// + + [HttpPost, Route("exchangeRate/addOrUpdateExchangeRate")] + public async Task AddOrUpdateExchangeRate([FromServices] IExchangeRateService _exchangeRateService, [FromServices] IPaymentAdjustmentService _costAdjustmentService, ExchangeRateCommand addOrUpdateModel) + { + var result = await _exchangeRateService.AddOrUpdateExchangeRate(addOrUpdateModel); + var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, addOrUpdateModel.YearMonth); + foreach (var item in calcList) + { + if (item != null) + { + await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO() + { + NeedCalculateReviewers = new List() + { + item.DoctorId + }, + CalculateMonth = DateTime.Parse(item.YearMonth) + }, User.FindFirst("id").Value); + } + } + await _costAdjustmentService.CalculateCNY(addOrUpdateModel.YearMonth, addOrUpdateModel.Rate); + return result; + } + + + /// + /// 添加或更新 职称单价[AUTH] + /// + + [HttpPost, Route("rankPrice/addOrUpdateRankPrice")] + public async Task AddOrUpdateRankPrice([FromServices] IReviewerPayInfoService _reviewerPayInfoService, [FromServices] IRankPriceService _rankPriceService, RankPriceCommand addOrUpdateModel) + { + if (addOrUpdateModel.Id != Guid.Empty && addOrUpdateModel.Id != null) + { + var needCalReviewerIds =await _reviewerPayInfoService.GetReviewerIdByRankId(Guid.Parse(addOrUpdateModel.Id.ToString())); + var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty); + + foreach (var item in calcList) + { + if (item != null && needCalReviewerIds.Contains(item.DoctorId)) + { + await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO() + { + NeedCalculateReviewers = new List() + { + item.DoctorId + }, + CalculateMonth = DateTime.Parse(item.YearMonth) + }, User.FindFirst("id").Value); + } + } + } + var userId = Guid.Parse(User.FindFirst("id").Value); + return await _rankPriceService.AddOrUpdateRankPrice(addOrUpdateModel, userId); + } + + + + /// + /// 添加或更新(替换)医生支付展信息[AUTH] + /// + + [HttpPost, Route("reviewerPayInfo/addOrUpdateReviewerPayInfo")] + public async Task AddOrUpdateReviewerPayInfo([FromServices] IReviewerPayInfoService _doctorPayInfoService, ReviewerPayInfoCommand addOrUpdateModel) + { + var userId = Guid.Parse(User.FindFirst("id").Value); + var result =await _doctorPayInfoService.AddOrUpdateReviewerPayInfo(addOrUpdateModel, userId); + var calcList = await _calculateService.GetNeedCalculateReviewerList(addOrUpdateModel.DoctorId, string.Empty); + foreach (var item in calcList) + { + if (item != null) + { + await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO() + { + NeedCalculateReviewers = new List() + { + item.DoctorId + }, + CalculateMonth = DateTime.Parse(item.YearMonth) + }, User.FindFirst("id").Value); + } + } + return result; + } + + + + /// + /// 保存(替换)项目支付价格信息(会触发没有被锁定的费用计算)[AUTH] + /// + + [HttpPost, Route("trialPaymentPrice/addOrUpdateTrialPaymentPrice")] + public async Task AddOrUpdateTrialPaymentPrice([FromServices] ITrialPaymentPriceService _trialPaymentPriceService, TrialPaymentPriceCommand addOrUpdateModel) + { + var userId = Guid.Parse(User.FindFirst("id").Value); + var result =await _trialPaymentPriceService.AddOrUpdateTrialPaymentPrice(addOrUpdateModel); + var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(addOrUpdateModel.TrialId); + var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty); + + foreach (var item in calcList) + { + if (item != null && needCalReviewerIds.Contains(item.DoctorId)) + { + await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO() + { + NeedCalculateReviewers = new List() + { + item.DoctorId + }, + CalculateMonth = DateTime.Parse(item.YearMonth) + }, User.FindFirst("id").Value); + } + } + return result; + } + + /// + /// 批量更新奖励费用[AUTH] + /// + + [HttpPost, Route("volumeReward/addOrUpdatevolumeRewardPriceList")] + public async Task AddOrUpdateAwardPriceList([FromServices] IVolumeRewardService _volumeRewardService, IEnumerable addOrUpdateModel) + { + + var result =await _volumeRewardService.AddOrUpdateVolumeRewardPriceList(addOrUpdateModel); + + var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty); + foreach (var item in calcList) + { + if (item != null) + { + await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO() + { + NeedCalculateReviewers = new List() + { + item.DoctorId + }, + CalculateMonth = DateTime.Parse(item.YearMonth) + }, User.FindFirst("id").Value); + } + } + return result; + } + + + + /// + /// 计算医生月度费用,并将计算的结果存入费用表 + /// + [HttpPost, Route("financial/calculateMonthlyPayment")] + public async Task CalculateMonthlyPayment(CalculateDoctorAndMonthDTO param) + { + if (!ModelState.IsValid) + { + return ResponseOutput.NotOk("Invalid parameter."); + } + return await _calculateService.CalculateMonthlyPayment(param, User.FindFirst("id").Value); + } + + /// + /// Financials /Monthly Payment 列表查询接口 + /// + [HttpPost, Route("financial/getMonthlyPaymentList")] + public async Task> GetMonthlyPaymentList([FromServices] IPaymentService _paymentService, [FromServices] IExchangeRateService _exchangeRateService, MonthlyPaymentQueryDTO queryParam) + { + return ResponseOutput.Ok(new PaymentDTO + { + CostList = await _paymentService.GetMonthlyPaymentList(queryParam), + ExchangeRate = await _exchangeRateService.GetExchangeRateByMonth(queryParam.StatisticsDate.ToString("yyyy-MM")) + }); + } + + + } +} diff --git a/IRaCIS.Core.API/Controllers/InspectionController.cs b/IRaCIS.Core.API/Controllers/InspectionController.cs new file mode 100644 index 0000000..e548d07 --- /dev/null +++ b/IRaCIS.Core.API/Controllers/InspectionController.cs @@ -0,0 +1,460 @@ + +using System.Threading.Tasks; +using AutoMapper; + +using IRaCIS.Application.Interfaces; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Application.Image.QA; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.Service.Inspection.DTO; +using IRaCIS.Core.Application.Service.Inspection.Interface; +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + + +namespace IRaCIS.Core.API.Controllers +{ + + [ApiController, ApiExplorerSettings(GroupName = "Reviewer")] + [UnitOfWork] + public class InspectionController : ControllerBase + { + private readonly IRepository _repository; + private readonly IMapper _mapper; + private readonly IUserInfo _userInfo; + private readonly ITrialDocumentService _trialDocumentService; + private readonly IQCListService _qCListService; + private readonly IReadingImageTaskService _iReadingImageTaskService; + private readonly IHttpContextAccessor _httpContext; + private readonly ITrialConfigService _trialConfigService; + private readonly INoneDicomStudyService _noneDicomStudyService; + private readonly ISubjectService _subjectService; + private readonly IReadingClinicalDataService _readingClinicalDataService; + private readonly ISubjectVisitService _subjectVisitService; + private readonly IQCOperationService _qCOperationService; + private readonly IClinicalDataService _clinicalDataService; + private readonly IVisitPlanService _visitPlanService; + + private readonly IInspectionService _inspectionService; + private readonly IReadingMedicalReviewService _readingMedicalReviewService; + private readonly IReadingMedicineQuestionService _readingMedicineQuestionService; + private readonly IRepository _dataInspectionRepository; + private delegate Task executionFun(dynamic data); + + public InspectionController(IRepository repository, + IRepository _repositoryDataInspection, + IMapper mapper, IUserInfo userInfo, + ITrialDocumentService trialDocumentService, + IRepository dataInspectionRepository, + IQCListService _qCListService, + IReadingImageTaskService _iReadingImageTaskService, + IHttpContextAccessor httpContext, + IInspectionService sinspectionService, + IReadingMedicalReviewService readingMedicalReviewService, + IReadingMedicineQuestionService readingMedicineQuestionService, + ITrialConfigService _trialConfigService, + INoneDicomStudyService noneDicomStudyService, + ISubjectService _subjectService, + IReadingClinicalDataService _readingClinicalDataService, + ISubjectVisitService subjectVisitService, + IQCOperationService qCOperationService, + IClinicalDataService clinicalDataService, + IVisitPlanService visitPlanService + ) + { + this._repository = repository; + this._mapper = mapper; + this._userInfo = userInfo; + this._inspectionService = sinspectionService; + this._readingMedicalReviewService = readingMedicalReviewService; + this._readingMedicineQuestionService = readingMedicineQuestionService; + this._trialDocumentService = trialDocumentService; + this._qCListService = _qCListService; + this._iReadingImageTaskService = _iReadingImageTaskService; + this._httpContext = httpContext; + this._trialConfigService = _trialConfigService; + this._noneDicomStudyService = noneDicomStudyService; + this._subjectService = _subjectService; + this._readingClinicalDataService = _readingClinicalDataService; + this._subjectVisitService = subjectVisitService; + this._qCOperationService = qCOperationService; + this._clinicalDataService = clinicalDataService; + this._visitPlanService = visitPlanService; + this._dataInspectionRepository = dataInspectionRepository; + } + + + + + #region 获取稽查数据 + /// + /// 获取稽查数据 + /// + /// + [HttpPost, Route("Inspection/GetInspectionList")] + public async Task> GetInspectionList(GetDataInspectionDto dto) + { + return await _inspectionService.GetInspectionList(dto); + } + #endregion + + /// + /// 提交肿瘤学阅片任务 + /// + /// + /// + [HttpPost, Route("Inspection/ReadingImageTask/SubmitOncologyReadingInfo")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + [UnitOfWork] + public async Task SetOncologyReadingInfo(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _iReadingImageTaskService.SubmitOncologyReadingInfo(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + /// + /// 提交Diocm阅片 + /// + /// + /// + [HttpPost, Route("Inspection/ReadingImageTask/SubmitDicomVisitTask")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + [UnitOfWork] + public async Task SubmitDicomVisitTask(DataInspectionDto opt) + { + + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _iReadingImageTaskService.SubmitDicomVisitTask(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + /// + /// 提交全局阅片任务 + /// + /// + /// + [HttpPost, Route("Inspection/ReadingImageTask/SubmitGlobalReadingInfo")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + [UnitOfWork] + public async Task SubmitGlobalReadingInfo(DataInspectionDto opt) + { + + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _iReadingImageTaskService.SubmitGlobalReadingInfo(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + /// + /// 项目阅片信息签名 + /// + /// + /// + [HttpPost, Route("Inspection/configTrialBasicInfo/TrialReadingInfoSign")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + [UnitOfWork] + public async Task TrialReadingInfoSign(DataInspectionDto opt) + { + + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _trialConfigService.TrialReadingInfoSign(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + /// + /// 医学审核完成 + /// + /// + /// + [HttpPost, Route("Inspection/ReadingMedicalReview/FinishMedicalReview")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + [UnitOfWork] + public async Task FinishMedicalReview(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _readingMedicalReviewService.FinishMedicalReview(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + /// + /// 确认项目医学审核问题 + /// + /// + /// + [HttpPost, Route("Inspection/ReadingMedicineQuestion/ConfirmReadingMedicineQuestion")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + [UnitOfWork] + public async Task ConfirmReadingMedicineQuestion(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _readingMedicineQuestionService.ConfirmReadingMedicineQuestion(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + /// + /// 提交阅片问题 + /// + /// + /// + [HttpPost, Route("Inspection/ReadingImageTask/SubmitVisitTaskQuestions")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + [UnitOfWork] + public async Task SubmitVisitTaskQuestions(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _iReadingImageTaskService.SubmitVisitTaskQuestions(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + /// + /// 提交阅片裁判问题 + /// + /// + /// + [HttpPost, Route("Inspection/ReadingImageTask/SubmitJudgeVisitTaskResult")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + [UnitOfWork] + public async Task SubmitJudgeVisitTaskResult(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _iReadingImageTaskService.SubmitJudgeVisitTaskResult(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + /// + /// 配置 基础逻辑信息并确认 + /// + /// + /// + [HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialBasicInfoConfirm")] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })] + public async Task ConfigTrialBasicInfoConfirm(DataInspectionDto opt) + { + + opt.Data.IsTrialBasicLogicConfirmed = true; + var singid= await _inspectionService.RecordSing(opt.SignInfo); + var result = await _trialConfigService.ConfigTrialBasicInfo(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + + /// + /// 配置流程并确认 + /// + /// + /// + [HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialProcessInfoConfirm")] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })] + public async Task ConfigTrialProcessInfoConfirm(DataInspectionDto opt) + { + opt.Data.IsTrialProcessConfirmed = true; + + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _trialConfigService.ConfigTrialProcessInfo(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + + + + /// + /// 配置加急信息并确认 + /// + /// + /// + [HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialUrgentInfoConfirm")] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })] + public async Task ConfigTrialUrgentInfoConfirm(DataInspectionDto opt) + { + opt.Data.IsTrialUrgentConfirmed = true; + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result= await _trialConfigService.ConfigTrialUrgentInfo(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + /// + /// 签名确认 + /// + /// + [HttpPost, Route("Inspection/configTrialBasicInfo/TrialConfigSignatureConfirm")] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task TrialConfigSignatureConfirm(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _trialConfigService.TrialConfigSignatureConfirm(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + + /// + /// IC RequestToQC 批量提交 + /// + /// + /// + [HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task CRCRequestToQC(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _qCOperationService.CRCRequestToQC(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + /// + /// 设置QC 通过或者不通过 7:QC failed 8:QC passed + /// + [HttpPost, Route("Inspection/QCOperation/QCPassedOrFailed")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task QCPassedOrFailed(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result= await _qCOperationService.QCPassedOrFailed(opt.Data.trialId, opt.Data.subjectVisitId, opt.Data.auditState); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + /// + /// 一致性核查 回退 对话记录不清除 只允许PM回退 + /// + [HttpPost, Route("Inspection/QCOperation/CheckBack")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task CheckBack(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _qCOperationService.CheckBack(opt.Data.Id); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + /// + /// 影像阅片临床数据签名 + /// + /// + /// + [HttpPost, Route("Inspection/ReadClinicalData/ReadClinicalDataSign")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task ReadClinicalDataSign(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _readingClinicalDataService.ReadClinicalDataSign(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + /// + /// IC 设置已经重传完成 + /// + [HttpPost, Route("Inspection/QCOperation/SetReuploadFinished")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task SetReuploadFinished(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _qCOperationService.SetReuploadFinished(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + /// + /// 更新项目状态 + /// + /// + /// + [HttpPost, Route("Inspection/TrialConfig/updateTrialState")] + //[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })] + [UnitOfWork] + public async Task UpdateTrialState(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _trialConfigService.UpdateTrialState(opt.Data.trialId, opt.Data.trialStatusStr, opt.Data.reason); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + /// + /// 用户 签名某个文档 + /// + /// + [HttpPost, Route("Inspection/TrialDocument/userConfirm")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "SignSystemDocNoTrialId", "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task UserConfirm(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + opt.Data.SignText = opt.SignInfo.SignText; + var result = await _trialDocumentService.UserConfirm(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + + /// + /// 重阅同意 + /// + /// + [HttpPost, Route("Inspection/VisitTask/ConfirmReReading")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + + public async Task ConfirmReReading(DataInspectionDto opt , [FromServices] IVisitTaskHelpeService _visitTaskCommonService,[FromServices] IVisitTaskService _visitTaskService) + { + var singId = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _visitTaskService.ConfirmReReading(opt.Data, _visitTaskCommonService); + await _inspectionService.CompletedSign(singId, result); + return result; + } + + } +} diff --git a/IRaCIS.Core.API/Controllers/UploadDownLoadController.cs b/IRaCIS.Core.API/Controllers/UploadDownLoadController.cs new file mode 100644 index 0000000..3a3b264 --- /dev/null +++ b/IRaCIS.Core.API/Controllers/UploadDownLoadController.cs @@ -0,0 +1,1203 @@ +using AutoMapper; +using DocumentFormat.OpenXml.Drawing; +using EasyCaching.Core; +using ExcelDataReader; +using IRaCIS.Application.Contracts; +using IRaCIS.Application.Interfaces; +using IRaCIS.Core.Application.Auth; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.Dicom; +using IRaCIS.Core.Application.Contracts.Dicom.DTO; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Application.Helper; +using IRaCIS.Core.Application.MediatR.CommandAndQueries; +using IRaCIS.Core.Application.MediatR.Handlers; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.Service.ImageAndDoc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Infrastructure.Extention; +using Magicodes.ExporterAndImporter.Excel; +using MassTransit; +using MediatR; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.SignalR; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; +using MiniExcelLibs; +using Newtonsoft.Json; +using SharpCompress.Archives; +using System; +using System.Collections.Generic; +using System.Data; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Path = System.IO.Path; + +namespace IRaCIS.Core.API.Controllers +{ + + #region 上传基类封装 + [DisableFormValueModelBinding] + public abstract class UploadBaseController : ControllerBase + { + /// 流式上传 直接返回 + [Route("base")] + public virtual async Task SingleFileUploadAsync(Func filePathFunc) + { + var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value; + + var reader = new MultipartReader(boundary, HttpContext.Request.Body); + + var section = await reader.ReadNextSectionAsync(); + + while (section != null) + { + var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); + + if (hasContentDispositionHeader) + { + + var (serverFilePath, relativePath) = filePathFunc(contentDisposition.FileName.Value); + + await FileStoreHelper.WriteFileAsync(section.Body, serverFilePath); + + //仅仅返回一个文件,如果多文件上传 在最后返回多个路径 + return ResponseOutput.Ok(new + { + FilePath = relativePath, + FullFilePath = relativePath /*+ "?access_token=" + _userInfo.UserToken*/ + }); + + } + section = await reader.ReadNextSectionAsync(); + } + return ResponseOutput.Ok(); + } + + + /// 流式上传 通用封装 不返回任何数据,后续还有事情处理 + [Route("base")] + public virtual async Task FileUploadAsync(Func> filePathFunc) + { + var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value; + + var reader = new MultipartReader(boundary, HttpContext.Request.Body); + + var section = await reader.ReadNextSectionAsync(); + + while (section != null) + { + var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); + if (hasContentDispositionHeader) + { + var fileName = contentDisposition.FileName.Value; + //处理压缩文件 + if (fileName.Contains(".Zip", StringComparison.OrdinalIgnoreCase) || fileName.Contains(".rar", StringComparison.OrdinalIgnoreCase)) + { + var archive = ArchiveFactory.Open(section.Body); + + foreach (var entry in archive.Entries) + { + if (!entry.IsDirectory) + { + var serverFilePath = await filePathFunc(entry.Key); + entry.WriteToFile(serverFilePath); + + } + } + } + //普通单个文件 + else + { + var serverFilePath = await filePathFunc(fileName); + + await FileStoreHelper.WriteFileAsync(section.Body, serverFilePath); + } + + } + section = await reader.ReadNextSectionAsync(); + } + } + + + + /// 流式上传 Dicom上传 + [Route("base")] + public virtual async Task DicomFileUploadAsync(Func filePathFunc, string boundary) + { + + var fileCount = 0; + + var reader = new MultipartReader(boundary, HttpContext.Request.Body); + + var section = await reader.ReadNextSectionAsync(); + + while (section != null) + { + var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); + + if (hasContentDispositionHeader) + { + + var fileName = contentDisposition.FileName.Value ?? String.Empty; + + string mediaType = section.ContentType ?? String.Empty; + + + //处理压缩文件 + if (fileName.Contains(".Zip", StringComparison.OrdinalIgnoreCase) || fileName.Contains(".rar", StringComparison.OrdinalIgnoreCase)) + { + var archive = ArchiveFactory.Open(section.Body); + + foreach (var entry in archive.Entries) + { + if (!entry.IsDirectory) + { + ++fileCount; + + await filePathFunc(entry.Key, entry.OpenEntryStream(), fileCount); + } + } + } + //普通单个文件 + else + { + if (mediaType.Contains("octet-stream") || mediaType.Contains("dicom")) + { + ++fileCount; + + await filePathFunc(fileName, section.Body, fileCount); + } + + } + } + section = await reader.ReadNextSectionAsync(); + } + + + + } + + + } + + #endregion + + #region Dicom 影像上传 临床数据 非diocm + + [ApiExplorerSettings(GroupName = "Image")] + [ApiController] + public class StudyController : UploadBaseController + { + public IMapper _mapper { get; set; } + public IUserInfo _userInfo { get; set; } + private readonly IMediator _mediator; + + private readonly IWebHostEnvironment _hostEnvironment; + + private readonly IRepository _repository; + + private readonly IEasyCachingProvider _provider; + + public StudyController(IMapper mapper, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IMediator mediator, IEasyCachingProvider provider, + + IRepository repository) + { + _provider = provider; + _hostEnvironment = hostEnvironment; + _mediator = mediator; + + _mapper = mapper; + _userInfo = userInfo; + _repository = repository; + } + + [HttpPost, Route("Study/PreArchiveStudy")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand, + [FromServices] IStudyService _studyService, + [FromServices] IRepository _studyMonitorRepository) + { + + if (_provider.Get>(StaticData.Anonymize.Anonymize_AddFixedFiled).Value == null) + { + await _mediator.Send(new AnonymizeCacheRequest()); + } + + var savedInfo = _studyService.GetSaveToDicomInfo(preArchiveStudyCommand.SubjectVisitId); + + var studyMonitor = new StudyMonitor() + { + TrialId = savedInfo.TrialId, + SiteId = savedInfo.SiteId, + SubjectId = savedInfo.SubjectId, + SubjectVisitId = savedInfo.SubjectVisitId, + + IsSuccess = false, + UploadStartTime = DateTime.Now, + IsDicom = preArchiveStudyCommand.IsDicom, + IP = _userInfo.IP + }; + + + var addEntity = await _studyMonitorRepository.AddAsync(studyMonitor, true); + + return ResponseOutput.Ok(addEntity.Id); + + } + + + /// Dicom 归档 + [HttpPost, Route("Study/ArchiveStudy")] + [DisableFormValueModelBinding] + [DisableRequestSizeLimit] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ArchiveStudyNew(/*[FromForm] ArchiveStudyCommand archiveStudyCommand,*/ Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId,Guid studyMonitorId, + [FromServices] ILogger _logger, + [FromServices] IEasyCachingProvider _provider, + [FromServices] IStudyService _studyService, + [FromServices] IHubContext _uploadHub, + [FromServices] IDicomArchiveService _dicomArchiveService, + [FromServices] IRepository _studyMonitorRepository + ) + { + + + + + + + if (!HttpContext.Request.HasFormContentType || + !MediaTypeHeaderValue.TryParse(HttpContext.Request.ContentType, out var mediaTypeHeader) || + string.IsNullOrEmpty(mediaTypeHeader.Boundary.Value)) + { + return ResponseOutput.NotOk("不支持的MediaType"); + } + + var archiveStudyCommand = new ArchiveStudyCommand() { AbandonStudyId = abandonStudyId, StudyInstanceUid = studyInstanceUid, SubjectVisitId = subjectVisitId }; + + string studycode = string.Empty; + + var startTime = DateTime.Now; + + if (_provider.Exists($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}")) + { + return ResponseOutput.NotOk("当前已有人正在上传和归档该检查!"); + } + else + { + _provider.Set($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}", _userInfo.Id, TimeSpan.FromMinutes(30)); + } + + //到了接口,代表上传结束了 + + var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == studyMonitorId); + + studyMonitor.UploadFinishedTime = DateTime.Now; + + + var (archiveResult, archivedStudyIds) = (new DicomArchiveResult(), new List()); + + + var (seriesInstanceUidList, sopInstanceUidList) = (new List(), new List()); + + //重传的时候,找出当前检查已经上传的series instance + if (archiveStudyCommand.AbandonStudyId != null) + { + (seriesInstanceUidList, sopInstanceUidList) = _studyService.GetHasUploadSeriesAndInstance(archiveStudyCommand.AbandonStudyId.Value); + } + + var savedInfo = _studyService.GetSaveToDicomInfo(archiveStudyCommand.SubjectVisitId); + + try + { + + await DicomFileUploadAsync(async (fileName, fileStream, receivedCount) => + { + + try + { + using (var memoryStream = new MemoryStream()) + { + await fileStream.CopyToAsync(memoryStream); + + memoryStream.Seek(0, SeekOrigin.Begin); + + var (studyId, studyCode) = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, sopInstanceUidList); + if (!archivedStudyIds.Contains(studyId)) + { + archivedStudyIds.Add(studyId); + archiveResult.ArchivedDicomStudies.Add(new DicomStudyBasicDTO() { StudyCode = studyCode, Id = studyId }); + } + } + + //await _uploadHub.Clients.All.ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount); + + + await _uploadHub.Clients.User(_userInfo.Id.ToString()).ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount); + + archiveResult.ReceivedFileCount = receivedCount; + + } + catch (Exception e) + { + _logger.LogError(e.Message + e.StackTrace); + + archiveResult.ErrorFiles.Add(fileName); + } + + + }, mediaTypeHeader.Boundary.Value); + + + } + catch (Exception ex) + { + _provider.Remove($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}"); + + throw new BusinessValidationFailedException("请求异常,请重试!"); + } + + studyMonitor.FileSize = (decimal)HttpContext.Request.ContentLength; + studyMonitor.FileCount = archiveResult.ReceivedFileCount; + studyMonitor.FailedFileCount = archiveResult.ErrorFiles.Count; + studyMonitor.IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null; + studyMonitor.Note = JsonConvert.SerializeObject(archiveResult); + + + try + { + + if (archivedStudyIds.Count > 0) // 上传成功,处理逻辑 + { + + // 同一个检查批次 多个线程上传处理 批量保存 可能造成死锁 https://www.cnblogs.com/johnblogs/p/9945767.html + + await _dicomArchiveService.DicomDBDataSaveChange(); + + archiveResult.ReuploadNewStudyId = archivedStudyIds[0] == archiveStudyCommand.AbandonStudyId ? archivedStudyIds[0] : Guid.Empty; + + studyMonitor.IsSuccess = true; + + } + + } + catch (Exception e) + { + + studyMonitor.Note = JsonConvert.SerializeObject(new { Message = e.Message, Result = archiveResult }); + _logger.LogError(e.Message + e.StackTrace); + + } + finally + { + _provider.Remove($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}"); + + studyMonitor.StudyId = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.Id ?? Guid.Empty; + studyMonitor.StudyCode = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.StudyCode; + studyMonitor.ArchiveFinishedTime = DateTime.Now; + + await _studyMonitorRepository.SaveChangesAsync(); + } + + + + return ResponseOutput.Result(studyMonitor.IsSuccess, archiveResult); + + + + } + + + + + + + /// + /// 上传临床数据 多文件 + /// + /// + /// + [HttpPost("ClinicalData/UploadVisitClinicalData/{trialId:guid}/{subjectVisitId:guid}")] + [DisableRequestSizeLimit] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task UploadVisitClinicalData(Guid subjectVisitId) + { + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); + var sv = _repository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefault().IfNullThrowException(); + + await FileUploadAsync(async (fileName) => + { + var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetClinicalDataPath(_hostEnvironment, fileName, sv.TrialId, sv.SiteId, sv.SubjectId, subjectVisitId); + //插入临床pdf 路径 + await _repository.AddAsync(new PreviousPDF() + { + SubjectVisitId = subjectVisitId, + + IsVisist = true, + DataType = ClinicalDataType.MedicalHistory, + UploadType = ClinicalUploadType.PDF, + SubjectId = sv.SubjectId, + TrialId = sv.TrialId, + ClinicalLevel = ClinicalLevel.Subject, + Path = relativePath, + FileName = fileRealName + }); + return serverFilePath; + }); + await _repository.SaveChangesAsync(); + return ResponseOutput.Ok(); + + } + + /// + /// 上传临床数据模板 + /// + /// + /// + [HttpPost("ClinicalData/UploadClinicalTemplate")] + [DisableRequestSizeLimit] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + + + public async Task>> UploadClinicalTemplate(Guid? trialId) + { + if (trialId == null) + trialId = default(Guid); + + var filerelativePath = string.Empty; + List fileDtos = new List(); + await FileUploadAsync(async (fileName) => + { + var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetClinicalTemplatePath(_hostEnvironment, fileName, trialId.Value); + //插入临床pdf 路径 + filerelativePath = relativePath; + + fileDtos.Add(new FileDto() + { + FileName = fileName, + Path = relativePath + + }); + await Task.CompletedTask; + return serverFilePath; + }); + + return ResponseOutput.Ok(fileDtos); + } + + + + /// + /// 上传阅片临床数据 + /// + /// + /// + /// + /// + [HttpPost("ClinicalData/UploadClinicalData/{trialId:guid}/{subjectId:guid}/{readingId:guid}")] + [DisableRequestSizeLimit] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task>> UploadReadClinicalData(Guid trialId, Guid subjectId, Guid readingId) + { + var filerelativePath = string.Empty; + List fileDtos = new List(); + + var siteid = await _repository.Where(x => x.Id == subjectId).Select(x => x.SiteId).FirstOrDefaultAsync(); + + await FileUploadAsync(async (fileName) => + { + var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetReadClinicalDataPath(_hostEnvironment, fileName, trialId, siteid, subjectId, readingId); + //插入临床pdf 路径 + filerelativePath = relativePath; + + fileDtos.Add(new FileDto() + { + FileName = fileName, + Path = relativePath + + }); + await Task.CompletedTask; + return serverFilePath; + }); + + return ResponseOutput.Ok(fileDtos); + + } + + + /// + /// 上传截图 + /// + /// + /// + [HttpPost("Printscreen/UploadPrintscreen/{subjectId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task> UploadPrintscreen(Guid subjectId) + { + + var subjectInfo = await this._repository.Where(x => x.Id == subjectId).FirstNotNullAsync(); + + FileDto fileDto = new FileDto(); + await FileUploadAsync(async (fileName) => + { + var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetUploadPrintscreenFilePath(_hostEnvironment, fileName, subjectInfo.TrialId, subjectInfo.SiteId, subjectInfo.Id); + fileDto.Path = relativePath; + fileDto.FileName = fileName; + await Task.CompletedTask; + return serverFilePath; + }); + + + return ResponseOutput.Ok(fileDto); + } + + + /// + /// 上传Reading问题的图像 + /// + /// + /// + /// + [HttpPost("VisitTask/UploadReadingAnswerImage/{trialId:guid}/{visitTaskId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task> UploadReadingAnswerImage(Guid trialId, Guid visitTaskId) + { + + FileDto fileDto = new FileDto(); + await FileUploadAsync(async (fileName) => + { + var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetFilePath(_hostEnvironment, fileName, trialId, visitTaskId, StaticData.Folder.JudgeTask); + fileDto.Path = relativePath; + fileDto.FileName = fileName; + await Task.CompletedTask; + return serverFilePath; + }); + + + return ResponseOutput.Ok(fileDto); + } + + /// + /// 上传裁判任务的图像 + /// + /// + /// + /// + [HttpPost("VisitTask/UploadJudgeTaskImage/{trialId:guid}/{visitTaskId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task> UploadJudgeTaskImage(Guid trialId, Guid visitTaskId) + { + + FileDto fileDto = new FileDto(); + await FileUploadAsync(async (fileName) => + { + var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetFilePath(_hostEnvironment, fileName, trialId, visitTaskId, StaticData.Folder.JudgeTask); + fileDto.Path = relativePath; + fileDto.FileName = fileName; + + await Task.CompletedTask; + + return serverFilePath; + }); + + return ResponseOutput.Ok(fileDto); + } + + + + /// + /// 上传医学审核图片 + /// + /// + /// + /// + [HttpPost("TaskMedicalReview/UploadMedicalReviewImage/{trialId:guid}/{taskMedicalReviewId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task> UploadMedicalReviewImage(Guid trialId, Guid taskMedicalReviewId) + { + string path = string.Empty; + FileDto fileDto = new FileDto(); + await FileUploadAsync(async (fileName) => + { + + var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetMedicalReviewImage(_hostEnvironment, fileName, trialId, taskMedicalReviewId); + + + //await _repository.UpdatePartialFromQueryAsync(x => x.Id == taskMedicalReviewId, x => new TaskMedicalReview() + //{ + // ImagePath = relativePath, + // FileName = fileName, + //}); + + path = relativePath; + fileDto.Path = relativePath; + fileDto.FileName = fileName; + return serverFilePath; + }); + + await _repository.SaveChangesAsync(); + return ResponseOutput.Ok(fileDto); + } + + + + /// + /// 上传非Dicom 文件 支持压缩包 多文件上传 + /// + /// + /// + /// + /// + /// + /// + //[DisableRequestSizeLimit] + [RequestSizeLimit(1_073_741_824)] + [HttpPost("NoneDicomStudy/UploadNoneDicomFile/{trialId:guid}/{subjectVisitId:guid}/{noneDicomStudyId:guid}/{studyMonitorId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task UploadNoneDicomFile(IFormCollection formCollection, Guid subjectVisitId, Guid noneDicomStudyId, Guid studyMonitorId, + [FromServices] IRepository _noneDicomStudyRepository, [FromServices] IRepository _studyMonitorRepository) + { + + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); + + var sv = (await _repository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException(); + + var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == studyMonitorId); + + studyMonitor.UploadFinishedTime = DateTime.Now; + + await FileUploadAsync(async (fileName) => + { + + var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetNoneDicomFilePath(_hostEnvironment, fileName, sv.TrialId, sv.SiteId, sv.SubjectId, subjectVisitId); + + await _repository.AddAsync(new NoneDicomStudyFile() { FileName = fileRealName, Path = relativePath, NoneDicomStudyId = noneDicomStudyId }); + + return serverFilePath; + }); + + var uploadFinishedTime = DateTime.Now; + + + //// 上传非Dicom 后 将状态改为待提交 分为普通上传 和QC后重传 普通上传时才改为待提交 + //await _repository.UpdatePartialFromQueryAsync(t => t.Id == subjectVisitId && t.SubmitState == SubmitStateEnum.None, u => new SubjectVisit() { SubmitState = SubmitStateEnum.ToSubmit }); + + var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync((t => t.Id == noneDicomStudyId)); + + noneDicomStudy.FileCount = noneDicomStudy.FileCount + formCollection.Files.Count; + + studyMonitor.FileCount = formCollection.Files.Count; + studyMonitor.FileSize = formCollection.Files.Sum(t => t.Length); + studyMonitor.IsDicom = false; + studyMonitor.IsDicomReUpload = false; + studyMonitor.StudyId = noneDicomStudyId; + studyMonitor.StudyCode = noneDicomStudy.StudyCode; + studyMonitor.ArchiveFinishedTime = DateTime.Now; + studyMonitor.IP = _userInfo.IP; + + await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + + + /// + /// 一致性核查 excel上传 支持三种格式 + /// + /// + /// + [HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + public async Task UploadVisitCheckExcel(Guid trialId) + { + + var (serverFilePath, relativePath, fileName) = (string.Empty, string.Empty, string.Empty); + await FileUploadAsync(async (realFileName) => + { + fileName = realFileName; + + if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".csv") && !fileName.EndsWith(".xls")) + { + throw new BusinessValidationFailedException("支持.xlsx、.xls、.csv格式的文件上传。"); + } + + (serverFilePath, relativePath) = FileStoreHelper.GetTrialCheckFilePath(_hostEnvironment, fileName, trialId); + + await _repository.AddAsync(new ConsistencyCheckFile() + { + TrialId = trialId, + CreateTime = DateTime.Now, + FileName = fileName, + FilePath = relativePath, + RelativePath = relativePath, + CreateUserId = _userInfo.Id + }); + + return serverFilePath; + }); + + + + + var etcCheckList = new List(); + + + + #region MiniExcel 需要自己验证数据格式规范 + + //if (fileName.EndsWith(".csv")) + //{ + // //因为csv 需要加配置文件 不然都是null + // etcCheckList = MiniExcel.Query(filePath, null, configuration: config).ToList(); + //} + //else if (fileName.EndsWith(".xlsx")) + //{ + // + + + // etcCheckList = MiniExcel.Query(filePath).ToList(); + //} + + #endregion + + //Magicodes 支持自定义特性验证 + if (fileName.EndsWith(".xlsx")) + { + var Importer = new ExcelImporter(); + + var import = await Importer.Import(System.IO.File.OpenRead(serverFilePath)); + + if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString()); + + //if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors)); + + if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors)); + + etcCheckList = import.Data.ToList(); + } + else if (fileName.EndsWith(".csv")) + { + #region 临时方案 MiniExcel读取 然后保存为xlsx 再用 Magicodes验证数据 + + //因为csv 需要加配置文件 不然都是null + etcCheckList = MiniExcel.Query(serverFilePath, null, configuration: new MiniExcelLibs.Csv.CsvConfiguration() + { + StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312")) + }).ToList(); + + var (csVToXlsxPath, csVToXlsxRelativePath) = FileStoreHelper.GetTrialCheckFilePath(_hostEnvironment, Path.GetFileNameWithoutExtension(fileName) + ".xlsx", trialId); + + + await MiniExcel.SaveAsAsync(csVToXlsxPath, etcCheckList, excelType: ExcelType.XLSX); + + + var Importer = new ExcelImporter(); + + var import = await Importer.Import(System.IO.File.OpenRead(csVToXlsxPath)); + + if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString()); + + //if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors)); + + if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors)); + + etcCheckList = import.Data.ToList(); + + #endregion + + + #region 导入组件有问题 excel编码格式 + //var Importer = new CsvImporter(); + + //var import = await Importer.Import(File.OpenRead(filePath)); + + //if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString()); + + //if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors)); + + //if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors)); + + //etcCheckList = import.Data.ToList(); + #endregion + + } + //ExcelReaderFactory 需要自己验证数据 并且从固定列取数据 + else + { + //为了支持 xls 引入新的组件库 + using (var stream = System.IO.File.Open(serverFilePath, FileMode.Open, FileAccess.Read)) + { + // Auto-detect format, supports: + // - Binary Excel files (2.0-2003 format; *.xls) + // - OpenXml Excel files (2007 format; *.xlsx, *.xlsb) + using (var reader = ExcelReaderFactory.CreateReader(stream)) + { + + // 2. Use the AsDataSet extension method + var dateset = reader.AsDataSet(); + + foreach (DataRow col in dateset.Tables[0].Rows) + { + + etcCheckList.Add(new CheckViewModel() + { + SiteCode = col[0].ToString(), + SubjectCode = col[1].ToString(), + VisitName = col[2].ToString(), + StudyDate = col[3].ToString(), + Modality = col[4].ToString(), + }); + } + + etcCheckList.Remove(etcCheckList[0]); + + // The result of each spreadsheet is in result.Tables + } + } + } + + if (etcCheckList == null || etcCheckList.Count == 0) + { + return ResponseOutput.NotOk("请保证上传数据符合模板文件中的样式,且存在有效数据。"); + } + else + { + //处理Excel 有时只是清除某些行的数据 读取也会读到数据,只是数据是null 后面处理的时候转为字符串为报错 + etcCheckList.ForEach(t => + { + t.Modality = t.Modality ?? string.Empty; + t.SiteCode = t.SiteCode ?? string.Empty; + t.SubjectCode = t.SubjectCode ?? string.Empty; + t.VisitName = t.VisitName ?? string.Empty; + t.StudyDate = t.StudyDate ?? string.Empty; + }); + + var dt = DateTime.Now; + + etcCheckList = etcCheckList.Where(t => !(t.Modality == string.Empty || t.SiteCode == string.Empty || t.SubjectCode == string.Empty || t.VisitName == string.Empty || t.StudyDate == string.Empty ||! DateTime.TryParse(t.StudyDate, out dt))).ToList(); + + if (etcCheckList.Count == 0) + { + return ResponseOutput.NotOk("请保证上传数据符合模板文件中的样式,且存在有效数据。"); + } + + } + + + await _mediator.Send(new ConsistencyVerificationRequest() { ETCList = etcCheckList, TrialId = trialId }); + + return ResponseOutput.Ok(); + + } + } + + + #endregion + + #region 医生文件上传下载 + + /// 医生文件上传下载 + [ApiExplorerSettings(GroupName = "Common")] + [ApiController] + public class FileController : UploadBaseController + { + public IMapper _mapper { get; set; } + public IUserInfo _userInfo { get; set; } + + + private readonly IWebHostEnvironment _hostEnvironment; + + private readonly IFileService _fileService; + + + + public FileController(IMapper mapper, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IFileService fileService) + { + _fileService = fileService; + _hostEnvironment = hostEnvironment; + + _mapper = mapper; + _userInfo = userInfo; + } + + /// + /// 上传文件[FileUpload] + /// + /// 附件类型 + /// 医生Id + /// 返回文件信息 + [HttpPost, Route("file/UploadFile/{attachmentType}/{doctorId}")] + [DisableFormValueModelBinding] + [DisableRequestSizeLimit] + public async Task UploadOrdinaryFile(string attachmentType, Guid doctorId) + { + + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetDoctorOrdinaryFilePath(_hostEnvironment, fileName, doctorId, attachmentType)); + + } + + + + + + /// + /// 上传文件( 不是医生个人的文件)[FileUpload] + /// 例如:阅片章程等 + /// + /// 文件类型 + /// + + [HttpPost, Route("file/UploadNonDoctorFile/{type}")] + [DisableFormValueModelBinding] + [DisableRequestSizeLimit] + public async Task UploadNonDoctorFile(string type) + { + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetNonDoctorFilePath(_hostEnvironment, fileName, type)); + } + + + /// + /// 下载多个医生的所有附件 + /// + /// + /// + + [HttpPost, Route("file/downloadDoctorAttachments")] + public async Task> DownloadAttachment(Guid[] doctorIds) + { + + var path = await _fileService.CreateDoctorsAllAttachmentZip(doctorIds); + + return ResponseOutput.Ok(new UploadFileInfoDTO + { + FilePath = path, + FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) + + }); + } + + /// + /// 下载医生官方简历 + /// + /// + /// + /// + [HttpPost, Route("file/downloadOfficialCV/{language}")] + public async Task> DownloadOfficialResume(int language, Guid[] doctorIds) + { + + var path = await _fileService.CreateDoctorsAllAttachmentZip(doctorIds); + return ResponseOutput.Ok(new UploadFileInfoDTO + { + FilePath = await _fileService.CreateOfficialResumeZip(language, doctorIds), + FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) + }); + } + + /// + /// 下载指定医生的指定附件 + /// + /// 医生Id + /// 要下载的附件Id + /// + [HttpPost, Route("file/downloadByAttachmentId/{doctorId}")] + public async Task> DownloadAttachmentById(Guid doctorId, Guid[] attachmentIds) + { + var path = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds); + return ResponseOutput.Ok(new UploadFileInfoDTO + { + FilePath = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds), + FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) + }); + } + + + + + [HttpPost, Route("enroll/downloadResume/{trialId:guid}/{language}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [AllowAnonymous] + public async Task> DownloadResume(int language, Guid trialId, Guid[] doctorIdArray) + { + var zipPath = await _fileService.CreateOfficialResumeZip(language, doctorIdArray); + + return ResponseOutput.Ok(zipPath); + } + } + #endregion + + + #region 项目 系统 基本文件 上传 下载 预览 + + [ApiExplorerSettings(GroupName = "Common")] + [ApiController] + public class UploadDownLoadController : UploadBaseController + { + public IMapper _mapper { get; set; } + public IUserInfo _userInfo { get; set; } + private readonly IMediator _mediator; + + private readonly IWebHostEnvironment _hostEnvironment; + + + + public UploadDownLoadController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment) + { + _hostEnvironment = hostEnvironment; + _mediator = mediator; + _mapper = mapper; + _userInfo = userInfo; + } + + /// 缩略图 + [AllowAnonymous] + [HttpGet("Common/LocalFilePreview")] + public async Task LocalFilePreview(string relativePath) + { + + var _fileStorePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, relativePath); + + var storePreviewPath = _fileStorePath + ".preview.jpeg"; + + if (!System.IO.File.Exists(storePreviewPath)) + { + ImageHelper.ResizeSave(_fileStorePath, storePreviewPath); + } + + return new FileContentResult(await System.IO.File.ReadAllBytesAsync(storePreviewPath), "image/jpeg"); + + } + + + /// 通用文件下载 + [AllowAnonymous] + [HttpGet("CommonDocument/DownloadCommonDoc")] + public async Task DownloadCommonFile(string code, [FromServices] IRepository _commonDocumentRepository) + { + var (filePath, fileName) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code); + + new FileExtensionContentTypeProvider().Mappings.TryGetValue(Path.GetExtension(filePath), out var contentType); + + return File(System.IO.File.OpenRead(filePath), contentType ?? "application/octet-stream", fileName); + + } + + /// + /// 下载项目临床数据文件 + /// + /// + /// + /// + [AllowAnonymous] + [HttpGet("CommonDocument/DownloadTrialClinicalFile")] + public async Task DownloadTrialClinicalFile(Guid clinicalDataTrialSetId, [FromServices] IRepository _clinicalDataTrialSetRepository) + { + var (filePath, fileName) = await FileStoreHelper.GetTrialClinicalPathAsync(_hostEnvironment, _clinicalDataTrialSetRepository, clinicalDataTrialSetId); + + new FileExtensionContentTypeProvider().Mappings.TryGetValue(Path.GetExtension(filePath), out var contentType); + + return File(System.IO.File.OpenRead(filePath), contentType ?? "application/octet-stream", fileName); + + } + + + /// + /// 下载系统临床数据文件 + /// + /// + /// + /// + [AllowAnonymous] + [HttpGet("CommonDocument/DownloadSystemClinicalFile")] + public async Task DownloadSystemClinicalFile(Guid clinicalDataSystemSetId, [FromServices] IRepository _clinicalDataSystemSetRepository) + { + var (filePath, fileName) = await FileStoreHelper.GetSystemClinicalPathAsync(_hostEnvironment, _clinicalDataSystemSetRepository, clinicalDataSystemSetId); + + new FileExtensionContentTypeProvider().Mappings.TryGetValue(Path.GetExtension(filePath), out var contentType); + + return File(System.IO.File.OpenRead(filePath), contentType ?? "application/octet-stream", fileName); + + } + + /// + ///上传项目签名文档 + /// + /// + /// + [HttpPost("TrialDocument/UploadTrialDoc/{trialId:guid}")] + [DisableRequestSizeLimit] + [DisableFormValueModelBinding] + public async Task UploadTrialDoc(Guid trialId) + { + + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetTrialSignDocPath(_hostEnvironment, trialId, fileName)); + + } + + /// + /// 上传系统签名文档 + /// + /// + [HttpPost("TrialDocument/UploadSystemDoc")] + [DisableRequestSizeLimit] + [DisableFormValueModelBinding] + public async Task UploadSysTemDoc() + { + + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemSignDocPath(_hostEnvironment, fileName)); + + } + + + /// + /// 上传通用文档 比如一致性核查的 比如导出的excel 模板 + /// + /// + [HttpPost("CommonDocument/UploadCommonDoc")] + [DisableRequestSizeLimit] + [DisableFormValueModelBinding] + public async Task UploadCommonDoc() + { + + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetCommonDocPath(_hostEnvironment, fileName)); + } + + + /// + /// 上传系统通知文档 + /// + /// + [HttpPost("SystemNotice/UploadSystemNoticeDoc")] + [DisableRequestSizeLimit] + [DisableFormValueModelBinding] + public async Task UploadSystemNoticeDoc() + { + + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemNoticePath(_hostEnvironment, fileName)); + + } + + } + #endregion + +} diff --git a/IRaCIS.Core.API/GrpcToken.proto b/IRaCIS.Core.API/GrpcToken.proto new file mode 100644 index 0000000..ace5d42 --- /dev/null +++ b/IRaCIS.Core.API/GrpcToken.proto @@ -0,0 +1,69 @@ +// ʹõproto3汾 +syntax = "proto3"; +// ռ䣬ɴʱͻɶӦռ +option csharp_namespace = "gRPC.ZHiZHUN.AuthServer.protos"; + +/* +ÿһҪ÷ֺŽβ +message ͷݸʽ +tag messageִֵֶεıʶ(tag),Ǹֵ +*/ + + +// ûʱҪϢ Ϊһ +message GetTokenReuqest{ + string id=1; + string userName=2; + string realName=3; + string reviewerCode=4; + int32 userTypeEnumInt=5; + string userTypeShortName=6; + bool isAdmin=7; + +} + +// ʱصϢʽ +message GetTokenResponse { + int32 code=1; + string token =2; +} + + +// service ñʶģдӦķ +service TokenGrpcService{ + // ȡtoken + rpc GetUserToken(GetTokenReuqest) returns (GetTokenResponse); + +} + +/* +// ûʱҪϢ Ϊһ +message AddUserReuqest{ + string name=1; + int32 age=2; + bool isBoy=3; +} +// ʱصϢʽ +message ResultResponse { + int32 code=1; + string msg =2; +} +//ݵIJѯϢʽΪƽʱIJѯ +message QueryUserReuqest{ + string name=1; +} +//ѯصûϢʽΪص +message UserInfoResponse { + string name=1; + int32 age=2; + string gender=3; +} + +// service ñʶģдӦķ +service UserService{ + // û + rpc AddUser(AddUserReuqest) returns (ResultResponse); + // ѯû + rpc GetAllUser(QueryUserReuqest) returns (UserInfoResponse); +} +*/ diff --git a/IRaCIS.Core.API/IRaCIS - Backup.Core.API.csproj b/IRaCIS.Core.API/IRaCIS - Backup.Core.API.csproj new file mode 100644 index 0000000..b14f8c9 --- /dev/null +++ b/IRaCIS.Core.API/IRaCIS - Backup.Core.API.csproj @@ -0,0 +1,107 @@ + + + + net6.0 + false + 354572d4-9e15-4099-807c-63a2d29ff9f2 + default + Linux + + + + .\IRaCIS.Core.API.xml + 1701;1702;1591; + ..\bin\ + + + + bin\Release\IRaCIS.Core.API.xml + bin\Release\ + 1701;1702;1591 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Client + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + + + + + diff --git a/IRaCIS.Core.API/IRaCIS.Core.API.csproj b/IRaCIS.Core.API/IRaCIS.Core.API.csproj new file mode 100644 index 0000000..502d6a5 --- /dev/null +++ b/IRaCIS.Core.API/IRaCIS.Core.API.csproj @@ -0,0 +1,179 @@ + + + + net6.0 + false + 354572d4-9e15-4099-807c-63a2d29ff9f2 + default + Linux + 武汉行藏科技有限公司版权所有 + + 1.0.1.001 + 医学影像处理软件 + 1.0.1.001 + favicon.ico + AnyCPU;x64 + EI_Image_Viewer + + + + .\IRaCIS.Core.API.xml + 1701;1702;1591; + ..\bin\ + + + + .\IRaCIS.Core.API.xml + 1701;1702;1591; + ..\bin\ + + + + .\IRaCIS.Core.API.xml + 1701;1702;1591; + ..\bin\ + + + + bin\Release\IRaCIS.Core.API.xml + bin\Release\ + 1701;1702;1591 + + + + bin\Release\IRaCIS.Core.API.xml + bin\Release\ + 1701;1702;1591 + + + + bin\Release\IRaCIS.Core.API.xml + bin\Release\ + 1701;1702;1591 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + + + Client + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + + + + + + + + + diff --git a/IRaCIS.Core.API/IRaCIS.Core.API.xml b/IRaCIS.Core.API/IRaCIS.Core.API.xml new file mode 100644 index 0000000..999ba0b --- /dev/null +++ b/IRaCIS.Core.API/IRaCIS.Core.API.xml @@ -0,0 +1,557 @@ + + + + EI_Image_Viewer + + + + + 主要处理 前端404等错误 全局业务异常已统一处理了,非业务错误会来到这里 + + + + + + + 医生基本信息 、工作信息 专业信息、审核状态 + + + + + 获取医生详情 + + + + + + + + + + + + 系统用户登录接口[New] + + + 添加实验项目-返回新增Id[AUTH] + + 新记录Id + + + + 添加或更新工作量[AUTH] + + + + + + + + 添加或更新汇率(会触发没有对锁定的费用计算) + + + + + 添加或更新 职称单价[AUTH] + + + + + 添加或更新(替换)医生支付展信息[AUTH] + + + + + 保存(替换)项目支付价格信息(会触发没有被锁定的费用计算)[AUTH] + + + + + 批量更新奖励费用[AUTH] + + + + + 计算医生月度费用,并将计算的结果存入费用表 + + + + + Financials /Monthly Payment 列表查询接口 + + + + + 获取稽查数据 + + + + + + 提交肿瘤学阅片任务 + + + + + + + 提交Diocm阅片 + + + + + + + 提交全局阅片任务 + + + + + + + 项目阅片信息签名 + + + + + + + 医学审核完成 + + + + + + + 确认项目医学审核问题 + + + + + + + 提交阅片问题 + + + + + + + 提交阅片裁判问题 + + + + + + + 配置 基础逻辑信息并确认 + + + + + + + 配置流程并确认 + + + + + + + 配置加急信息并确认 + + + + + + + 签名确认 + + + + + + IC RequestToQC 批量提交 + + + + + + + 设置QC 通过或者不通过 7:QC failed 8:QC passed + + + + + 一致性核查 回退 对话记录不清除 只允许PM回退 + + + + + 影像阅片临床数据签名 + + + + + + + IC 设置已经重传完成 + + + + + 更新项目状态 + + + + + + + 用户 签名某个文档 + + + + + + 重阅同意 + + + + + 流式上传 直接返回 + + + 流式上传 通用封装 不返回任何数据,后续还有事情处理 + + + 流式上传 Dicom上传 + + + Dicom 归档 + + + + 上传临床数据 多文件 + + + + + + + 上传临床数据模板 + + + + + + + 上传阅片临床数据 + + + + + + + + + 上传截图 + + + + + + + 上传Reading问题的图像 + + + + + + + + 上传裁判任务的图像 + + + + + + + + 上传医学审核图片 + + + + + + + + 上传非Dicom 文件 支持压缩包 多文件上传 + + + + + + + + + + + 一致性核查 excel上传 支持三种格式 + + + + + + 医生文件上传下载 + + + + 上传文件[FileUpload] + + 附件类型 + 医生Id + 返回文件信息 + + + + 上传文件( 不是医生个人的文件)[FileUpload] + 例如:阅片章程等 + + 文件类型 + + + + + 下载多个医生的所有附件 + + + + + + + 下载医生官方简历 + + + + + + + + 下载指定医生的指定附件 + + 医生Id + 要下载的附件Id + + + + 缩略图 + + + 通用文件下载 + + + + 下载项目临床数据文件 + + + + + + + + 下载系统临床数据文件 + + + + + + + + 上传项目签名文档 + + + + + + + 上传系统签名文档 + + + + + + 上传通用文档 比如一致性核查的 比如导出的excel 模板 + + + + + + 上传系统通知文档 + + + + + + IPLimit限流 启动服务 + + + + + 创建属性 + + 类型 + 序列化成员 + + + + + 为了前端 一段时间无操作,需要重新登陆 + + + + + + + 对称可逆加密 + + + + + 用户登录成功以后,用来生成Token的方法 + + + + + + + + 非对称可逆加密 + + + + + 从本地文件中读取用来签发 Token 的 RSA Key + + 存放密钥的文件夹路径 + + + + + + + 生成并保存 RSA 公钥与私钥 + + + + + + + Holder for reflection information generated from Protos/GrpcToken.proto + + + File descriptor for Protos/GrpcToken.proto + + + + 新增用户时需要传递数据消息, 可理解为一个类 + + + + Field number for the "id" field. + + + Field number for the "userName" field. + + + Field number for the "realName" field. + + + Field number for the "reviewerCode" field. + + + Field number for the "userTypeEnumInt" field. + + + Field number for the "userTypeShortName" field. + + + Field number for the "isAdmin" field. + + + + 新增时返回的消息格式 + + + + Field number for the "code" field. + + + Field number for the "token" field. + + + + service 用标识定义服务的,里面写对应的方法 + + + + Service descriptor + + + Client for TokenGrpcService + + + Creates a new client for TokenGrpcService + The channel to use to make remote calls. + + + Creates a new client for TokenGrpcService that uses a custom CallInvoker. + The callInvoker to use to make remote calls. + + + Protected parameterless constructor to allow creation of test doubles. + + + Protected constructor to allow creation of configured clients. + The client configuration. + + + + 获取token + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The response received from the server. + + + + 获取token + + The request to send to the server. + The options for the call. + The response received from the server. + + + + 获取token + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The call object. + + + + 获取token + + The request to send to the server. + The options for the call. + The call object. + + + Creates a new instance of client from given ClientBaseConfiguration. + + + diff --git a/IRaCIS.Core.API/Program.cs b/IRaCIS.Core.API/Program.cs new file mode 100644 index 0000000..2255af3 --- /dev/null +++ b/IRaCIS.Core.API/Program.cs @@ -0,0 +1,186 @@ +using System; +using Autofac.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Configuration; +using Serilog; +using MediatR; +using IRaCIS.Core.Application.MediatR.Handlers; +using System.Threading.Tasks; +using MassTransit; +using MassTransit.NewIdProviders; +using System.IO; +using Newtonsoft.Json.Linq; +using System.Security.Cryptography; +using IRaCIS.Core.Infrastructure; +using System.Net.NetworkInformation; +using System.Linq; +using System.Text.Json.Nodes; +using Microsoft.EntityFrameworkCore; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.Data.SqlClient; + +namespace IRaCIS.Core.API +{ + public class Program + { + public readonly string environment; + public static async Task Main(string[] args) + { + + + + try + { + //ļΪ׼ urlȡֵ(дݲļ˾ͲҪݻ) + var config = new ConfigurationBuilder() + .AddEnvironmentVariables() + .Build(); + + var enviromentName = config["ASPNETCORE_ENVIRONMENT"]; + + if (string.IsNullOrWhiteSpace(enviromentName)) + { + + var index = Array.IndexOf(args, "--env"); + enviromentName = index > -1 + ? args[index + 1] + : "Development"; + } + + //Dicom + //ImageManager.SetImplementation(WinFormsImageManager.Instance); + + var host = CreateHostBuilder(args) + .UseEnvironment(enviromentName) //д뻷 + .ConfigureAppConfiguration((hostContext, config) => + { + + //Console.WriteLine(hostContext.HostingEnvironment.EnvironmentName); + config.AddJsonFile("appsettings.json", false, true) + .AddJsonFile($"appsettings.{enviromentName}.json", false, true); + }) + .Build(); + + + + NewId.SetProcessIdProvider(new CurrentProcessIdProvider()); + + + + + //// Serilog + SerilogExtension.AddSerilogSetup(enviromentName, host.Services); + + + + #region ֤ + //if (!File.Exists($@"C:\ProgramData\.xingcang\config.json")) + //{ + // Console.WriteLine("ǰδע"); + // Log.Logger.Error("ǰδע"); + // Console.ReadLine(); + // return; + //} + //else + //{ + // var json = File.ReadAllText($@"C:\ProgramData\.xingcang\config.json"); + + // JObject jsonObject = JObject.Parse(json); + + // var key = jsonObject["key"].ToString(); + + // var value = jsonObject["value"].ToString(); + + + // var physicalAddressList = NetworkInterface.GetAllNetworkInterfaces().Select(t => t.GetPhysicalAddress().ToString()); + + // // жļĻǷDZ + // if (!physicalAddressList.Contains(key)) + // { + // Console.WriteLine("ͱӦ"); + // Log.Logger.Error("ͱӦ"); + // Console.ReadLine(); + + // return; + // } + + + // var secrete = MD5Helper.Md5($"{key}_XINGCANG"); + + // if (value != secrete) + // { + // Console.WriteLine("ע벻ƥ"); + // Log.Logger.Error("ע벻ƥ"); + // Console.ReadLine(); + + // return; + + // } + + //} + + #endregion + + + + //Ŀ״̬ + await InitCache(host); + + + + host.Run(); + + Log.Logger.Warning($"ǰ{enviromentName}"); + + } + catch (Exception e) + { + + Log.Logger.Error(e.InnerException is null ? e.Message + e.StackTrace : e.InnerException?.Message + e.InnerException?.StackTrace); + } + + + + + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseWindowsService() + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.ConfigureKestrel((context, options) => + { + //Ӧ÷KestrelΪ1GB // if don't set default value is: 30 MB + options.Limits.MaxRequestBodySize = long.MaxValue; + options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(30); + options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(20); + + }); + //webBuilder.UseSerilog();//ʱserilog,΢ILogger + webBuilder.UseStartup(); + }).UseSerilog() + .UseServiceProviderFactory(new AutofacServiceProviderFactory());//ʹAutofac + //ConfigureLogging(logging => + //{ + // logging.ClearProviders(); + // logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); + //}) + //.UseNLog() + // NLog: Setup NLog for Dependency injection; + + + + + private static async Task InitCache(IHost host) + { + var _mediator = host.Services.GetService(typeof(IMediator)) as IMediator; + + await _mediator.Send(new AnonymizeCacheRequest()); + + await _mediator.Send(new TrialStateCacheRequest()); + } + + } +} diff --git a/IRaCIS.Core.API/Properties/launchSettings.json b/IRaCIS.Core.API/Properties/launchSettings.json new file mode 100644 index 0000000..a5c135e --- /dev/null +++ b/IRaCIS.Core.API/Properties/launchSettings.json @@ -0,0 +1,58 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:3305", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IRaCIS.Development": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:6100" + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", + "publishAllPorts": true + }, + "IRaCIS.Staging": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Staging" + }, + "applicationUrl": "http://localhost:6200" + }, + "IRaCIS.Production": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Production" + }, + "applicationUrl": "http://localhost:6300" + }, + "IRaCIS.CertificateApply": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "CertificateApply" + }, + "applicationUrl": "http://localhost:6400" + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.API/Protos/GrpcToken.proto b/IRaCIS.Core.API/Protos/GrpcToken.proto new file mode 100644 index 0000000..ace5d42 --- /dev/null +++ b/IRaCIS.Core.API/Protos/GrpcToken.proto @@ -0,0 +1,69 @@ +// ʹõproto3汾 +syntax = "proto3"; +// ռ䣬ɴʱͻɶӦռ +option csharp_namespace = "gRPC.ZHiZHUN.AuthServer.protos"; + +/* +ÿһҪ÷ֺŽβ +message ͷݸʽ +tag messageִֵֶεıʶ(tag),Ǹֵ +*/ + + +// ûʱҪϢ Ϊһ +message GetTokenReuqest{ + string id=1; + string userName=2; + string realName=3; + string reviewerCode=4; + int32 userTypeEnumInt=5; + string userTypeShortName=6; + bool isAdmin=7; + +} + +// ʱصϢʽ +message GetTokenResponse { + int32 code=1; + string token =2; +} + + +// service ñʶģдӦķ +service TokenGrpcService{ + // ȡtoken + rpc GetUserToken(GetTokenReuqest) returns (GetTokenResponse); + +} + +/* +// ûʱҪϢ Ϊһ +message AddUserReuqest{ + string name=1; + int32 age=2; + bool isBoy=3; +} +// ʱصϢʽ +message ResultResponse { + int32 code=1; + string msg =2; +} +//ݵIJѯϢʽΪƽʱIJѯ +message QueryUserReuqest{ + string name=1; +} +//ѯصûϢʽΪص +message UserInfoResponse { + string name=1; + int32 age=2; + string gender=3; +} + +// service ñʶģдӦķ +service UserService{ + // û + rpc AddUser(AddUserReuqest) returns (ResultResponse); + // ѯû + rpc GetAllUser(QueryUserReuqest) returns (UserInfoResponse); +} +*/ diff --git a/IRaCIS.Core.API/SignalRHub/UploadHub.cs b/IRaCIS.Core.API/SignalRHub/UploadHub.cs new file mode 100644 index 0000000..7fae50d --- /dev/null +++ b/IRaCIS.Core.API/SignalRHub/UploadHub.cs @@ -0,0 +1,58 @@ +using EasyCaching.Core; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Cors; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; + +namespace IRaCIS.Core.API +{ + public interface IUploadClient + { + Task ReceivProgressAsync(string studyInstanceUid, int haveReceivedCount); + } + + + public class IRaCISUserIdProvider : IUserIdProvider + { + public virtual string GetUserId(HubConnectionContext connection) + { + return connection.User?.FindFirst(JwtIRaCISClaimType.Id)?.Value!; + } + } + + [AllowAnonymous] + [DisableCors] + public class UploadHub : Hub + { + + public ILogger _logger { get; set; } + //public IUserInfo _userInfo { get; set; } + public UploadHub(/*IUserInfo userInfo,*/ ILogger logger) + { + //_userInfo = userInfo; + + _logger = logger; + } + + public override Task OnConnectedAsync() + { + //base.Context.User.id + + + _logger.LogError("连接: " + Context.ConnectionId); + + + return base.OnConnectedAsync(); + } + + //public async Task SendProgress(string studyInstanceUid, int haveReceivedCount) + //{ + + + // await Clients.All.ReceivProgressAsync(studyInstanceUid, haveReceivedCount); + //} + + } +} diff --git a/IRaCIS.Core.API/Startup.cs b/IRaCIS.Core.API/Startup.cs new file mode 100644 index 0000000..b086a99 --- /dev/null +++ b/IRaCIS.Core.API/Startup.cs @@ -0,0 +1,231 @@ +using Autofac; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using IRaCIS.Core.Application.Filter; +using LogDashboard; +using MediatR; +using IRaCIS.Core.Application.MediatR.Handlers; +using Microsoft.Extensions.Logging; +using AspNetCoreRateLimit; +using Microsoft.AspNetCore.HttpOverrides; +using IRaCIS.Core.Infra.EFCore; +using System.Globalization; +using Microsoft.AspNetCore.Localization; +using Localization; +using Magicodes.ExporterAndImporter.Core.Filters; +using IRaCIS.Core.Application.MediatR.CommandAndQueries; +using IRaCIS.Core.Infra.EFCore.Common; +using Invio.Extensions.Authentication.JwtBearer; +using Microsoft.AspNetCore.SignalR; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.API +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + _configuration = configuration; + } + public ILogger _logger { get; } + + public IConfiguration _configuration { get; } + + //// ConfigureContainer is where you can register things directly + //// with Autofac. This runs after ConfigureServices so the things + //// here will override registrations made in ConfigureServices. + //// Don't build the container; that gets done for you by the factory. + // for castle + public void ConfigureContainer(ContainerBuilder containerBuilder) + { + containerBuilder.RegisterModule(); + + #region Test + //containerBuilder.RegisterType().PropertiesAutowired().InstancePerLifetimeScope();//עִ + + //var container = containerBuilder.Build(); + + //// Now you can resolve services using Autofac. For example, + //// this line will execute the lambda expression registered + //// to the IConfigReader service. + //using (var scope = container.BeginLifetimeScope()) + //{ + // var reader = scope.Resolve(); + + // var test = scope.Resolve(); + // var test2 = scope.Resolve(); + + // var test3 = scope.Resolve>(); + //} + #endregion + } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + //ػ + services.AddJsonLocalization(options => options.ResourcesPath = "Resources"); + + // 쳣ͳһ֤JsonлáַͳһTrim() + services.AddControllers(options => + { + //options.Filters.Add(); + options.Filters.Add(); + options.Filters.Add(); + options.Filters.Add(); + + if (_configuration.GetSection("BasicSystemConfig").GetValue("OpenLoginLimit")) + { + options.Filters.Add(); + + } + + + }) + + .AddNewtonsoftJsonSetup(); // NewtonsoftJson л + + services.AddOptions().Configure( _configuration.GetSection("SystemEmailSendConfig")); + services.AddOptions().Configure(_configuration.GetSection("BasicSystemConfig")); + + + + //̬WebApi + UnifiedApiResultFilter ʡ + services.AddDynamicWebApiSetup(); + //AutoMapper + services.AddAutoMapperSetup(); + //EF ORM QueryWithNoLock + services.AddEFSetup(_configuration); + //Http Ӧѹ + services.AddResponseCompressionSetup(); + //Swagger Api ĵ + services.AddSwaggerSetup(); + //JWT Token ֤ + services.AddJWTAuthSetup(_configuration); + // MediatR Ϣ ¼ ӳ עhandlerӦϵ + services.AddMediatR(typeof(ConsistencyVerificationHandler).Assembly); + // EasyCaching + services.AddEasyCachingSetup(); + + //services.AddDistributedMemoryCache(); + + // hangfire ʱ н棬Ѻ~ + //services.AddhangfireSetup(_configuration); + + // QuartZ ʱ ʹhangfire ʱãҪԴ򿪣Ѿ + services.AddQuartZSetup(_configuration); + + // ϴļ + //services.AddStaticFileAuthorizationSetup(); + + + ////HttpReports ʱ + //services.AddHttpReports().AddHttpTransport(); + //Serilog ־ӻ LogDashboard־ + services.AddLogDashboardSetup(); + //ϴ + services.Configure(options => + { + options.MultipartBodyLengthLimit = int.MaxValue; + options.ValueCountLimit = int.MaxValue; + options.ValueLengthLimit = int.MaxValue; + }); + //IP ð ߺ + //services.AddIpPolicyRateLimitSetup(_configuration); + // û Ȩ + //services.AddAuthorizationPolicySetup(_configuration); + + services.AddJsonConfigSetup(_configuration); + //תͷ ȡʵIP + services.Configure(options => + { + options.ForwardedHeaders = + ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + }); + //DicomӰȾͼƬ ƽ̨ + services.AddDicomSetup(); + + // ʵʱӦ + services.AddSignalR(); + + + services.AddSingleton(); + + //services.AddSingleton(); + + services.AddMemoryCache(); + + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + //ػ + app.UseLocalization(); + + app.UseForwardedHeaders(); + + //Ӧѹ + app.UseResponseCompression(); + + + //Ҫ token ʵľ̬ļ wwwroot css, JavaScript, and images don't require authentication. + app.UseStaticFiles(); + + + //LogDashboard + app.UseLogDashboard("/LogDashboard"); + + ////hangfire + //app.UseHangfireConfig(env); + + + + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + //app.UseHsts(); + } + Console.WriteLine("ǰ " + env.EnvironmentName); + + + // 쳣 404 + app.UseStatusCodePagesWithReExecute("/Error/{0}"); + + SwaggerSetup.Configure(app, env); + + + + ////serilog ¼ûϢ + app.UseSerilogConfig(env); + + app.UseRouting(); + + app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); + + app.UseIRacisHostStaticFileStore(env); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + + + endpoints.MapControllers(); + + endpoints.MapHub("/UploadHub"); + }); + + } + } +} diff --git a/IRaCIS.Core.API/Test.cs b/IRaCIS.Core.API/Test.cs new file mode 100644 index 0000000..f7567c3 --- /dev/null +++ b/IRaCIS.Core.API/Test.cs @@ -0,0 +1,99 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Microsoft.EntityFrameworkCore; + +namespace EFSaving.Concurrency +{ + public class Sample + { + public static void Run() + { + // Ensure database is created and has a person in it + using (var setupContext = new PersonContext()) + { + setupContext.Database.EnsureDeleted(); + setupContext.Database.EnsureCreated(); + + setupContext.People.Add(new Person { FirstName = "John", LastName = "Doe" }); + setupContext.SaveChanges(); + } + + #region ConcurrencyHandlingCode + using var context = new PersonContext(); + // Fetch a person from database and change phone number + var person = context.People.Single(p => p.PersonId == 1); + person.PhoneNumber = "555-555-5555"; + + // Change the person's name in the database to simulate a concurrency conflict + context.Database.ExecuteSqlRaw( + "UPDATE dbo.People SET FirstName = 'Jane' WHERE PersonId = 1"); + + var saved = false; + while (!saved) + { + try + { + // Attempt to save changes to the database + context.SaveChanges(); + saved = true; + } + catch (DbUpdateConcurrencyException ex) + { + foreach (var entry in ex.Entries) + { + if (entry.Entity is Person) + { + var proposedValues = entry.CurrentValues; + var databaseValues = entry.GetDatabaseValues(); + + foreach (var property in proposedValues.Properties) + { + var proposedValue = proposedValues[property]; + var databaseValue = databaseValues[property]; + + // TODO: decide which value should be written to database + // proposedValues[property] = ; + } + + // Refresh original values to bypass next concurrency check + entry.OriginalValues.SetValues(databaseValues); + } + else + { + throw new NotSupportedException( + "Don't know how to handle concurrency conflicts for " + + entry.Metadata.Name); + } + } + } + } + #endregion + } + + public class PersonContext : DbContext + { + public DbSet People { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + // Requires NuGet package Microsoft.EntityFrameworkCore.SqlServer + optionsBuilder.UseSqlServer( + @"Server=(localdb)\mssqllocaldb;Database=EFSaving.Concurrency;Trusted_Connection=True"); + } + } + + public class Person + { + public int PersonId { get; set; } + + [ConcurrencyCheck] + public string FirstName { get; set; } + + [ConcurrencyCheck] + public string LastName { get; set; } + + public string PhoneNumber { get; set; } + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.API/Utility/EnableBufferingAttribute .cs b/IRaCIS.Core.API/Utility/EnableBufferingAttribute .cs new file mode 100644 index 0000000..35d698c --- /dev/null +++ b/IRaCIS.Core.API/Utility/EnableBufferingAttribute .cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using System; + +namespace IRaCIS.Core.API.Filter +{ + public class EnableBufferingAttribute : Attribute, IResourceFilter + { + public void OnResourceExecuting(ResourceExecutingContext context) + { + context.HttpContext.Request.EnableBuffering(); + } + + public void OnResourceExecuted(ResourceExecutedContext context) + { + } + } +} diff --git a/IRaCIS.Core.API/Utility/FileHelpers.cs b/IRaCIS.Core.API/Utility/FileHelpers.cs new file mode 100644 index 0000000..a77ac04 --- /dev/null +++ b/IRaCIS.Core.API/Utility/FileHelpers.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Net.Http.Headers; + +namespace IRaCIS.Core.API.Utility +{ + public static class FileHelpers + { + private static readonly byte[] _allowedChars = { }; + // For more file signatures, see the File Signatures Database (https://www.filesignatures.net/) + // and the official specifications for the file types you wish to add. + private static readonly Dictionary> _fileSignature = new Dictionary> + { + { ".gif", new List { new byte[] { 0x47, 0x49, 0x46, 0x38 } } }, + { ".png", new List { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } } }, + { ".jpeg", new List + { + new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, + new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 }, + new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 }, + } + }, + { ".jpg", new List + { + new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, + new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 }, + new byte[] { 0xFF, 0xD8, 0xFF, 0xE8 }, + } + }, + { ".zip", new List + { + new byte[] { 0x50, 0x4B, 0x03, 0x04 }, + new byte[] { 0x50, 0x4B, 0x4C, 0x49, 0x54, 0x45 }, + new byte[] { 0x50, 0x4B, 0x53, 0x70, 0x58 }, + new byte[] { 0x50, 0x4B, 0x05, 0x06 }, + new byte[] { 0x50, 0x4B, 0x07, 0x08 }, + new byte[] { 0x57, 0x69, 0x6E, 0x5A, 0x69, 0x70 }, + } + }, + }; + + // **WARNING!** + // In the following file processing methods, the file's content isn't scanned. + // In most production scenarios, an anti-virus/anti-malware scanner API is + // used on the file before making the file available to users or other + // systems. For more information, see the topic that accompanies this sample + // app. + + public static async Task ProcessFormFile(IFormFile formFile, + ModelStateDictionary modelState, string[] permittedExtensions, + long sizeLimit) + { + var fieldDisplayName = string.Empty; + + // Use reflection to obtain the display name for the model + // property associated with this IFormFile. If a display + // name isn't found, error messages simply won't show + // a display name. + MemberInfo property = + typeof(T).GetProperty( + formFile.Name.Substring(formFile.Name.IndexOf(".", + StringComparison.Ordinal) + 1)); + + if (property != null) + { + if (property.GetCustomAttribute(typeof(DisplayAttribute)) is + DisplayAttribute displayAttribute) + { + fieldDisplayName = $"{displayAttribute.Name} "; + } + } + + // Don't trust the file name sent by the client. To display + // the file name, HTML-encode the value. + var trustedFileNameForDisplay = WebUtility.HtmlEncode( + formFile.FileName); + + // Check the file length. This check doesn't catch files that only have + // a BOM as their content. + if (formFile.Length == 0) + { + modelState.AddModelError(formFile.Name, + $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); + + return new byte[0]; + } + + if (formFile.Length > sizeLimit) + { + var megabyteSizeLimit = sizeLimit / 1048576; + modelState.AddModelError(formFile.Name, + $"{fieldDisplayName}({trustedFileNameForDisplay}) exceeds " + + $"{megabyteSizeLimit:N1} MB."); + + return new byte[0]; + } + + try + { + using (var memoryStream = new MemoryStream()) + { + await formFile.CopyToAsync(memoryStream); + + // Check the content length in case the file's only + // content was a BOM and the content is actually + // empty after removing the BOM. + if (memoryStream.Length == 0) + { + modelState.AddModelError(formFile.Name, + $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); + } + + if (!IsValidFileExtensionAndSignature( + formFile.FileName, memoryStream, permittedExtensions)) + { + modelState.AddModelError(formFile.Name, + $"{fieldDisplayName}({trustedFileNameForDisplay}) file " + + "type isn't permitted or the file's signature " + + "doesn't match the file's extension."); + } + else + { + return memoryStream.ToArray(); + } + } + } + catch (Exception ex) + { + modelState.AddModelError(formFile.Name, + $"{fieldDisplayName}({trustedFileNameForDisplay}) upload failed. " + + $"Please contact the Help Desk for support. Error: {ex.HResult}"); + } + return new byte[0]; + } + + public static async Task ProcessStreamedFile( + MultipartSection section, ContentDispositionHeaderValue contentDisposition, + ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit) + { + try + { + using (var memoryStream = new MemoryStream()) + { + await section.Body.CopyToAsync(memoryStream); + + // Check if the file is empty or exceeds the size limit. + if (memoryStream.Length == 0) + { + modelState.AddModelError("File", "The file is empty."); + } + else if (memoryStream.Length > sizeLimit) + { + var megabyteSizeLimit = sizeLimit / 1048576; + modelState.AddModelError("File", + $"The file exceeds {megabyteSizeLimit:N1} MB."); + } + else if (!IsValidFileExtensionAndSignature( + contentDisposition.FileName.Value, memoryStream, + permittedExtensions)) + { + modelState.AddModelError("File", + "The file type isn't permitted or the file's " + + "signature doesn't match the file's extension."); + } + else + { + return memoryStream.ToArray(); + } + } + } + catch (Exception ex) + { + modelState.AddModelError("File", + "The upload failed. Please contact the Help Desk " + + $" for support. Error: {ex.HResult}"); + // Log the exception + } + + return new byte[0]; + } + + private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions) + { + if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0) + { + return false; + } + + var ext = Path.GetExtension(fileName).ToLowerInvariant(); + + if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext)) + { + return false; + } + + data.Position = 0; + + using (var reader = new BinaryReader(data)) + { + if (ext.Equals(".txt") || ext.Equals(".csv") || ext.Equals(".prn")) + { + if (_allowedChars.Length == 0) + { + // Limits characters to ASCII encoding. + for (var i = 0; i < data.Length; i++) + { + if (reader.ReadByte() > sbyte.MaxValue) + { + return false; + } + } + } + else + { + // Limits characters to ASCII encoding and + // values of the _allowedChars array. + for (var i = 0; i < data.Length; i++) + { + var b = reader.ReadByte(); + if (b > sbyte.MaxValue || + !_allowedChars.Contains(b)) + { + return false; + } + } + } + + return true; + } + + // Uncomment the following code block if you must permit + // files whose signature isn't provided in the _fileSignature + // dictionary. We recommend that you add file signatures + // for files (when possible) for all file types you intend + // to allow on the system and perform the file signature + // check. + + //if (!_fileSignature.ContainsKey(ext)) + //{ + // return true; + //} + + + // File signature check + // -------------------- + // With the file signatures provided in the _fileSignature + // dictionary, the following code tests the input content's + // file signature. + + //var signatures = _fileSignature[ext]; + //var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length)); + + //return signatures.Any(signature => + // headerBytes.Take(signature.Length).SequenceEqual(signature)); + + //test + return true; + } + } + } +} diff --git a/IRaCIS.Core.API/Utility/Jwt/CustomHSJWTService.cs b/IRaCIS.Core.API/Utility/Jwt/CustomHSJWTService.cs new file mode 100644 index 0000000..26e6128 --- /dev/null +++ b/IRaCIS.Core.API/Utility/Jwt/CustomHSJWTService.cs @@ -0,0 +1,65 @@ +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; + +namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt +{ + /// + /// 对称可逆加密 + /// + public class CustomHSJWTService : ICustomJWTService + { + #region Option注入 + private readonly JWTTokenOptions _JWTTokenOptions; + public CustomHSJWTService(IOptionsMonitor jwtTokenOptions) + { + this._JWTTokenOptions = jwtTokenOptions.CurrentValue; + } + #endregion + + /// + /// 用户登录成功以后,用来生成Token的方法 + /// + /// + /// + /// + public string GetToken(string UserName, string password) + { + #region 有效载荷,大家可以自己写,爱写多少写多少;尽量避免敏感信息 + var claims = new[] + { + new Claim(ClaimTypes.Name, UserName), + new Claim("NickName",UserName), + new Claim("Role","Administrator"),//传递其他信息 + new Claim("ABCC","ABCC"), + new Claim("ABCCDDDDD","ABCCDDDDD"), + new Claim("Student","甜酱油") + }; + + //需要加密:需要加密key: + //Nuget引入:Microsoft.IdentityModel.Tokens + SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_JWTTokenOptions.SecurityKey)); + + SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + //Nuget引入:System.IdentityModel.Tokens.Jwt + JwtSecurityToken token = new JwtSecurityToken( + issuer: _JWTTokenOptions.Issuer, + audience: _JWTTokenOptions.Audience, + claims: claims, + expires: DateTime.Now.AddMinutes(5),//5分钟有效期 + signingCredentials: creds); + + string returnToken = new JwtSecurityTokenHandler().WriteToken(token); + return returnToken; + #endregion + + } + } +} diff --git a/IRaCIS.Core.API/Utility/Jwt/CustomRSSJWTervice.cs b/IRaCIS.Core.API/Utility/Jwt/CustomRSSJWTervice.cs new file mode 100644 index 0000000..b93b444 --- /dev/null +++ b/IRaCIS.Core.API/Utility/Jwt/CustomRSSJWTervice.cs @@ -0,0 +1,60 @@ +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.IO; +using System.Linq; +using System.Security.Claims; +using System.Security.Cryptography; +using System.Threading.Tasks; + +namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt +{ + /// + /// 非对称可逆加密 + /// + public class CustomRSSJWTervice : ICustomJWTService + + { + #region Option注入 + private readonly JWTTokenOptions _JWTTokenOptions; + public CustomRSSJWTervice(IOptionsMonitor jwtTokenOptions) + { + this._JWTTokenOptions = jwtTokenOptions.CurrentValue; + } + #endregion + + public string GetToken(string userName, string password) + { + #region 使用加密解密Key 非对称 + string keyDir = Directory.GetCurrentDirectory(); + if (RSAHelper.TryGetKeyParameters(keyDir, true, out RSAParameters keyParams) == false) + { + keyParams = RSAHelper.GenerateAndSaveKey(keyDir); + } + #endregion + + //string jtiCustom = Guid.NewGuid().ToString();//用来标识 Token + Claim[] claims = new[] + { + new Claim(ClaimTypes.Name, userName), + new Claim(ClaimTypes.Role,"admin"), + new Claim("password",password) + }; + + SigningCredentials credentials = new SigningCredentials(new RsaSecurityKey(keyParams), SecurityAlgorithms.RsaSha256Signature); + + var token = new JwtSecurityToken( + issuer: this._JWTTokenOptions.Issuer, + audience: this._JWTTokenOptions.Audience, + claims: claims, + expires: DateTime.Now.AddMinutes(60),//5分钟有效期 + signingCredentials: credentials); + + var handler = new JwtSecurityTokenHandler(); + string tokenString = handler.WriteToken(token); + return tokenString; + } + } +} diff --git a/IRaCIS.Core.API/Utility/Jwt/ICustomJWTService.cs b/IRaCIS.Core.API/Utility/Jwt/ICustomJWTService.cs new file mode 100644 index 0000000..ac1af95 --- /dev/null +++ b/IRaCIS.Core.API/Utility/Jwt/ICustomJWTService.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt +{ + public interface ICustomJWTService + { + string GetToken(string UserName, string password); + } +} diff --git a/IRaCIS.Core.API/Utility/Jwt/JWTTokenOptions.cs b/IRaCIS.Core.API/Utility/Jwt/JWTTokenOptions.cs new file mode 100644 index 0000000..e8a0876 --- /dev/null +++ b/IRaCIS.Core.API/Utility/Jwt/JWTTokenOptions.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt +{ + public class JWTTokenOptions + { + public string Audience + { + get; + set; + } + public string SecurityKey + { + get; + set; + } + //public SigningCredentials Credentials + //{ + // get; + // set; + //} + public string Issuer + { + get; + set; + } + } +} diff --git a/IRaCIS.Core.API/Utility/Jwt/RSAHelper.cs b/IRaCIS.Core.API/Utility/Jwt/RSAHelper.cs new file mode 100644 index 0000000..707ba46 --- /dev/null +++ b/IRaCIS.Core.API/Utility/Jwt/RSAHelper.cs @@ -0,0 +1,61 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading.Tasks; + +namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt +{ + public class RSAHelper + { + /// + /// 从本地文件中读取用来签发 Token 的 RSA Key + /// + /// 存放密钥的文件夹路径 + /// + /// + /// + public static bool TryGetKeyParameters(string filePath, bool withPrivate, out RSAParameters keyParameters) + { + string filename = withPrivate ? "key.json" : "key.public.json"; + string fileTotalPath = Path.Combine(filePath, filename); + keyParameters = default(RSAParameters); + if (!File.Exists(fileTotalPath)) + { + return false; + } + else + { + keyParameters = JsonConvert.DeserializeObject(File.ReadAllText(fileTotalPath)); + return true; + } + } + /// + /// 生成并保存 RSA 公钥与私钥 + /// + /// + /// + /// + public static RSAParameters GenerateAndSaveKey(string filePath, bool withPrivate = true) + { + RSAParameters publicKeys, privateKeys; + using (var rsa = new RSACryptoServiceProvider(2048))//即时生成 + { + try + { + privateKeys = rsa.ExportParameters(true); + publicKeys = rsa.ExportParameters(false); + } + finally + { + rsa.PersistKeyInCsp = false; + } + } + File.WriteAllText(Path.Combine(filePath, "key.json"), JsonConvert.SerializeObject(privateKeys)); + File.WriteAllText(Path.Combine(filePath, "key.public.json"), JsonConvert.SerializeObject(publicKeys)); + return withPrivate ? privateKeys : publicKeys; + } + } +} diff --git a/IRaCIS.Core.API/Utility/MultipartRequestHelper.cs b/IRaCIS.Core.API/Utility/MultipartRequestHelper.cs new file mode 100644 index 0000000..6be5c7b --- /dev/null +++ b/IRaCIS.Core.API/Utility/MultipartRequestHelper.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using Microsoft.Net.Http.Headers; + +namespace IRaCIS.Core.API.Utility +{ + public static class MultipartRequestHelper + { + // Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq" + // The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit. + public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit) + { + var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value; + + if (string.IsNullOrWhiteSpace(boundary)) + { + throw new InvalidDataException("Missing content-type boundary."); + } + + if (boundary.Length > lengthLimit) + { + throw new InvalidDataException( + $"Multipart boundary length limit {lengthLimit} exceeded."); + } + + return boundary; + } + + public static bool IsMultipartContentType(string contentType) + { + return !string.IsNullOrEmpty(contentType) + && contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0; + } + + public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition) + { + // Content-Disposition: form-data; name="key"; + return contentDisposition != null + && contentDisposition.DispositionType.Equals("form-data") + && string.IsNullOrEmpty(contentDisposition.FileName.Value) + && string.IsNullOrEmpty(contentDisposition.FileNameStar.Value); + } + + public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition) + { + // Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg" + return contentDisposition != null + && contentDisposition.DispositionType.Equals("form-data") + && (!string.IsNullOrEmpty(contentDisposition.FileName.Value) + || !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value)); + } + } +} diff --git a/IRaCIS.Core.API/_PipelineExtensions/AuthMiddleware.cs b/IRaCIS.Core.API/_PipelineExtensions/AuthMiddleware.cs new file mode 100644 index 0000000..9f0a733 --- /dev/null +++ b/IRaCIS.Core.API/_PipelineExtensions/AuthMiddleware.cs @@ -0,0 +1,66 @@ +using System; +using System.IdentityModel.Tokens.Jwt; +using System.Net; +using System.Threading.Tasks; +using EasyCaching.Core; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Http; + +namespace IRaCIS.WX.CoreApi.Auth +{ + public class AuthMiddleware + { + private readonly RequestDelegate _next; + private readonly IEasyCachingProvider _provider; + public AuthMiddleware(RequestDelegate next, IEasyCachingProvider provider) + { + _next = next; + _provider = provider; + } + /// + ///为了前端 一段时间无操作,需要重新登陆 + /// + /// + /// + public async Task Invoke(HttpContext httpContext) + { + + var isLogin = httpContext.Request.Path.ToString().ToLower().Contains("login"); + + var result = await httpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme); + + if (!isLogin) + { + if (!result.Succeeded) + { + httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + await httpContext.Response.WriteAsync("Unauthorized"); + } + else + { + var toekn = result.Properties.Items[".Token.access_token"]; + var jwtHandler = new JwtSecurityTokenHandler(); + JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(toekn); + object userId; + jwtToken.Payload.TryGetValue("id", out userId); + + var cacheValueExist = await _provider.ExistsAsync(userId.ToString()); //Get(userId.ToString()).ToString(); + if (!cacheValueExist) + { + httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + await httpContext.Response.WriteAsync("Unauthorized"); + } + else + { + await _provider.SetAsync(userId.ToString(), userId.ToString(), TimeSpan.FromMinutes(15)); + httpContext.User = result.Principal; + await _next.Invoke(httpContext); + } + } + } + else await _next.Invoke(httpContext); + } + } +} diff --git a/IRaCIS.Core.API/_PipelineExtensions/Hangfire/HangfireConfig.cs b/IRaCIS.Core.API/_PipelineExtensions/Hangfire/HangfireConfig.cs new file mode 100644 index 0000000..4842353 --- /dev/null +++ b/IRaCIS.Core.API/_PipelineExtensions/Hangfire/HangfireConfig.cs @@ -0,0 +1,40 @@ +using Hangfire; +using Hangfire.Dashboard; +using IRaCIS.Application.Services.BackGroundJob; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; + +namespace IRaCIS.Core.API +{ + + + public static class HangfireConfig + { + + public static void UseHangfireConfig(this IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseHangfireDashboard("/api/hangfire", new DashboardOptions() + { + //直接访问,没有带token 获取不到用户身份信息,所以这种自定义授权暂时没法使用 + //Authorization = new[] { new hangfireAuthorizationFilter() } + + //本地请求 才能看 + Authorization = new[] { new LocalRequestsOnlyAuthorizationFilter() } + + }); + + #region hangfire + //// 延迟任务执行 1秒之后执行 有时启动没运行 换成添加到队列中 + //BackgroundJob.Schedule(t => t.MemoryCacheTrialStatus(), TimeSpan.FromSeconds(1)); + ////添加到后台任务队列, + //BackgroundJob.Enqueue(t => t.MemoryCacheTrialStatus()); + + //周期性任务,1天执行一次 + + RecurringJob.AddOrUpdate(t => t.ProjectStartCache(), Cron.Daily); + + #endregion + + } + } +} diff --git a/IRaCIS.Core.API/_PipelineExtensions/Hangfire/hangfireAuthorizationFilter.cs b/IRaCIS.Core.API/_PipelineExtensions/Hangfire/hangfireAuthorizationFilter.cs new file mode 100644 index 0000000..707b552 --- /dev/null +++ b/IRaCIS.Core.API/_PipelineExtensions/Hangfire/hangfireAuthorizationFilter.cs @@ -0,0 +1,17 @@ +using Hangfire.Dashboard; + +namespace IRaCIS.Core.API.Filter +{ + public class hangfireAuthorizationFilter : IDashboardAuthorizationFilter + { + public bool Authorize(DashboardContext context) + { + var httpContext = context.GetHttpContext(); + + // Allow all authenticated users to see the Dashboard (potentially dangerous). + return httpContext.User.Identity.IsAuthenticated; + + //return true; + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.API/_PipelineExtensions/IRacisHostFileStoreConfig.cs b/IRaCIS.Core.API/_PipelineExtensions/IRacisHostFileStoreConfig.cs new file mode 100644 index 0000000..449c48d --- /dev/null +++ b/IRaCIS.Core.API/_PipelineExtensions/IRacisHostFileStoreConfig.cs @@ -0,0 +1,38 @@ +using IRaCIS.Core.Application.Helper; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.FileProviders; +using System.IO; + +namespace IRaCIS.Core.API +{ + public static class IRacisHostFileStore + { + + public static void UseIRacisHostStaticFileStore(this IApplicationBuilder app, IWebHostEnvironment _hostEnvironment) + { + + var iRaCISDataFolder = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + if (!Directory.Exists(iRaCISDataFolder)) + { + Directory.CreateDirectory(iRaCISDataFolder); + } + + app.UseStaticFiles(new StaticFileOptions + { + FileProvider = new PhysicalFileProvider(iRaCISDataFolder), + RequestPath = $"/{StaticData.Folder.IRaCISDataFolder}", + ServeUnknownFileTypes = true, + DefaultContentType = "application/octet-stream" + + // // Set up custom content types - associating file extension to MIME type + //var provider = new FileExtensionContentTypeProvider(); + // // Add new mappings + // provider.Mappings[".myapp"] = "application/x-msdownload"; + + }); + } + } +} diff --git a/IRaCIS.Core.API/_PipelineExtensions/LocalizationConfig.cs b/IRaCIS.Core.API/_PipelineExtensions/LocalizationConfig.cs new file mode 100644 index 0000000..45ab1ec --- /dev/null +++ b/IRaCIS.Core.API/_PipelineExtensions/LocalizationConfig.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Localization; +using System.Collections.Generic; +using System.Globalization; + +namespace IRaCIS.Core.API +{ + public static class LocalizationConfig + { + + public static void UseLocalization(this IApplicationBuilder app) + { + var supportedCultures = new List + { + new CultureInfo("en-US"), + new CultureInfo("zh-CN") + }; + + var options = new RequestLocalizationOptions + { + //DefaultRequestCulture = new RequestCulture("en-US"), + SupportedCultures = supportedCultures, + SupportedUICultures = supportedCultures, + + ApplyCurrentCultureToResponseHeaders = true + }; + + //options.RequestCultureProviders.RemoveAt(0); + //options.RequestCultureProviders.RemoveAt(1); + + app.UseRequestLocalization(options); + } + } +} diff --git a/IRaCIS.Core.API/_PipelineExtensions/LogDashboard/LogDashBoardAuthFilter.cs b/IRaCIS.Core.API/_PipelineExtensions/LogDashboard/LogDashBoardAuthFilter.cs new file mode 100644 index 0000000..2fd35ea --- /dev/null +++ b/IRaCIS.Core.API/_PipelineExtensions/LogDashboard/LogDashBoardAuthFilter.cs @@ -0,0 +1,15 @@ +using LogDashboard; +using LogDashboard.Authorization; + +namespace IRaCIS.Core.API.Filter +{ + + public class LogDashBoardAuthFilter : ILogDashboardAuthorizationFilter + { + //在此可以利用 本系统的UerTypeEnum 判断 + public bool Authorization(LogDashboardContext context) + { + return context.HttpContext.User.Identity.IsAuthenticated; + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.API/_PipelineExtensions/Serilog/RequestResponseLoggingMiddleware.cs b/IRaCIS.Core.API/_PipelineExtensions/Serilog/RequestResponseLoggingMiddleware.cs new file mode 100644 index 0000000..112314b --- /dev/null +++ b/IRaCIS.Core.API/_PipelineExtensions/Serilog/RequestResponseLoggingMiddleware.cs @@ -0,0 +1,106 @@ +using Microsoft.AspNetCore.Http; +using Serilog; +using Serilog.Context; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace IRaCIS.Core.API._PipelineExtensions.Serilog +{ + public class RequestResponseLoggingMiddleware + { + private readonly RequestDelegate _next; + + public RequestResponseLoggingMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context) + { + var ContentType = context.Request.ContentType; + + var isUploadRelation = false; + + if (ContentType != null) + { + isUploadRelation = context.Request.ContentType.Contains("multipart/form-data") || context.Request.ContentType.Contains("boundary") || context.Request.ContentType.Contains("octet-stream"); + } + + + if (isUploadRelation) + { + using (LogContext.PushProperty("RequestBody", string.Empty)) + { + await _next.Invoke(context); + } + } + else + { + + var requestBodyPayload = await ReadRequestBody(context.Request); + + using (LogContext.PushProperty("RequestBody", requestBodyPayload)) + { + //await _next.Invoke() + await _next.Invoke(context); + } + } + + //var request = context.Request; + + + // Read and log request body data + + //SerilogHelper.RequestPayload = requestBodyPayload; + + #region ResponseBody + + //// Read and log response body data + //// Copy a pointer to the original response body stream + //var originalResponseBodyStream = context.Response.Body; + + //// Create a new memory stream... + //using (var responseBody = new MemoryStream()) + //{ + // // ...and use that for the temporary response body + // context.Response.Body = responseBody; + + // // Continue down the Middleware pipeline, eventually returning to this class + //await _next(context); + + // // Copy the contents of the new memory stream (which contains the response) to the original stream, which is then returned to the client. + // await responseBody.CopyToAsync(originalResponseBodyStream); + //} + //string responseBodyPayload = await ReadResponseBody(context.Response); + //_diagnosticContext.Set("ResponseBody", responseBodyPayload); + + #endregion + + + } + + private async Task ReadRequestBody(HttpRequest request) + { + // Ensure the request's body can be read multiple times (for the next middlewares in the pipeline). + request.EnableBuffering(); + + using var streamReader = new StreamReader(request.Body, leaveOpen: true); + var requestBody = await streamReader.ReadToEndAsync(); + + // Reset the request's body stream position for next middleware in the pipeline. + request.Body.Position = 0; + return requestBody == null ? String.Empty : requestBody.Trim(); + } + + private static async Task ReadResponseBody(HttpResponse response) + { + response.Body.Seek(0, SeekOrigin.Begin); + string responseBody = await new StreamReader(response.Body).ReadToEndAsync(); + response.Body.Seek(0, SeekOrigin.Begin); + + return $"{responseBody}"; + } + + } +} diff --git a/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogConfig.cs b/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogConfig.cs new file mode 100644 index 0000000..c5aab64 --- /dev/null +++ b/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogConfig.cs @@ -0,0 +1,30 @@ +using Hangfire; +using Hangfire.Dashboard; +using IRaCIS.Core.API._PipelineExtensions.Serilog; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Serilog; + +namespace IRaCIS.Core.API +{ + + + public static class SerilogConfig + { + + public static void UseSerilogConfig(this IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseMiddleware(); + + app.UseSerilogRequestLogging(opts + => + { + opts.MessageTemplate = "{TokenUserRealName} {TokenUserType} {ClientIp} {RequestIP} {Host} {Protocol} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms"; + opts.EnrichDiagnosticContext = SerilogHelper.EnrichFromRequest; + }); + + + + } + } +} diff --git a/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogHelper.cs b/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogHelper.cs new file mode 100644 index 0000000..1bb30e7 --- /dev/null +++ b/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogHelper.cs @@ -0,0 +1,58 @@ +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Http; +using Serilog; +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.API +{ + public class SerilogHelper + { + //public static string RequestPayload = ""; + + public static void EnrichFromRequest(IDiagnosticContext diagnosticContext, HttpContext httpContext) + { + var request = httpContext.Request; + + // Set all the common properties available for every request + diagnosticContext.Set("Host", request.Host); + + //这种获取的Ip不准 配置服务才行 + diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString()); + + //这种方式可以,但是serilog提供了 就不用了 + //diagnosticContext.Set("TestIP", httpContext.GetUserIp()); + + diagnosticContext.Set("Protocol", request.Protocol); + diagnosticContext.Set("Scheme", request.Scheme); + + //这种方式不行 读取的body为空字符串 必须在中间件中读取 + //diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request)); + //diagnosticContext.Set("RequestBody", RequestPayload); + + // Only set it if available. You're not sending sensitive data in a querystring right?! + if (request.QueryString.HasValue) + { + diagnosticContext.Set("QueryString", request.QueryString.Value); + } + + // Set the content-type of the Response at this point + diagnosticContext.Set("ContentType", httpContext.Response.ContentType); + + diagnosticContext.Set("TokenUserRealName", httpContext?.User?.FindFirst("realName")?.Value); + + diagnosticContext.Set("TokenUserType", httpContext?.User?.FindFirst("userTypeEnumName")?.Value); + + // Retrieve the IEndpointFeature selected for the request + var endpoint = httpContext.GetEndpoint(); + if (endpoint is object) // endpoint != null + { + diagnosticContext.Set("EndpointName", endpoint.DisplayName); + } + } + + + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/Authorization/ApiResponseHandler.cs b/IRaCIS.Core.API/_ServiceExtensions/Authorization/ApiResponseHandler.cs new file mode 100644 index 0000000..32f9995 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/Authorization/ApiResponseHandler.cs @@ -0,0 +1,38 @@ +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using System; +using System.Text.Encodings.Web; +using System.Threading.Tasks; + +namespace IRaCIS.Core.API +{ + public class ApiResponseHandler : AuthenticationHandler + { + public ApiResponseHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) + { + } + + protected override Task HandleAuthenticateAsync() + { + throw new NotImplementedException(); + } + protected override async Task HandleChallengeAsync(AuthenticationProperties properties) + { + Response.ContentType = "application/json"; + Response.StatusCode = StatusCodes.Status401Unauthorized; + await Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk("您无权访问该接口", ApiResponseCodeEnum.NoToken))); + } + + protected override async Task HandleForbiddenAsync(AuthenticationProperties properties) + { + Response.ContentType = "application/json"; + Response.StatusCode = StatusCodes.Status403Forbidden; + await Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk("您的权限不允许进行该操作",ApiResponseCodeEnum.HaveTokenNotAccess))); + } + + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/Authorization/AuthorizationPolicySetup.cs b/IRaCIS.Core.API/_ServiceExtensions/Authorization/AuthorizationPolicySetup.cs new file mode 100644 index 0000000..8e3ec14 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/Authorization/AuthorizationPolicySetup.cs @@ -0,0 +1,82 @@ +using IRaCIS.Core.Application.Auth; +using IRaCIS.Core.Domain.Share; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace IRaCIS.Core.API +{ + public static class AuthorizationPolicySetup + { + + public static void AddAuthorizationPolicySetup(this IServiceCollection services, IConfiguration configuration) + { + services.AddAuthorization(options => + { + //影像质控策略 只允许 IC IQC进行操作 + options.AddPolicy(IRaCISPolicy.CRC_IQC, policyBuilder => + { + policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ClinicalResearchCoordinator).ToString(), ((int)UserTypeEnum.IQC).ToString()); + }); + + //一致性核查策略 只允许 IC PM APM 进行操作 + options.AddPolicy(IRaCISPolicy.PM_APM_CRC, policyBuilder => + { + policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.ClinicalResearchCoordinator).ToString(), ((int)UserTypeEnum.APM).ToString()); + }); + + + options.AddPolicy(IRaCISPolicy.PM_APM, policyBuilder => + { + policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.APM).ToString()); + }); + + options.AddPolicy(IRaCISPolicy.PM_IQC, policyBuilder => + { + policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.IQC).ToString()); + }); + + + options.AddPolicy(IRaCISPolicy.PM, policyBuilder => + { + policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString()); + }); + + + options.AddPolicy(IRaCISPolicy.IQC, policyBuilder => + { + policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.IQC).ToString()); + }); + + + options.AddPolicy(IRaCISPolicy.PM_APM_CRC_QC, policyBuilder => + { + policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(),((int)UserTypeEnum.ClinicalResearchCoordinator).ToString(), ((int)UserTypeEnum.APM).ToString(), ((int)UserTypeEnum.IQC).ToString()); + }); + + options.AddPolicy(IRaCISPolicy.SPM_CPM, policyBuilder => + { + policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.SPM).ToString(), ((int)UserTypeEnum.CPM).ToString()); + }); + + options.AddPolicy(IRaCISPolicy.PM_APM_SPM_CPM, policyBuilder => + { + policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.APM).ToString(),((int)UserTypeEnum.SPM).ToString(), ((int)UserTypeEnum.CPM).ToString()); + }); + + options.AddPolicy(IRaCISPolicy.PM_APM_SPM_CPM_SMM_CMM, policyBuilder => + { + policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.APM).ToString(), ((int)UserTypeEnum.SPM).ToString(), + ((int)UserTypeEnum.CPM).ToString(), ((int)UserTypeEnum.SMM).ToString(), ((int)UserTypeEnum.CMM).ToString()); + }); + + + + + }); + } + + + + + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/Authorization/JWTAuthSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/Authorization/JWTAuthSetup.cs new file mode 100644 index 0000000..dbe605f --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/Authorization/JWTAuthSetup.cs @@ -0,0 +1,133 @@ +using IRaCIS.Core.Application.Auth; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Primitives; +using Microsoft.IdentityModel.Tokens; +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.API +{ + public static class JWTAuthSetup + { + public static void AddJWTAuthSetup(this IServiceCollection services, IConfiguration configuration) + { + services.Configure(configuration.GetSection("JwtSetting")); + + var jwtSetting = new JwtSetting(); + configuration.Bind("JwtSetting", jwtSetting); + + services + .AddAuthentication(o => + { + o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultChallengeScheme = nameof(ApiResponseHandler); + o.DefaultForbidScheme = nameof(ApiResponseHandler); + }) + .AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidIssuer = jwtSetting.Issuer, + ValidAudience = jwtSetting.Audience, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSetting.SecurityKey)), + // 默认 300s + ClockSkew = TimeSpan.Zero + }; + + + + options.Events = new JwtBearerEvents + { + OnMessageReceived = (context) => + { + + if (context.Request.Query.TryGetValue("access_token", out StringValues values)) + { + var queryToken = values.FirstOrDefault(); + + if (!String.IsNullOrWhiteSpace(queryToken)) + { + context.Token = queryToken; + + return Task.CompletedTask; + } + } + + + //仅仅是访问文件的时候才会去取token认证 前端对cookie设置了有效期 + + if (context.Request.Path.ToString().Contains("IRaCISData") || context.Request.Path.ToString().Contains("SystemData") ) + { + var cookieToken = context.Request.Cookies["access_token"]; + + if (!String.IsNullOrWhiteSpace(cookieToken)) + { + context.Token = cookieToken; + } + + } + return Task.CompletedTask; + + } + }; + + // OPTION 1: use `Invio.Extensions.Authentication.JwtBearer` + + //options.AddQueryStringAuthentication(); + + + + // OPTION 2: do it manually + + #region + //options.Events = new JwtBearerEvents + //{ + // OnMessageReceived = (context) => { + + // if (!context.Request.Query.TryGetValue("access_token", out StringValues values)) + // { + // return Task.CompletedTask; + // } + + // if (values.Count > 1) + // { + // context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + // context.Fail( + // "Only one 'access_token' query string parameter can be defined. " + + // $"However, {values.Count:N0} were included in the request." + // ); + + // return Task.CompletedTask; + // } + + // var token = values.Single(); + + // if (String.IsNullOrWhiteSpace(token)) + // { + // context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + // context.Fail( + // "The 'access_token' query string parameter was defined, " + + // "but a value to represent the token was not included." + // ); + + // return Task.CompletedTask; + // } + + // context.Token = token; + + // return Task.CompletedTask; + // } + //}; + #endregion + + }) + .AddScheme(nameof(ApiResponseHandler), o => { }); + + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/AutoMapperSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/AutoMapperSetup.cs new file mode 100644 index 0000000..5797d89 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/AutoMapperSetup.cs @@ -0,0 +1,36 @@ +using AutoMapper.EquivalencyExpression; +using IRaCIS.Core.Application.Service; +using Microsoft.Extensions.DependencyInjection; + +namespace IRaCIS.Core.API +{ + public static class AutoMapperSetup + { + public static void AddAutoMapperSetup(this IServiceCollection services) + { + + services.AddAutoMapper(automapper => + { + //AutoMapper.Collection.EntityFrameworkCore + automapper.AddCollectionMappers(); + + #region 会使 IncludeMembers 失效 不能全局使用 + //mapping an EntityFramework Core DbContext-object. + //automapper.UseEntityFrameworkCoreModel(services); + + + //automapper.ForAllMaps((a, b) => b.ForAllMembers(opt => opt.Condition((src, dest, srcMember, desMenber) => + //{ + // //// Can test When Guid? -> Guid if source is null will change to Guid.Empty + // //Console.WriteLine("srcMember:" + srcMember + "desMenber:" + desMenber); + // return srcMember != null && srcMember.ToString() != Guid.Empty.ToString(); + // // not want to map a null Guid? value to db Guid value + //}))); + #endregion + + }, typeof(QCConfig).Assembly); + + + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/AutofacModuleSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/AutofacModuleSetup.cs new file mode 100644 index 0000000..fd32159 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/AutofacModuleSetup.cs @@ -0,0 +1,85 @@ +using Autofac; +using Autofac.Extras.DynamicProxy; +using IRaCIS.Core.Application; +using IRaCIS.Core.Application.AOP; +using IRaCIS.Core.Application.BackGroundJob; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Http; +using Panda.DynamicWebApi; +using System; +using System.Linq; +using System.Reflection; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; +using MediatR; +using IRaCIS.Application.Services; +using IRaCIS.Application.Interfaces; +using AutoMapper; + +namespace IRaCIS.Core.API +{ + // ReSharper disable once IdentifierTypo + public class AutofacModuleSetup : Autofac.Module + { + protected override void Load(ContainerBuilder containerBuilder) + { + + #region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义 + + containerBuilder.RegisterGeneric(typeof(Repository<>)) + .As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储 + + containerBuilder.RegisterType().As().InstancePerLifetimeScope(); + + //containerBuilder.RegisterType().As().InstancePerLifetimeScope(); + + //containerBuilder.RegisterGeneric(typeof(EFUnitOfWork<>)) + // .As(typeof(IEFUnitOfWork<>)).InstancePerLifetimeScope();//注册仓储 + + #endregion + + #region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html + + //获取所有控制器类型并使用属性注入 + containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly) + .Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type)) + .PropertiesAutowired(); + + + + #endregion + + + //containerBuilder.RegisterType().As().PropertiesAutowired().InstancePerLifetimeScope(); + //containerBuilder.RegisterType().As().InstancePerLifetimeScope(); + + + + Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.Application.dll"); + containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service")) + .PropertiesAutowired().AsImplementedInterfaces().EnableClassInterceptors(); + + //Assembly infrastructure = Assembly.Load("IRaCIS.Core.Infra.EFCore"); + //containerBuilder.RegisterAssemblyTypes(infrastructure).AsImplementedInterfaces(); + + containerBuilder.RegisterType().As().SingleInstance(); + containerBuilder.RegisterType().As().InstancePerLifetimeScope(); + + + //containerBuilder.RegisterType().InstancePerLifetimeScope(); + //Autofac 注册拦截器 需要注意的是生成api上服务上的动态代理AOP失效 间接掉用不影响 + //containerBuilder.RegisterType(); + //containerBuilder.RegisterType(); + //containerBuilder.RegisterType(); + //containerBuilder.RegisterType().As().SingleInstance(); + + + //注册hangfire任务 依赖注入 + containerBuilder.RegisterType().As().InstancePerDependency(); + + + + + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.API/_ServiceExtensions/DicomSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/DicomSetup.cs new file mode 100644 index 0000000..c32dbd4 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/DicomSetup.cs @@ -0,0 +1,20 @@ +using FellowOakDicom; +using FellowOakDicom.Imaging; +using Microsoft.Extensions.DependencyInjection; + +namespace IRaCIS.Core.API +{ + public static class DicomSetup + { + public static void AddDicomSetup(this IServiceCollection services) + { + new DicomSetupBuilder() + .RegisterServices(s => s.AddFellowOakDicom() + .AddTranscoderManager() + .AddImageManager() + ) + .SkipValidation() + .Build(); + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/DynamicWebApiSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/DynamicWebApiSetup.cs new file mode 100644 index 0000000..787c391 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/DynamicWebApiSetup.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using Panda.DynamicWebApi; + +namespace IRaCIS.Core.API +{ + public static class DynamicWebApiSetup + { + //20210910 避免冗余的控制器层代码编写,仅仅包了一层前后台定义的格式 这里采用动态webAPi+IResultFilter 替代大部分情况 + public static void AddDynamicWebApiSetup(this IServiceCollection services) + { + //动态webApi 目前存在的唯一小坑是生成api上服务上的动态代理AOP失效 间接掉用不影响 + services.AddDynamicWebApi(dynamicWebApiOption => + { + //默认是 api + dynamicWebApiOption.DefaultApiPrefix = ""; + //首字母小写 + dynamicWebApiOption.GetRestFulActionName = (actionName) => char.ToLower(actionName[0]) + actionName.Substring(1); + //删除 Service后缀 + dynamicWebApiOption.RemoveControllerPostfixes.Add("Service"); + + }); + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/EFSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/EFSetup.cs new file mode 100644 index 0000000..8e737a5 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/EFSetup.cs @@ -0,0 +1,61 @@ +using IRaCIS.Core.Application.Triggers; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace IRaCIS.Core.API +{ + public static class EFSetup + { + public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration) + { + //services.AddScoped(); + + //这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext + //Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点 + services.AddDbContext(options => + { + options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, + contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure()); + + options.EnableSensitiveDataLogging(); + + options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor()); + + options.UseProjectables(); + + //options.AddInterceptors(new AuditingInterceptor(configuration.GetSection("ConnectionStrings:RemoteNew").Value)); + + //options.UseTriggers(triggerOptions => triggerOptions.AddTrigger()); + + //options.UseTriggers(triggerOptions => triggerOptions.AddAssemblyTriggers(typeof(SubjectVisitTrigger).Assembly)); + + + options.UseTriggers(triggerOptions => + { + triggerOptions.AddTrigger(); + triggerOptions.AddTrigger(); + triggerOptions.AddTrigger(); + triggerOptions.AddTrigger(); + triggerOptions.AddTrigger(); + triggerOptions.AddTrigger(); + triggerOptions.AddTrigger(); + triggerOptions.AddTrigger(); + //triggerOptions.AddTrigger(); + triggerOptions.AddTrigger(); + + + + + + }); + + + }); + + + //services.AddAssemblyTriggers(typeof(SubjectVisitImageDateTrigger).Assembly); + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/EasyCachingSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/EasyCachingSetup.cs new file mode 100644 index 0000000..7d7a28c --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/EasyCachingSetup.cs @@ -0,0 +1,18 @@ +using EasyCaching.Core; +using EasyCaching.Interceptor.Castle; +using Microsoft.Extensions.DependencyInjection; + +namespace IRaCIS.Core.API +{ + public static class EasyCachingSetup + { + public static void AddEasyCachingSetup(this IServiceCollection services) + { + services.AddEasyCaching(options => + { + options.UseInMemory(); + }); + services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultInMemoryName); + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/IpPolicyRateLimitSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/IpPolicyRateLimitSetup.cs new file mode 100644 index 0000000..74ea222 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/IpPolicyRateLimitSetup.cs @@ -0,0 +1,38 @@ +using AspNetCoreRateLimit; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace IRaCIS.Core.API +{ + /// + /// IPLimit限流 启动服务 + /// + public static class IpPolicyRateLimitSetup + { + public static void AddIpPolicyRateLimitSetup(this IServiceCollection services, IConfiguration Configuration) + { + + // needed to store rate limit counters and ip rules + services.AddMemoryCache(); + + //load general configuration from appsettings.json + services.Configure(Configuration.GetSection("IpRateLimiting")); + + //load ip rules from appsettings.json + //services.Configure(Configuration.GetSection("IpRateLimitPolicies")); + + // inject counter and rules stores + services.AddInMemoryRateLimiting(); + //services.AddDistributedRateLimiting(); + //services.AddDistributedRateLimiting(); + //services.AddRedisRateLimiting(); + + + + // configuration (resolvers, counter key builders) + services.AddSingleton(); + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.API/_ServiceExtensions/JsonConfigSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/JsonConfigSetup.cs new file mode 100644 index 0000000..3707ae1 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/JsonConfigSetup.cs @@ -0,0 +1,17 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace IRaCIS.Core.API +{ + public static class JsonConfigSetup + { + public static void AddJsonConfigSetup(this IServiceCollection services, IConfiguration configuration) + { + + services.Configure(configuration.GetSection("BasicSystemConfig")); + + + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/LogDashboardSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/LogDashboardSetup.cs new file mode 100644 index 0000000..4042dfe --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/LogDashboardSetup.cs @@ -0,0 +1,26 @@ + +using LogDashboard; +using LogDashboard.Authorization.Filters; +using Microsoft.Extensions.DependencyInjection; + +namespace IRaCIS.Core.API +{ + public static class LogDashboardSetup + { + public static void AddLogDashboardSetup(this IServiceCollection services) + { + //IIS 配置虚拟路径部署,会出现IIS静态文件404 + services.AddLogDashboard(opt => + { + //opt.PathMatch = "/api/LogDashboard"; + opt.PathMatch = "/LogDashboard"; + + //opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "zhizhun2018")); + + //opt.AddAuthorizationFilter(new LogDashBoardAuthFilter()); + + }); + + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/MiniProfilerConfigure.cs b/IRaCIS.Core.API/_ServiceExtensions/MiniProfilerConfigure.cs new file mode 100644 index 0000000..f08e27a --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/MiniProfilerConfigure.cs @@ -0,0 +1,81 @@ +//using System; +//using Microsoft.Extensions.DependencyInjection; +//using StackExchange.Profiling.Storage; + +//namespace IRaCIS.Core.API +//{ +// public class MiniProfilerConfigure +// { +// public static void ConfigureMiniProfiler(IServiceCollection services) +// { + +// services.AddMiniProfiler(options => +// { +// // All of this is optional. You can simply call .AddMiniProfiler() for all defaults + +// // (Optional) Path to use for profiler URLs, default is /mini-profiler-resources +// options.RouteBasePath = "/profiler"; + +// //// (Optional) Control storage +// //// (default is 30 minutes in MemoryCacheStorage) +// (options.Storage as MemoryCacheStorage).CacheDuration = TimeSpan.FromMinutes(10); + +// //// (Optional) Control which SQL formatter to use, InlineFormatter is the default +// //options.SqlFormatter = new StackExchange.Profiling.SqlFormatters.InlineFormatter(); + +// //// (Optional) To control authorization, you can use the Func options: +// //// (default is everyone can access profilers) +// //options.ResultsAuthorize = request => MyGetUserFunction(request).CanSeeMiniProfiler; +// //options.ResultsListAuthorize = request => MyGetUserFunction(request).CanSeeMiniProfiler; +// //// Or, there are async versions available: +// //options.ResultsAuthorizeAsync = async request => (await MyGetUserFunctionAsync(request)).CanSeeMiniProfiler; +// //options.ResultsAuthorizeListAsync = async request => (await MyGetUserFunctionAsync(request)).CanSeeMiniProfilerLists; + +// //// (Optional) To control which requests are profiled, use the Func option: +// //// (default is everything should be profiled) +// //options.ShouldProfile = request => MyShouldThisBeProfiledFunction(request); + +// //// (Optional) Profiles are stored under a user ID, function to get it: +// //// (default is null, since above methods don't use it by default) +// //options.UserIdProvider = request => MyGetUserIdFunction(request); + +// //// (Optional) Swap out the entire profiler provider, if you want +// //// (default handles async and works fine for almost all applications) +// //options.ProfilerProvider = new MyProfilerProvider(); + +// //// (Optional) You can disable "Connection Open()", "Connection Close()" (and async variant) tracking. +// //// (defaults to true, and connection opening/closing is tracked) +// //options.TrackConnectionOpenClose = true; + +// //// (Optional) Use something other than the "light" color scheme. +// //// (defaults to "light") +// //options.ColorScheme = StackExchange.Profiling.ColorScheme.Auto; + +// //// The below are newer options, available in .NET Core 3.0 and above: + +// //// (Optional) You can disable MVC filter profiling +// //// (defaults to true, and filters are profiled) +// //options.EnableMvcFilterProfiling = true; +// //// ...or only save filters that take over a certain millisecond duration (including their children) +// //// (defaults to null, and all filters are profiled) +// //// options.MvcFilterMinimumSaveMs = 1.0m; + +// //// (Optional) You can disable MVC view profiling +// //// (defaults to true, and views are profiled) +// //options.EnableMvcViewProfiling = true; +// //// ...or only save views that take over a certain millisecond duration (including their children) +// //// (defaults to null, and all views are profiled) +// //// options.MvcViewMinimumSaveMs = 1.0m; + +// //// (Optional) listen to any errors that occur within MiniProfiler itself +// //// options.OnInternalError = e => MyExceptionLogger(e); + +// //// (Optional - not recommended) You can enable a heavy debug mode with stacks and tooltips when using memory storage +// //// It has a lot of overhead vs. normal profiling and should only be used with that in mind +// //// (defaults to false, debug/heavy mode is off) +// ////options.EnableDebugMode = true; +// }); +// //.AddEntityFramework(); +// } +// } +//} \ No newline at end of file diff --git a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONCustomDateConverter.cs b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONCustomDateConverter.cs new file mode 100644 index 0000000..15b7fa5 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONCustomDateConverter.cs @@ -0,0 +1,46 @@ +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; + +namespace IRaCIS.Core.API +{ + public class JSONCustomDateConverter : DateTimeConverterBase + { + private TimeZoneInfo _timeZoneInfo; + private string _dateFormat; + + private IUserInfo _userInfo; + public JSONCustomDateConverter(string dateFormat, TimeZoneInfo timeZoneInfo, IUserInfo userInfo) + { + _dateFormat = dateFormat; + _timeZoneInfo = timeZoneInfo; + + _userInfo = userInfo; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var timeZoneId = _userInfo.TimeZoneId; + + var needConvertUtcDateTime = Convert.ToDateTime(value); + + + var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); + + + var dateTimeOffset = new DateTimeOffset(needConvertUtcDateTime); + + var time = TimeZoneInfo.ConvertTimeFromUtc(needConvertUtcDateTime, tz).ToString(_dateFormat); + + writer.WriteValue(time); + writer.Flush(); + } + + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NewtonsoftJsonSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NewtonsoftJsonSetup.cs new file mode 100644 index 0000000..438e180 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NewtonsoftJsonSetup.cs @@ -0,0 +1,64 @@ + +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; + +namespace IRaCIS.Core.API +{ + public static class NewtonsoftJsonSetup + { + public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder) + { + + builder.AddNewtonsoftJson(options => + { + //options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects; + // 忽略循环引用 + options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + //options.SerializerSettings.TypeNameHandling = TypeNameHandling.All; + + //处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0 + options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver(); + // 设置时间格式 + options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + + //options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ; + + //IsoDateTimeConverter + //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + + }) + .AddControllersAsServices()//动态webApi属性注入需要 + .ConfigureApiBehaviorOptions(o => + { + o.SuppressModelStateInvalidFilter = true; //自己写验证 + + ////这里是自定义验证结果和返回状态码 因为这里是在[ApiController]控制器层校验,动态webApi的不会校验 所以需要单独写一个Filter + //o.InvalidModelStateResponseFactory = (context) => + //{ + // var error = context.ModelState .Keys + // .SelectMany(k => context.ModelState[k].Errors) + // .Select(e => e.ErrorMessage) + // .ToArray(); + + //return new JsonResult(ResponseOutput.NotOk("The inputs supplied to the API are invalid. " + JsonConvert.SerializeObject( error))); + //}; + + }); + + + Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings(); + JsonConvert.DefaultSettings = new Func(() => + { + //日期类型默认格式化处理 + setting.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + return setting; + }); + + + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NullToEmptyStringResolver.cs b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NullToEmptyStringResolver.cs new file mode 100644 index 0000000..6b01306 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NullToEmptyStringResolver.cs @@ -0,0 +1,54 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.API +{ + public class NullToEmptyStringResolver : DefaultContractResolver + { + /// + /// 创建属性 + /// + /// 类型 + /// 序列化成员 + /// + //protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + //{ + // IList properties = base.CreateProperties(type, memberSerialization); + + + // foreach (var jsonProperty in properties) + // { + // jsonProperty.DefaultValue = new NullToEmptyStringValueProvider(jsonProperty); + // } + + // return properties; + + //} + + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + IList properties = base.CreateProperties(type, memberSerialization); + + var list= type.GetProperties() + .Select(p => + { + var jp = base.CreateProperty(p, memberSerialization); + jp.ValueProvider = new NullToEmptyStringValueProvider(p); + return jp; + }).ToList(); + + var uu = list.Select(t => t.PropertyName).ToList(); + + //获取复杂对象属性 + properties = properties.TakeWhile(t => !uu.Contains(t.PropertyName)).ToList(); + + list.AddRange(properties); + return list; + } + + + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NullToEmptyStringValueProvider.cs b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NullToEmptyStringValueProvider.cs new file mode 100644 index 0000000..512efb7 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NullToEmptyStringValueProvider.cs @@ -0,0 +1,42 @@ +using System; +using System.Reflection; +using Newtonsoft.Json.Serialization; + +namespace IRaCIS.Core.API +{ + + public class NullToEmptyStringValueProvider : IValueProvider + { + PropertyInfo _MemberInfo; + public NullToEmptyStringValueProvider(PropertyInfo memberInfo) + { + _MemberInfo = memberInfo; + } + public object GetValue(object target) + { + object result = _MemberInfo.GetValue(target); + if (_MemberInfo.PropertyType == typeof(string) && result == null) result = ""; + else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { }; + //else if (_MemberInfo.PropertyType == typeof(Nullable) && result == null) result = 0; + else if (_MemberInfo.PropertyType == typeof(Nullable) && result == null) result = 0.00M; + + return result; + } + public void SetValue(object target, object value) + { + + if(_MemberInfo.PropertyType == typeof(string)) + { + //去掉前后空格 + _MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim()); + + } + else + { + _MemberInfo.SetValue(target, value); + } + + } + } + +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/QuartZSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/QuartZSetup.cs new file mode 100644 index 0000000..dec9e06 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/QuartZSetup.cs @@ -0,0 +1,45 @@ + +using IRaCIS.Application.Services.BackGroundJob; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Quartz; + +namespace IRaCIS.Core.API +{ + public static class QuartZSetup + { + public static void AddQuartZSetup(this IServiceCollection services, IConfiguration configuration) + { + services.AddTransient(); + + services.AddQuartz(q => + { + // base quartz scheduler, job and trigger configuration + + // as of 3.3.2 this also injects scoped services (like EF DbContext) without problems + q.UseMicrosoftDependencyInjectionJobFactory(); + + // 基本Quartz调度器、作业和触发器配置 + var jobKey = new JobKey("RegularTrialWork", "regularWorkGroup"); + q.AddJob(jobKey, j => j + .WithDescription("Trial regular work") + ); + q.AddTrigger(t => t + .WithIdentity("TrialStatusTrigger") + .ForJob(jobKey) + + .WithCronSchedule("0 0 * * * ?")//每小时执行一次 + .WithDescription("My regular trial work trigger") + ); + }); + + // ASP.NET Core hosting + services.AddQuartzHostedService(options => + { + // when shutting down we want jobs to complete gracefully + options.WaitForJobsToComplete = true; + }); + + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/ResponseCompressionSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/ResponseCompressionSetup.cs new file mode 100644 index 0000000..6037273 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/ResponseCompressionSetup.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.ResponseCompression; +using Microsoft.Extensions.DependencyInjection; + +namespace IRaCIS.Core.API +{ + public static class ResponseCompressionSetup + { + public static void AddResponseCompressionSetup(this IServiceCollection services) + { + services.AddResponseCompression(options => + { + options.Providers.Add(); + options.Providers.Add(); + }); + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/Serilog/EnricherExtensions.cs b/IRaCIS.Core.API/_ServiceExtensions/Serilog/EnricherExtensions.cs new file mode 100644 index 0000000..a6f10b4 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/Serilog/EnricherExtensions.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Http; +using Serilog; +using Serilog.Configuration; +using Serilog.Core; +using Serilog.Events; +using System; + +namespace IRaCIS.Core.API +{ + public static class EnricherExtensions + { + public static LoggerConfiguration WithHttpContextInfo(this LoggerEnrichmentConfiguration enrich, IServiceProvider serviceProvider) + { + if (enrich == null) + throw new ArgumentNullException(nameof(enrich)); + + return enrich.With(new HttpContextEnricher(serviceProvider)); + } + public static LoggerConfiguration WithHttpContextInfo(this LoggerEnrichmentConfiguration enrich, IServiceProvider serviceProvider, Action enrichAction) + { + if (enrich == null) + throw new ArgumentNullException(nameof(enrich)); + + return enrich.With(new HttpContextEnricher(serviceProvider, enrichAction)); + } + + } + +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/Serilog/HttpContextEnricher.cs b/IRaCIS.Core.API/_ServiceExtensions/Serilog/HttpContextEnricher.cs new file mode 100644 index 0000000..330cbc0 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/Serilog/HttpContextEnricher.cs @@ -0,0 +1,89 @@ +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Serilog.Core; +using Serilog.Events; +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.API +{ + public class HttpContextEnricher : ILogEventEnricher + { + private readonly IServiceProvider _serviceProvider; + private readonly Action _enrichAction; + + public HttpContextEnricher(IServiceProvider serviceProvider) : this(serviceProvider, null) + { } + + public HttpContextEnricher(IServiceProvider serviceProvider, Action enrichAction) + { + _serviceProvider = serviceProvider; + if (enrichAction == null) + { + _enrichAction = (logEvent, propertyFactory, httpContext) => + { + logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", httpContext.Connection.RemoteIpAddress.ToString())); + + logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("LocalIP", httpContext.Connection.LocalIpAddress.MapToIPv4().ToString())); + + + //这样读取没用 + //logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestBody", await ReadRequestBody(httpContext.Request))); + //logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", IPHelper.GetIP(httpContext.Request) )); + logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserRealName", httpContext?.User?.FindFirst(ClaimAttributes.RealName)?.Value)); + logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value)); + + //logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Referer", httpContext.Request.Headers["Referer"].ToString())); + //logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_path", httpContext.Request.Path)); + //logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_method", httpContext.Request.Method)); + //if (httpContext.Response.HasStarted) + //{ + // logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("response_status", httpContext.Response.StatusCode)); + //} + }; + } + else + { + _enrichAction = enrichAction; + } + } + + + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + var httpContext = _serviceProvider.GetService()?.HttpContext; + if (null != httpContext) + { + _enrichAction.Invoke(logEvent, propertyFactory, httpContext); + } + } + + private async Task ReadRequestBody(HttpRequest request) + { + // Ensure the request's body can be read multiple times (for the next middlewares in the pipeline). + request.EnableBuffering(); + + using var streamReader = new StreamReader(request.Body, leaveOpen: true); + var requestBody = await streamReader.ReadToEndAsync(); + + // Reset the request's body stream position for next middleware in the pipeline. + request.Body.Position = 0; + return requestBody==null?String.Empty: requestBody.Trim(); + } + + private async Task ReadResponseBody(HttpResponse response) + { + response.Body.Seek(0, SeekOrigin.Begin); + string responseBody = await new StreamReader(response.Body).ReadToEndAsync(); + response.Body.Seek(0, SeekOrigin.Begin); + + return $"{responseBody}"; + } + } + +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/Serilog/SerilogSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/Serilog/SerilogSetup.cs new file mode 100644 index 0000000..f4af9e1 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/Serilog/SerilogSetup.cs @@ -0,0 +1,53 @@ +using Microsoft.AspNetCore.Builder; +using Serilog; +using Serilog.Events; +using Serilog.Sinks.Email; +using System; +using System.Net; + +namespace IRaCIS.Core.API +{ + public class SerilogExtension + { + + public static void AddSerilogSetup(string environment, IServiceProvider serviceProvider) + { + + var config = new LoggerConfiguration() + .MinimumLevel.Information() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + // Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条 + .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) + .MinimumLevel.Override("Hangfire", LogEventLevel.Warning) + .MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning) + .Enrich.WithClientIp() + .Enrich.WithClientAgent() + .Enrich.FromLogContext() + + //控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型 + .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning, + outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3} ] {LocalIP} {ClientIp} {TokenUserRealName} {TokenUserType} {Message:lj} {Properties:j}{NewLine} {Exception}") + .WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day, + outputTemplate: "{Timestamp:HH:mm:ss} || {Level} || {SourceContext:l} || {Message} ||{Exception} ||end {NewLine}"); + //.WriteTo.MSSqlServer("Data Source=DESKTOP-4TU9A6M;Initial Catalog=CoreFrame;User ID=sa;Password=123456", "logs", autoCreateSqlTable: true, restrictedToMinimumLevel: LogEventLevel.Information)//从左至右四个参数分别是数据库连接字符串、表名、如果表不存在是否创建、最低等级。Serilog会默认创建一些列。 + + if (environment == "Production") + { + config.WriteTo.Email(new EmailConnectionInfo() + { + EmailSubject = "系统警告,请速速查看!",//邮件标题 + FromEmail = "iracis_grr@163.com",//发件人邮箱 + MailServer = "smtp.163.com",//smtp服务器地址 + NetworkCredentials = new NetworkCredential("iracis_grr@163.com", "XLWVQKZAEKLDWOAH"),//两个参数分别是发件人邮箱与客户端授权码 + Port = 25,//端口号 + ToEmail = "872297557@qq.com"//收件人 + }, restrictedToMinimumLevel: LogEventLevel.Error, + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [ {Level} {ClientIp} {ClientAgent} {TokenUserRealName} {TokenUserType} ] || [path: {RequestPath} arguments: {RequestBody}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine})"); + } + + //扩展方法 获取上下文的ip 用户名 用户类型 + Log.Logger = config.Enrich.WithHttpContextInfo(serviceProvider).CreateLogger(); + } + + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/StaticFileAuthorizationSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/StaticFileAuthorizationSetup.cs new file mode 100644 index 0000000..0ec0aa4 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/StaticFileAuthorizationSetup.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; + +namespace IRaCIS.Core.API +{ + public static class StaticFileAuthorizationSetup + { + public static void AddStaticFileAuthorizationSetup(this IServiceCollection services) + { + services.AddAuthorization(options => + { + options.FallbackPolicy = new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .Build(); + }); + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/Swagger/JsonPatchDocumentFilter.cs b/IRaCIS.Core.API/_ServiceExtensions/Swagger/JsonPatchDocumentFilter.cs new file mode 100644 index 0000000..9f35620 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/Swagger/JsonPatchDocumentFilter.cs @@ -0,0 +1,55 @@ +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.API +{ + public class JsonPatchDocumentFilter : IDocumentFilter + { + public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) + { + var schemas = swaggerDoc.Components.Schemas.ToList(); + foreach (var item in schemas) + { + if (item.Key.StartsWith("Operation") || item.Key.StartsWith("JsonPatchDocument")) + swaggerDoc.Components.Schemas.Remove(item.Key); + } + + swaggerDoc.Components.Schemas.Add("Operation", new OpenApiSchema + { + Type = "object", + Properties = new Dictionary + { + { "op", new OpenApiSchema { Type = "string" } }, + {"value", new OpenApiSchema{ Type = "object", Nullable = true } }, + { "path", new OpenApiSchema { Type = "string" } } + } + }); + + swaggerDoc.Components.Schemas.Add("JsonPatchDocument", new OpenApiSchema + { + Type = "array", + Items = new OpenApiSchema + { + Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = "Operation" } + }, + Description = "Array of operations to perform" + }); + + foreach (var path in swaggerDoc.Paths.SelectMany(p => p.Value.Operations) + .Where(p => p.Key == Microsoft.OpenApi.Models.OperationType.Patch)) + { + foreach (var item in path.Value.RequestBody.Content.Where(c => c.Key != "application/json-patch+json")) + path.Value.RequestBody.Content.Remove(item.Key); + + var response = path.Value.RequestBody.Content.SingleOrDefault(c => c.Key == "application/json-patch+json"); + + response.Value.Schema = new OpenApiSchema + { + Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = "JsonPatchDocument" } + }; + } + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/Swagger/SwaggerSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/Swagger/SwaggerSetup.cs new file mode 100644 index 0000000..68905d6 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/Swagger/SwaggerSetup.cs @@ -0,0 +1,141 @@ +using IRaCIS.Core.Application.Contracts; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.Filters; +using Swashbuckle.AspNetCore.SwaggerGen; +using Swashbuckle.AspNetCore.SwaggerUI; +using System; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace IRaCIS.Core.API +{ + public static class SwaggerSetup + { + public static void AddSwaggerSetup(this IServiceCollection services) + { + services.AddSwaggerExamplesFromAssemblyOf(); + + services.AddSwaggerGen(options => + { + //此处的Name 是控制器上分组的名称 Title是界面的大标题 + //分组 + + options.SwaggerDoc("Reviewer", new OpenApiInfo {Title = "医生模块",Version = "Reviewer", }); + options.SwaggerDoc("Trial", new OpenApiInfo { Title = "项目模块", Version = "Trial" }); + options.SwaggerDoc("Enroll", new OpenApiInfo { Title = "入组模块", Version = "Enroll" }); + options.SwaggerDoc("Workload", new OpenApiInfo { Title = "工作量模块", Version = "Workload" }); + options.SwaggerDoc("Common", new OpenApiInfo { Title = "通用信息获取", Version = "Common" }); + options.SwaggerDoc("Institution", new OpenApiInfo { Title = "机构信息模块", Version = "Institution" }); + options.SwaggerDoc("Dashboard&Statistics", new OpenApiInfo { Title = "统计模块", Version = "Dashboard&Statistics" }); + + options.SwaggerDoc("Financial", new OpenApiInfo { Title = "财务模块", Version = "Financial" }); + options.SwaggerDoc("Management", new OpenApiInfo { Title = "管理模块", Version = "Management" }); + options.SwaggerDoc("Image", new OpenApiInfo { Title = "影像模块", Version = "Image" }); + options.SwaggerDoc("Reading", new OpenApiInfo { Title = "读片模块", Version = "Reading" }); + + + // 接口排序 + options.OrderActionsBy(o => o.GroupName); + + options.DocInclusionPredicate((docName, apiDes) => + { + if (!apiDes.TryGetMethodInfo(out MethodInfo methodInfo)) return false; + var versions = methodInfo.DeclaringType.GetCustomAttributes(true) + .OfType() + .Select(attr => attr.GroupName); + + return versions.Any(v => v.ToString() == docName); + }); + + var xmlPath = Path.Combine(AppContext.BaseDirectory, "IRaCIS.Core.API.xml");//这个就是刚刚配置的xml文件名 + options.IncludeXmlComments(xmlPath, true); + + var xmlPath2 = Path.Combine(AppContext.BaseDirectory, "IRaCIS.Core.Application.xml");//这个就是刚刚配置的xml文件名 + options.IncludeXmlComments(xmlPath2, true); + //默认的第二个参数是false,这个是controller的注释,记得修改 + + + // 在header中添加token,传递到后台 + options.OperationFilter(); + + options.DocumentFilter(); + + // 添加登录按钮 + options.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme() + { + Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + + //In = "header", + //Type = "apiKey" + }); + + + //// Bearer + //options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + //{ + // Description = "JWT Authorization header using the Bearer scheme.", + // Name = "Authorization", + // In = ParameterLocation.Header, + // Scheme = "bearer", + // Type = SecuritySchemeType.Http, + // BearerFormat = "JWT" + //}); + }); + } + + public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(options => + { + //此处的Name 是页面 选择文档下拉框 显示的名称 + options.SwaggerEndpoint($"swagger/Reviewer/swagger.json", "医生模块"); + options.SwaggerEndpoint($"swagger/Trial/swagger.json", "项目模块"); + options.SwaggerEndpoint($"swagger/Enroll/swagger.json", "入组模块"); + options.SwaggerEndpoint($"swagger/Workload/swagger.json", "工作量模块"); + options.SwaggerEndpoint($"swagger/Dashboard&Statistics/swagger.json", "统计模块"); + options.SwaggerEndpoint($"swagger/Common/swagger.json", "通用模块"); + + options.SwaggerEndpoint($"swagger/Financial/swagger.json", "财务模块"); + options.SwaggerEndpoint($"swagger/Institution/swagger.json", "机构信息模块"); + options.SwaggerEndpoint($"swagger/Management/swagger.json", "管理模块"); + options.SwaggerEndpoint($"swagger/Image/swagger.json", "影像模块"); + options.SwaggerEndpoint($"swagger/Reading/swagger.json", "读片模块"); + + + //路径配置,设置为空,表示直接在根域名(localhost:8001)访问该文件, + //注意localhost:8001/swagger是访问不到的,去launchSettings.json把launchUrl去掉,如果你想换一个路径,直接写名字即可,比如直接写c.Route = "doc"; + //options.RoutePrefix = string.Empty; + + + + + options.IndexStream = () => Assembly.GetExecutingAssembly() + .GetManifestResourceStream("IRaCIS.Core.API.wwwroot.swagger.ui.Index.html"); + + options.RoutePrefix = string.Empty; + + //DocExpansion设置为none可折叠所有方法 + options.DocExpansion(DocExpansion.None); + //DefaultModelsExpandDepth设置为 - 1 可不显示models + options.DefaultModelsExpandDepth(-1); + + + // 引入静态文件添加登录功能 + // 清除静态文件缓存 + // options.IndexStream = () => null; + + + }); + + + } + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/hangfireSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/hangfireSetup.cs new file mode 100644 index 0000000..a5dd1fc --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/hangfireSetup.cs @@ -0,0 +1,39 @@ +using Hangfire; +using Hangfire.SqlServer; +using Hangfire.Tags.SqlServer; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace IRaCIS.Core.API +{ + public static class hangfireSetup + { + public static void AddhangfireSetup(this IServiceCollection services, IConfiguration configuration) + { + var hangFireConnStr = configuration.GetSection("ConnectionStrings:Hangfire").Value; + + services.AddHangfire(hangFireConfig => + { + //指定存储介质 + hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions() + { + SchemaName = "hangfire", + CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), + SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5), + QueuePollInterval = TimeSpan.Zero, + UseRecommendedIsolationLevel = true, + UsePageLocksOnDequeue = true, + DisableGlobalLocks = true + }); + + hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer + //.UseHangfireHttpJob(); + + }); + + services.AddHangfireServer(); + + } + } +} diff --git a/IRaCIS.Core.API/appsettings.Test-EIImageViewer.json b/IRaCIS.Core.API/appsettings.Test-EIImageViewer.json new file mode 100644 index 0000000..025f135 --- /dev/null +++ b/IRaCIS.Core.API/appsettings.Test-EIImageViewer.json @@ -0,0 +1,33 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ConnectionStrings": { + "RemoteNew": "Server=123.56.94.154,1433\\MSSQLSERVER;Database=Test.EIImageViewer;User ID=sa;Password=dev123456DEV;TrustServerCertificate=true", + "Hangfire": "Server=123.56.94.154,1433\\MSSQLSERVER;Database=Hangfire_IRaCIS;User ID=sa;Password=dev123456DEV;TrustServerCertificate=true" + + }, + "BasicSystemConfig": { + + "OpenUserComplexPassword": true, + + "OpenSignDocumentBeforeWork": false, + + "OpenTrialRelationDelete": true, + + "OpenLoginLimit": true + }, + + "SystemEmailSendConfig": { + "Port": 465, + "Host": "smtp.qiye.aliyun.com", + "FromEmail": "test@extimaging.com", + "FromName": "Test_IRC", + "AuthorizationCode": "SHzyyl2021" + } + +} \ No newline at end of file diff --git a/IRaCIS.Core.API/appsettings.json b/IRaCIS.Core.API/appsettings.json new file mode 100644 index 0000000..ac43d2e --- /dev/null +++ b/IRaCIS.Core.API/appsettings.json @@ -0,0 +1,90 @@ +{ + "JwtSetting": { + "SecurityKey": "3e6dbfa227234a03977a2f421bdb7f4f", // 密钥 + "Issuer": "IRaCIS", // 颁发者 + "Audience": "ZhiZhun", // 接收者 + "TokenExpireDays": "7" // 过期时间(7day) + }, + "IpRateLimiting": { + "EnableEndpointRateLimiting": true, //False: globally executed, true: executed for each + "StackBlockedRequests": false, // set to false, rejected calls are not added to the throttle counter + "RealIpHeader": "X-Real-IP", + "ClientIdHeader": "X-ClientId", + "QuotaExceededResponse": { + "Content": "{{\"ErrorMessage\":\"The program performs flow limiting,Your requests are too frequent, please try again later, or contact the administrator to modify the IP flow restriction rules\",\"IsSuccess\":false}}", + "ContentType": "application/json", + "StatusCode": 429 + }, + "HttpStatusCode": 429, + //"IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ], + "EndpointWhitelist": [ "post:/study/archivestudy/*" ], + //"EndpointWhitelist": [ ], + //"EndpointWhitelist": ["post:/trial/getTrialList"], //demo + "IpWhitelist": [], + //"ClientWhitelist": [ "dev-id-1", "dev-id-2" ], + "GeneralRules": [ + { + "Endpoint": "*", + "Period": "1s", + "Limit": 3 + }, + { + "Endpoint": "*", + "Period": "15m", + "Limit": 100 + }, + { + "Endpoint": "*", + "Period": "12h", + "Limit": 1000 + }, + { + "Endpoint": "*", + "Period": "7d", + "Limit": 10000 + } + ] + }, + + "easycaching": { + "inmemory": { + "MaxRdSecond": 120, + "EnableLogging": false, + "LockMs": 5000, + "SleepMs": 300, + "DBConfig": { + "SizeLimit": 10000, + "ExpirationScanFrequency": 60, + "EnableReadDeepClone": true, + "EnableWriteDeepClone": false + } + } + }, + + "IRaCISBasicConfig": { + + "DoctorCodePrefix": "RE", + + "UserCodePrefix": "U", + + "QCChallengeCodePrefix": "Q", + + "NoneDicomStudyCodePrefix": "NST", + + + "DicomStudyCodePrefix": "ST", + + "SystemSiteCodePrefix": "S", + + "DefaultPassword": "EIImage@2023", + + "DefaultInternalOrganizationName": "ExtImaging", + + "ImageShareExpireDays": 10 + } + + //网站根地址,为了访问文件 dicom 和上传的文档... 实测发现不用将域名拼接返回,浏览器会自动加上当前ip,避免了多环境读取环境配置文件 + + + +} \ No newline at end of file diff --git a/IRaCIS.Core.API/favicon.ico b/IRaCIS.Core.API/favicon.ico new file mode 100644 index 0000000..70e9d37 Binary files /dev/null and b/IRaCIS.Core.API/favicon.ico differ diff --git a/IRaCIS.Core.API/web.config b/IRaCIS.Core.API/web.config new file mode 100644 index 0000000..cd48e31 --- /dev/null +++ b/IRaCIS.Core.API/web.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminAddUser.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminAddUser.html new file mode 100644 index 0000000..aabdcaf --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminAddUser.html @@ -0,0 +1,40 @@ + + + + + Title + + +
+
+
+ 尊敬的 {0} ,您好: +
+
+ 行藏医学影像处理软件系统为您添加了账户,账户信息如下: +
+ +
+
+ 用户名: {1} +
+
+ 角色: {2} +
+
+ 首次登陆前,请通过该链接修改您的账户信息: + + 初始化账号信息 + +
+
+ + +
+
祝您顺利!/Best Regards
+
武汉行藏科技有限公司
+
+
+
+ + diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminResetUser.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminResetUser.html new file mode 100644 index 0000000..8f8bb43 --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminResetUser.html @@ -0,0 +1,37 @@ + + + + + Title + + +
+
+
+ 尊敬的 {0} ,您好: +
+
+ 行藏医学影像处理软件系统将您的账户密码已重置,账户信息如下: +
+ +
+
+ 用户名: {1} +
+
+ 角色: {2} +
+
+ 密码: {3} +
+
+ + +
+
祝您顺利!/Best Regards
+
武汉行藏科技有限公司
+
+
+
+ + diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/EmailConfigTest.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/EmailConfigTest.html new file mode 100644 index 0000000..6f1d3a5 --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/EmailConfigTest.html @@ -0,0 +1,27 @@ + + + + + Title + + +
+
+
+ 您好: +
+
+ 感谢您使用行藏医学影像处理软件。 +
+
+ {0}。 +
+ +
+
祝您顺利!/Best Regards
+
武汉行藏科技有限公司
+
+
+
+ + diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/SubjectEnrollConfirmOrPDProgress.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/SubjectEnrollConfirmOrPDProgress.html new file mode 100644 index 0000000..0d184d0 --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/SubjectEnrollConfirmOrPDProgress.html @@ -0,0 +1,27 @@ + + + + + Title + + +
+
+
+ 您好: +
+
+ 感谢您使用展影云平台。 +
+
+ {0}。 +
+ +
+
祝您顺利!/Best Regards
+
上海展影医疗科技有限公司
+
+
+
+ + diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorExistJoin.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorExistJoin.html new file mode 100644 index 0000000..99d20f4 --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorExistJoin.html @@ -0,0 +1,49 @@ + + + + + Title + + +
+
+
+ 尊敬的 {0} ,您好: +
+
+ 展影医疗作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC阅片相关工作,欢迎您提供指导和建议,非常感谢! +
+
+ 该项目采用电子化工作流,系统及您的账号信息如下: +
+ +
+
+ 项目编号: {3} +
+
+ 试验方案号: {2} +
+
+ 试验名称: {1} +
+
+ 用户名: {4} +
+
+ 角色: {5} +
+
+ 系统登录地址:{6} +
+
+ + +
+
祝您顺利!/Best Regards
+
上海展影医疗科技有限公司
+
+
+
+ + diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorFirstJoin.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorFirstJoin.html new file mode 100644 index 0000000..4bfce0d --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorFirstJoin.html @@ -0,0 +1,52 @@ + + + + + Title + + +
+
+
+ 尊敬的 {0} ,您好: +
+
+ 展影医疗作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC阅片相关工作,欢迎您提供指导和建议,非常感谢! +
+
+ 该项目采用电子化工作流,系统及您的账号信息如下: +
+ +
+
+ 项目编号: {3} +
+
+ 试验方案号: {2} +
+
+ 试验名称: {1} +
+
+ 用户名: {4} +
+
+ 角色: {5} +
+
+ 首次登陆前,请通过该链接修改您的账户信息: + + 初始化账号信息 + +
+
+ + +
+
祝您顺利!/Best Regards
+
上海展影医疗科技有限公司
+
+
+
+ + diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialSiteSurveyReject.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialSiteSurveyReject.html new file mode 100644 index 0000000..29b84ee --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialSiteSurveyReject.html @@ -0,0 +1,49 @@ + + + + + Title + + +
+
+
+ 尊敬的 {0} ,您好: +
+
+ 您好, 您填写的中心调研表被驳回,详细信息如下: +
+ +
+
+ 项目编号: {1} +
+
+ 试验方案号: {2} +
+
+ 试验名称: {3} +
+
+ 中心编号: {4} +
+
+ 中心名称: {5} +
+
+ 驳回原因: {6} +
+ +
+ + 登陆并查看 + + +
+
祝您顺利!/Best Regards
+
上海展影医疗科技有限公司
+
+
+
+ + diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserExistJoin.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserExistJoin.html new file mode 100644 index 0000000..14cbda1 --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserExistJoin.html @@ -0,0 +1,49 @@ + + + + + Title + + +
+
+
+ 尊敬的 {0} ,您好: +
+
+ 展影医疗作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC相关工作,欢迎您提供指导和建议,非常感谢! +
+
+ 该项目采用电子化工作流,系统及您的账号信息如下: +
+ +
+
+ 项目编号: {3} +
+
+ 试验方案号: {2} +
+
+ 试验名称: {1} +
+
+ 用户名: {4} +
+
+ 角色: {5} +
+
+ 系统登录地址:{6} +
+
+ + +
+
祝您顺利!/Best Regards
+
上海展影医疗科技有限公司
+
+
+
+ + diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserFirstJoin.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserFirstJoin.html new file mode 100644 index 0000000..83cb9d4 --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserFirstJoin.html @@ -0,0 +1,52 @@ + + + + + Title + + +
+
+
+ 尊敬的 {0} ,您好: +
+
+ 展影医疗作为 [{1} (试验方案号:{2 })] 项目的IRC供应商,诚邀您参加该项目IRC相关工作,欢迎您提供指导和建议,非常感谢! +
+
+ 该项目采用电子化工作流,系统及您的账号信息如下: +
+ +
+
+ 项目编号: {3} +
+
+ 试验方案号: {2} +
+
+ 试验名称: {1} +
+
+ 用户名: {4} +
+
+ 角色: {5} +
+
+ 首次登陆前,请通过该链接修改您的账户信息: + + 初始化账号信息 + +
+
+ + +
+
祝您顺利!/Best Regards
+
上海展影医疗科技有限公司
+
+
+
+ + diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/UserOptCommon.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/UserOptCommon.html new file mode 100644 index 0000000..f21fa50 --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/UserOptCommon.html @@ -0,0 +1,27 @@ + + + + + Title + + +
+
+
+ {0}您好: +
+
+ 感谢您使用行藏医学影像处理软件。 +
+
+ {1},验证码是: {2},请在3分钟内输入该验证码,进行后续操作。如非本人操作,请忽略该邮件。 +
+ +
+
祝您顺利!/Best Regards
+
武汉行藏科技有限公司
+
+
+
+ + diff --git a/IRaCIS.Core.API/wwwroot/swagger/ui/Index.html b/IRaCIS.Core.API/wwwroot/swagger/ui/Index.html new file mode 100644 index 0000000..3101e03 --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/swagger/ui/Index.html @@ -0,0 +1,128 @@ + + + + + + %(DocumentTitle) + + + + + + %(HeadContent) + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + \ No newline at end of file diff --git a/IRaCIS.Core.API/wwwroot/swagger/ui/abp.js b/IRaCIS.Core.API/wwwroot/swagger/ui/abp.js new file mode 100644 index 0000000..9957fdf --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/swagger/ui/abp.js @@ -0,0 +1,117 @@ +var abp = abp || {}; +(function () { + + /* Application paths *****************************************/ + + // Current application root path (including virtual directory if exists). + abp.appPath = abp.appPath || '/'; + + /* AUTHORIZATION **********************************************/ + // Implements Authorization API that simplifies usage of authorization scripts generated by Abp. + + abp.auth = abp.auth || {}; + + abp.auth.tokenCookieName = 'Abp.AuthToken'; + abp.auth.tokenHeaderName = 'Authorization'; + + abp.auth.setToken = function (authToken, expireDate) { + abp.utils.setCookieValue(abp.auth.tokenCookieName, authToken, expireDate, abp.appPath); + }; + + abp.auth.getToken = function () { + return abp.utils.getCookieValue(abp.auth.tokenCookieName); + } + + abp.auth.clearToken = function () { + abp.auth.setToken(); + } + + /* UTILS ***************************************************/ + + abp.utils = abp.utils || {}; + + /** + * Sets a cookie value for given key. + * This is a simple implementation created to be used by ABP. + * Please use a complete cookie library if you need. + * @param {string} key + * @param {string} value + * @param {Date} expireDate (optional). If not specified the cookie will expire at the end of session. + * @param {string} path (optional) + */ + abp.utils.setCookieValue = function (key, value, expireDate, path) { + var cookieValue = encodeURIComponent(key) + '='; + + if (value) { + cookieValue = cookieValue + encodeURIComponent(value); + } + + if (expireDate) { + cookieValue = cookieValue + "; expires=" + expireDate.toUTCString(); + } + + if (path) { + cookieValue = cookieValue + "; path=" + path; + } + + document.cookie = cookieValue; + }; + + /** + * Gets a cookie with given key. + * This is a simple implementation created to be used by ABP. + * Please use a complete cookie library if you need. + * @param {string} key + * @returns {string} Cookie value or null + */ + abp.utils.getCookieValue = function (key) { + var equalities = document.cookie.split('; '); + for (var i = 0; i < equalities.length; i++) { + if (!equalities[i]) { + continue; + } + + var splitted = equalities[i].split('='); + if (splitted.length != 2) { + continue; + } + + if (decodeURIComponent(splitted[0]) === key) { + return decodeURIComponent(splitted[1] || ''); + } + } + + return null; + }; + + /** + * Deletes cookie for given key. + * This is a simple implementation created to be used by ABP. + * Please use a complete cookie library if you need. + * @param {string} key + * @param {string} path (optional) + */ + abp.utils.deleteCookie = function (key, path) { + var cookieValue = encodeURIComponent(key) + '='; + + cookieValue = cookieValue + "; expires=" + (new Date(new Date().getTime() - 86400000)).toUTCString(); + + if (path) { + cookieValue = cookieValue + "; path=" + path; + } + + document.cookie = cookieValue; + } + + /* SECURITY ***************************************/ + abp.security = abp.security || {}; + abp.security.antiForgery = abp.security.antiForgery || {}; + + abp.security.antiForgery.tokenCookieName = 'XSRF-TOKEN'; + abp.security.antiForgery.tokenHeaderName = 'X-XSRF-TOKEN'; + + abp.security.antiForgery.getToken = function () { + return abp.utils.getCookieValue(abp.security.antiForgery.tokenCookieName); + }; + +})(); diff --git a/IRaCIS.Core.API/wwwroot/swagger/ui/abp.swagger.js b/IRaCIS.Core.API/wwwroot/swagger/ui/abp.swagger.js new file mode 100644 index 0000000..eb708cb --- /dev/null +++ b/IRaCIS.Core.API/wwwroot/swagger/ui/abp.swagger.js @@ -0,0 +1,470 @@ +var abp = abp || {}; +(function () { + + /* md5*/ + + /* + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for more info. + */ + + /* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ + var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ + var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ + var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + + /* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ + function hex_md5(s) { return binl2hex(core_md5(str2binl(s), s.length * chrsz)); } + function b64_md5(s) { return binl2b64(core_md5(str2binl(s), s.length * chrsz)); } + function str_md5(s) { return binl2str(core_md5(str2binl(s), s.length * chrsz)); } + function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); } + function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); } + function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); } + + /* + * Perform a simple self-test to see if the VM is working + */ + function md5_vm_test() { + return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72"; + } + + /* + * Calculate the MD5 of an array of little-endian words, and a bit length + */ + function core_md5(x, len) { + /* append padding */ + x[len >> 5] |= 0x80 << ((len) % 32); + x[(((len + 64) >>> 9) << 4) + 14] = len; + + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + + for (var i = 0; i < x.length; i += 16) { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + + a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936); + d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586); + c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819); + b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330); + a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897); + d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426); + c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341); + b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983); + a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416); + d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417); + c = md5_ff(c, d, a, b, x[i + 10], 17, -42063); + b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162); + a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682); + d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101); + c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290); + b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329); + + a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510); + d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632); + c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713); + b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302); + a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691); + d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083); + c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335); + b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848); + a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438); + d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690); + c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961); + b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501); + a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467); + d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784); + c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473); + b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734); + + a = md5_hh(a, b, c, d, x[i + 5], 4, -378558); + d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463); + c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562); + b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556); + a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060); + d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353); + c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632); + b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640); + a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174); + d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222); + c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979); + b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189); + a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487); + d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835); + c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520); + b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651); + + a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844); + d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415); + c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905); + b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055); + a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571); + d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606); + c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523); + b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799); + a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359); + d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744); + c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380); + b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649); + a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070); + d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379); + c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259); + b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551); + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + } + return Array(a, b, c, d); + + } + + /* + * These functions implement the four basic operations the algorithm uses. + */ + function md5_cmn(q, a, b, x, s, t) { + return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b); + } + function md5_ff(a, b, c, d, x, s, t) { + return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); + } + function md5_gg(a, b, c, d, x, s, t) { + return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); + } + function md5_hh(a, b, c, d, x, s, t) { + return md5_cmn(b ^ c ^ d, a, b, x, s, t); + } + function md5_ii(a, b, c, d, x, s, t) { + return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); + } + + /* + * Calculate the HMAC-MD5, of a key and some data + */ + function core_hmac_md5(key, data) { + var bkey = str2binl(key); + if (bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for (var i = 0; i < 16; i++) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz); + return core_md5(opad.concat(hash), 512 + 128); + } + + /* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ + function safe_add(x, y) { + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + } + + /* + * Bitwise rotate a 32-bit number to the left. + */ + function bit_rol(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); + } + + /* + * Convert a string to an array of little-endian words + * If chrsz is ASCII, characters >255 have their hi-byte silently ignored. + */ + function str2binl(str) { + var bin = Array(); + var mask = (1 << chrsz) - 1; + for (var i = 0; i < str.length * chrsz; i += chrsz) + bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (i % 32); + return bin; + } + + /* + * Convert an array of little-endian words to a string + */ + function binl2str(bin) { + var str = ""; + var mask = (1 << chrsz) - 1; + for (var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i >> 5] >>> (i % 32)) & mask); + return str; + } + + /* + * Convert an array of little-endian words to a hex string. + */ + function binl2hex(binarray) { + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i++) { + str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xF) + + hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xF); + } + return str; + } + + /* + * Convert an array of little-endian words to a base-64 string + */ + function binl2b64(binarray) { + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i += 3) { + var triplet = (((binarray[i >> 2] >> 8 * (i % 4)) & 0xFF) << 16) + | (((binarray[i + 1 >> 2] >> 8 * ((i + 1) % 4)) & 0xFF) << 8) + | ((binarray[i + 2 >> 2] >> 8 * ((i + 2) % 4)) & 0xFF); + for (var j = 0; j < 4; j++) { + if (i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6 * (3 - j)) & 0x3F); + } + } + return str; + } + + + /* Swagger */ + + abp.swagger = abp.swagger || {}; + + abp.swagger.addAuthToken = function () { + var authToken = abp.auth.getToken(); + if (!authToken) { + return false; + } + + var cookieAuth = new SwaggerClient.ApiKeyAuthorization(abp.auth.tokenHeaderName, 'Bearer ' + authToken, 'header'); + swaggerUi.api.clientAuthorizations.add('bearerAuth', cookieAuth); + return true; + } + + abp.swagger.addCsrfToken = function () { + var csrfToken = abp.security.antiForgery.getToken(); + if (!csrfToken) { + return false; + } + var csrfCookieAuth = new SwaggerClient.ApiKeyAuthorization(abp.security.antiForgery.tokenHeaderName, csrfToken, 'header'); + swaggerUi.api.clientAuthorizations.add(abp.security.antiForgery.tokenHeaderName, csrfCookieAuth); + return true; + } + + function loginUserInternal(tenantId, callback) { + + var usernameOrEmailAddress = document.getElementById('userName').value; + if (!usernameOrEmailAddress) { + alert('UserName Can Not Be Null'); + return false; + } + + var password = document.getElementById('password').value; + if (!password) { + alert('PassWord Can Not Be Null'); + return false; + } + + var xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = function () { + + if (xhr.readyState === XMLHttpRequest.DONE) { + debugger; + if (xhr.status === 200) { + debugger; + + var resultdata = JSON.parse(xhr.responseText); + if (resultdata.ErrorMessage != '') { + alert(resultdata.ErrorMessage); + return false; + } + if (resultdata.code == 300) { + alert(resultdata.message); + } + else { + var responseJSON = JSON.parse(xhr.responseText); + var result = responseJSON; + var expireDate = new Date(Date.now() + (60*60*24 * 1000)); + abp.auth.setToken(result.Result.JWTStr, expireDate); + callback(); + } + + } else { + alert('Login failed !'); + } + } + }; + + xhr.open('POST', '/user/login', true); + xhr.setRequestHeader('Abp.TenantId', tenantId); + xhr.setRequestHeader('Content-type', 'application/json'); + var parm = { + + + UserName: usernameOrEmailAddress, + Password: hex_md5(password), + } + xhr.send(JSON.stringify(parm)); + //xhr.send("{" + "userName:'" + usernameOrEmailAddress + "'," + "passWord:'" + password + "'}"); + + }; + + abp.swagger.login = function (callback) { + //Get TenantId first + var tenancyName = document.getElementById('tenancyName').value; + + if (tenancyName) { + var xhrTenancyName = new XMLHttpRequest(); + xhrTenancyName.onreadystatechange = function () { + if (xhrTenancyName.readyState === XMLHttpRequest.DONE && xhrTenancyName.status === 200) { + var responseJSON = JSON.parse(xhrTenancyName.responseText); + var result = responseJSON.result; + if (result.state === 1) { // Tenant exists and active. + loginUserInternal(result.tenantId, callback); // Login for tenant + } else { + alert('There is no such tenant or tenant is not active !'); + } + } + }; + + xhrTenancyName.open('POST', '/api/services/app/Account/IsTenantAvailable', true); + xhrTenancyName.setRequestHeader('Content-type', 'application/json'); + xhrTenancyName.send("{" + "tenancyName:'" + tenancyName + "'}"); + } else { + loginUserInternal(null, callback); // Login for host + } + }; + + abp.swagger.logout = function () { + abp.auth.clearToken(); + } + + abp.swagger.closeAuthDialog = function () { + if (document.getElementById('abp-auth-dialog')) { + document.getElementsByClassName("swagger-ui")[1].removeChild(document.getElementById('abp-auth-dialog')); + } + } + + abp.swagger.openAuthDialog = function (loginCallback) { + abp.swagger.closeAuthDialog(); + + var abpAuthDialog = document.createElement('div'); + abpAuthDialog.className = 'dialog-ux'; + abpAuthDialog.id = 'abp-auth-dialog'; + + document.getElementsByClassName("swagger-ui")[1].appendChild(abpAuthDialog); + + // -- backdrop-ux + var backdropUx = document.createElement('div'); + backdropUx.className = 'backdrop-ux'; + abpAuthDialog.appendChild(backdropUx); + + // -- modal-ux + var modalUx = document.createElement('div'); + modalUx.className = 'modal-ux'; + abpAuthDialog.appendChild(modalUx); + + // -- -- modal-dialog-ux + var modalDialogUx = document.createElement('div'); + modalDialogUx.className = 'modal-dialog-ux'; + modalUx.appendChild(modalDialogUx); + + // -- -- -- modal-ux-inner + var modalUxInner = document.createElement('div'); + modalUxInner.className = 'modal-ux-inner'; + modalDialogUx.appendChild(modalUxInner); + + // -- -- -- -- modal-ux-header + var modalUxHeader = document.createElement('div'); + modalUxHeader.className = 'modal-ux-header'; + modalUxInner.appendChild(modalUxHeader); + + var modalHeader = document.createElement('h3'); + modalHeader.innerText = 'Authorize'; + modalUxHeader.appendChild(modalHeader); + + // -- -- -- -- modal-ux-content + var modalUxContent = document.createElement('div'); + modalUxContent.className = 'modal-ux-content'; + modalUxInner.appendChild(modalUxContent); + + modalUxContent.onkeydown = function (e) { + if (e.keyCode === 13) { + //try to login when user presses enter on authorize modal + abp.swagger.login(loginCallback); + } + }; + + //Inputs + createInput(modalUxContent, 'tenancyName', 'Tenancy Name (Leave empty for Host)'); + createInput(modalUxContent, 'userName', 'Username or email address','text','admin'); + createInput(modalUxContent, 'password', 'Password','password'); + + //Buttons + var authBtnWrapper = document.createElement('div'); + authBtnWrapper.className = 'auth-btn-wrapper'; + modalUxContent.appendChild(authBtnWrapper); + + //Close button + var closeButton = document.createElement('button'); + closeButton.className = 'btn modal-btn auth btn-done button'; + closeButton.innerText = 'Close'; + closeButton.style.marginRight = '5px'; + closeButton.onclick = abp.swagger.closeAuthDialog; + authBtnWrapper.appendChild(closeButton); + + //Authorize button + var authorizeButton = document.createElement('button'); + authorizeButton.className = 'btn modal-btn auth authorize button'; + authorizeButton.innerText = 'Login'; + authorizeButton.onclick = function () { + abp.swagger.login(loginCallback); + }; + authBtnWrapper.appendChild(authorizeButton); + } + + function createInput(container, id, title, type, value="") { + var wrapper = document.createElement('div'); + wrapper.className = 'wrapper'; + if (id == "tenancyName") { + wrapper.style.display = 'none'; + } + container.appendChild(wrapper); + + var label = document.createElement('label'); + label.innerText = title; + wrapper.appendChild(label); + + var section = document.createElement('section'); + section.className = 'block-tablet col-10-tablet block-desktop col-10-desktop'; + wrapper.appendChild(section); + + var input = document.createElement('input'); + input.id = id; + input.type = type ? type : 'text'; + input.style.width = '100%'; + input.value = value; + input.autocomplete = "off"; + + + section.appendChild(input); + } + +})(); \ No newline at end of file diff --git a/IRaCIS.Core.Application/AOP/AsyncInterceptor.cs b/IRaCIS.Core.Application/AOP/AsyncInterceptor.cs new file mode 100644 index 0000000..d08f164 --- /dev/null +++ b/IRaCIS.Core.Application/AOP/AsyncInterceptor.cs @@ -0,0 +1,97 @@ +using Castle.DynamicProxy; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.AOP +{ + public abstract class AsyncInterceptorBase : IInterceptor + { + public AsyncInterceptorBase() + { + } + + public void Intercept(IInvocation invocation) + { + BeforeProceed(invocation); + invocation.Proceed(); + if (IsAsyncMethod(invocation.MethodInvocationTarget)) + { + invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue, invocation); + } + else + { + AfterProceedSync(invocation); + } + } + + private bool CheckMethodReturnTypeIsTaskType(MethodInfo method) + { + var methodReturnType = method.ReturnType; + if (methodReturnType.IsGenericType) + { + if (methodReturnType.GetGenericTypeDefinition() == typeof(Task<>) || + methodReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>)) + return true; + } + else + { + if (methodReturnType == typeof(Task) || + methodReturnType == typeof(ValueTask)) + return true; + } + return false; + } + + private bool IsAsyncMethod(MethodInfo method) + { + bool isDefAsync = Attribute.IsDefined(method, typeof(AsyncStateMachineAttribute), false); + bool isTaskType = CheckMethodReturnTypeIsTaskType(method); + bool isAsync = isDefAsync && isTaskType; + + return isAsync; + } + + protected object ProceedAsyncResult { get; set; } + + + private async Task InterceptAsync(Task task, IInvocation invocation) + { + await task.ConfigureAwait(false); + await AfterProceedAsync(invocation, false); + } + + private async Task InterceptAsync(Task task, IInvocation invocation) + { + ProceedAsyncResult = await task.ConfigureAwait(false); + await AfterProceedAsync(invocation, true); + return (TResult)ProceedAsyncResult; + } + + private async ValueTask InterceptAsync(ValueTask task, IInvocation invocation) + { + await task.ConfigureAwait(false); + await AfterProceedAsync(invocation, false); + } + + private async ValueTask InterceptAsync(ValueTask task, IInvocation invocation) + { + ProceedAsyncResult = await task.ConfigureAwait(false); + await AfterProceedAsync(invocation, true); + return (TResult)ProceedAsyncResult; + } + + protected virtual void BeforeProceed(IInvocation invocation) { } + + protected virtual void AfterProceedSync(IInvocation invocation) { } + + protected virtual Task AfterProceedAsync(IInvocation invocation, bool hasAsynResult) + { + return Task.CompletedTask; + } + } +} diff --git a/IRaCIS.Core.Application/AOP/QANoticeAOP.cs b/IRaCIS.Core.Application/AOP/QANoticeAOP.cs new file mode 100644 index 0000000..79ea577 --- /dev/null +++ b/IRaCIS.Core.Application/AOP/QANoticeAOP.cs @@ -0,0 +1,499 @@ +//using System; +//using Castle.DynamicProxy; +//using IRaCIS.Core.Application.Contracts.Dicom.DTO; +//using IRaCIS.Core.Infra.EFCore; + +//using System.Linq; +//using IRaCIS.Core.Domain.Models; +//using IRaCIS.Core.Domain.Share; + +//namespace IRaCIS.Core.API.Utility.AOP +//{ +//#pragma warning disable +// public class QANoticeAOP : IInterceptor +// { +// private readonly IRepository _qaNoticeRepository; + +// private readonly IRepository _studyRepository; +// private readonly IRepository _userTrialRepository; +// private readonly IRepository _userTrialSiteRepository; +// private readonly IUserInfo _userInfo; + +// public QANoticeAOP(IRepository qaNoticeRepository, +// IUserInfo userInfo, IRepository studyRepository, IRepository userTrialRepository, IRepository userTrialSiteRepository) +// { +// _qaNoticeRepository = qaNoticeRepository; + +// _studyRepository = studyRepository; +// _userTrialRepository = userTrialRepository; +// _userTrialSiteRepository = userTrialSiteRepository; +// _userInfo = userInfo; +// } + +// public void Intercept(IInvocation invocation) +// { +// //处理拦截的方法 +// invocation.Proceed(); + +// if (invocation.Method.Name == "UpdateStudyStatus") +// { +// var studyStatus = invocation.Arguments[0] as StudyStatusDetailCommand; + +// var study = _studyRepository.FirstOrDefault(t=>t.Id==studyStatus.StudyId); + + +// if (study.Status == (int)StudyStatus.Uploaded) +// { +// _qaNoticeRepository.Add(new QANotice() +// { +// TrialId = study.TrialId, +// SubjectVisitId = study.Id, +// FromUser = _userInfo.RealName, +// FromUserId = _userInfo.Id, +// FromUserType = _userInfo.UserTypeShortName, + +// NoticeTypeEnum = NoticeType.NotNeedNotice, +// NeedDeal = false, +// StudyStatusStr = "Uploaded", +// Message = $"IC : {_userInfo.RealName} has uploaded {study.StudyCode} ", +// SendTime = DateTime.Now, +// }); +// } + +// #region 处理QA通知模块 + +// //查询项目的参与者 和 负责site下IC用户 +// var trialUserList = _userTrialRepository.Where(t => t.TrialId == study.TrialId).ToList(); + +// // 找到该study 关联Site 下的IC + +// var crcList = _userTrialSiteRepository.Where(t => +// t.SiteId == study.SiteId && t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator && t.TrialId == study.TrialId).ToList(); + + +// var qaList = trialUserList.Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).ToList(); + +// var pm = trialUserList.FirstOrDefault(t => t.User.UserTypeEnum == UserTypeEnum.ProjectManager); + + + + +// // IC =>QA +// if (studyStatus.Status == (int)StudyStatus.QARequested) +// { +// //找出当前操作的IC +// //PM 或者admin可以代替IC角色 不能从IC列表中查询用户 +// //var currentCRC = trialUserList.First(t => t.UserId == _userInfo.Id); + +// var notice = new QANotice() +// { +// TrialId = study.TrialId, +// SubjectVisitId = study.Id, +// FromUser = _userInfo.RealName, +// FromUserId = _userInfo.Id, +// FromUserType = _userInfo.UserTypeShortName, + +// //FromUser = currentCRC.UserRealName, +// //FromUserId = _userInfo.Id, +// //FromUserType = currentCRC.UserType, +// NoticeTypeEnum = NoticeType.CRC_RequestToQA_NoticeQA, +// NeedDeal = true, +// StudyStatusStr = "QA Requested", +// Message = +// $"IC -> QA : {_userInfo.RealName} request QA {study.StudyCode} , Inquiry can be performed! ", +// SendTime = DateTime.Now, +// }; + +// qaList.ForEach(t => notice.QANoticeUserList.Add(new QANoticeUser() +// { +// QANoticeId = notice.Id, +// SubjectVisitId = study.Id, +// ToUser = t.User.LastName + " / " + t.User.FirstName, +// ToUserId = t.UserId, +// ToUserType = t.User.UserTypeRole.UserTypeShortName +// })); + +// _qaNoticeRepository.Add(notice); + +// //DealRequestToQA(study.Id); + +// var needDealNoticeList = _qaNoticeRepository.AsQueryable() +// .Where(t => t.SubjectVisitId == study.Id && t.NeedDeal && t.NoticeTypeEnum == NoticeType.CRC_RequestToQA_NoticeQA).ToList(); + +// needDealNoticeList.ForEach(t => +// { +// t.NeedDeal = false; +// t.DealTime = DateTime.Now; +// _qaNoticeRepository.Update(t); +// }); +// } + +// // QA =>IC 向IC推送消息影像有问题 同时作为 requestToQA 的边界 +// else if (studyStatus.Status == (int)StudyStatus.QAing) +// { +// //找出当前操作的QA 如果是pm 或者admin 代替操作 此时会有问题 所以 谁代替,就以谁的名义执行 +// //var currentQA = qaList.First(t => t.UserId == _userInfo.Id); +// //var currentQA = trialUserList.First(t => t.UserId == _userInfo.Id); + +// //在项目IC列表中筛选出 负责该study关联 site的IC +// var siteCRCList = _userTrialSiteRepository.Where(t => +// t.SiteId == study.SiteId && t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator && t.TrialId == study.TrialId).ToList(); + +// //查询项目的参与者 和 负责site下IC用户 + + + +// var notice = new QANotice() +// { +// TrialId = study.TrialId, +// SubjectVisitId = study.Id, +// //FromUser = currentQA.UserRealName, +// //FromUserId = _userInfo.Id, +// //FromUserType = currentQA.UserType, +// FromUser = _userInfo.RealName, +// FromUserId = _userInfo.Id, +// FromUserType = _userInfo.UserTypeShortName, +// NoticeTypeEnum = NoticeType.QA_InQA_NoticeCRC, +// NeedDeal = true, +// StudyStatusStr = "In QA", +// Message = $"QA -> IC : {_userInfo.RealName} inquiry {study.StudyCode} ", +// SendTime = DateTime.Now, +// }; + +// siteCRCList.ForEach(t => notice.QANoticeUserList.Add(new QANoticeUser() +// { +// QANoticeId = notice.Id, +// SubjectVisitId = study.Id, +// ToUser = t.User.LastName + " / " + t.User.FirstName, +// ToUserId = t.UserId, +// ToUserType = t.UserTypeRole.UserTypeShortName +// })); + +// //添加 发送给IC的消息 消息和IC是 一对多 +// _qaNoticeRepository.Add(notice); + + +// //处理 消息 标记已处理 +// var needDealNoticeList = _qaNoticeRepository.AsQueryable() +// .Where(t => t.SubjectVisitId == study.Id && t.NeedDeal && +// (t.NoticeTypeEnum == NoticeType.CRC_RequestToQA_NoticeQA || +// t.NoticeTypeEnum == NoticeType.CRC_ReUpload_NoticeQA || +// t.NoticeTypeEnum == NoticeType.CRC_QARecordDialogPost_NoticeQA)).ToList(); + +// needDealNoticeList.ForEach(t => +// { +// t.NeedDeal = false; +// t.DealTime = DateTime.Now; +// _qaNoticeRepository.Update(t); +// }); + + +// } +// // QA =>QA 给自己的消息 通知需要匿名化 同时作为 requestToQA 的边界 +// else if (studyStatus.Status == (int)StudyStatus.QAFinish) +// { + +// //找出当前操作的QA 如果是pm 或者admin 代替操作 此时会有问题 所以 谁代替,就以谁的名义执行 +// //var currentQA = qaList.First(t => t.UserId == _userInfo.Id); +// //var currentQA = trialUserList.First(t => t.UserId == _userInfo.Id); + +// //发送给当前项目QA列表 + +// var notice = new QANotice() +// { +// TrialId = study.TrialId, +// SubjectVisitId = study.Id, +// //FromUser = currentQA.UserRealName, +// //FromUserId = _userInfo.Id, +// //FromUserType = currentQA.UserType, +// FromUser = _userInfo.RealName, +// FromUserId = _userInfo.Id, +// FromUserType = _userInfo.UserTypeShortName, +// NoticeTypeEnum = NoticeType.QA_QAPass_NoticeQA, +// NeedDeal = true, +// StudyStatusStr = "QA-Passed", +// Message = +// $"QA -> QA : {_userInfo.RealName} inquiry {study.StudyCode} finished,Anonymization can be performed!", +// SendTime = DateTime.Now, +// }; + +// qaList.ForEach(t => notice.QANoticeUserList.Add(new QANoticeUser() +// { +// QANoticeId = notice.Id, +// SubjectVisitId = study.Id, +// ToUser = t.User.LastName+" / "+t.User.FirstName, +// ToUserId = t.UserId, +// ToUserType = t.User.UserTypeRole.UserTypeShortName +// })); + +// _qaNoticeRepository.Add(notice); + +// //处理 消息 标记已处理 存在意外情况 qa发给IC的 但是qa里面设置了 通过或者不通过 此时qa发送的消息也设置为已处理 +// var needDealNoticeList = _qaNoticeRepository.AsQueryable() +// .Where(t => t.SubjectVisitId == study.Id && t.NeedDeal && +// (t.NoticeTypeEnum == NoticeType.CRC_RequestToQA_NoticeQA || +// t.NoticeTypeEnum == NoticeType.CRC_ReUpload_NoticeQA || +// t.NoticeTypeEnum == NoticeType.CRC_QARecordDialogPost_NoticeQA || + +// t.NoticeTypeEnum == NoticeType.QA_QARecordDialogPost_NoticeCRC || +// t.NoticeTypeEnum == NoticeType.QA_InQA_NoticeCRC || +// t.NoticeTypeEnum == NoticeType.QA_AddQARecord_NoticeCRC)).ToList(); + +// needDealNoticeList.ForEach(t => +// { +// t.NeedDeal = false; +// t.DealTime = DateTime.Now; +// _qaNoticeRepository.Update(t); +// }); + + +// } +// // QA =>IC 暂时不用发送消息给IC 因为IC 暂时没有入口回复 同时作为 requestToQA 的边界 +// else if (studyStatus.Status == (int)StudyStatus.QAFInishNotPass) +// { + +// _qaNoticeRepository.Add(new QANotice() +// { +// TrialId = study.TrialId, +// SubjectVisitId = study.Id, +// FromUser = _userInfo.RealName, +// FromUserId = _userInfo.Id, +// FromUserType = _userInfo.UserTypeShortName, +// NoticeTypeEnum = NoticeType.NotNeedNotice, +// NeedDeal = false, +// StudyStatusStr = "QA-Failed", +// Message = $"QA : {_userInfo.RealName} set {study.StudyCode} QA-Failed !", +// SendTime = DateTime.Now, +// }); + +// //处理 消息 标记已处理 +// var needDealNoticeList = _qaNoticeRepository.AsQueryable() +// .Where(t => t.SubjectVisitId == study.Id && t.NeedDeal && +// (t.NoticeTypeEnum == NoticeType.CRC_RequestToQA_NoticeQA || +// t.NoticeTypeEnum == NoticeType.CRC_ReUpload_NoticeQA || +// t.NoticeTypeEnum == NoticeType.CRC_QARecordDialogPost_NoticeQA || + + +// t.NoticeTypeEnum == NoticeType.QA_QARecordDialogPost_NoticeCRC || +// t.NoticeTypeEnum == NoticeType.QA_InQA_NoticeCRC || +// t.NoticeTypeEnum == NoticeType.QA_AddQARecord_NoticeCRC)).ToList(); + +// needDealNoticeList.ForEach(t => +// { +// t.NeedDeal = false; +// t.DealTime = DateTime.Now; +// _qaNoticeRepository.Update(t); +// }); +// } + +// #endregion + + +// } + +// else if (invocation.Method.Name == "ReUploadSameStudy") +// { +// var studyId = Guid.Parse(invocation.Arguments[0].ToString()); + +// var study = _studyRepository.FirstOrDefault(t => t.Id == studyId); +// var status = study.Status; + +// //处理IC 重传时 QA消息 + +// if (status == (int)StudyStatus.QAing) +// { +// //查询项目的参与者 和 负责site下IC用户 +// var trialUserList = _userTrialRepository.Where(t => t.TrialId == study.TrialId).ToList(); + +// // 找到该study 关联Site 下的IC + +// var crcList = _userTrialSiteRepository.Where(t => +// t.SiteId == study.SiteId && t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator && t.TrialId == study.TrialId).ToList(); + +// var qaList = trialUserList.Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).ToList(); + +// //IC =>QA IC的职能被PM 或者admin代替 +// //if (_userInfo.UserTypeEnumInt == (int)UserType.ClinicalResearchCoordinator) +// { +// //PM 或者admin可以代替IC角色 不能从IC列表中查询用户 +// //var currentCRC = trialUserList.First(t => t.UserId == _userInfo.Id); + +// var notice = new QANotice() +// { +// TrialId = study.TrialId, +// SubjectVisitId = study.Id, +// //FromUser = currentCRC.UserRealName, +// //FromUserId = _userInfo.Id, +// //FromUserType = currentCRC.UserType, +// FromUser = _userInfo.RealName, +// FromUserId = _userInfo.Id, +// FromUserType = _userInfo.UserTypeShortName, +// NoticeTypeEnum = NoticeType.CRC_ReUpload_NoticeQA, +// NeedDeal = true, +// Message = $"IC -> QA :{_userInfo.RealName} has reuploaded {study.StudyCode} , Need to be inquiry again", +// SendTime = DateTime.Now +// }; + +// qaList.ForEach(t => notice.QANoticeUserList.Add(new QANoticeUser() +// { +// QANoticeId = notice.Id, +// SubjectVisitId = study.Id, +// ToUser = t.User.LastName+" / "+t.User.FirstName, +// ToUserId = t.UserId, +// ToUserType = t.User.UserTypeRole.UserTypeShortName +// })); + +// _qaNoticeRepository.Add(notice); + +// //这里作为 QA 设置 Inqa 状态的回复 或者QA和IC对话的 +// var needDealNoticeList = _qaNoticeRepository.Where(t => t.SubjectVisitId == study.Id && t.NeedDeal +// && (t.NoticeTypeEnum == NoticeType.QA_InQA_NoticeCRC || t.NoticeTypeEnum == NoticeType.QA_QARecordDialogPost_NoticeCRC)) +// .ToList(); + +// needDealNoticeList.ForEach(t => +// { +// t.NeedDeal = false; +// t.DealTime = DateTime.Now; +// _qaNoticeRepository.Update(t); +// }); + +// } +// } +// else +// { +// //不是QAing 的重传 不发送qa消息 +// return; +// } + + + +// } + +// else if (invocation.Method.Name == "DicomAnonymize") +// { +// var studyId = Guid.Parse(invocation.Arguments[0].ToString()); + +// var study = _studyRepository.FirstOrDefault(t => t.Id == studyId); + +// #region 处理QA通知 匿名化完毕 通知PM + +// //查询项目的参与者 和 负责site下IC用户 +// var trialUserList = _userTrialRepository.Where(t => t.TrialId == study.TrialId).ToList(); + +// // 找到该study 关联Site 下的IC + +// var crcList = _userTrialSiteRepository.Where(t => +// t.SiteId == study.SiteId && t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator && t.TrialId == study.TrialId).ToList(); + +// var qaList = trialUserList.Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).ToList(); + +// // +// var pm = trialUserList.FirstOrDefault(t => t.User.UserTypeEnum == UserTypeEnum.ProjectManager); + + +// //找出当前操作的QA 如果是pm 或者admin 代替操作 此时会有问题 所以 谁代替,就以谁的名义执行 +// //var currentQA = trialUserList.First(t => +// // t.UserTypeEnum == UserType.IQC && t.UserId == _userInfo.Id); +// //var currentQA = trialUserList.First(t => t.UserId == _userInfo.Id); + +// var notice = new QANotice() +// { +// TrialId = study.TrialId, +// SubjectVisitId = study.Id, +// //FromUser = currentQA.UserRealName, +// //FromUserId = _userInfo.Id, +// //FromUserType = currentQA.UserType, +// FromUser = _userInfo.RealName, +// FromUserId = _userInfo.Id, +// FromUserType = _userInfo.UserTypeShortName, +// NoticeTypeEnum = NoticeType.QA_Anonymized_NoticeQA, +// NeedDeal = true, +// StudyStatusStr = "Anonymized", +// //Message = $"QA -> PM :{_userInfo.RealName} has anonymized {study.StudyCode} ,Forward can be performed!!", +// Message = $"QA -> QA :{_userInfo.RealName} has anonymized {study.StudyCode} ,Forward can be performed!!", + +// SendTime = DateTime.Now, + +// }; + +// //notice.QANoticeUserList.Add(new QANoticeUser() +// //{ +// // QANoticeId = notice.Id, +// // StudyId = study.Id, +// // ToUser = pm.UserRealName, +// // ToUserId = pm.UserId, +// // ToUserType = pm.UserType +// //}); + +// qaList.ForEach(t => notice.QANoticeUserList.Add(new QANoticeUser() +// { +// QANoticeId = notice.Id, +// SubjectVisitId = study.Id, +// ToUser = t.User.LastName+" / "+t.User.FirstName, +// ToUserId = t.UserId, +// ToUserType = t.User.UserTypeRole.UserTypeShortName +// })); + +// _qaNoticeRepository.Add(notice); + +// var needDealNoticeList = _qaNoticeRepository.AsQueryable() +// .Where(t => t.SubjectVisitId == study.Id && t.NeedDeal && (t.NoticeTypeEnum == NoticeType.QA_QAPass_NoticeQA)).ToList(); + +// needDealNoticeList.ForEach(t => +// { +// t.NeedDeal = false; +// t.DealTime = DateTime.Now; +// _qaNoticeRepository.Update(t); +// }); + + +// #endregion +// } + +// else if (invocation.Method.Name == "ForwardStudy") +// { +// var studyId = Guid.Parse(invocation.Arguments[0].ToString()); + +// var study = _studyRepository.FirstOrDefault(t => t.Id == studyId); + +// //匿名化操作产生的消息 设置为已经处理 +// _qaNoticeRepository.Add(new QANotice() +// { +// TrialId = study.TrialId, +// SubjectVisitId = study.Id, +// //FromUser = currentQA.UserRealName, +// //FromUserId = _userInfo.Id, +// //FromUserType = currentQA.UserType, +// FromUser = _userInfo.RealName, +// FromUserId = _userInfo.Id, +// FromUserType = _userInfo.UserTypeShortName, +// NoticeTypeEnum = NoticeType.NotNeedNotice, +// NeedDeal = false, +// StudyStatusStr = "Forwarded", +// //Message = $"PM :{_userInfo.RealName} has forwarded {study.StudyCode} !", +// Message = $"QA :{_userInfo.RealName} has forwarded {study.StudyCode} !", +// SendTime = DateTime.Now, +// }); + +// var needDealList = _qaNoticeRepository.Where(t => +// t.SubjectVisitId == study.Id && t.NeedDeal && t.NoticeTypeEnum == NoticeType.QA_Anonymized_NoticeQA).ToList(); + +// needDealList.ForEach(t => +// { +// t.NeedDeal = false; +// t.DealTime = DateTime.Now; +// _qaNoticeRepository.Update(t); +// }); +// } + +// var success = _qaNoticeRepository.SaveChanges(); + +// if (!success) +// { +// throw new Exception("Send QA message failed"); +// } +// } + + +// } +//} \ No newline at end of file diff --git a/IRaCIS.Core.Application/AOP/TrialStatusAutofacAOP.cs b/IRaCIS.Core.Application/AOP/TrialStatusAutofacAOP.cs new file mode 100644 index 0000000..e1523b6 --- /dev/null +++ b/IRaCIS.Core.Application/AOP/TrialStatusAutofacAOP.cs @@ -0,0 +1,89 @@ +using Castle.DynamicProxy; +using EasyCaching.Core; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.AOP +{ + public class TrialStatusAutofacAOP : IAsyncInterceptor + { + private readonly IEasyCachingProvider _provider; + + public TrialStatusAutofacAOP(IEasyCachingProvider provider) + { + _provider = provider; + } + + + + public void InterceptAsynchronous(IInvocation invocation) + { + invocation.Proceed(); + } + + //这里AOP 处理两个方法 分别是 项目的添加和更新、项目状态的变更 + + public void InterceptAsynchronous(IInvocation invocation) + { + + + //处理拦截的方法 + invocation.Proceed(); + + + + dynamic result = invocation.ReturnValue; + + //接口成功了,才修改缓存 + if (!result.IsSuccess) + { + return; + } + + #region 处理项目列表的查询 在前端界面已经在某个界面,但是服务器重置了,此时没有缓存项目信息,接口不能正确返回,因故采用,启动时查询,每天查询一次,缓存一天,然后项目添加、更改状态时,及时更新 + + //if (invocation.Method.Name == "GetTrialList") + //{ + // //在此 将当前查询的项目Id 和对应的项目状态进行缓存 + // dynamic result = invocation.ReturnValue; + // foreach (var item in result.CurrentPageData) + // { + // _provider.Remove(item.Id.ToString()); + // _provider.Set(item.Id.ToString(), item.TrialStatusStr.ToString(), TimeSpan.FromDays(1)); + // } + //} + + #endregion + + if (invocation.Method.Name == "AddOrUpdateTrial") + { + //如果是添加 那么将对应的初始状态加进去 更新状态是单独操作的 + + var trialModel = (invocation.Arguments[0] as TrialCommand).IfNullThrowConvertException(); + if (trialModel.Id == null || trialModel.Id == Guid.Empty) + { + _provider.Set(result.Data.Id.ToString(), StaticData.TrialState.TrialOngoing, TimeSpan.FromDays(1)); + } + + + } + // 更新缓存 + else if (invocation.Method.Name == "UpdateTrialStatus") + { + //项目状态更新,也需要及时更新 + _provider.Set(invocation.Arguments[0].ToString(), invocation.Arguments[1].ToString(), TimeSpan.FromDays(1)); + + ////Test参数是否符合要求 + //var tt = invocation.Arguments[0].ToString(); + //var cc = _provider.Get(invocation.Arguments[0].ToString()); + + } + + } + + public void InterceptSynchronous(IInvocation invocation) + { + invocation.Proceed(); + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Auth/IRaCISClaims.cs b/IRaCIS.Core.Application/Auth/IRaCISClaims.cs new file mode 100644 index 0000000..4461407 --- /dev/null +++ b/IRaCIS.Core.Application/Auth/IRaCISClaims.cs @@ -0,0 +1,53 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Auth +{ + public class IRaCISClaims + { + public Guid Id { get; set; } + public string FullName { get; set; } = String.Empty; + public string Code { get; set; } = String.Empty; + public string RealName { get; set; } = String.Empty; + + public string UserTypeShortName { get; set; } = String.Empty; + + public UserTypeEnum UserTypeEnum { get; set; } + + public string PermissionStr { get; set; } = String.Empty; + + public Guid UserTypeId { get; set; } + + public int IsAdmin { get; } + + public bool IsTestUser { get; set; } + + public string Phone { get; set; } = String.Empty; + + public static IRaCISClaims Create(UserBasicInfo user) + { + return new IRaCISClaims + { + Id = user.Id, + FullName = user.UserName, + RealName = user.RealName, + UserTypeEnum=user.UserTypeEnum, + UserTypeId=user.UserTypeId, + IsTestUser=user.IsTestUser, + Code = user.Code, + PermissionStr = user.PermissionStr, + + UserTypeShortName = user.UserTypeShortName + }; + } + public static IRaCISClaims Create(DoctorAccountDTO doctor) + { + return new IRaCISClaims + { + Id = doctor.Id, + FullName = doctor.FirstName + doctor.LastName, + Phone = doctor.Phone, + }; + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Auth/IRaCISPolicy.cs b/IRaCIS.Core.Application/Auth/IRaCISPolicy.cs new file mode 100644 index 0000000..e469ec2 --- /dev/null +++ b/IRaCIS.Core.Application/Auth/IRaCISPolicy.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Auth +{ + + + public static class IRaCISPolicy + { + public const string IC = "IC"; + + public const string PM = "PM"; + + public const string IQC = "IQC"; + + public const string PM_IQC = "PM_IQC"; + + public const string CRC_IQC = "CRC_IQC"; + + + public const string SPM_CPM = "SPM_CPM"; + public const string PM_APM = "PM_APM"; + public const string PM_APM_CRC = "PM_APM_CRC"; + + + + + public const string PM_APM_SPM_CPM = "PM_APM_SPM_CPM"; + + public const string PM_APM_SPM_CPM_SMM_CMM = "PM_APM_SPM_CPM_SMM_CMM"; + + public const string PM_APM_CRC_QC = "PM_APM_CRC_QC"; + + + } +} diff --git a/IRaCIS.Core.Application/Auth/JwtSetting.cs b/IRaCIS.Core.Application/Auth/JwtSetting.cs new file mode 100644 index 0000000..1987c91 --- /dev/null +++ b/IRaCIS.Core.Application/Auth/JwtSetting.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Text; +using Microsoft.IdentityModel.Tokens; + +namespace IRaCIS.Core.Application.Auth +{ + public class JwtSetting + { + /// + /// 颁发者 + /// + public string Issuer { get; set; } = String.Empty; + + /// + /// 接收者 + /// + public string Audience { get; set; } = String.Empty; + + /// + /// 令牌密码 + /// + public string SecurityKey { get; set; } = String.Empty; + + /// + /// 过期时间 + /// + public int TokenExpireDays { get; set; } + + //public Dictionary Claims { get; set; } + + /// + /// 签名 + /// + public SigningCredentials Credentials + { + get + { + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey)); + return new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + } + } + } +} diff --git a/IRaCIS.Core.Application/Auth/TokenService.cs b/IRaCIS.Core.Application/Auth/TokenService.cs new file mode 100644 index 0000000..4890a03 --- /dev/null +++ b/IRaCIS.Core.Application/Auth/TokenService.cs @@ -0,0 +1,60 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using IRaCIS.Core.Domain.Share; +using Microsoft.Extensions.Options; + +namespace IRaCIS.Core.Application.Auth +{ + + public interface ITokenService + { + string GetToken(IRaCISClaims user); + } + + + public class TokenService : ITokenService + { + private readonly JwtSetting _jwtSetting; + + public TokenService(IOptions option) + { + _jwtSetting = option.Value; + } + + public string GetToken(IRaCISClaims user) + { + //创建用户身份标识,可按需要添加更多信息 + var claims = new Claim[] + { + new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + new Claim(JwtIRaCISClaimType.Id, user.Id.ToString()), + new Claim(JwtIRaCISClaimType.Name, user.FullName), + new Claim(JwtIRaCISClaimType.RealName, user.RealName), + new Claim(JwtIRaCISClaimType.Code,user.Code), + new Claim(JwtIRaCISClaimType.UserTypeId,user.UserTypeId.ToString()), + new Claim(JwtIRaCISClaimType.UserTypeEnum,user.UserTypeEnum.ToString()), + new Claim(JwtIRaCISClaimType.UserTypeEnumInt,((int)user.UserTypeEnum).ToString()), + new Claim(JwtIRaCISClaimType.UserTypeShortName,user.UserTypeShortName), + new Claim(JwtIRaCISClaimType.PermissionStr,user.PermissionStr), + + new Claim(JwtIRaCISClaimType.IsTestUser,user.IsTestUser.ToString()) + }; + + ////创建令牌 + var token = new JwtSecurityToken( + issuer: _jwtSetting.Issuer, + audience: _jwtSetting.Audience, + signingCredentials: _jwtSetting.Credentials, + claims: claims, + notBefore: DateTime.Now, + expires: DateTime.Now.AddDays(_jwtSetting.TokenExpireDays) + ); + + string jwtToken = new JwtSecurityTokenHandler().WriteToken(token); + return jwtToken; + + } + } + + +} diff --git a/IRaCIS.Core.Application/BackGroundJob/CacheTrialStatusQuartZJob.cs b/IRaCIS.Core.Application/BackGroundJob/CacheTrialStatusQuartZJob.cs new file mode 100644 index 0000000..e3be274 --- /dev/null +++ b/IRaCIS.Core.Application/BackGroundJob/CacheTrialStatusQuartZJob.cs @@ -0,0 +1,72 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using EasyCaching.Core; +using IRaCIS.Core.Domain; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; +using Microsoft.Extensions.Logging; +using Quartz; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Application.Services.BackGroundJob +{ + + public class CacheTrialStatusQuartZJob: IJob + { + + private readonly IRepository _trialRepository; + private readonly IEasyCachingProvider _provider; + private readonly ILogger _logger; + private readonly IRepository _systemAnonymizationRepository; + + public CacheTrialStatusQuartZJob(IRepository trialRepository, IEasyCachingProvider provider,ILogger logger, IRepository systemAnonymizationRepository) + { + _trialRepository = trialRepository; + _provider = provider; + _logger = logger; + _systemAnonymizationRepository = systemAnonymizationRepository; + } + + public async Task Execute(IJobExecutionContext context) + + { + _logger.LogInformation($"开始执行QuartZ定时任务作业"); + try + { + await MemoryCacheTrialStatus(); + + await MemoryCacheAnonymizeData(); + + } + catch (Exception e) + { + _logger.LogError($" 查询和缓存过程出现异常"+e.Message); + } + _logger.LogInformation("QuartZ定时任务作业结束"); + } + + + public async Task MemoryCacheTrialStatus() + { + var list = await _trialRepository.Select(t => new { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr }) + .ToListAsync(); + + list.ForEach(t => _provider.Set(t.TrialId.ToString(), t.TrialStatusStr, TimeSpan.FromDays(7))); + + } + + public async Task MemoryCacheAnonymizeData() + { + var systemAnonymizationList = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync(); + + _provider.Set(StaticData.Anonymize.Anonymize_AddFixedFiled, systemAnonymizationList.Where(t => t.IsAdd && t.IsFixed).ToList(), TimeSpan.FromDays(7)); + _provider.Set(StaticData.Anonymize.Anonymize_AddIRCInfoFiled, systemAnonymizationList.Where(t => t.IsAdd && t.IsFixed == false).ToList(), TimeSpan.FromDays(7)); + _provider.Set(StaticData.Anonymize.Anonymize_FixedField, systemAnonymizationList.Where(t => t.IsAdd == false && t.IsFixed).ToList(), TimeSpan.FromDays(7)); + _provider.Set(StaticData.Anonymize.Anonymize_IRCInfoField, systemAnonymizationList.Where(t => t.IsAdd == false && t.IsFixed == false).ToList(), TimeSpan.FromDays(7)); + } + + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/BackGroundJob/IRaCISCacheHangfireJob.cs b/IRaCIS.Core.Application/BackGroundJob/IRaCISCacheHangfireJob.cs new file mode 100644 index 0000000..80a3d16 --- /dev/null +++ b/IRaCIS.Core.Application/BackGroundJob/IRaCISCacheHangfireJob.cs @@ -0,0 +1,99 @@ +using EasyCaching.Core; +using IRaCIS.Core.Domain.Share; +using Microsoft.EntityFrameworkCore.SqlServer.Query.Internal; +using Microsoft.Extensions.Logging; + +namespace IRaCIS.Application.Services.BackGroundJob +{ + + public interface IIRaCISCacheHangfireJob + { + + Task ProjectStartCache(); + Task MemoryCacheTrialStatus(); + + Task MemoryCacheAnonymizeData(); + + Task CacheUserTypePermission(Guid? cacheUserTypeId); + } + public class IRaCISCacheHangfireJob: IIRaCISCacheHangfireJob + { + private readonly IRepository _trialRepository; + private readonly IEasyCachingProvider _provider; + private readonly ILogger _logger; + private readonly IRepository _systemAnonymizationRepository; + + private readonly IRepository _userTypeMenuRepository; + + public IRaCISCacheHangfireJob(IRepository trialRepository, + IRepository systemAnonymizationRepository, IRepository userTypeMenuRepository, + IEasyCachingProvider provider,ILogger logger) + { + _trialRepository = trialRepository; + _provider = provider; + _logger = logger; + _systemAnonymizationRepository = systemAnonymizationRepository; + _userTypeMenuRepository = userTypeMenuRepository; + } + + + public async Task ProjectStartCache() + { + _logger.LogInformation("hangfire 定时缓存项目状态任务开始~"); + try + { + await MemoryCacheTrialStatus(); + + await MemoryCacheAnonymizeData(); + + await CacheUserTypePermission(); + } + catch (Exception e) + { + _logger.LogError("hangfire 定时任务执行失败" + e.Message); + + } + + _logger.LogInformation("hangfire 定时任务执行结束"); + } + + public async Task MemoryCacheTrialStatus() + { + var list = await _trialRepository.Select(t => new { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr }) + .ToListAsync(); + + list.ForEach(t => _provider.Set(t.TrialId.ToString(), t.TrialStatusStr, TimeSpan.FromDays(7))); + + } + + + + + + + + public async Task MemoryCacheAnonymizeData() + { + var systemAnonymizationList = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync(); + + _provider.Set(StaticData.Anonymize.Anonymize_AddFixedFiled, systemAnonymizationList.Where(t => t.IsAdd && t.IsFixed).ToList(), TimeSpan.FromDays(7)); + _provider.Set(StaticData.Anonymize.Anonymize_AddIRCInfoFiled, systemAnonymizationList.Where(t => t.IsAdd && t.IsFixed == false).ToList(), TimeSpan.FromDays(7)); + _provider.Set(StaticData.Anonymize.Anonymize_FixedField, systemAnonymizationList.Where(t => t.IsAdd == false && t.IsFixed).ToList(), TimeSpan.FromDays(7)); + _provider.Set(StaticData.Anonymize.Anonymize_IRCInfoField, systemAnonymizationList.Where(t => t.IsAdd == false && t.IsFixed == false).ToList(), TimeSpan.FromDays(7)); + } + + public async Task CacheUserTypePermission(Guid? cacheUserTypeId=null) + { + + var permissionList = await _userTypeMenuRepository.Where(t => t.Menu.MenuType == "F") + .WhereIf(cacheUserTypeId != null, t => t.UserTypeId == cacheUserTypeId.Value).Select(t => new { t.UserTypeId, t.Menu.PermissionStr }).ToListAsync(); + + foreach (var userTypeGroup in permissionList.GroupBy(t => t.UserTypeId)) + { + _provider.Set($"{StaticData.CacheKey.UserTypeId}_{userTypeGroup.Key}", userTypeGroup.Select(t => t.PermissionStr).ToList(), TimeSpan.FromDays(7)); + } + } + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/BackGroundJob/ObtainTaskAutoCancelJob.cs b/IRaCIS.Core.Application/BackGroundJob/ObtainTaskAutoCancelJob.cs new file mode 100644 index 0000000..9401188 --- /dev/null +++ b/IRaCIS.Core.Application/BackGroundJob/ObtainTaskAutoCancelJob.cs @@ -0,0 +1,43 @@ +using IRaCIS.Core.Infra.EFCore; +using Microsoft.Extensions.Logging; + +namespace IRaCIS.Core.Application.BackGroundJob +{ + public interface IObtainTaskAutoCancelJob + { + Task CancelQCObtaion(Guid subjectVisitId,DateTime startTime); + } + + public class ObtainTaskAutoCancelJob : IObtainTaskAutoCancelJob + { + private readonly IRepository _subjectVisitRepository; + private readonly ILogger _logger; + + public ObtainTaskAutoCancelJob(IRepository subjectVisitRepository, ILogger logger) + { + _subjectVisitRepository = subjectVisitRepository; + _logger = logger; + } + public async Task CancelQCObtaion(Guid subjectVisitId, DateTime startTime) + { + try + { + var dbSubjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId).IfNullThrowException(); + + dbSubjectVisit.IsTake = false; + dbSubjectVisit.CurrentActionUserId = null; + dbSubjectVisit.CurrentActionUserExpireTime = null; + + var success = await _subjectVisitRepository.SaveChangesAsync(); + + _logger.LogWarning($"任务建立时间:{startTime} 取消时间:{DateTime.Now} 取消 患者检查批次:{ subjectVisitId }success:{success}"); + } + catch (Exception e) + { + _logger.LogError("hangfire 定时任务执行失败" + e.Message); + + } + + } + } +} diff --git a/IRaCIS.Core.Application/BaseService.cs b/IRaCIS.Core.Application/BaseService.cs new file mode 100644 index 0000000..9101ffa --- /dev/null +++ b/IRaCIS.Core.Application/BaseService.cs @@ -0,0 +1,116 @@ +using AutoMapper; +using IRaCIS.Application.Services.BusinessFilter; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Localization; +using Panda.DynamicWebApi; +using Panda.DynamicWebApi.Attributes; +using System.Diagnostics.CodeAnalysis; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application +{ + +#pragma warning disable CS8618 + + + #region 非泛型版本 + + [Authorize, DynamicWebApi, UnifiedApiResultFilter] + public class BaseService : IBaseService, IDynamicWebApi + { + public IMapper _mapper { get; set; } + + public IUserInfo _userInfo { get; set; } + + public IRepository _repository { get; set; } + + public IStringLocalizer _localizer { get; set; } + + public IWebHostEnvironment _hostEnvironment { get; set; } + + + + + public static IResponseOutput Null404NotFound(TEntity? businessObject) where TEntity : class + { + return new ResponseOutput() + .NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist); + } + } + + + public interface IBaseService + { + [MemberNotNull(nameof(_mapper))] + public IMapper _mapper { get; set; } + + [MemberNotNull(nameof(_userInfo))] + public IUserInfo _userInfo { get; set; } + + [MemberNotNull(nameof(_repository))] + public IRepository _repository { get; set; } + + [MemberNotNull(nameof(_localizer))] + public IStringLocalizer _localizer { get; set; } + + [MemberNotNull(nameof(_hostEnvironment))] + public IWebHostEnvironment _hostEnvironment { get; set; } + + } + #endregion + + + #region 泛型版本测试 + + + public interface IBaseServiceTest where T : Entity + { + [MemberNotNull(nameof(_mapper))] + public IMapper _mapper { get; set; } + + [MemberNotNull(nameof(_userInfo))] + public IUserInfo _userInfo { get; set; } + + [MemberNotNull(nameof(_repository))] + public IRepository _repository { get; set; } + + [MemberNotNull(nameof(_localizer))] + public IStringLocalizer _localizer { get; set; } + + + + } + + + [Authorize, DynamicWebApi, UnifiedApiResultFilter] + public class BaseServiceTest : IBaseServiceTest, IDynamicWebApi where T : Entity + { + public IMapper _mapper { get; set; } + + public IUserInfo _userInfo { get; set; } + + public IRepository _repository { get; set; } + + public IStringLocalizer _localizer { get; set; } + + public static IResponseOutput Null404NotFound(TEntity? businessObject) where TEntity : class + { + return new ResponseOutput() + .NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist); + } + + + } + + + #endregion + + + + + + + +} diff --git a/IRaCIS.Core.Application/BusinessFilter/LimitUserRequestAuthorization.cs b/IRaCIS.Core.Application/BusinessFilter/LimitUserRequestAuthorization.cs new file mode 100644 index 0000000..953f1ab --- /dev/null +++ b/IRaCIS.Core.Application/BusinessFilter/LimitUserRequestAuthorization.cs @@ -0,0 +1,144 @@ +using EasyCaching.Core; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Application.Filter; + + + + +public class LimitUserRequestAuthorization : IAsyncAuthorizationFilter +{ + + + private readonly IEasyCachingProvider _provider; + + private readonly IUserInfo _userInfo; + + private readonly IOptionsMonitor _verifyConfig; + + public LimitUserRequestAuthorization(IEasyCachingProvider provider, IUserInfo userInfo, IOptionsMonitor verifyConfig) + { + _provider = provider; + _userInfo = userInfo; + _verifyConfig = verifyConfig; + } + + + public async Task OnAuthorizationAsync(AuthorizationFilterContext context) + { + if (_verifyConfig.CurrentValue.OpenLoginLimit) + { + if (context.ActionDescriptor.EndpointMetadata.Any(item => item is IAllowAnonymous)) + { + + return; + //匿名访问的不处理 + } + else + { + //1、用户登陆的时候,设置缓存 + + + + //没有从请求中取到token + if (string.IsNullOrWhiteSpace(_userInfo.UserToken)) + { + context.HttpContext.Response.ContentType = "application/json"; + context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; + context.Result = new JsonResult(ResponseOutput.NotOk("当前请求未从Header/Url取到用户Token")); + + //await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk("当前请求未从Header/Url取到用户Token,请联系开发者"))); + } + + //2、在这里取缓存 进行比较 看是否有其他人进行了登陆,如果其他人登陆了,就把之前用户挤掉 + var cacheUserToken = (await _provider.GetAsync(_userInfo.Id.ToString())).Value; + + //缓存中没有取到Token + if (string.IsNullOrWhiteSpace(cacheUserToken)) + { + //设置当前用户最新Token + await _provider.SetAsync(_userInfo.Id.ToString(), _userInfo.UserToken, TimeSpan.FromDays(7)); + + cacheUserToken = _userInfo.UserToken; + + } + //是同一个人 + else if (cacheUserToken == _userInfo.UserToken) + { + + } + + else + { + + context.HttpContext.Response.ContentType = "application/json"; + context.HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden; + context.Result = new JsonResult(ResponseOutput.NotOk("您的账户在其他地方已登陆,您被迫下线。", ApiResponseCodeEnum.LoginInOtherPlace)); + //await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk("您的账户在其他地方已登陆,您被迫下线。", ApiResponseCodeEnum.LoginInOtherPlace))); + + } + + + + } + } + + + } +} + + + + + + + + + + + + + + +//public class UserTypeRequirement : IAuthorizationRequirement +//{ +//} + +//public class UserTypeHandler : AuthorizationHandler +//{ + +// private IUserInfo _userInfo; + +// public UserTypeHandler(IUserInfo userInfo) +// { +// _userInfo = userInfo; +// } + + +// protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserTypeRequirement requirement) +// { + +// //if (context.User.Claims.Count() == 0) +// //{ +// // return Task.CompletedTask; +// //} + +// //string userId = context.User.Claims.First(c => c.Type == "Userid").Value; +// //string qq = context.User.Claims.First(c => c.Type == "QQ").Value; + +// //if (_UserService.Validata(userId, qq)) +// //{ +// // context.Succeed(requirement); //验证通过了 +// //} +// ////在这里就可以做验证 + +// return Task.CompletedTask; +// } +//} + diff --git a/IRaCIS.Core.Application/BusinessFilter/LogActionFilter.cs b/IRaCIS.Core.Application/BusinessFilter/LogActionFilter.cs new file mode 100644 index 0000000..64f1158 --- /dev/null +++ b/IRaCIS.Core.Application/BusinessFilter/LogActionFilter.cs @@ -0,0 +1,81 @@ +//using System.Diagnostics; +//using IRaCIS.Application.Interfaces; +//using IRaCIS.Application.Contracts; +//using IRaCIS.Core.Infra.EFCore; +//using IRaCIS.Core.Infrastructure.Extention; +//using Microsoft.AspNetCore.Mvc; +//using Microsoft.AspNetCore.Mvc.Filters; +//using Microsoft.Extensions.Logging; +//using Newtonsoft.Json; + +//namespace IRaCIS.Core.Application.Filter +//{ + +// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] +// public class LogFilter : Attribute +// { +// } +// public class LogActionFilter : IAsyncActionFilter +// { +// private readonly ILogService _logService; +// private readonly IUserInfo _userInfo; +// private readonly ILogger _logger; + +// public LogActionFilter(ILogService logService, IUserInfo userInfo , ILogger logger) +// { +// _logService = logService; +// _userInfo = userInfo; +// _logger = logger; +// } + +// public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) +// { + +// if (context.ActionDescriptor.EndpointMetadata!=null&& context.ActionDescriptor.EndpointMetadata.Any(m => m.GetType() == typeof(LogFilter))) +// { +// return LogAsync(context, next); +// } +// return next(); +// } + +// public async Task LogAsync(ActionExecutingContext context, ActionExecutionDelegate next) +// { +// var sw = new Stopwatch(); +// sw.Start(); + +// dynamic actionResult = (await next()).Result; +// sw.Stop(); +// var args = JsonConvert.SerializeObject(context.ActionArguments); +// var result = JsonConvert.SerializeObject(actionResult?.Value); + +// var attr = (ApiExplorerSettingsAttribute)context.ActionDescriptor.EndpointMetadata.FirstOrDefault(m => m.GetType() == typeof(ApiExplorerSettingsAttribute)); +// var groupName = attr?.GroupName; +// var res = actionResult?.Value as IResponseOutput; +// var input = new SystemLogDTO +// { +// ClientIP = string.Empty, +// OptUserId = _userInfo.Id, +// OptUserName = _userInfo.UserName, +// ApiPath = context.ActionDescriptor.AttributeRouteInfo.Template.ToLower(), +// Params = args, +// Result = result, +// RequestTime = DateTime.Now, +// ElapsedMilliseconds = sw.ElapsedMilliseconds, +// Status =res?.IsSuccess?? false, +// Message = res?.ErrorMessage, +// LogCategory = groupName +// }; + +// try +// { +// _logService.SaveLog2Db(input); +// } +// catch (Exception ex) +// { + +// _logger.LogError(ex.Message); +// } + +// } +// } +//} diff --git a/IRaCIS.Core.Application/BusinessFilter/ModelActionFilter .cs b/IRaCIS.Core.Application/BusinessFilter/ModelActionFilter .cs new file mode 100644 index 0000000..457a5d3 --- /dev/null +++ b/IRaCIS.Core.Application/BusinessFilter/ModelActionFilter .cs @@ -0,0 +1,27 @@ +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Newtonsoft.Json; + + +namespace IRaCIS.Core.Application.Filter +{ + public class ModelActionFilter : ActionFilterAttribute, IActionFilter + { + public override void OnActionExecuting(ActionExecutingContext context) + { + if (!context.ModelState.IsValid) + { + + var validationErrors = context.ModelState + .Keys + .SelectMany(k => context.ModelState[k]!.Errors) + .Select(e => e.ErrorMessage) + .ToArray(); + + context.Result = new JsonResult(ResponseOutput.NotOk("提供给接口的参数无效。" +JsonConvert.SerializeObject( validationErrors))); + } + } + } + +} diff --git a/IRaCIS.Core.Application/BusinessFilter/ModelBinding.cs b/IRaCIS.Core.Application/BusinessFilter/ModelBinding.cs new file mode 100644 index 0000000..c04d0ba --- /dev/null +++ b/IRaCIS.Core.Application/BusinessFilter/ModelBinding.cs @@ -0,0 +1,28 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace IRaCIS.Core.Application.Filter +{ + #region snippet_DisableFormValueModelBindingAttribute + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter + { + public void OnResourceExecuting(ResourceExecutingContext context) + { + + + var factories = context.ValueProviderFactories; + //factories.RemoveType(); + factories.RemoveType(); + factories.RemoveType(); + context.HttpContext.Request.EnableBuffering(); + } + + public void OnResourceExecuted(ResourceExecutedContext context) + { + } + } + #endregion +} diff --git a/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs b/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs new file mode 100644 index 0000000..e510e44 --- /dev/null +++ b/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs @@ -0,0 +1,53 @@ +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; + +namespace IRaCIS.Core.Application.Filter +{ + public class ProjectExceptionFilter : Attribute, IExceptionFilter + { + private readonly ILogger _logger; + + public ProjectExceptionFilter(ILogger logger) + { + _logger = logger; + } + public void OnException(ExceptionContext context) + { + //context.ExceptionHandled;//记录当前这个异常是否已经被处理过了 + + if (!context.ExceptionHandled) + { + if (context.Exception.GetType().Name == "DbUpdateConcurrencyException") + { + context.Result = new JsonResult(ResponseOutput.NotOk("并发更新,当前不允许该操作" + context.Exception.Message)); + } + + if (context.Exception.GetType() == typeof(BusinessValidationFailedException) || context.Exception.GetType() == typeof(DBSaveFailedException)) + { + context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message,ApiResponseCodeEnum.BusinessValidationFailed)); + } + else if(context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException)) + { + context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message, ApiResponseCodeEnum.DataNotExist)); + } + else + { + context.Result = new JsonResult(ResponseOutput.NotOk("当前操作失败,请联系开发人员查看系统日志排查 ", ApiResponseCodeEnum.ProgramException)); + } + + + _logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace)); + + + } + else + { + //继续 + } + context.ExceptionHandled = true;//标记当前异常已经被处理过了 + } + } +} diff --git a/IRaCIS.Core.Application/BusinessFilter/TrialAuditFilter.cs b/IRaCIS.Core.Application/BusinessFilter/TrialAuditFilter.cs new file mode 100644 index 0000000..578431f --- /dev/null +++ b/IRaCIS.Core.Application/BusinessFilter/TrialAuditFilter.cs @@ -0,0 +1,1575 @@ +//using IRaCIS.Application.Contracts; +//using IRaCIS.Core.Application.Contracts.Dicom.DTO; +//using IRaCIS.Core.Application.Contracts.DTO; +//using IRaCIS.Core.Infra.EFCore; +//using IRaCIS.Core.Domain.Models; +//using IRaCIS.Core.Domain.Share; +//using IRaCIS.Core.Infrastructure.Extention; +//using Microsoft.AspNetCore.Mvc.Filters; +//using Microsoft.EntityFrameworkCore; +//using Microsoft.Extensions.Logging; +//using IRaCIS.Core.Infrastructure; +//using IRaCIS.Core.Application.Contracts; + +//namespace IRaCIS.Core.Application.Filter +//{ +//#pragma warning disable CS8618 + +//#pragma warning disable CS8062 +// //by zhouhang 2021.08 + + +//审计类型大类 +using Microsoft.AspNetCore.Mvc.Filters; + +public enum AuditType +{ + TrialAudit = 0, + SubjectAudit = 1, + StudyAudit = 2 +} + +//具体审计操作 +public enum AuditOptType +{ + //DeleteTrial = 2, + AddOrUpdateTrial = 0, + + AddTrialSiteSurvey = 1, + + //参与人员 + AddTrialStaff = 3, + DeleteTrailStaff = 4, + + //Site + AddTrialSite = 5, + DeleteTrialSite = 6, + + //Site IC + AddTrialSiteCRC = 7, + DeleteTrialSiteCRC = 8, + + //检查批次计划 + AddOrUpdateTrialVisitPlanItem = 9, + DeleteTrialVisitPlanItem = 10, + ConfirmTrialVisitPlan = 11, + + //项目模板 + AddOrUpdateTrialTemplate = 12, + DeleteTrialTemplate = 13, + + //subject 检查批次计划 + AddOrUpdateSubjectOutPlanVisit = 14, + DeleteSubjectOutPlanVisit = 15, + SetSVExecuted = 16, + + AddOrUpdateSubject = 17, + DeleteSubject = 18, + + //影像上传 + UploadImage = 19, + + //变更QA状态 比如设置为QA中 QA结束通过、不通过 + ChangeStudyStatus = 20, + + //匿名化 + Anonymized = 22, + + //转发 + Forwarded = 24 +} + +[AttributeUsage(AttributeTargets.Method)] +public class TrialAuditAttribute : Attribute,/*IActionFilter, */IAsyncActionFilter +{ + private readonly AuditType _auditType; + private readonly AuditOptType _auditOptType; + public TrialAuditAttribute(AuditType auditType, AuditOptType auditOptType) + { + _auditType = auditType; + _auditOptType = auditOptType; + } + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + await next(); + + } +} + +// [AttributeUsage(AttributeTargets.Method)] +// public class TrialAuditAttribute : Attribute,/*IActionFilter, */IAsyncActionFilter +// { +// private readonly AuditType _auditType; +// private readonly AuditOptType _auditOptType; +// public IDictionary _actionArguments; + +// #region 根据不同接口构造的属性 + +// private TrialAuditAttribute _attr; + +// private string _userIdStr; + +// private string _userRealName; + +// private IRepository _auditRepository; + +// private TrialUser _userTrial; + +// private TrialSiteUser _userTrialSite; + +// private VisitStage _visitPlan; + +// //private QATrialTemplate _qATrailTemplate; + +// private Subject _subject; + +// private SubjectVisit _subjectVisit; + +// private TrialSite _trialSite; + +// private DicomStudy _study; + +// private IRepository _studyRepository; + +// //private IRaCISDBContext _dbContext; + +// //private IDbContextTransaction transaction; + + +// #endregion + +// public TrialAuditAttribute(AuditType auditType, AuditOptType auditOptType) +// { +// _auditType = auditType; +// _auditOptType = auditOptType; +// } + + +// #region 同步版本 废弃 + +// //public void OnActionExecuted(ActionExecutedContext executedcontext) +// //{ + +// // //上传影像接口返回值类型特殊 需要单独处理 +// // if (_attr._auditOptType == AuditOptType.UploadImage) +// // { +// // //接口参数 +// // var archiveStudyInfo = (ArchiveStudyCommand)_actionArguments.Values.ToList()[0]; + +// // //接口返回结果 因为上传成功和不成功 泛型类型不一样 +// // dynamic archive = executedcontext.Result; +// // if (archive == null) +// // { +// // var logger = (ILogger)executedcontext.HttpContext.RequestServices.GetService( +// // typeof(ILogger)); + +// // logger.LogError("影像上传,OnActionExecuted Reuslt 为null"); +// // return; +// // } +// // var archiveResult = (IResponseOutput)archive.Value; + + + +// // if (archiveResult.IsSuccess) +// // { +// // _studyRepository = (IRepository)executedcontext.HttpContext.RequestServices.GetService( +// // typeof(IRepository)); + +// // var successResult = (IResponseOutput)archiveResult; + +// // _study = _studyRepository.GetAll().First(t => +// // t.Id == successResult.Data.ArchivedDicomStudies.ToList()[0].Id); + +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = _study.TrialId, +// // SubjectId = _study.SubjectId, +// // StudyId = _study.Id, +// // AuditType = (int)AuditType.StudyAudit, +// // Note = $"{ _userRealName} 动作变更了 { _study.StudyCode} 检查状态为 {DealStudyStatus(_study.Status)} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); + +// // _auditRepository.SaveChanges(); +// // } + +// // return; +// // } + +// // //接口返回结果 +// // dynamic actionResult = executedcontext.Result; +// // var result = (IResponseOutput)actionResult.Value;//此处审计只涉及到添加更新和删除, 统一了相关接口的返回结果,才能在此强制转换 + + + +// // switch (_attr._auditType) +// // { +// // //项目审计 +// // case AuditType.TrialAudit: + +// // switch (_attr._auditOptType) +// // { +// // //项目的添加和更新接口 +// // case AuditOptType.AddOrUpdateTrial: + +// // //接口参数 +// // var trialInfo = (TrialCommand)_actionArguments.Values.ToList()[0]; + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { +// // //判断是添加还是更新 +// // TrialId = trialInfo.Id == null ? Guid.Parse(result.Data) : trialInfo.Id.Value, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = trialInfo.Id == null ? $"{ _userRealName} 添加了项目 {trialInfo.Code} " : $"{ _userRealName} 更新了项目 {trialInfo.Code} 基本信息 ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; +// // //添加项目运维人员 +// // case AuditOptType.AddTrialStaff: + +// // //接口参数 +// // var trialUsers = (List)_actionArguments.Values.ToList()[0]; + +// // if (result.IsSuccess) +// // { + +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = trialUsers[0].TrialId, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = $"{ _userRealName} 添加了项目运维人员 {string.Join(',', trialUsers.Select(t => t.UserRealName).ToList())} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; + +// // //添加研究中心 +// // case AuditOptType.AddTrialSite: +// // //接口参数 +// // var trialSites = (List)_actionArguments.Values.ToList()[0]; + +// // if (result.IsSuccess) +// // { +// // //查询site信息 +// // var siteIds = trialSites.Select(t => t.SiteId).ToList(); +// // var _siteRepository = (IRepository)executedcontext.HttpContext.RequestServices.GetService(typeof(IRepository)); +// // var siteNames = _siteRepository.Where(t => siteIds.Contains(t.Id)).Select(u => u.SiteName); + +// // _auditRepository.Add(new TrialAudit() +// // { +// // //判断是添加还是更新 +// // TrialId = trialSites[0].TrialId, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = $"{ _userRealName} 添加了项目研究中心 {string.Join(',', siteNames)} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; + +// // //研究中心添加运维人员 +// // case AuditOptType.AddTrialSiteCRC: +// // //接口参数 +// // var trialSiteCRCs = (List)_actionArguments.Values.ToList()[0]; + +// // if (result.IsSuccess) +// // { +// // //查询site信息 +// // var _siteRepository = (IRepository)executedcontext.HttpContext.RequestServices.GetService(typeof(IRepository)); +// // var siteName = _siteRepository.GetAll().First(t => t.Id == trialSiteCRCs[0].SiteId).SiteName; + +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = trialSiteCRCs[0].TrialId, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = $"{ _userRealName} 为{ siteName } 添加了运维人员 {string.Join(',', trialSiteCRCs.Select(t => t.UserRealName).ToList())}", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; + +// // //删除项目运维人员 +// // case AuditOptType.DeleteTrailStaff: + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = _userTrial.TrialId, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = $"{ _userRealName} 移除了项目参与人员 {_userTrial.UserRealName} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; +// // //删除项目site +// // case AuditOptType.DeleteTrialSite: + +// // if (result.IsSuccess) +// // { +// // //查询site信息 +// // var _siteRepository = (IRepository)executedcontext.HttpContext.RequestServices.GetService(typeof(IRepository)); +// // var siteName = _siteRepository.GetAll().First(t => t.Id == _trialSite.SiteId).SiteName; + +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = _trialSite.TrialId, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = $"{ _userRealName} 移除了研究中心 {siteName} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; +// // //删除项目Site IC +// // case AuditOptType.DeleteTrialSiteCRC: + +// // if (result.IsSuccess) +// // { +// // //查询site信息 +// // var _siteRepository = (IRepository)executedcontext.HttpContext.RequestServices.GetService(typeof(IRepository)); +// // var siteName = _siteRepository.GetAll().First(t => t.Id == _userTrial.SiteId).SiteName; + +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = _userTrial.TrialId, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = $"{ _userRealName} 移除了 {siteName} 的运维人员 {_userTrial.UserRealName} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } +// // break; + +// // //添加检查批次计划Item +// // case AuditOptType.AddOrUpdateTrialVisitPlanItem: +// // //接口参数 +// // var visitPlanItem = (VisitPlanCommand)_actionArguments.Values.ToList()[0]; + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = visitPlanItem.TrialId, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = visitPlanItem.Id == null ? $"{ _userRealName} 添加了检查批次计划项 {visitPlanItem.VisitName} " : $"{ _userRealName} 更新了检查批次计划项为 {visitPlanItem.VisitName} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; +// // //删除检查批次计划Item +// // case AuditOptType.DeleteTrialVisitPlanItem: + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = _visitPlan.TrialId, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = $"{ _userRealName} 移除了检查批次计划项 {_visitPlan.VisitName} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } +// // break; + +// // //确认了检查批次计划 +// // case AuditOptType.ConfirmTrialVisitPlan: +// // var trialId = (Guid)_actionArguments.Values.ToList()[0]; + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = trialId, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = $"{ _userRealName} 确认了检查批次计划,不允许修改 ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; +// // //添加了QA模板 +// // case AuditOptType.AddOrUpdateTrialTemplate: +// // //接口参数 +// // var trialQATemplate = (TrialQATemplateAddCommand)_actionArguments.Values.ToList()[0]; + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { + +// // TrialId = trialQATemplate.TrialId, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = trialQATemplate.Id == null ? $"{ _userRealName} 添加了QA模板 {trialQATemplate.Name} " : $"{ _userRealName} 更新了QA模板 {trialQATemplate.Name} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + + +// // break; +// // //删除项目模板 +// // case AuditOptType.DeleteTrialTemplate: + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { + +// // TrialId = _qATrailTemplate.TrialId, +// // SubjectId = Guid.Empty, +// // AuditType = (int)AuditType.TrialAudit, +// // Note = $"{ _userRealName} 移除了QA模板 {_qATrailTemplate.Name} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } +// // break; + +// // } + +// // break; +// // //患者审计 +// // case AuditType.SubjectAudit: + + +// // switch (_attr._auditOptType) +// // { +// // // 添加或者更新患者 +// // case AuditOptType.AddOrUpdateSubject: + +// // //接口参数 +// // var subject = (SubjectCommand)_actionArguments.Values.ToList()[0]; + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = subject.TrialId, +// // SubjectId = subject.Id == null ? Guid.Parse(result.Data) : subject.Id.Value, +// // AuditType = (int)AuditType.SubjectAudit, +// // Note = subject.Id == null ? $"{ _userRealName} 添加了患者 {subject.LastName + subject.FirstName} 并初始化了检查批次计划" : $"{ _userRealName} 对患者 { subject.LastName + subject.FirstName} 信息进行了更新 ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; +// // // 删除患者 +// // case AuditOptType.DeleteSubject: +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = _subject.TrialId, +// // SubjectId = _subject.Id, +// // AuditType = (int)AuditType.SubjectAudit, +// // Note = $"{ _userRealName} 移除了患者 {_subject.LastName + _subject.FirstName} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; +// // // 患者计划外检查批次 +// // case AuditOptType.AddOrUpdateSubjectOutPlanVisit: + +// // //接口参数 +// // var subjectVisit = (SubjectVisitCommand)_actionArguments.Values.ToList()[0]; + +// // if (result.IsSuccess) +// // { +// // var _subjectRepository = +// // (IRepository)executedcontext.HttpContext.RequestServices.GetService( +// // typeof(IRepository)); + +// // _subject = _subjectRepository.FirstOrDefault(t => t.Id == subjectVisit.SubjectId); + +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = _subject.TrialId, +// // SubjectId = _subject.Id, +// // AuditType = (int)AuditType.SubjectAudit, +// // Note = subjectVisit.Id == null ? $"{ _userRealName} 为患者 {_subject.LastName + _subject.FirstName} 添加了计划外检查批次 {subjectVisit.VisitName}" : $"{ _userRealName} 更新患者 {_subject.LastName + _subject.FirstName} 计划外检查批次{subjectVisit.VisitName}", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; +// // // 删除计划外检查批次 +// // case AuditOptType.DeleteSubjectOutPlanVisit: + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = _subjectVisit.TrialId, +// // SubjectId = _subject.Id, +// // AuditType = (int)AuditType.SubjectAudit, +// // Note = $"{ _userRealName} 移除了患者{_subject.LastName + _subject.FirstName} 计划外检查批次 {_subjectVisit.VisitName} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; +// // // 人工设置已执行 +// // case AuditOptType.SetSVExecuted: + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = _subjectVisit.TrialId, +// // SubjectId = _subject.Id, +// // AuditType = (int)AuditType.SubjectAudit, +// // Note = $"{ _userRealName} 将患者 {_subject.LastName + _subject.FirstName} 检查批次 {_subjectVisit.VisitName} 设置为已执行 ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + + +// // break; +// // } + +// // break; + +// // //检查审计 +// // case AuditType.StudyAudit: +// // _studyRepository = +// // (IRepository)executedcontext.HttpContext.RequestServices.GetService( +// // typeof(IRepository)); + +// // switch (_attr._auditOptType) +// // { + +// // case AuditOptType.ChangeStudyStatus: + +// // //接口参数 +// // var studyStatusInfo = (StudyStatusDetailCommand)_actionArguments.Values.ToList()[0]; + +// // _study = _studyRepository.FirstOrDefault(t => t.Id == studyStatusInfo.StudyId); + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = _study.TrialId, +// // SubjectId = _study.SubjectId, +// // StudyId = _study.Id, +// // AuditType = (int)AuditType.StudyAudit, +// // Note = $"{ _userRealName} 动作变更了 { _study.StudyCode} 检查状态为 {DealStudyStatus(_study.Status)} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + +// // break; + +// // case AuditOptType.Anonymized: + +// // case AuditOptType.Forwarded: + +// // //接口参数 +// // var studyId = (Guid)_actionArguments.Values.ToList()[0]; + +// // _study = _studyRepository.FirstOrDefault(t => t.Id == studyId); + +// // if (result.IsSuccess) +// // { +// // _auditRepository.Add(new TrialAudit() +// // { +// // TrialId = _study.TrialId, +// // SubjectId = _study.SubjectId, +// // StudyId = _study.Id, +// // AuditType = (int)AuditType.StudyAudit, +// // Note = $"{ _userRealName} 动作变更了 { _study.StudyCode} 检查状态为 {DealStudyStatus(_study.Status)} ", +// // Detail = string.Empty, +// // OptUserId = Guid.Parse(_userIdStr), +// // OptUser = _userRealName, +// // OptTime = DateTime.Now +// // }); +// // } + + +// // break; +// // } + +// // break; + +// // } + +// // _auditRepository.SaveChanges(); + +// //} + + +// //public void OnActionExecuting(ActionExecutingContext context) +// //{ +// // //_dbContext = (IRaCISDBContext)context.HttpContext.RequestServices.GetService(typeof(IRaCISDBContext)); + + +// // _actionArguments = context.ActionArguments; + +// // _attr = (TrialAuditAttribute)context.ActionDescriptor.EndpointMetadata.First(m => m.GetType() == typeof(TrialAuditAttribute)); + +// // _userIdStr = context.HttpContext.User.Claims.First(t => t.Type == "id")?.Value ?? Guid.Empty.ToString(); +// // _userRealName = context.HttpContext.User.Claims.First(t => t.Type == "realName")?.Value ?? string.Empty; + +// // //获取接口仓储 +// // _auditRepository = (IRepository)context.HttpContext.RequestServices.GetService(typeof(IRepository)); + + + +// // switch (_attr._auditType) +// // { +// // //项目审计 +// // case AuditType.TrialAudit: + +// // switch (_attr._auditOptType) +// // { +// // //删除项目运维人员 +// // case AuditOptType.DeleteTrailStaff: + +// // // 删除项目Site IC +// // case AuditOptType.DeleteTrialSiteCRC: + +// // //接口参数 +// // var userTrialId = (Guid)_actionArguments.Values.ToList()[0]; + +// // var _userTrialRepository = +// // (IRepository)context.HttpContext.RequestServices.GetService( +// // typeof(IRepository)); + +// // _userTrial = _userTrialRepository.GetAll().First(t => t.Id == userTrialId); +// // break; + + +// // //删除项目site +// // case AuditOptType.DeleteTrialSite: + +// // //接口参数 +// // var trialSiteId = (Guid)_actionArguments.Values.ToList()[0]; + +// // var _TrialsiteRepository = +// // (IRepository)context.HttpContext.RequestServices.GetService( +// // typeof(IRepository)); + +// // _trialSite = _TrialsiteRepository.GetAll().First(t => t.Id == trialSiteId); +// // break; + +// // case AuditOptType.DeleteTrialVisitPlanItem: + +// // //接口参数 +// // var visitPlanId = (Guid)_actionArguments.Values.ToList()[0]; + +// // var _visitPlanRepository = +// // (IRepository)context.HttpContext.RequestServices.GetService( +// // typeof(IRepository)); + +// // _visitPlan = _visitPlanRepository.GetAll().First(t => t.Id == visitPlanId); + +// // break; +// // case AuditOptType.DeleteTrialTemplate: + +// // //接口参数 +// // var qATrailTemplateId = (Guid)_actionArguments.Values.ToList()[0]; +// // var _qATrailTemplateRepository = +// // (IRepository)context.HttpContext.RequestServices.GetService( +// // typeof(IRepository)); + +// // _qATrailTemplate = _qATrailTemplateRepository.GetAll() +// // .FirstOrDefault(t => t.Id == qATrailTemplateId); + +// // break; + +// // } + +// // break; + +// // //患者审计 +// // case AuditType.SubjectAudit: + +// // switch (_auditOptType) +// // { +// // case AuditOptType.DeleteSubject: +// // //接口参数 +// // var subjectId = (Guid)_actionArguments.Values.ToList()[0]; + +// // var _subjectRepository = +// // (IRepository)context.HttpContext.RequestServices.GetService( +// // typeof(IRepository)); + +// // _subject = _subjectRepository.FirstOrDefault(t => t.Id == subjectId); + +// // break; + +// // case AuditOptType.DeleteSubjectOutPlanVisit: +// // case AuditOptType.SetSVExecuted: + +// // //接口参数 +// // var subjectVisitId = (Guid)_actionArguments.Values.ToList()[0]; + +// // var _subjectVisitRepository = +// // (IRepository)context.HttpContext.RequestServices.GetService( +// // typeof(IRepository)); + +// // _subjectVisit = _subjectVisitRepository.FirstOrDefault(t => t.Id == subjectVisitId); + +// // _subjectRepository = +// // (IRepository)context.HttpContext.RequestServices.GetService( +// // typeof(IRepository)); + +// // _subject = _subjectRepository.FirstOrDefault(t => t.Id == _subjectVisit.SubjectId); + +// // break; +// // } +// // break; + + +// // //检查审计 +// // case AuditType.StudyAudit: + +// // switch (_attr._auditOptType) +// // { +// // case AuditOptType.UploadImage: + +// // break; +// // } +// // break; + +// // } + +// //} +// #endregion + +// public static string DealStudyStatus(int status) +// { + +// var message = string.Empty; +// switch (status) +// { +// case (int)StudyStatus.Uploaded: +// message = "Uploaded"; +// break; + +// case (int)StudyStatus.QARequested: + +// message = "QA Requested"; +// break; +// case (int)StudyStatus.QAing: + +// message = "In QA"; +// break; + +// //case (int)StudyStatus.Abandon: +// // message = "Study has abandon"; +// // break; + +// case (int)StudyStatus.QAFinish: +// message = "QA Completed Passed"; +// break; + +// case (int)StudyStatus.QAFInishNotPass: +// message = "QA Completed Failed"; +// break; + +// case (int)StudyStatus.Anonymizing: +// message = "Anonymizing"; +// break; + +// case (int)StudyStatus.Anonymized: +// message = "Anonymized"; +// break; + +// case (int)StudyStatus.AnonymizeFailed: +// message = "Anonymize Failed"; +// break; + +// case (int)StudyStatus.Forwarding: +// message = "Forwarding"; +// break; + +// case (int)StudyStatus.Forwarded: +// message = "Forwarded "; +// break; + +// case (int)StudyStatus.ForwardFailed: +// message = "Forward Failed "; +// break; +// } + +// return message; +// } + +// public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) +// { +// // 开启AOP事务(审计消息记录和 添加更新记录 在一个事务里面,不再是分开的事务) +// //var _dbContext = (IRaCISDBContext)context.HttpContext.RequestServices.GetService(typeof(IRaCISDBContext)); + +// //var strategy = _dbContext.Database.CreateExecutionStrategy(); + +// var _dbContext = (IRaCISDBContext)context.HttpContext.RequestServices.GetService(typeof(IRaCISDBContext)); + +// var strategy = _dbContext.Database.CreateExecutionStrategy(); + +// await strategy.Execute(async () => +// { +// var currentTransaction = _dbContext.Database.CurrentTransaction; + +// var transaction = currentTransaction ?? _dbContext.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted); + +// #region 执行方法前 + +// _actionArguments = context.ActionArguments; + +// _attr = (TrialAuditAttribute)context.ActionDescriptor.EndpointMetadata.First(m => m.GetType() == typeof(TrialAuditAttribute)); + +// _userIdStr = context.HttpContext.User.Claims.First(t => t.Type == "id")?.Value ?? Guid.Empty.ToString(); +// _userRealName = context.HttpContext.User.Claims.First(t => t.Type == "realName")?.Value ?? string.Empty; + +// //获取接口仓储 +// _auditRepository = (IRepository)context.HttpContext.RequestServices.GetService(typeof(IRepository)); + +// switch (_attr._auditType) +// { +// //项目审计 +// case AuditType.TrialAudit: + +// switch (_attr._auditOptType) +// { +// //删除项目运维人员 +// case AuditOptType.DeleteTrailStaff: + +// //接口参数 +// var userTrial = (Guid)_actionArguments.Values.ToList()[0]; + +// var _userTrialRepository = +// (IRepository)context.HttpContext.RequestServices.GetService( +// typeof(IRepository)); + +// _userTrial = _userTrialRepository.AsQueryable().Include(t => t.User).FirstOrDefault(t => t.Id == userTrial); +// break; + +// // 删除项目Site IC +// case AuditOptType.DeleteTrialSiteCRC: + +// //接口参数 +// var userTrialSiteId = (Guid)_actionArguments.Values.ToList()[0]; + +// var _userTrialSiteRepository = +// (IRepository)context.HttpContext.RequestServices.GetService( +// typeof(IRepository)); + +// _userTrialSite = _userTrialSiteRepository.AsQueryable().Include(t => t.User).FirstOrDefault(t => t.Id == userTrialSiteId); +// break; + + +// //删除项目site +// case AuditOptType.DeleteTrialSite: + +// //接口参数 +// var trialSiteId = (Guid)_actionArguments.Values.ToList()[0]; + +// var _TrialsiteRepository = +// (IRepository)context.HttpContext.RequestServices.GetService( +// typeof(IRepository)); + +// _trialSite = _TrialsiteRepository.AsQueryable().Include(t => t.Site).FirstOrDefault(t => t.Id == trialSiteId); +// break; + +// case AuditOptType.DeleteTrialVisitPlanItem: + +// //接口参数 +// var visitPlanId = (Guid)_actionArguments.Values.ToList()[0]; + +// var _visitPlanRepository = +// (IRepository)context.HttpContext.RequestServices.GetService( +// typeof(IRepository)); + +// _visitPlan = _visitPlanRepository.FirstOrDefault(t => t.Id == visitPlanId).IfNullThrowException(); + +// break; +// case AuditOptType.DeleteTrialTemplate: + + + +// break; + +// } + +// break; + +// //患者审计 +// case AuditType.SubjectAudit: + +// switch (_auditOptType) +// { +// case AuditOptType.DeleteSubject: +// //接口参数 +// var subjectId = (Guid)_actionArguments.Values.ToList()[0]; + +// var _subjectRepository = +// (IRepository)context.HttpContext.RequestServices.GetService( +// typeof(IRepository)); + +// _subject = _subjectRepository.FirstOrDefault(t => t.Id == subjectId).IfNullThrowException(); + +// break; + +// case AuditOptType.DeleteSubjectOutPlanVisit: +// case AuditOptType.SetSVExecuted: + +// //接口参数 +// var subjectVisitId = (Guid)_actionArguments.Values.ToList()[0]; + +// var _subjectVisitRepository = +// (IRepository)context.HttpContext.RequestServices.GetService( +// typeof(IRepository)); + +// _subjectVisit = _subjectVisitRepository.FirstOrDefault(t => t.Id == subjectVisitId).IfNullThrowException(); + +// _subjectRepository = +// (IRepository)context.HttpContext.RequestServices.GetService( +// typeof(IRepository)); + +// _subject = _subjectRepository.FirstOrDefault(t => t.Id == _subjectVisit.SubjectId).IfNullThrowException(); + +// break; +// } +// break; + + +// //检查审计 +// case AuditType.StudyAudit: + +// switch (_attr._auditOptType) +// { +// case AuditOptType.UploadImage: + +// break; +// } +// break; + +// } + +// #endregion + +// //获取实际执行方法next() 的结果 +// ActionExecutedContext executedcontext = await next(); + +// #region 方法执行后 + +// //上传影像接口返回值类型特殊 需要单独处理 +// if (_attr._auditOptType == AuditOptType.UploadImage) +// { + +// //接口参数 +// var archiveStudyInfo = (ArchiveStudyCommand)_actionArguments.Values.ToList()[0]; + +// if (executedcontext.Exception != null) +// { +// var logger = (ILogger)executedcontext.HttpContext.RequestServices.GetService( +// typeof(ILogger)); + +// logger!.LogError("影像上传 中间发生异常", executedcontext.Exception.StackTrace); + +// return; +// } + +// //接口返回结果 因为上传成功和不成功 泛型类型不一样 +// dynamic archive = executedcontext.Result; + + +// var archiveResult = (IResponseOutput)archive.Value; + +// if (archiveResult.IsSuccess) +// { + + +// _studyRepository = (IRepository)executedcontext.HttpContext.RequestServices.GetService(typeof(IRepository)); + +// _auditRepository = (IRepository)context.HttpContext.RequestServices.GetService(typeof(IRepository)); + +// var successResult = (IResponseOutput)archiveResult; + +// var studyList = _studyRepository.Where(t => t.SubjectVisitId == successResult.Data.ArchivedDicomStudies.ToList()[0].SubjectVisitId).ToList(); + +// studyList.ForEach(study => +// { +// var mes = study.Status == (int)StudyStatus.Uploaded ? "上传了" : "重传了"; +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = study.TrialId, +// SubjectId = study.SubjectId, +// StudyId = study.Id, +// AuditType = (int)AuditType.StudyAudit, +// //Note = $"{ _userRealName} 动作变更了 { _study.StudyCode} 检查状态为 {DealStudyStatus(_study.Status)} ", +// Note = $"{ _userRealName} {mes} { study.StudyCode} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// }); + + + +// _auditRepository.SaveChanges(); + +// transaction.Commit(); + +// return; + +// } + + +// } + +// //接口返回结果 +// if (executedcontext.Result is null) +// { +// throw new BusinessValidationFailedException(executedcontext.Exception.Message); +// } +// dynamic actionResult = executedcontext.Result; + +// //此处审计只涉及到添加更新和删除, 统一了相关接口的返回结果,才能在此强制转换 +// //var result = (IResponseOutput)actionResult.Value; + +// var result = (IResponseOutput)actionResult.Value; + + +// switch (_attr._auditType) +// { +// //项目审计 +// case AuditType.TrialAudit: + +// switch (_attr._auditOptType) +// { +// //项目的添加和更新接口 +// case AuditOptType.AddOrUpdateTrial: + +// //接口参数 +// var trialInfo = (TrialCommand)_actionArguments.Values.ToList()[0]; + +// if (result.IsSuccess) +// { +// _auditRepository.Add(new TrialAudit() +// { +// //判断是添加还是更新 +// TrialId = trialInfo.Id == null ? Guid.Parse(result.Data) : trialInfo.Id.Value, +// SubjectId = Guid.Empty, +// AuditType = (int)AuditType.TrialAudit, +// Note = trialInfo.Id == null ? $"{ _userRealName} 添加了项目 {trialInfo.TrialCode} " : $"{ _userRealName} 更新了项目 {trialInfo.TrialCode} 基本信息 ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; +// //添加项目运维人员 +// case AuditOptType.AddTrialStaff: + +// //接口参数 +// var trialUsers = (TrialUserAddCommand[])_actionArguments.Values.ToList()[0]; + +// if (result.IsSuccess) +// { + +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = trialUsers[0].TrialId, +// SubjectId = Guid.Empty, +// AuditType = (int)AuditType.TrialAudit, +// //Note = $"{ _userRealName} 添加了项目运维人员 {string.Join(',', trialUsers.Select(t => t.UserRealName).ToList())} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; + +// //添加研究中心 +// case AuditOptType.AddTrialSite: +// //接口参数 +// var trialSites = (List)_actionArguments.Values.ToList()[0]; + +// if (result.IsSuccess) +// { +// //查询site信息 +// var siteIds = trialSites.Select(t => t.SiteId).ToList(); +// var _siteRepository = (IRepository)executedcontext.HttpContext.RequestServices.GetService(typeof(IRepository)); +// var siteNames = _siteRepository.Where(t => siteIds.Contains(t.Id)).Select(u => u.SiteName); + +// _auditRepository.Add(new TrialAudit() +// { +// //判断是添加还是更新 +// TrialId = trialSites[0].TrialId, +// SubjectId = Guid.Empty, +// AuditType = (int)AuditType.TrialAudit, +// Note = $"{ _userRealName} 添加了项目研究中心 {string.Join(',', siteNames)} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; + +// //研究中心添加运维人员 +// case AuditOptType.AddTrialSiteCRC: +// //接口参数 +// var trialSiteCRCs = (List)_actionArguments.Values.ToList()[0]; + +// if (result.IsSuccess) +// { +// //查询site信息 +// var _siteRepository = (IRepository)executedcontext.HttpContext.RequestServices.GetService(typeof(IRepository)); +// var siteName = _siteRepository.FirstOrDefault(t => t.Id == trialSiteCRCs[0].SiteId).IfNullThrowException().SiteName; + +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = trialSiteCRCs[0].TrialId, +// SubjectId = Guid.Empty, +// AuditType = (int)AuditType.TrialAudit, +// Note = $"{ _userRealName} 为{ siteName } 添加了运维人员 {string.Join(',', trialSiteCRCs.Select(t => t.UserRealName).ToList())}", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; + +// //删除项目运维人员 +// case AuditOptType.DeleteTrailStaff: + +// if (result.IsSuccess) +// { +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = _userTrial.TrialId, +// SubjectId = Guid.Empty, +// AuditType = (int)AuditType.TrialAudit, +// Note = $"{ _userRealName} 移除了项目参与人员 {_userTrial.User.LastName + " / " + _userTrial.User.FirstName} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; +// //删除项目site +// case AuditOptType.DeleteTrialSite: + +// if (result.IsSuccess) +// { +// //查询site信息 +// var _siteRepository = (IRepository)executedcontext.HttpContext.RequestServices.GetService(typeof(IRepository)); +// var siteName = _siteRepository.FirstOrDefault(t => t.Id == _trialSite.SiteId).IfNullThrowException().SiteName; + +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = _trialSite.TrialId, +// SubjectId = Guid.Empty, +// AuditType = (int)AuditType.TrialAudit, +// Note = $"{ _userRealName} 移除了研究中心 {siteName} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; +// //删除项目Site IC +// case AuditOptType.DeleteTrialSiteCRC: + +// if (result.IsSuccess) +// { +// //查询site信息 +// var _siteRepository = (IRepository)executedcontext.HttpContext.RequestServices.GetService(typeof(IRepository)); +// var siteName = _siteRepository.FirstOrDefault(t => t.Id == _userTrialSite.SiteId).IfNullThrowException().SiteName; + +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = _userTrialSite.TrialId, +// SubjectId = Guid.Empty, +// AuditType = (int)AuditType.TrialAudit, +// Note = $"{ _userRealName} 移除了 {siteName} 的运维人员 {_userTrialSite.User.UserName} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } +// break; + +// //添加检查批次计划Item +// case AuditOptType.AddOrUpdateTrialVisitPlanItem: +// //接口参数 +// var visitPlanItem = (VisitPlanCommand)_actionArguments.Values.ToList()[0]; + +// if (result.IsSuccess) +// { +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = visitPlanItem.TrialId, +// SubjectId = Guid.Empty, +// AuditType = (int)AuditType.TrialAudit, +// Note = visitPlanItem.Id == null ? $"{ _userRealName} 添加了检查批次计划项 {visitPlanItem.VisitName} " : $"{ _userRealName} 更新了检查批次计划项为 {visitPlanItem.VisitName} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; +// //删除检查批次计划Item +// case AuditOptType.DeleteTrialVisitPlanItem: + +// if (result.IsSuccess) +// { +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = _visitPlan.TrialId, +// SubjectId = Guid.Empty, +// AuditType = (int)AuditType.TrialAudit, +// Note = $"{ _userRealName} 移除了检查批次计划项 {_visitPlan.VisitName} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } +// break; + +// //确认了检查批次计划 +// case AuditOptType.ConfirmTrialVisitPlan: +// var trialId = (Guid)_actionArguments.Values.ToList()[0]; + +// if (result.IsSuccess) +// { +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = trialId, +// SubjectId = Guid.Empty, +// AuditType = (int)AuditType.TrialAudit, +// Note = $"{ _userRealName} 确认了检查批次计划,不允许修改 ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; +// //添加了QA模板 +// case AuditOptType.AddOrUpdateTrialTemplate: +// //接口参数 + +// if (result.IsSuccess) +// { + +// } + + +// break; +// //删除项目模板 +// case AuditOptType.DeleteTrialTemplate: + +// if (result.IsSuccess) +// { + +// } +// break; + +// } + +// break; +// //患者审计 +// case AuditType.SubjectAudit: + + +// switch (_attr._auditOptType) +// { +// // 添加或者更新患者 +// case AuditOptType.AddOrUpdateSubject: + +// //接口参数 +// var subject = (SubjectCommand)_actionArguments.Values.ToList()[0]; + +// if (result.IsSuccess) +// { +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = subject.TrialId, +// SubjectId = subject.Id == null ? Guid.Parse(result.Data) : subject.Id.Value, +// AuditType = (int)AuditType.SubjectAudit, +// Note = subject.Id == null ? $"{ _userRealName} 添加了患者 {subject.ShortName } 并初始化了检查批次计划" : $"{ _userRealName} 对患者 { subject.ShortName } 信息进行了更新 ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; +// // 删除患者 +// case AuditOptType.DeleteSubject: +// if (result.IsSuccess) +// { +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = _subject.TrialId, +// SubjectId = _subject.Id, +// AuditType = (int)AuditType.SubjectAudit, +// Note = $"{ _userRealName} 移除了患者 {_subject.LastName + _subject.FirstName} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; +// // 患者计划外检查批次 +// case AuditOptType.AddOrUpdateSubjectOutPlanVisit: + +// //接口参数 +// var subjectVisit = (SubjectVisitCommand)_actionArguments.Values.ToList()[0]; + +// if (result.IsSuccess) +// { +// var _subjectRepository = +// (IRepository)executedcontext.HttpContext.RequestServices.GetService( +// typeof(IRepository)); + +// _subject = _subjectRepository.FirstOrDefault(t => t.Id == subjectVisit.SubjectId).IfNullThrowException(); + +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = _subject.TrialId, +// SubjectId = _subject.Id, +// AuditType = (int)AuditType.SubjectAudit, +// Note = subjectVisit.Id == null ? $"{ _userRealName} 为患者 {_subject.LastName + _subject.FirstName} 添加了计划外检查批次 {subjectVisit.VisitName}" : $"{ _userRealName} 更新患者 {_subject.LastName + _subject.FirstName} 计划外检查批次{subjectVisit.VisitName}", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; +// // 删除计划外检查批次 +// case AuditOptType.DeleteSubjectOutPlanVisit: + +// if (result.IsSuccess) +// { +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = _subjectVisit.TrialId, +// SubjectId = _subject.Id, +// AuditType = (int)AuditType.SubjectAudit, +// Note = $"{ _userRealName} 移除了患者{_subject.LastName + _subject.FirstName} 计划外检查批次 {_subjectVisit.VisitName} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; +// // 人工设置已执行 +// case AuditOptType.SetSVExecuted: + +// if (result.IsSuccess) +// { +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = _subjectVisit.TrialId, +// SubjectId = _subject.Id, +// AuditType = (int)AuditType.SubjectAudit, +// Note = $"{ _userRealName} 将患者 {_subject.LastName + _subject.FirstName} 检查批次 {_subjectVisit.VisitName} 设置为已执行 ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + + +// break; +// } + +// break; + +// //检查审计 +// case AuditType.StudyAudit: +// _studyRepository = +// (IRepository)executedcontext.HttpContext.RequestServices.GetService( +// typeof(IRepository)); + +// switch (_attr._auditOptType) +// { + +// case AuditOptType.ChangeStudyStatus: + +// //接口参数 +// var studyStatusInfo = (StudyStatusDetailCommand)_actionArguments.Values.ToList()[0]; + +// _study = _studyRepository.FirstOrDefault(t => t.Id == studyStatusInfo.StudyId).IfNullThrowException(); + +// if (result.IsSuccess) +// { +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = _study.TrialId, +// SubjectId = _study.SubjectId, +// StudyId = _study.Id, +// AuditType = (int)AuditType.StudyAudit, +// Note = $"{ _userRealName} 动作变更了 { _study.StudyCode} 检查状态为 {DealStudyStatus(_study.Status)} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + +// break; + +// case AuditOptType.Anonymized: + +// case AuditOptType.Forwarded: + +// //接口参数 +// var studyId = (Guid)_actionArguments.Values.ToList()[0]; + +// _study = _studyRepository.FirstOrDefault(t => t.Id == studyId).IfNullThrowException(); + +// if (result.IsSuccess) +// { +// _auditRepository.Add(new TrialAudit() +// { +// TrialId = _study.TrialId, +// SubjectId = _study.SubjectId, +// StudyId = _study.Id, +// AuditType = (int)AuditType.StudyAudit, +// Note = $"{ _userRealName} 动作变更了 { _study.StudyCode} 检查状态为 {DealStudyStatus(_study.Status)} ", +// Detail = string.Empty, +// OptUserId = Guid.Parse(_userIdStr), +// OptUser = _userRealName, +// OptTime = DateTime.Now +// }); +// } + + +// break; +// } + +// break; + +// } + +// _auditRepository.SaveChanges(); + +// #endregion + + +// //提交事务 Commit transaction if all commands succeed, transaction will auto-rollback when disposed if either commands fails +// transaction.Commit(); +// } +// ); + + + + + + + + + +// } +// } +//} + + + + diff --git a/IRaCIS.Core.Application/BusinessFilter/TrialResourceFilter.cs b/IRaCIS.Core.Application/BusinessFilter/TrialResourceFilter.cs new file mode 100644 index 0000000..f2103ce --- /dev/null +++ b/IRaCIS.Core.Application/BusinessFilter/TrialResourceFilter.cs @@ -0,0 +1,208 @@ +using EasyCaching.Core; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using System.Text.RegularExpressions; +using static IRaCIS.Core.Domain.Share.StaticData; + +namespace IRaCIS.Core.Application.Filter +{ + /// + /// 主要为了 处理项目结束 锁库,不允许操作 + /// + public class TrialResourceFilter : Attribute, IAsyncResourceFilter + { + private readonly IEasyCachingProvider _provider; + private readonly IUserInfo _userInfo; + + + private readonly List _trialOptList=new List(); + + + public TrialResourceFilter(IEasyCachingProvider provider, IUserInfo userInfo, string trialOpt = null, string trialOpt2 = null, string trialOpt3 = null) + { + _provider = provider; + _userInfo = userInfo; + //_trialOpt = trialOpt; + + if (!string.IsNullOrWhiteSpace(trialOpt)) _trialOptList.Add(trialOpt.Trim()); + if (!string.IsNullOrWhiteSpace(trialOpt2)) _trialOptList.Add(trialOpt2.Trim()); + if (!string.IsNullOrWhiteSpace(trialOpt3)) _trialOptList.Add(trialOpt3.Trim()); + + + } + + + + //public TrialResourceFilter(IEasyCachingProvider provider, IUserInfo userInfo) + //{ + // _provider = provider; + // _userInfo = userInfo; + //} + + + + + + //优先选择异步的方法 + public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) + { + // var typeFilter = context.ActionDescriptor.EndpointMetadata.Where(t => t.GetType() == typeof(TypeFilterAttribute)).Select(t => (TypeFilterAttribute)t).ToList().FirstOrDefault(); + //var _trialOptList= typeFilter.Arguments.Select(t => t.ToString()).ToList(); + + #region 处理新的用户类型,不能操作项目相关接口 + + // 后期列举出具体的类型,其他任何用户类型,都不允许操作 + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA) + { + context.Result = new JsonResult(ResponseOutput.NotOk("对不起,您的账户没有操作权限。")); + + return; + } + + #endregion + + + + //TrialId 传递的途径多种,可能在path 可能在body 可能在数组中,也可能在对象中,可能就在url + var trialIdStr = string.Empty; + + if (!string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["trialId"])) + { + trialIdStr = context.HttpContext.Request.Query["trialId"]; + } + + //先尝试从path中取TrialId + else if (context.RouteData.Values.Keys.Any(t => t.Contains("trialId"))) + { + var index = context.RouteData.Values.Keys.ToList().IndexOf("trialId"); + trialIdStr = context.RouteData.Values.Values.ToList()[index] as string; + } + else if (context.HttpContext.Request.Headers["RefererUrl"].ToString().Contains("trialId")) + { + var headerStr = context.HttpContext.Request.Headers["RefererUrl"].ToString(); + + var trialIdIndex = headerStr.IndexOf("trialId"); + + + var matchResult = Regex.Match(headerStr.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}"); + + if (matchResult.Success) + { + trialIdStr = matchResult.Value; + } + else + { + context.Result = new JsonResult(ResponseOutput.NotOk("正则取请求Refer 中trialId 失败,请联系开发人员核查")); + } + + } + else + { + #region body 中取数据 + + //设置可以多次读 + context.HttpContext.Request.EnableBuffering(); + var reader = new StreamReader(context.HttpContext.Request.Body); + var contentFromBody = await reader.ReadToEndAsync(); + //读取后,流的位置还原 + context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin); + //context.HttpContext.Request.Body.Position = 0; + + //找到参数位置在字符串中的索引 + var trialIdIndex = contentFromBody.IndexOf("\"TrialId\"", StringComparison.OrdinalIgnoreCase); + + if (trialIdIndex > -1) + { + // (?<="trialId" *: *").*?(?=",) + + //使用正则 [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12} + + var matchResult = Regex.Match(contentFromBody.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}"); + + if (matchResult.Success) + { + //有可能匹配错误 "trialId":"","documentId":"b8180000-3e2c-0016-9fe0-08da33f96236" 从缓存里面验证下 + var cacheResultDic = _provider.GetAll(new[] { matchResult.Value }); + + var trialStatusStr = cacheResultDic[matchResult.Value].Value; + + if (!string.IsNullOrWhiteSpace(trialStatusStr)) + { + trialIdStr = matchResult.Value; + } + + + } + else + { + context.Result = new JsonResult(ResponseOutput.NotOk("正则取请求Refer 中trialId 失败,请联系开发人员核查")); + } + + //使用字符串取 如果是swagger 可能有时取的不对 因为空格的原因 + //trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 3 + } + + #endregion + } + + //通过path 或者body 找到trialId 了 + if ( !string.IsNullOrWhiteSpace(trialIdStr)) + { + + //如果没缓存数据,可能定时任务没执行或者缓存丢失 在此重新缓存 + if (_provider.GetCount() == 0) + { + + var _trialRepository = context.HttpContext.RequestServices.GetService(typeof(IRepository)) as IRepository; + + var list = _trialRepository.IfNullThrowException().Select(t => new { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr }) + .ToList(); + + list.ForEach(t => _provider.Set(t.TrialId.ToString(), t.TrialStatusStr, TimeSpan.FromDays(7))); + } + + var cacheResultDic = _provider.GetAll(new[] { trialIdStr }); + + var trialStatusStr = cacheResultDic[trialIdStr].Value; + + //意外 导致缓存过期,调整服务器时间,测试不想重启程序 + if (string.IsNullOrWhiteSpace(trialStatusStr)) + { + var trialRepository = context.HttpContext.RequestServices.GetService(typeof(IRepository)) as IRepository; + trialStatusStr = trialRepository?.Where(t => t.Id == Guid.Parse(trialIdStr)).Select(t => t.TrialStatusStr).FirstOrDefault(); + } + + // 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口) + if (trialStatusStr == StaticData.TrialState.TrialOngoing || _trialOptList.Any(t=>t== TrialOpt.BeforeOngoingCantOpt) ) + { + + await next.Invoke(); + + } + // 项目停止、或者完成 不允许操作 + else + { + context.Result = new JsonResult(ResponseOutput.NotOk("本次请求被配置规则拦截:项目状态处于进行中状态时,才允许操作。")); + + } + + } + //添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截 + else if (_trialOptList.Any(t => t == TrialOpt.AddOrUpdateTrial ||t ==TrialOpt.SignSystemDocNoTrialId)) + { + await next.Invoke(); + } + + else + { + //如果项目相关接口没有传递trialId 会来到这里,提醒,以便修改 + + context.Result = new JsonResult(ResponseOutput.NotOk("该接口参数中,没有传递项目编号,请核对。")); + } + + + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/BusinessFilter/UnifiedApiResultFilter.cs b/IRaCIS.Core.Application/BusinessFilter/UnifiedApiResultFilter.cs new file mode 100644 index 0000000..8e5b0ff --- /dev/null +++ b/IRaCIS.Core.Application/BusinessFilter/UnifiedApiResultFilter.cs @@ -0,0 +1,117 @@ +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Application.Services.BusinessFilter +{ + /// + /// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码 + /// by zhouhang 2021.09.12 周末 + /// + public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter + { + /// + /// 异步版本 + /// + /// + /// + /// + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + + if (context.Result is ObjectResult objectResult) + { + var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode; + + //是200 并且没有包装 那么包装结果 + if (statusCode == 200 && !(objectResult.Value is IResponseOutput)) + { + //if (objectResult.Value == null) + //{ + // var apiResponse = ResponseOutput.DBNotExist(); + + // objectResult.Value = apiResponse; + // objectResult.DeclaredType = apiResponse.GetType(); + //} + //else + //{ + + var type = objectResult.Value?.GetType(); + + + if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>))) + { + + //报错 + //var tuple = (object, object))objectResult.Value; + + //var (val1, val2) = ((dynamic, dynamic))objectResult.Value; + //var apiResponse = ResponseOutput.Ok(val1, val2); + + //OK + var tuple = (dynamic)objectResult.Value; + var apiResponse = ResponseOutput.Ok(tuple.Item1, tuple.Item2); + + + objectResult.Value = apiResponse; + objectResult.DeclaredType = apiResponse.GetType(); + } + else + { + var apiResponse = ResponseOutput.Ok(objectResult.Value); + + objectResult.Value = apiResponse; + objectResult.DeclaredType = apiResponse.GetType(); + } + + + //} + + } + //如果不是200 是IResponseOutput 不处理 + else if (statusCode != 200 && (objectResult.Value is IResponseOutput)) + { + } + + else if(statusCode != 200&&!(objectResult.Value is IResponseOutput)) + { + var apiResponse = ResponseOutput.NotOk("程序错误,请联系开发人员。"); + + objectResult.Value = apiResponse; + objectResult.DeclaredType = apiResponse.GetType(); + } + + } + + await next.Invoke(); + + } + + public static bool IsTupleType(Type type, bool checkBaseTypes = false) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + if (type == typeof(Tuple)) + return true; + + while (type != null) + { + if (type.IsGenericType) + { + var genType = type.GetGenericTypeDefinition(); + if (genType == typeof(Tuple<>) + || genType == typeof(Tuple<,>) + || genType == typeof(Tuple<,>)) + return true; + } + + if (!checkBaseTypes) + break; + + type = type.BaseType; + } + + return false; + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Helper/Attribute/DictionaryTranslateAttribute.cs b/IRaCIS.Core.Application/Helper/Attribute/DictionaryTranslateAttribute.cs new file mode 100644 index 0000000..459557f --- /dev/null +++ b/IRaCIS.Core.Application/Helper/Attribute/DictionaryTranslateAttribute.cs @@ -0,0 +1,68 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Helper +{ + [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)] + public class DictionaryTranslateAttribute : Attribute + { + + public string DicParentCode { get; set; } + + public DicDataTypeEnum DataTypeEnum { get; set; } + + public CriterionType? CriterionType { get; set; } + + + //是否翻译依赖其他属性 + public bool IsTranslateDenpendOtherProperty =>!string.IsNullOrWhiteSpace(DependPropertyName); + + public string DependPropertyName { get; set; }=string.Empty; + + public string DependPropertyValueStr { get; set; } = string.Empty; + + // 普通翻译的字典 + public DictionaryTranslateAttribute(string dicParentCode) + { + DicParentCode = dicParentCode; + + } + + //针对不同的标准 翻译的字典不一样 + public DictionaryTranslateAttribute(string dicParentCode, CriterionType criterionType ) + { + DicParentCode = dicParentCode; + + CriterionType = criterionType; + } + + //针对业务某个属性的值 不一样 用的翻译字典不一样 + + public DictionaryTranslateAttribute(string dicParentCode,string dependPropertyName, string dependPropertyValueStr) + { + DicParentCode = dicParentCode; + + DependPropertyName = dependPropertyName; + + DependPropertyValueStr = dependPropertyValueStr; + + } + + public DictionaryTranslateAttribute(string dicParentCode, CriterionType criterionType, string dependPropertyName, string dependPropertyValueStr) + { + DicParentCode = dicParentCode; + + DependPropertyName = dependPropertyName; + + CriterionType = criterionType; + + DependPropertyValueStr = dependPropertyValueStr; + + } + + } +} diff --git a/IRaCIS.Core.Application/Helper/ExcelExportHelper.cs b/IRaCIS.Core.Application/Helper/ExcelExportHelper.cs new file mode 100644 index 0000000..84a8f7d --- /dev/null +++ b/IRaCIS.Core.Application/Helper/ExcelExportHelper.cs @@ -0,0 +1,262 @@ +using DocumentFormat.OpenXml.Drawing.Diagrams; +using IRaCIS.Application.Interfaces; +using IRaCIS.Core.Application.Helper; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using MiniExcelLibs; +using MiniExcelLibs.OpenXml; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NPOI.HSSF.UserModel; + +namespace IRaCIS.Core.Application.Service; + +public static class ExcelExportHelper +{ + //MiniExcel_Export + public static async Task DataExportAsync(string code, object data, string exportFileNamePrefix, IRepository _commonDocumentRepository, IWebHostEnvironment _hostEnvironment, IDictionaryService? _dictionaryService = null, Type? translateType = null, CriterionType? criterionType = null) + { + + //判断是否有字典翻译 + + if (_dictionaryService != null && translateType != null) + { + + ////只标注单个的时候 + //var needTranslatePropertyList = translateType.GetProperties().Where(t => t.IsDefined(typeof(DictionaryTranslateAttribute), true)) + // .Select(c => new { c.Name, DicParentCode = ((DictionaryTranslateAttribute?)c.GetCustomAttributes(typeof(DictionaryTranslateAttribute), false)[0])?.DicParentCode }).ToList(); + + + //一个值 对应不同的字典翻译 + var needTranslatePropertyList = translateType.GetProperties().Where(t => t.IsDefined(typeof(DictionaryTranslateAttribute), true)) + .SelectMany(c => + c.GetCustomAttributes(typeof(DictionaryTranslateAttribute), false).Select(f => (DictionaryTranslateAttribute?)f).Where(t => t?.CriterionType == criterionType || t?.CriterionType == null) + .Select(k => new { c.Name, k.DicParentCode ,k.IsTranslateDenpendOtherProperty, k.DependPropertyName,k.DependPropertyValueStr}) + ).ToList(); + + + + //字典表查询出所有需要翻译的数据 + + var translateDataList = await _dictionaryService.GetBasicDataSelect(needTranslatePropertyList.Select(t => t.DicParentCode).Distinct().ToArray()); + + + var dic = JsonConvert.DeserializeObject>(data.ToJsonNotIgnoreNull()); + + foreach (var key in dic.Keys) + { + //是数组 那么找到对应的属性 进行翻译 + if (dic[key].GetType().IsAssignableFrom(typeof(JArray))) + { + + var newObjList = new List(); + var no = 1; + + foreach (var item in dic[key] as JArray) + { + var itemDic = JsonConvert.DeserializeObject>(item.ToJsonNotIgnoreNull()); + + + foreach (var needTranslateProperty in needTranslatePropertyList) + { + //翻译的属性依赖其他属性 + if(needTranslateProperty.IsTranslateDenpendOtherProperty) + { + if (item[needTranslateProperty.DependPropertyName]?.ToString().ToLower() == needTranslateProperty.DependPropertyValueStr.ToLower()) + { + var beforeValue = itemDic[needTranslateProperty.Name]?.ToString(); + + itemDic[needTranslateProperty.Name] = translateDataList[needTranslateProperty.DicParentCode].Where(t => t.Code.ToLower() == beforeValue?.ToLower()).FirstOrDefault()?.ValueCN ?? String.Empty; + } + } + //普通翻译 或者某一标准翻译 + else + { + var beforeValue = itemDic[needTranslateProperty.Name]?.ToString(); + + + itemDic[needTranslateProperty.Name] = translateDataList[needTranslateProperty.DicParentCode].Where(t => t.Code.ToLower() == beforeValue?.ToLower()).FirstOrDefault()?.ValueCN ?? String.Empty; + } + + + + } + + itemDic.Add("No", no++); + + + newObjList.Add(itemDic); + } + + dic[key] = newObjList ; + + } + } + + + data = dic; + + + } + + + + + + var (physicalPath, fileNmae) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code); + + + //模板路径 + var tplPath = physicalPath; + + #region MiniExcel + + var memoryStream = new MemoryStream(); + + var config = new OpenXmlConfiguration() + { + IgnoreTemplateParameterMissing = true, + }; + + await MiniExcel.SaveAsByTemplateAsync(memoryStream, tplPath, data, config); + + memoryStream.Seek(0, SeekOrigin.Begin); + + return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + { + FileDownloadName = $"{exportFileNamePrefix}_{fileNmae.Substring(0, fileNmae.LastIndexOf('.'))}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx" + }; + + #endregion + + #region Magicodes 模板规则不一样 + + ////创建Excel导出对象 + //IExportFileByTemplate exporter = new ExcelExporter(); + + ////根据模板导出 + //var result = await exporter.ExportBytesByTemplate(exportInfo, tplPath); + + + //return new XlsxFileResult(bytes: result, fileDownloadName: $"{doc.Name}_{DateTime.Now.ToString("yyyy-MM-dd:hh:mm:ss")}.xlsx"); + #endregion + + } + + + + public static async Task<(MemoryStream,string)> DataExport_NpoiTestAsync(string code, object data, IRepository _commonDocumentRepository, IWebHostEnvironment _hostEnvironment, IDictionaryService? _dictionaryService = null, Type? translateType = null, CriterionType? criterionType = null) + { + //判断是否有字典翻译 + + if (_dictionaryService != null && translateType != null) + { + + ////只标注单个的时候 + //var needTranslatePropertyList = translateType.GetProperties().Where(t => t.IsDefined(typeof(DictionaryTranslateAttribute), true)) + // .Select(c => new { c.Name, DicParentCode = ((DictionaryTranslateAttribute?)c.GetCustomAttributes(typeof(DictionaryTranslateAttribute), false)[0])?.DicParentCode }).ToList(); + + + //一个值 对应不同的字典翻译 + var needTranslatePropertyList = translateType.GetProperties().Where(t => t.IsDefined(typeof(DictionaryTranslateAttribute), true)) + .SelectMany(c => + c.GetCustomAttributes(typeof(DictionaryTranslateAttribute), false).Select(f => (DictionaryTranslateAttribute?)f).Where(t => t.CriterionType == criterionType || t.CriterionType == null) + .Select(k => new { c.Name, k.DicParentCode, k.IsTranslateDenpendOtherProperty, k.DependPropertyName, k.DependPropertyValueStr }) + ).ToList(); + + + + //字典表查询出所有需要翻译的数据 + + var translateDataList = await _dictionaryService.GetBasicDataSelect(needTranslatePropertyList.Select(t => t.DicParentCode).Distinct().ToArray()); + + + var dic = JsonConvert.DeserializeObject>(data.ToJsonNotIgnoreNull()); + + foreach (var key in dic.Keys) + { + //是数组 那么找到对应的属性 进行翻译 + if (dic[key].GetType().IsAssignableFrom(typeof(JArray))) + { + + var newObjList = new List(); + var no = 1; + + foreach (var item in dic[key] as JArray) + { + var itemDic = JsonConvert.DeserializeObject>(item.ToJsonNotIgnoreNull()); + + + foreach (var needTranslateProperty in needTranslatePropertyList) + { + //翻译的属性依赖其他属性 + if (needTranslateProperty.IsTranslateDenpendOtherProperty) + { + if (item[needTranslateProperty.DependPropertyName]?.ToString().ToLower() == needTranslateProperty.DependPropertyValueStr.ToLower()) + { + var beforeValue = itemDic[needTranslateProperty.Name]?.ToString(); + + itemDic[needTranslateProperty.Name] = translateDataList[needTranslateProperty.DicParentCode].Where(t => t.Code.ToLower() == beforeValue?.ToLower()).FirstOrDefault()?.ValueCN ?? String.Empty; + } + } + //普通翻译 或者某一标准翻译 + else + { + var beforeValue = itemDic[needTranslateProperty.Name]?.ToString(); + + + itemDic[needTranslateProperty.Name] = translateDataList[needTranslateProperty.DicParentCode].Where(t => t.Code.ToLower() == beforeValue?.ToLower()).FirstOrDefault()?.ValueCN ?? String.Empty; + } + + + + } + + itemDic.Add("No", no++); + newObjList.Add(itemDic); + } + + dic[key] = newObjList; + + } + } + + + data = dic; + + + } + + + var (physicalPath, fileName) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code); + + + //模板路径 + var tplPath = physicalPath; + + #region MiniExcel + + var memoryStream = new MemoryStream(); + + var config = new OpenXmlConfiguration() + { + IgnoreTemplateParameterMissing = true, + }; + + await MiniExcel.SaveAsByTemplateAsync(memoryStream, tplPath, data, config); + + memoryStream.Seek(0, SeekOrigin.Begin); + + + return (memoryStream, fileName); + + //var wb = new HSSFWorkbook(memoryStream); + + //var sheet = wb.GetSheetAt(0); + + + #endregion + } + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Helper/FileFormatConvertHelper.cs b/IRaCIS.Core.Application/Helper/FileFormatConvertHelper.cs new file mode 100644 index 0000000..bca41c8 --- /dev/null +++ b/IRaCIS.Core.Application/Helper/FileFormatConvertHelper.cs @@ -0,0 +1,16 @@ +using DocumentFormat.OpenXml.Bibliography; +using NPOI.XWPF.UserModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Helper +{ + public class FileFormatConvertHelper + { + + + } +} diff --git a/IRaCIS.Core.Application/Helper/FileStoreHelper.cs b/IRaCIS.Core.Application/Helper/FileStoreHelper.cs new file mode 100644 index 0000000..19ced47 --- /dev/null +++ b/IRaCIS.Core.Application/Helper/FileStoreHelper.cs @@ -0,0 +1,564 @@ + + +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using Microsoft.AspNetCore.Hosting; +using System.Text.RegularExpressions; + +namespace IRaCIS.Core.Application.Helper; + +public static class FileStoreHelper +{ + + + //处理文件名 压缩包,或者目录类的 会带上相对路径 + public static (string TrustedFileNameForFileStorage, string RealName) GetStoreFileName(string fileName,bool isChangeToPdfFormat=false) + { + + //带目录层级,需要后端处理前端的路径 + if (fileName.Contains("\\")) + { + fileName = fileName.Split("\\").Last(); + } + + if (fileName.Contains("/")) + { + fileName = fileName.Split("/").Last(); + } + + var matchResult = Regex.Match(fileName, @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}"); + + //如果有guid + if (matchResult.Success) + { + fileName = fileName.Replace($"{matchResult.Value}", ""); + } + + + var trustedFileNameForFileStorage = string.Empty; + + if (isChangeToPdfFormat==false) + { + trustedFileNameForFileStorage= Guid.NewGuid().ToString() + fileName; + + } + else + { + trustedFileNameForFileStorage=Guid.NewGuid().ToString() + Path.GetFileNameWithoutExtension(fileName) + ".pdf"; + } + + return (trustedFileNameForFileStorage, fileName); + } + + //API vue 部署目录 + public static string GetIRaCISRootPath(IWebHostEnvironment _hostEnvironment) + { + var rootPath = (Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\'))).IfNullThrowException().FullName; + return rootPath; + + } + + + //获取环境存储根目录 + public static string GetIRaCISRootDataFolder(IWebHostEnvironment _hostEnvironment) + { + var rootPath = GetIRaCISRootPath(_hostEnvironment); + + var rootFolder = Path.Combine(rootPath, StaticData.Folder.IRaCISDataFolder); + + return rootFolder; + } + + //根据相对路径 获取具体文件物理地址 + public static string GetPhysicalFilePath(IWebHostEnvironment _hostEnvironment, string relativePath) + { + var rootPath = GetIRaCISRootPath(_hostEnvironment); + + var physicalFilePath = Path.Combine(rootPath, relativePath.Trim('/')); + + return physicalFilePath; + } + + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task<(string PhysicalPath, string FileName)> GetSystemClinicalPathAsync(IWebHostEnvironment _hostEnvironment, IRepository _clinicalDataTrialSetRepository, Guid id) + { + var systemClinicalData = await _clinicalDataTrialSetRepository.FirstOrDefaultAsync(t => t.Id == id); + + if (systemClinicalData == null || systemClinicalData.Path == string.Empty) + { + throw new BusinessValidationFailedException("数据库没有找到对应的数据模板文件,请联系系统运维人员。"); + } + + var filePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, systemClinicalData.Path); + + if (!System.IO.File.Exists(filePath)) + { + throw new BusinessValidationFailedException("数据模板文件存储路径上未找对应文件,请联系系统运维人员。"); + } + + return (filePath, systemClinicalData.FileName); + } + + + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task<(string PhysicalPath, string FileName)> GetTrialClinicalPathAsync(IWebHostEnvironment _hostEnvironment, IRepository _clinicalDataTrialSetRepository, Guid id) + { + var trialClinicalData = await _clinicalDataTrialSetRepository.FirstOrDefaultAsync(t => t.Id == id); + + if (trialClinicalData == null|| trialClinicalData.Path==string.Empty) + { + throw new BusinessValidationFailedException("数据库没有找到对应的数据模板文件,请联系系统运维人员。"); + } + + var filePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, trialClinicalData.Path); + + if (!System.IO.File.Exists(filePath)) + { + throw new BusinessValidationFailedException("数据模板文件存储路径上未找对应文件,请联系系统运维人员。"); + } + + return (filePath, trialClinicalData.FileName); + } + + + //通过编码获取通用文档具体物理路径 + + public static async Task<(string PhysicalPath, string FileName)> GetCommonDocPhysicalFilePathAsync(IWebHostEnvironment _hostEnvironment, IRepository _commonDocumentRepository, string code) + { + var doc = await _commonDocumentRepository.FirstOrDefaultAsync(t => t.Code == code); + + if (doc == null) + { + throw new BusinessValidationFailedException("数据库没有找到对应的数据模板文件,请联系系统运维人员。"); + } + + var filePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, doc.Path); + + if (!System.IO.File.Exists(filePath)) + { + throw new BusinessValidationFailedException("数据模板文件存储路径上未找对应文件,请联系系统运维人员。"); + } + + return (filePath, doc.Name.Trim('/')); + } + + + /// + /// 写文件导到磁盘 + /// + /// 流 + /// 文件保存路径 + /// + public static async Task WriteFileAsync(System.IO.Stream stream, string path) + { + const int FILE_WRITE_SIZE = 84975;//写出缓冲区大小 + int writeCount = 0; + using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true)) + { + byte[] byteArr = new byte[FILE_WRITE_SIZE]; + int readCount = 0; + while ((readCount = await stream.ReadAsync(byteArr, 0, byteArr.Length)) > 0) + { + await fileStream.WriteAsync(byteArr, 0, readCount); + writeCount += readCount; + } + } + return writeCount; + } + + // 获取项目签名文档存放路径 + + public static (string PhysicalPath, string RelativePath) GetTrialSignDocPath(IWebHostEnvironment _hostEnvironment, Guid trialId, string fileName) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), StaticData.Folder.SignDocumentFolder); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{StaticData.Folder.SignDocumentFolder}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + // 获取系统签名文档存放路径 + public static (string PhysicalPath, string RelativePath) GetSystemSignDocPath(IWebHostEnvironment _hostEnvironment, string fileName) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.SystemDataFolder, StaticData.Folder.SignDocumentFolder); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.SystemDataFolder}/{ StaticData.Folder.SignDocumentFolder}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + // 获取通用文档存放路径(excel模板 ) + + public static (string PhysicalPath, string RelativePath) GetCommonDocPath(IWebHostEnvironment _hostEnvironment, string fileName) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.SystemDataFolder, StaticData.Folder.DataTemplate); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.SystemDataFolder}/{StaticData.Folder.DataTemplate}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + //获取系统通知文档存放路径 + + public static (string PhysicalPath, string RelativePath) GetSystemNoticePath(IWebHostEnvironment _hostEnvironment, string fileName) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.SystemDataFolder, StaticData.Folder.NoticeAttachment); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.SystemDataFolder}/{StaticData.Folder.NoticeAttachment}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + // 获取一致性核查路径 + public static (string PhysicalPath, string RelativePath) GetTrialCheckFilePath(IWebHostEnvironment _hostEnvironment, string fileName,Guid trialId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //上传根路径 + string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), StaticData.Folder.UploadEDCData); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + //存放核对表 + var (trustedFileNameForFileStorage, realFileName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{StaticData.Folder.UploadEDCData}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + + public static (string PhysicalPath, string RelativePath, string FileRealName) GetClinicalTemplatePath(IWebHostEnvironment _hostEnvironment, string fileName,Guid trialId) + { + + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, "ClinicalTemplate", trialId.ToString(), StaticData.Folder.TreatmenthistoryFolder); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/ClinicalTemplate/{trialId}/{StaticData.Folder.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath, fileRealName); + + } + + + public static (string PhysicalPath, string RelativePath, string FileRealName) GetReadClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteId, Guid subjectId, Guid readingId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString(), StaticData.Folder.Reading, readingId.ToString()); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{StaticData.Folder.Reading}/{readingId}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath, fileRealName); + + } + + //获取临床数据存放路径 + public static (string PhysicalPath, string RelativePath,string FileRealName) GetClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName,Guid trialId,Guid siteId,Guid subjectId,Guid subjectVisitId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(),siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.Folder.TreatmenthistoryFolder); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{StaticData.Folder.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath, fileRealName); + + } + + + public static (string PhysicalPath, string RelativePath, string FileRealName) GetClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId , Guid subjectId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), subjectId.ToString(), StaticData.Folder.TreatmenthistoryFolder); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{subjectId}/{StaticData.Folder.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath, fileRealName); + + } + + + /// + /// 上传截图 + /// + /// + /// + /// + /// + /// + /// + /// + public static (string PhysicalPath, string RelativePath, string FileRealName) GetUploadPrintscreenFilePath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteid, Guid subjectId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteid.ToString(), subjectId.ToString()); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteid}/{subjectId}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath, fileRealName); + } + + + /// + /// 通用获取文件路径 + /// + /// + /// + /// + /// + /// + /// + public static (string PhysicalPath, string RelativePath, string FileRealName) GetFilePath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid id,string type) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), id.ToString(), type); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{id}/{type}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath, fileRealName); + } + + public static (string PhysicalPath, string RelativePath, string FileRealName) GetMedicalReviewImage(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid taskMedicalReviewId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), taskMedicalReviewId.ToString(), StaticData.Folder.MedicalReview); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{taskMedicalReviewId}/{StaticData.Folder.MedicalReview}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath, fileRealName); + } + + //获取非dicom文件存放路径 + public static (string PhysicalPath, string RelativePath, string FileRealName) GetNoneDicomFilePath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.Folder.NoneDicomFolder); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{StaticData.Folder.NoneDicomFolder}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath, fileRealName); + } + + // 获取 入组确认 PD 进展发送邮件Word|PDF 存放路径 + + public static (string PhysicalPath, string RelativePath, string FileRealName) GetSubjectEnrollConfirmOrPDEmailPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteId, Guid subjectId,bool isChangeToPdfFormat=false) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString()); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName, isChangeToPdfFormat); + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath, fileRealName); + } + + + public static string GetSubjectVisitDicomFolderPhysicalPath(IWebHostEnvironment _hostEnvironment,Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + return Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.Folder.DicomFolder); + } + + public static (string PhysicalPath, string RelativePath) GetDicomInstanceFilePath(IWebHostEnvironment _hostEnvironment, Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId, Guid studyId, Guid instanceId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //加入检查批次层级 和Data + var path = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), + siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.Folder.DicomFolder, studyId.ToString()); + + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + var physicalPath = Path.Combine(path, instanceId.ToString() ); + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{StaticData.Folder.DicomFolder}/{studyId}/{instanceId}"; + + //var physicalPath = Path.Combine(path, instanceId.ToString() + ".dcm"); + + //var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{StaticData.Folder.DicomFolder}/{studyId}/{instanceId}.dcm"; + + return (physicalPath, relativePath); + } + + + + + // 获取医生通用文件存放路径 + + public static (string PhysicalPath, string RelativePath) GetDoctorOrdinaryFilePath(IWebHostEnvironment _hostEnvironment, string fileName,Guid doctorId,string attachmentType) + { + + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, "UploadFile", doctorId.ToString(), attachmentType); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.UploadFileFolder}/{doctorId}/{attachmentType}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + public static (string PhysicalPath, string RelativePath) GetNonDoctorFilePath(IWebHostEnvironment _hostEnvironment, string fileName, string attachmentType) + { + + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.UploadFileFolder, attachmentType); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + + var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.UploadFileFolder}/{attachmentType}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + + +} + diff --git a/IRaCIS.Core.Application/Helper/ImageHelper.cs b/IRaCIS.Core.Application/Helper/ImageHelper.cs new file mode 100644 index 0000000..6772722 --- /dev/null +++ b/IRaCIS.Core.Application/Helper/ImageHelper.cs @@ -0,0 +1,64 @@ + +using FellowOakDicom.Imaging; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; +using System.Security.Cryptography; +using System.Text; +using Dicom.Imaging; +using DicomImage = FellowOakDicom.Imaging.DicomImage; + +namespace IRaCIS.Core.Application.Helper; + +public static class ImageHelper +{ + + // 采用ImageSharp组件 跨平台,不依赖windows 平台图像处理组件,这里大坑(net 6 下,之前由于Dicom处理最新组件 依赖了这个库的一个旧版本,导致缩略图bug,单独升级依赖组件才解决) + public static void ResizeSave(string filePath, string? fileStorePath) + { + + fileStorePath = fileStorePath ?? filePath + ".preview.jpeg"; + + using (var image = SixLabors.ImageSharp.Image.Load(filePath)) + { + + image.Mutate(x => x.Resize(500, 500)); + + image.Save(fileStorePath); + + } + } + + + + public static Stream RenderPreviewJpeg(string filePath) + { + string jpegPath = filePath + ".preview.jpg"; + + if (!File.Exists(jpegPath)|| new FileInfo(jpegPath).Length==0) + { + using (Stream stream = new FileStream(jpegPath, FileMode.Create)) + { + DicomImage image = new DicomImage(filePath); + + var sharpimage = image.RenderImage().AsSharpImage(); + + sharpimage.Save(stream, new JpegEncoder()); + + } + } + + return new FileStream(jpegPath, FileMode.Open); + } + + public static void RemovePreviewJpeg(string filePath) + { + string jpegPath = filePath + ".preview.jpg"; + if (File.Exists(jpegPath)) File.Delete(jpegPath); + } + + +} + + + diff --git a/IRaCIS.Core.Application/Helper/SendEmailHelper.cs b/IRaCIS.Core.Application/Helper/SendEmailHelper.cs new file mode 100644 index 0000000..5eb9e59 --- /dev/null +++ b/IRaCIS.Core.Application/Helper/SendEmailHelper.cs @@ -0,0 +1,179 @@ +using DocumentFormat.OpenXml.Spreadsheet; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using MailKit; +using MailKit.Security; +using MimeKit; +using NPOI.HPSF; + + +namespace IRaCIS.Core.Application.Helper; + +public static class SendEmailHelper +{ + + public static async Task SendEmailAsync(MimeMessage messageToSend, SystemEmailSendConfig _systemEmailConfig, EventHandler? messageSentSuccess = null) + { + try + { + using (var smtp = new MailKit.Net.Smtp.SmtpClient()) + { + if (messageSentSuccess != null) + { + smtp.MessageSent += messageSentSuccess; + } + + + smtp.ServerCertificateValidationCallback = (s, c, h, e) => true; + + //await smtp.ConnectAsync("smtp.qq.com", 465, SecureSocketOptions.SslOnConnect); + + //await smtp.AuthenticateAsync("zhou941003@qq.com", "sqfhlpfdvnexbcab"); + + await smtp.ConnectAsync(_systemEmailConfig.Host, _systemEmailConfig.Port, SecureSocketOptions.SslOnConnect); + + await smtp.AuthenticateAsync(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode); + + await smtp.SendAsync(messageToSend); + + await smtp.DisconnectAsync(true); + + } + } + catch (Exception ex) + { + + throw new BusinessValidationFailedException("邮件发送失败,您进行的操作未能成功,请检查邮箱或联系维护人员"); + } + + + } + + public static async Task SendEmailAsync(SMTPEmailConfig sMTPEmailConfig, EventHandler? messageSentSuccess = null) + { + var messageToSend = new MimeMessage(); + + //主题 + messageToSend.Subject = sMTPEmailConfig.TopicDescription; + + //发件地址 + messageToSend.From.Add(sMTPEmailConfig.FromEmailAddress); + + //收件地址 + + if (sMTPEmailConfig.ToMailAddressList.Count == 0) + { + throw new BusinessValidationFailedException("没有收件人"); + } + else + { + foreach (var item in sMTPEmailConfig.ToMailAddressList) + { + messageToSend.To.Add(item); + } + //抄送 + foreach (var copyToMailAddress in sMTPEmailConfig.CopyToMailAddressList) + { + messageToSend.Cc.Add(copyToMailAddress); + } + } + + var builder = new BodyBuilder(); + + //html body + + builder.HtmlBody = sMTPEmailConfig.HtmlBodyStr; + + + //附件 + + foreach (var item in sMTPEmailConfig.EmailAttachMentConfigList) + { + //builder.Attachments.Add(item.FileName, item.FileData); + + var attachment = builder.Attachments.Add(item.FileName, item.FileStream); + + //解决附件名过长 奇怪的名字 + foreach (var param in attachment.ContentDisposition.Parameters) + param.EncodingMethod = ParameterEncodingMethod.Rfc2047; + foreach (var param in attachment.ContentType.Parameters) + param.EncodingMethod = ParameterEncodingMethod.Rfc2047; + + } + + + + + messageToSend.Body = builder.ToMessageBody(); + + using (var smtp = new MailKit.Net.Smtp.SmtpClient()) + { + if (messageSentSuccess != null) + { + smtp.MessageSent += messageSentSuccess; + } + + + smtp.ServerCertificateValidationCallback = (s, c, h, e) => true; + + await smtp.ConnectAsync(sMTPEmailConfig.Host, sMTPEmailConfig.Port, SecureSocketOptions.SslOnConnect); + + await smtp.AuthenticateAsync(sMTPEmailConfig.UserName, sMTPEmailConfig.AuthorizationCode); + + await smtp.SendAsync(messageToSend); + + await smtp.DisconnectAsync(true); + + } + + } +} + + + + + +public class SMTPEmailConfig +{ + public int Port { get; set; } + + public string Host { get; set; } + + + public string UserName { get; set; } + + public string AuthorizationCode { get; set; } + + + //邮件HtmlBody + + public string HtmlBodyStr { get; set; } = string.Empty; + + + //发 + public MailboxAddress FromEmailAddress { get; set; } + + //收 + public List ToMailAddressList { get; set; } = new List(); + + //抄送 + public List CopyToMailAddressList { get; set; } = new List(); + + //邮件主题 + public string TopicDescription { get; set; } = string.Empty; + + //邮件附件 + public List EmailAttachMentConfigList { get; set; } = new List() { }; + +} + + +public class EmailAttachMentConfig +{ + public string FileName { get; set; } + + public Stream FileStream { get; set; } + + + public byte[] FileData { get; set; } +} diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.csproj b/IRaCIS.Core.Application/IRaCIS.Core.Application.csproj new file mode 100644 index 0000000..fa169fa --- /dev/null +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.csproj @@ -0,0 +1,133 @@ + + + + net6.0 + default + enable + enable + True + AnyCPU;x64 + + + + ..\bin\ + .\IRaCIS.Core.Application.xml + 1701;1702;1591 + + + + ..\bin\ + .\IRaCIS.Core.Application.xml + 1701;1702;1591 + + + + ..\bin\ + .\IRaCIS.Core.Application.xml + 1701;1702;1591 + + + + 1701;1702;1591;1587 + + + + 1701;1702;1591;1587 + + + + 1701;1702;1591;1587 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + true + PreserveNewest + + + Always + true + PreserveNewest + + + + + + + + + + + + true + + + + + + true + + + + true + + + true + + + + + + + true + + + + + + true + + + + + + + + + + + + + diff --git a/IRaCIS.Core.Application/Resources/en-US.json b/IRaCIS.Core.Application/Resources/en-US.json new file mode 100644 index 0000000..0c0783e --- /dev/null +++ b/IRaCIS.Core.Application/Resources/en-US.json @@ -0,0 +1,263 @@ +{ + "test{0}{1}": "英文本地化{0}{1}", + "RequiredAttribute": "{0} is required", + + // SiteSurvey 服务-------------------------------------------------------------------------------------------------------------------------- + + // TrialSiteEquipmentSurveyService + "CPMNotOperation": "CPM/APM Disallow operation", //CPM/APM 不允许操作 + "IsLockNotOperation": "The operation cannot be performed if it is locked", //已锁定 不允许操作 + + // TrialSiteSurveyService + "ValidationEmail": "Please input a legal email", // 验证邮箱 + "ValidationPhone": "Please input a legal phone", // 验证手机号 + "SiteNotExistUpdateDisable": "The project Site does not have the survey record of the handover person, so it is not allowed to choose to update", // site不存在禁止更新 + "RecordLockUpdateDisable": "Your record is not locked, you are not allowed to choose to update, if submitted, can be rejected after operation", //记录锁定禁止更新 + "SiteLockUpdateDisableOther": "At the current Site, your survey record has been locked, and it is not allowed to update other people's email survey record", //Site锁定禁止更新其他人 + "SiteLockUpdateDisableSelf": "At the current Site, your survey record has been locked, and there are other unlocked records, so you are not allowed to update your own survey record", //Site锁定禁止更新自己 + "SiteLockUpdateDisableEmail": "Currently, there are unlocked survey records in the Site, and the survey records of the locked mailbox are not allowed to be updated", //Site锁定禁止更新邮箱调研记录 + "EmailNotLatestDisableEmail{0}": "The questionnaire corresponding to this mailbox {0} is not the latest locked record and is not allowed to be updated!", //邮箱调研表不是最新 不允许更新 + "SiteExistOtherUpdateDisable": "Other users have already filled in the survey form under this Site, you are not allowed to continue to fill in", //存在其他用户调研表不允许填写 + "IsLockUpdateDisable": "The operation is not allowed because it has been locked", //已锁定,不允许操作 + "OnlyAbolishNotFiled": "Only uncommitted records are allowed to be annulled", //只允许废除未提交的记录 + "AdminOperateDisable": "The Admin operation is not allowed", // 不允许Admin操作 + "UserWrongNotSubmit": "Personnel information is not allowed to be submitted because there are incorrect items", // 人员信息有不正确项,不允许提交 + "FillInTheType{0}": "Please enter the type of account to be generated. Name of person {0}", // 请填写生成账号的类型。人员姓名 + + // TrialSiteUserSurveyService + "InfoInconformity": "The user name in the system is {0}, which is inconsistent with the entered information. You can modify the information to be consistent with the system and save the information", // 信息不一致 修改一致进行保存 + + + // TrialSiteUser 服务------------------------------------------------------------------------------------------------------------ + + // TrialConfigService + "NoDataDound": "No data for the signature scenario was found in the system", //未在系统中找到该签名场景的数据 + "PasswordError": "password error", //密码错误 + "UserBeDisabled": "The user has been disabled!", + "ProjSetDisable": "The project is not Initializing/Ongoing and configuration confirmation is not allowed", //不允许确认配置 + "QCRepate": "The serial number of QC problem display is not allowed to repeat", // QC问题显示序号不允许重复 + "ParentNumToLow": "Please confirm that the parent problem number is smaller than the child problem number", // 父问题的序号要比子问题序号小,请确认 + "ExistUnconfirmedItems": "Project, basic configuration, process configuration, urgent configuration, visit plan, unconfirmed items", // 存在未确认项 + "NotAllCanOperate": "only in Initializing or Ongoing State can operate", //只有“初始化中”和“正在进行中”才能进行操作 + + // TrialExternalUserService + "InfoDoNotAgree{0}{1}": "The user account name in the system is {0} Phone number: {1}. The information is inconsistent with the entered information. Now, modify the page information to be consistent with the system information and save the information", // 用户信息不一致 + "UserExist": "The email address and user type already exist", //该邮箱和用户类型,已存在该账户 + + // TrialMaintenanceService + "OneTrial": "Only one pm is allowed in a trial", + "Participant": "Participant has participated in site maintenance", + + // TrialService + "OnlyPMAPM": "Only PM/APM can perform this operation!", + "TrialIDExist": "Same Trial ID already exists.", + "ProjNot": "Operations are allowed only when the project is initialized or in progress", + "TrialConfirm": "The Trial visit schedule has been confirmed and cannot be deleted", + "TrialIntoTheGroup": "The Trial cannot be deleted because a doctor has been enrolled in the Trial or is in the enrollment process", + "TrialHaveSite": "There is a Site under this Trial and it cannot be deleted", + "TrialHaveParticipants": "There are participants under this Trial and they cannot be deleted", + "NotRevertEditable": "VisitPlan has been generated for some subjects,can not revert editable state", + + // TrialSiteService + "CodeRepeated": "Code is not allowed to be repeated", + "CRCCanNotDeleted": "The site has been associated with IC, and couldn't be deleted", + "SiteCanNotDeleted": "The subjects has been added to this site, and couldn't be deleted", + "StudyCanNotDeleted": "The site has been uploaded study, and couldn't be deleted.", + + // Visit ------------------------------------------------------------------------------------------------------------------------------------------- + + // SubjectService + "VisitNotConfirmed": "The trial visit plan has not been confirmed yet.Please contact the project manager to confirm the visit plan before adding subject", + "ExistedTrial": "A subjects with the same subject ID already existed in this trial.", + "TheLastVisit": "The subject is not allowed to set this visit as the last visit since there is already an visit set as the last visit", + "ImagesUploaded": "The subject uploaded the image after this visit, and this visit is not allowed to be set as the last visit", + "StudyImagesCanBotDeleted": "This subject has executed a visit with uploading study images,and couldn't be deleted", + + // SubjectVisitService + "ContainsVisitnum": "This subject's visit plan already contains a visit with the same visitnum", + "LastEvaluationNotAllowed": "After setting the last evaluation, you are not allowed to add scheduled outbound visits", + "CRCSubmit": "PD confirmation status cannot be modified after IC submission", + "BacktrackingVisits": "After the IC is submitted, the PD confirmation status cannot be changed", + "ImageUpload": "The subject uploaded the image after this visit, and this visit is not allowed to be set as the last visit", + "AssociatedUploaded": "This visit is associated with the uploaded study images and couldn't be deleted", + "InPlanCanNotDeleted": "This visit is InPlan and couldn't be deleted", + + // VisitPlanService + "OnlyInitializing": "only in Initializing or Ongoing State can operate", + "AccordWithVisitNum": "For the visit plan, the VisitDay with a larger VisitNum should be larger than the VisitDay with a smaller VisitNum", + "same/VisitName": "A visit with the same VisitName/VisitNum already existed in the current visit plan", + "AlreadyBaseline": "A visit already is baseline in the current visit plan", + "IsUploadVideo": "A IC has uploaded image data for the baseline and is not allowed to modify the baseline", + "VisitHasBeenExecuted": "The visit plan has been assigned to the subjects and executed", + "OnlyProgressCanEdit": "Modification validation is allowed only during project initialization or in progress", + "NoBaselineNoConfirmation": "No baseline, no confirmation allowed", + "NotConfirmedCanNotVisit": "If the project configuration is not confirmed, the visit plan is not allowed to be confirmed", + "VisitDayNotMinimum": "Baseline VisitDay is not minimum and confirmation is not allowed", + + //Document----------------------------------------------------------------------------------------------------------------------------------------------- + + //SystemDocumentService + "ExistsFileName": "The filename of the same type already exists", + "SignedUser": "A user has signed the document", + + //TrialDocumentService + + //同类型已存在该文件名 上面存在 ExistsFileName + "ExistsSignedUser": "This document, which has been signed by the user, cannot be deleted", + //password error 上面存在 PasswordError + //The user has been disabled 上面存在 UserBeDisabled + "DocumentsSigned": "The document has been signed", + "FileBeDelete": "File deleted or cancelled, signing failed", + + //QC-------------------------------------------------------------------------------------------------------------------------- + + // ClinicalDataService + + // NoneDicomStudyService + + // QCListService + "NotFirstTrial": "The current user is not a preliminary user and cannot perform this operation", + "NotReviewUser": "The current user is not a review user and cannot perform this operation", + + // QCOperationService + "AuditQuestionMust": "The audit question must be filled out and saved, otherwise no challenge is allowed to be added", + "VisitNoClose": "The current viewer has unclosed query for IC upload permission. No further query is allowed", + "NotFinish": "IC retransmission request /QC agrees to retransmission challenge and is not allowed to close the challenge until IC setup retransmission is complete", + "QCHasBeenReplied": "This QC inquiry has been answered", + "OperationUserMustBeCRC/PM": "Only IC and PM are allowed", + "CanNotCloseQuery": "Visits that perform conformance checks are not allowed to close queries", + "OnlyPMCanSet": "Only PM manual Settings are allowed to pass the consistency check", + "CanNotSettingConsistency": "Currently, the PM approves the rollback and does not allow the consistency check to pass", + "ConsistencyNotClose": "Compliance check challenge unclosed/audit status is not passed, setting conformance check is not allowed to pass", + "PassDataNotFallback": "The data that passes the verification is not allowed to apply for rollback", + "OnlyPMFallback": "Only PM rollback is allowed", + "UnderDetectionNotFallback": "Data to be verified/verified is not allowed to be rolled back", + "OnlyPMCanOperation": "Only PM operations are allowed", + "OnlyExcelCSV": "Only Excel and CSV are allowed!", + "ExcelVerificationFailed": "Please upload an Excel file in the specified format to ensure valid data", + "QCUnconfirmed": "The QC problem is not confirmed, it is not allowed to receive the task", + "MustBeFinish": "You are not allowed to pick up new subjects until you have already picked up other subjects", + "BeReceive": "It is currently claimed and not allowed to be claimed", + "CancelQCTask": "The project is configured to not review, not receive or cancel the QC Task", + "FirstTriaPassed": "The first examination has been passed, can not continue to receive", + "CanNotReceive": "The item is configured as single audit and cannot be collected if SubmmitState: submitted or AuditState: to be audited/audited is not met. Please refresh the interface", + "MustBeDifferent": "The retrial cannot be the same person as the first trial", + "IsNotSatisfied": "The project is configured for review and does not meet the submission status: Submitted or reviewed status: pending review /QC, it is not allowed to receive, please refresh the interface", + "VisitNotUploaded": "Visitors who have not uploaded any Dicom/ non-DICOM data are not allowed to submit", + "MandatoryFailed": "The project is configured to require filling in the date of first administration, but subjects do not fill in the date of first administration, which is not allowed to submit", + "YouBeenRemoved": "You have been removed from the project and are not allowed to do this", + "QuestionsNotClosed": "The operation is not allowed if it is not closed", + "CanNotSetQCPassed": "QC Passed cannot be set if the value is not Passed", + "MustSetQuestion": "The audit question must be filled out and saved, otherwise submission will not be allowed", + "CanNotChangeQCPassed": "The audit status of the item is not InPrimaryQC and cannot be changed to QCPassed", + // "必须填写审核问题,并保存,否则不允许提交" 上面 MustSetQuestion + "TrialIsDoubleCheck{0}": "The audit status of the item is {0} and cannot be changed to QCPassed", + "CanNotSetQCFailed": "QC Failed cannot be set in the unchecked state", + "NotInPrimaryQCQCFailed": "The project is configured as a single audit. The current audit status is not InPrimaryQC and cannot be changed to QCFailed", + "TrialIsDoubleCheckCanNotQCFailed{0}": "The project is configured with double audit. The current audit status is {0} and cannot be changed to QCFailed", + "CRCHasBeenSubmitted": "IC has submitted an unmodified group confirmation status", + "CannotBeChangedPD": "PD confirmation status cannot be modified for rollback visits", + "OtherQCProcessAudit": "Other QC is conducting audit, current operation is not allowed", + "ForwardFailed": "Forward failure", + + // QCQuestionService + + // TrialQCQuestionService + "QCHasConfirmed": "QC has confirmed that operation is not allowed", + "BeforeClearSubproblem": "The parent problem can only be deleted if the child problem is cleared", + "HasQCAuditRecord": "There is QC audit record, it is not allowed to delete the problem item", + + //ImageAndDoc ----------------------------------------------------------------------------------------------------------------------------- + + //DicomArchiveService + + //ImageShareService + "NoImage": "There is no image in the current study and cannot be shared!", + "ResourceNotExist": "The resource does not exist! ", + "PasswordIsError": "Shared password error!", + "ResourceExpired": "Resource sharing has expired!", + + //InstanceService + + //SeriesService + + //StudyListService + + "TimeTooLow": "当前检查批次检查时间不能小于该检查批次之前检查的时间,请核对检查数据是否有误", + "SomeoneLoading": "当前有人正在上传归档该检查!", + "VisitEnd": "患者检查批次结束,不允许上传!", + "DICOMUploaded{0}{1}{2}{3}": "{0}: This DICOM images have been uploaded and allocate to the {1} of the subject {2} (Study ID: {3}), which cannot continue to upload and assign it to others.", + + //StudyService + + //SystemAnonymizationService + + + //ReadingGlobalTaskService + "ReadingGlobal_NotGlobal": "System call error. The current read is not a global review read.", + //ReadingImageTaskService + "ReadingImage_NotVisit": "System call error. The current read is not a timepoint read.", + "ReadingImage_CantSplit": "The current read is a baseline read, and lesion split is not allowed.", + "ReadingImage_BeSigned": "The current read has already been signed off. Please do not submit it again.", + "ReadingImage_Beinvalid": "The current read has already been invalidated, and reading is not allowed anymore.", + "ReadingImage_NotaTask": "The coalesced lesions are not from the same timepoint. ", + "ReadingImage_DeleteError": "Other lesions have split from or coalesced into the current lesion. Deletion failed.", + "ReadingImage_Idnotcorrespond": "Failed to add the lesion mark. The Instance ID and Series ID of the image do not match.", + "ReadingImage_IsLymphNotbigger": "The short diameter of this nodal non-target lesion on the current visit is smaller than the value on the previous visit, and the state cannot be set to \"Unequivocal progression\".", + "ReadingImage_NotLymphNotbigger": "The long diameter of this non-nodal non-target lesion on the current visit is smaller than the value on the previous visit, and the state cannot be set to \"Unequivocal progression\".", + "ReadingImage_Twice": "System call error. Questions & answers submitted are duplicated.", + "ReadingImage_MaxQuestion": "According to the imaging charter, the number of the current type of lesion cannot exceed {0}.", + "ReadingImage_Maxlesion": "According to the imaging charter, the number of target lesions in the same organ cannot exceed {0}. Please confirm.", + "ReadingImage_Maximum": "\"{0}\" can only be repeated {1} times, and it has been repeated {2} times so far.", + "ReadingImage_PCWGMaximum": "According to the imaging charter, the number of baseline lesions at the same location should enter only {0} time. Please confirm.", + "ReadingImage_RequiredQuestion": "Before submission, please fill in \"{0}\".", + "ReadingImage_ClinicalRead": "The clinical data has not been read. Please confirm!", + "ReadingImage_IDMust": "System call failed. When there is no Read ID, the standard ID must be passed.", + "ReadingImage_TaskFinish": "Please note that all reads of the current subject have been completed.", + "ReadingImage_NeedRest": "You have been continuously reading for {0} hours. Please take a break of {1} minutes before resumption.", + //ReadingJudgeTaskService + "ReadingJudge_SouceIdNull": "'System call failed. The Source ID of the global review which is being adjudicated currently is 'null'. '", + //ReadingOncologyTaskService + "ReadingOncology_TaskError": "System call error. The current read is not an oncology read.", + "ReadingOncology_Abnormal": "System call exception. The result of adjudication is null", + //ReadingCalculate + "ReadingCalculate_Abnormal": "Configuration error of PCWG3 criteria. Data verification failed.", + "ReadingCalculate_questionable": "Problems with the lesions are as follows:", + "ReadingCalculate_NoMarker": "Lesion {0} lacks mark.", + "ReadingCalculate_StatusIsEmpty": "The state of Lesion {0} is empty.", + "ReadingCalculate_NoMarkerEmpty": "Lesion {0} is not marked, and its state is empty.", + "ReadingCalculate_NoDeveloped": "Please note that the current standard automatic calculation has not been developed.", + //ReadingMedicalReviewService + "MedicalReview_invalid": "This medical review read has already been invalidated, and the operation failed.", + "MedicalReview_SaveQuestion": "Unable to perform the current operation. Please save the medical review questions first.", + "MedicalReview_NeedSave": "Unable to perform the current operation. Please save the medical review questions and conclusions first.", + "MedicalReview_NotClosed": "Unable to perform the current operation. The current medical inquiry conversation has not been closed.", + "MedicalReview_Finish": "The current medical review is the last one.", + //UserService + "User_CheckNameOrPw": "Please check the username or password.", + //Repository + "Repository_UpdateError": "Updated data does not exist in the database. ", + "Repository_DeleteError": "Deleted data does not exist in the database." + + + + + + + + + + + + + + + + + + + + + + +} diff --git a/IRaCIS.Core.Application/Resources/zh-CN.json b/IRaCIS.Core.Application/Resources/zh-CN.json new file mode 100644 index 0000000..6408137 --- /dev/null +++ b/IRaCIS.Core.Application/Resources/zh-CN.json @@ -0,0 +1,256 @@ +{ + "test{0}{1}": "中文本地化{0}{1}", + "RequiredAttribute": "{0} 字段是必须的", + // SiteSurvey 服务-------------------------------------------------------------------------------------------------------------------------- + + // TrialSiteEquipmentSurveyService + "CPMNotOperation": "CPM/APM 不允许操作", //CPM/APM 不允许操作 + "IsLockNotOperation": "已锁定 不允许操作", //已锁定 不允许操作 + + // TrialSiteSurveyService + "ValidationEmail": "请输入正确的邮箱", // 验证邮箱 + "ValidationPhone": "请输入正确的手机号", // 验证手机号 + "SiteNotExistUpdateDisable": "该项目Site不存在该交接人的调研记录,不允许选择更新", // site不存在禁止更新 + "RecordLockUpdateDisable": "您的记录未锁定,不允许选择更新,若已经提交,可被驳回后进行操作", //记录锁定禁止更新 + "SiteLockUpdateDisableOther": "当前Site 您的调研记录已锁定,不允许更新其他人邮箱调研记录", //Site锁定禁止更新其他人 + "SiteLockUpdateDisableSelf": "当前Site 您的调研记录已锁定,也存在其他未锁定的记录,不允许更新自己的调研记录", //Site锁定禁止更新自己 + "SiteLockUpdateDisableEmail": "当前Site 存在未锁定的调研记录,不允许更新已锁定邮箱的调研记录", //Site锁定禁止更新邮箱调研记录 + "EmailNotLatestDisableEmail{0}": "该邮箱{0}对应的调查表不是最新锁定的记录,不允许更新!", //邮箱调研表不是最新 不允许更新 + "SiteExistOtherUpdateDisable": "该Site下已经有其他用户已填写的调研表,您不被允许继续填写", //存在其他用户调研表不允许填写 + "IsLockUpdateDisable": "已锁定,不允许操作", //已锁定,不允许操作 + "OnlyAbolishNotFiled": "只允许废除未提交的记录", //只允许废除未提交的记录 + "AdminOperateDisable": "不允许Admin操作", // 不允许Admin操作 + "UserWrongNotSubmit": "人员信息有不正确项,不允许提交", // 人员信息有不正确项,不允许提交 + "FillInTheType{0}": "请填写生成账号的类型。人员姓名{0}", // 请填写生成账号的类型。人员姓名 + + // TrialSiteUserSurveyService + "InfoInconformity{0}": "该用户在系统中账户名为:{0} ,与填写信息存在不一致项, 现将界面信息修改为与系统一致,可进行保存", // 信息不一致 修改一致进行保存 + + + // TrialSiteUser 服务------------------------------------------------------------------------------------------------------------ + + // TrialConfigService + "NoDataDound": "未在系统中找到该签名场景的数据", //未在系统中找到该签名场景的数据 + "PasswordError": "密码错误", //密码错误 + "UserBeDisabled": "用户被禁用", // 用户被禁用 + "ProjSetDisable": "项目不在Initializing/Ongoing,不允许确认配置", //不允许确认配置 + "QCRepate": "QC问题显示序号不允许重复", // QC问题显示序号不允许重复 + "ParentNumToLow": "父问题的序号要比子问题序号小,请确认", // 父问题的序号要比子问题序号小,请确认 + "ExistUnconfirmedItems": "项目、基础配置、流程配置、加急配置、检查批次计划,有未确认项", // 存在未确认项 + "NotAllCanOperate": "只有“初始化中”和“正在进行中”才能进行操作", //只有“初始化中”和“正在进行中”才能进行操作 + + // TrialExternalUserService + "InfoDoNotAgree{0}{1}": "该用户在系统中账户名为:{0} 电话:{1},与填写信息存在不一致项, 现将界面信息修改为与系统一致,可进行保存", // 用户信息不一致 + "UserExist": "该邮箱和用户类型,已存在该账户", //该邮箱和用户类型,已存在该账户 + + // TrialMaintenanceService + "OneTrial": "一次验证中只能一个PM", + "Participant": "参与过现场维护工作", + + // TrialService + "OnlyPMAPM": "仅PM/APM可以操作!", + "TrialIDExist": "相同的试用ID已经存在。", + "ProjNot": "项目初始化或者进行中时,才允许操作", + "TrialConfirm": "Trial检查批次计划已经确认,无法删除", + "TrialIntoTheGroup": "该Trial有医生入组或在入组流程中,无法删除", + "TrialHaveSite": "该Trial下面有Site,无法删除", + "TrialHaveParticipants": "该Trial下面有参与者,无法删除", + "NotRevertEditable": "部分主题已生成VisitPlan,无法恢复编辑状态", + + // TrialSiteService + "CodeRepeated": "编码不允许重复", + "CRCCanNotDeleted": "该站点已与IC关联,无法删除", + "SiteCanNotDeleted": "主题已添加到本网站,无法删除", + "StudyCanNotDeleted": "该网站已上传研究,无法删除", + + // Visit ------------------------------------------------------------------------------------------------------------------------------------------- + + // SubjectService + "VisitNotConfirmed": "试访计划尚未确定。添加主题前请联系项目经理确认访问计划", + "ExistedTrial": "在本试验中已经存在一个具有相同患者ID的患者.", + "TheLastVisit": "该患者已经有检查批次设置为末次检查批次,不允许将该检查批次设置为末次检查批次", + "ImagesUploaded": "该患者此检查批次后有影像上传,该检查批次不允许设置为末次检查批次", + "StudyImagesCanBotDeleted": "本课题已上传研究图像进行访问,无法删除", + + // SubjectVisitService + "ContainsVisitnum": "这个主题的访问计划已经包含了一个访问次数相同的访问", + //该患者已经有检查批次设置为末次检查批次,不允许将该检查批次设置为末次检查批次 这个在上面有 TheLastVisit + "LastEvaluationNotAllowed": "设置末次评估后,不允许添加计划外检查批次", + "CRCSubmit": "IC提交后,不允许修改PD确认状态", + "BacktrackingVisits": "回退的检查批次,不允许修改PD确认状态", + "ImageUpload": "该患者此检查批次后有影像上传,该检查批次不允许设置为末次检查批次", + "AssociatedUploaded": "本次访问与上传的研究图片有关,无法删除", + "InPlanCanNotDeleted": "此访问为InPlan,不能删除", + + // VisitPlanService + "OnlyInitializing": "只有“初始化中”和“正在进行中”才能进行操作", + "AccordWithVisitNum": "对于访问计划,具有较大VisitNum的VisitDay应该大于具有较小VisitNum的VisitDay", + "same/VisitName": "当前访问计划中已经存在具有相同VisitName/VisitNum的访问", + "AlreadyBaseline": "已检查批次是当前检查批次计划的基线", + "IsUploadVideo": "有IC已经为基线上传了影像数据,不允许修改基线", + "VisitHasBeenExecuted": "访问计划已分配给患者并执行", + "OnlyProgressCanEdit": "仅仅在项目初始化或者进行中时,才允许修改确认", + "NoBaselineNoConfirmation": "没有基线,不允许确认", + "NotConfirmedCanNotVisit": "项目配置未确认,不允许确认检查批次计划", + "VisitDayNotMinimum": "基线VisitDay 不是最小的, 不允许确认", + + + //Document----------------------------------------------------------------------------------------------------------------------------------------------- + + //SystemDocumentService + "ExistsFileName": "同类型已存在该文件名", + "SignedUser": "该文档下已有签名的用户", + + //TrialDocumentService + + //同类型已存在该文件名 上面存在 ExistsFileName + "ExistsSignedUser": "该文档,已有用户签名,不允许删除", + //password error 上面存在 PasswordError + //The user has been disabled 上面存在 UserBeDisabled + "DocumentsSigned": "该文档已经签名", + "FileBeDelete": "文件已删除或者废除,签署失败", + + //QC-------------------------------------------------------------------------------------------------------------------------- + + // ClinicalDataService + + // NoneDicomStudyService + + // QCListService + "NotFirstTrial": "当前用户不是初审用户,不允许操作", + "NotReviewUser": "当前用户不是复审用户,不允许操作", + + // QCOperationService + "AuditQuestionMust": "必须填写审核问题,并保存,否则不允许添加质疑", + "VisitNoClose": "当前检查批次有未关闭的 同意IC上传的质疑,不允许再次添加质疑", + "NotFinish": "IC申请重传的/QC同意重传的质疑,在IC设置重传完成前不允许关闭质疑", + "QCHasBeenReplied": "此QC质询已回复", + "OperationUserMustBeCRC/PM": "一致性核查对话操作用户 只允许 IC/PM", + "CanNotCloseQuery": "执行一致性核查的检查批次 不允许关闭质疑", + "OnlyPMCanSet": "只允许PM 手动设置一致性核查通过", + "CanNotSettingConsistency": "当前是PM同意回退,不允许设置一致性核查通过", + "ConsistencyNotClose": "致性核查质疑未关闭/审核状态不是通过,不允许设置一致性核查通过", + "PassDataNotFallback": "核查通过的数据不允许申请回退", + "OnlyPMFallback": "只允许PM 回退", + "UnderDetectionNotFallback": "待核查/核查通过的数据不允许回退", + "OnlyPMCanOperation": "只允许PM 操作", + "OnlyExcelCSV": "只允许Excel、 CSV!", + "ExcelVerificationFailed": "请上传规定格式的Excel文件,保证有有效数据", + "QCUnconfirmed": "QC问题未确认,不允许领取任务", + "MustBeFinish": "您已经领取了其他患者,完成后才允许领取新的患者", + "BeReceive": "当前已被领取,不允许领取", + "CancelQCTask": "项目配置为不审,没有领取或者取消QC Task", + "FirstTriaPassed": "初审已通过,不能继续领取", + "CanNotReceive": "项目配置为单审,不满足SubmmitState:已提交 或者 AuditState:待审核/审核中, 不允许领取,请刷新界面", + "MustBeDifferent": "复审不能和初审是同一个人", + "IsNotSatisfied": "项目配置为复审,不满足提交状态:已提交 或者 审核状态:待审核/QC中, 不允许领取,请刷新界面", + "VisitNotUploaded": "有检查批次未上传任何Dicom/非Dicom数据 不允许提交", + "MandatoryFailed": "项目配置了需要填写首次给药日期 但是患者没有填写首次给药日期,不允许提交", + "YouBeenRemoved": "您已经被移出项目,不允许该操作", + "QuestionsNotClosed": "有质疑未关闭,不允许该操作", + "CanNotSetQCPassed": "不审状态下,不允许设置为QC Passed", + "MustSetQuestion": "必须填写审核问题,并保存,否则不允许提交", + "CanNotChangeQCPassed": "项目配置为单审 当前审核状态不为 InPrimaryQC,不能变更到 QCPassed", + // "必须填写审核问题,并保存,否则不允许提交" 上面 MustSetQuestion + "TrialIsDoubleCheck{0}": "项目配置为双审 当前审核状态为 {0},不能变更到 QCPassed", + "CanNotSetQCFailed": "不审状态下,不允许设置为QC Failed", + "NotInPrimaryQCQCFailed": "项目配置为单审 当前审核状态不为 InPrimaryQC,不能变更到 QCFailed", + "TrialIsDoubleCheckCanNotQCFailed{0}": "项目配置为双审 当前审核状态为 {0},不能变更到 QCFailed", + "CRCHasBeenSubmitted": "IC已提交了 不能修改入组确认状态", + "CannotBeChangedPD": "回退的检查批次,不允许修改PD确认状态", + "OtherQCProcessAudit": "其他QC正在进行审核,当前操作不允许", + "ForwardFailed": "转发失败", + + // QCQuestionService + + // TrialQCQuestionService + "QCHasConfirmed": "QC已确认,不允许操作", + "BeforeClearSubproblem": "清除子问题 才允许删除父问题", + "HasQCAuditRecord": "已有QC审核记录,不允许删除问题项", + + + //ImageAndDoc ----------------------------------------------------------------------------------------------------------------------------- + + //DicomArchiveService + + //ImageShareService + "NoImage": "目前研究中没有图片,不能分享!", + "ResourceNotExist": "资源不存在!", + "PasswordIsError": "共享密码错误!", + "ResourceExpired": "资源共享过期!", + + //InstanceService + + //SeriesService + + //StudyListService + + "TimeTooLow": "当前检查批次检查时间不能小于该检查批次之前检查的时间,请核对检查数据是否有误", + "SomeoneLoading": "当前有人正在上传归档该检查!", + "VisitEnd": "患者检查批次结束,不允许上传!", + "DICOMUploaded{0}{1}{2}{3}": "{0}:此DICOM图片已上传并分配给课题{2}(研究ID:{3})的{1},课题{2}无法继续上传并分配给他人。", + + //StudyService + + //SystemAnonymizationService + + //ReadingImageTaskService + "ReadingImage_NotVisit": "系统调用错误,当前任务不是检查批次任务。", + "ReadingImage_CantSplit": "当前任务是基线任务,不能执行分裂病灶的操作。", + "ReadingImage_BeSigned": "当前任务已经签名,请勿重复提交。", + "ReadingImage_Beinvalid": "当前任务已失效,不能执行阅片。", + "ReadingImage_NotaTask": "合并的病灶并非同一个检查批次任务", + "ReadingImage_DeleteError": "当前病灶分裂出其他病灶或者其他病灶合并到了当前病灶,删除失败。", + "ReadingImage_Idnotcorrespond": "病灶标记添加失败,影像的Instance ID和Series ID不对应。", + "ReadingImage_IsLymphNotbigger": "当前检查批次该淋巴结病灶的短径小于上一检查批次的值,不能设置为显著增大。", + "ReadingImage_NotLymphNotbigger": "当前检查批次该非淋巴结病灶的长径小于上一检查批次的值,不能设置为显著增大。", + "ReadingImage_Twice": "系统调用错误,提交的问题及答案重复。", + "ReadingImage_MaxQuestion": "按照《独立影像评估章程》的相关规则,当前病灶类型的病灶数量不超过{0}个。", + "ReadingImage_Maxlesion": "按照《独立影像评估章程》的相关规则,同一器官的靶病灶数量不超过{0}个,请确认", + "ReadingImage_Maximum": "\"{0}\"的重复次数限制为{1},目前已重复{2}次。", + "ReadingImage_PCWGMaximum": "按照《独立影像评估章程》的相关规则,同一部位的基线病灶只需录入{0}次,请确认。", + "ReadingImage_RequiredQuestion": "提交前,请完成\"{0}\"", + "ReadingImage_ClinicalRead": "临床数据未阅读,请确认!", + "ReadingImage_IDMust": "系统调用失败,当没有任务ID的时候,标准ID必传。", + "ReadingImage_TaskFinish": "当前患者所有阅片任务已完成,请知悉。", + "ReadingImage_NeedRest": "您已连续阅片{0}个小时,请休息{1}分钟后,再继续阅片。", + //ReadingJudgeTaskService + "ReadingJudge_SouceIdNull": "系统调用失败,当前裁判的全局任务的SouceId为null。", + //ReadingOncologyTaskService + "ReadingOncology_TaskError": "系统调用错误,当前任务不是肿瘤学任务。", + "ReadingOncology_Abnormal": "系统调用异常,裁判结果为null。", + //ReadingCalculate + "ReadingCalculate_Abnormal": "PCWG3标准配置错误,数据验证失败。", + "ReadingCalculate_questionable": "病灶存在以下问题:", + "ReadingCalculate_NoMarker": "病灶{0}缺少标记", + "ReadingCalculate_StatusIsEmpty": "病灶{0}状态为空", + "ReadingCalculate_NoMarkerEmpty": "病灶{0}未做标记,且状态为空", + "ReadingCalculate_NoDeveloped": "当前标准自动计算未完成开发,请知悉。", + //ReadingMedicalReviewService + "MedicalReview_invalid": "该医学审核任务已失效,操作失败。", + "MedicalReview_SaveQuestion": "无法执行当前操作,请先保存医学审核问题。", + "MedicalReview_NeedSave": "无法执行当前操作,请先保存医学审核问题和结论。", + "MedicalReview_NotClosed": "无法执行当前操作,当前医学质询对话未关闭。", + "MedicalReview_Finish": "当前医学审核任务为最后一个任务。", + //UserService + "User_CheckNameOrPw": "请检查用户名或者密码。", + //Repository + "Repository_UpdateError": "修改的数据在数据库不存在。", + "Repository_DeleteError": "删除的数据在数据库不存在。" + + + + + + + + + + + + + + + +} + diff --git a/IRaCIS.Core.Application/Service/Allocation/DTO/AllocationListDTO.cs b/IRaCIS.Core.Application/Service/Allocation/DTO/AllocationListDTO.cs new file mode 100644 index 0000000..3be6c67 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/DTO/AllocationListDTO.cs @@ -0,0 +1,37 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.ViewModel +{ + public class AllocationVisitViewModel + { + + public Guid SubjectVisitId { get; set; } + public Guid SubjectId { get; set; } + + public string BlindName { get; set; } = string.Empty; + + public decimal VisitNum { get; set; } + + + public Guid SiteId { get; set; } + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + public string VisitName { get; set; } = string.Empty; + + public DateTime? CheckPassedTime { get; set; } + + public bool IsUrgent { get; set; } + + + public ReadingCategory ReadingCategoryEnum { get; set; } + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Allocation/DTO/TaskAllocationRuleViewModel.cs b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskAllocationRuleViewModel.cs new file mode 100644 index 0000000..ac9eb48 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskAllocationRuleViewModel.cs @@ -0,0 +1,303 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 13:16:33 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using IRaCIS.Application.Contracts; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Application.ViewModel +{ + /// TaskAllocationRuleView 列表视图模型 + public class TaskAllocationRuleView : TaskAllocationRuleAddOrEdit + { + public string UserCode { get; set; } + public string UserName { get; set; } + public string FullName { get; set; } + + public string UserTypeShortName { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + } + + + + public class DoctorVisitTaskStatView : TaskAllocationRuleView + { + //该医生已经应用的 Subject数量 + public int? SelfApplyedSubjectCount { get; set; } + + //该医生已经分配Subject数量 + public int? SelfSubjectCount { get; set; } + + + //该医生未应用Subject 数量 不是两者相减 跟批量勾选有关系 + public int? WaitApplySelfSubjectCount { get; set; } + + + + //该医生未应用的任务数 + public int? WaitApplySelfTaskCount { get; set; } + + //该医生已应用的任务数 + public int? SelfApplyedTaskCount { get; set; } + + + + //总共未应用的Subejct 数量 + public int? WaitApplyTotalSubjectCount { get; set; } + + public int? WaitApplyTotalTaskCount { get; set; } + + + + //已分配该医生但是未读片签名的数量 + public int? SelfUndoTaskCount { get; set; } + + + + //总共已应用 的Subject 数 + public int? ApplyedTotalSubjectCount { get; set; } + + + //总共 已经应用的任务总数 + public int? ApplyedTotalTaskCount { get; set; } + + + //系统 Subject数 + public int? TotalSubjectCount { get; set; } + + //总任务数 + public int? TotalTaskCount { get; set; } + } + + + + + + + ///TaskAllocationRuleQuery 列表查询参数模型 + public class TaskAllocationRuleQuery + { + [NotDefault] + public Guid TrialId { get; set; } + + public bool IsJudgeDoctor { get; set; } + } + + public class TaskAllocationRuleDTO : TaskAllocationRuleAddOrEdit + { + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + public UserSimpleInfo DoctorUser { get; set; } + + public List ReadingCategoryList { get; set; } + + public List TrialReadingCriterionList { get; set; } + + [JsonIgnore] + public List CriterionReadingCategoryList { get; set; } + + public List CriterionCategoryList => + + TrialReadingCriterionList.Select(t => + new CriterionReadingCategory() { + EnrollId = EnrollId, + TrialReadingCriterionId = t.TrialReadingCriterionId, + ReadingCategorys = CriterionReadingCategoryList.Where(c => c.TrialReadingCriterionId == t.TrialReadingCriterionId).Select(t => t.ReadingCategory).OrderBy(c => c).ToList() + }).ToList(); + + + + //CriterionReadingCategoryList.Count == 0 ? TrialReadingCriterionList.Select(t => new CriterionReadingCategory() { EnrollId = EnrollId, TrialReadingCriterionId = t.TrialReadingCriterionId }).ToList() : + // CriterionReadingCategoryList + //.GroupBy(t => new { t.TrialReadingCriterionId, t.EnrollId }) + //.Select(g => new CriterionReadingCategory() { EnrollId = g.Key.EnrollId, TrialReadingCriterionId = g.Key.TrialReadingCriterionId, ReadingCategorys = g.Select(t => t.ReadingCategory).OrderBy(t=>t).ToList() }).ToList(); + + } + + + public class DoctorSelectQuery + { + [NotDefault] + public Guid TrialId { get; set; } + + public ReadingCategory? ReadingCategory { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + } + + + public class AssignDoctorStatView : TaskAllocationRuleDTO + { + public int? AssignedSubjectCount { get; set; } + + public int? WaitDealTrialTaskCount { get; set; } + + public int? WaitDealAllTaskCount { get; set; } + } + + /// TaskAllocationRuleAddOrEdit 列表查询参数模型 + public class TaskAllocationRuleAddOrEdit + { + public Guid? Id { get; set; } + public Guid TrialId { get; set; } + + public Guid EnrollId { get; set; } + + [NotDefault] + public Guid DoctorUserId { get; set; } + + public int PlanSubjectCount { get; set; } + + public bool IsEnable { get; set; } + + public string Note { get; set; } = string.Empty; + } + + public class CancelDoctorCommand + { + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + + public string Note { get; set; } + + } + + + public class SubjectCancelDoctorView + { + public Guid SubjectId { get; set; } + + public string Note { get; set; } + + public DateTime CreateTime { get; set; } + } + + public class TrialDoctorUserSelectView + { + public Guid TrialId { get; set; } + + //public ReadingMethod ReadingType { get; set; } + + public Guid? DoctorUserId { get; set; } + + public string UserCode { get; set; } + + public string UserName { get; set; } + + public string FullName { get; set; } + + public UserTypeEnum UserTypeEnum { get; set; } + + + public List ReadingCategoryList { get; set; } + } + + public class GenerateTaskCommand + { + public Guid TrialId { get; set; } + + //针对检查批次产生任务的 有用 + public bool IsAssignSubjectToDoctor { get; set; } + + //检查批次任务产生的时候传递 + + public List VisitGenerataTaskList { get; set; } = new List(); + + + + public GenerateTaskCategory ReadingCategory { get; set; } + + /// + /// 原任务的Id + /// + public Guid OriginalVisitId { get; set; } + + //裁判的时候传递 + public List JudgeVisitTaskIdList { get; set; } = new List(); + + /// + /// 阅片期 + /// + public List ReadingGenerataTaskList = new List(); + + + //重阅 产生任务 + public VisitTask ReReadingTask { get; set; } + + public Action Action; + + + //自身一致性 + public List GenerataConsistentTaskList { get; set; } + + //组间一致性 + + public List GenerataGroupConsistentTaskList { get; set; } + + + + } + + //public class ReReadingApplyGenerateTaskCommand + //{ + // public RequestReReadingType RequestReReadingType { get; set; } + + // public string RequestReReadingReason { get; set; } + + // public List ReReadingTaskList = new List(); + //} + + + public class ReadingGenerataTaskDTO + { + public Guid ReadModuleId { get; set; } + + public Guid SubjectId { get; set; } + + public decimal VisitNum { get; set; } + + public bool IsUrgent { get; set; } + + + public string ReadingName { get; set; } + + public ReadingCategory ReadingCategory { get; set; } + + } + + public class VisitGenerataTaskDTO + { + public Guid Id { get; set; } + + public Guid SubjectId { get; set; } + + public bool IsUrgent { get; set; } + + public String VisitName { get; set; } + + public bool IsBaseLine { get; set; } + + public DateTime? CheckPassedTime { get; set; } + + public decimal VisitNum { get; set; } + + + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Allocation/DTO/TaskConsistentRuleViewModel.cs b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskConsistentRuleViewModel.cs new file mode 100644 index 0000000..a8b4f7a --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskConsistentRuleViewModel.cs @@ -0,0 +1,321 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-07-01 15:33:01 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Core.Application.ViewModel +{ + /// TaskConsistentRuleView 列表视图模型 + public class TaskConsistentRuleView : TaskConsistentRuleAddOrEdit + { + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + public UserSimpleInfo AnalysisDoctorUser { get; set; } + + + public int? GeneratedSubjectCount { get; set; } + + + + } + + + public class TaskConsistentRuleBasic : TaskConsistentRuleAddOrEdit + { + + } + + public class ConsistentQuery : PageInput + { + [NotDefault] + public Guid DoctorUserId { get; set; } + [NotDefault] + public Guid TaskConsistentRuleId { get; set; } + + //[NotDefault] + //public Guid TrialReadingCriterionId { get; set; } + + } + + public class GroupConsistentQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + + public Guid TrialReadingCriterionId { get; set; } + } + + public class ConsistentConfirmGenerateCommand + { + public Guid TaskConsistentRuleId { get; set; } + [NotDefault] + public Guid DoctorUserId { get; set; } + + public List SubejctIdList { get; set; } + } + + + public class GroupConsistentConfirmGenrateCommand + { + [NotDefault] + public Guid TrialId { get; set; } + public List SubejctIdList { get; set; } + } + + public class DoctorSelfConsistentSubjectView: ConsistentCommonView + { + + public string? BlindSubjectCode { get; set; } + + public List VisitTaskList { get; set; } + + } + + + public class ConsistentCommonView + { + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + + public String TrialSiteCode { get; set; } + public string SubjectCode { get; set; } + + public Guid SubjectId { get; set; } + + public bool IsHaveGeneratedTask { get; set; } + + public bool IsReReadingOrBackInfluenceAnalysis { get; set; } + public int? ValidVisitCount { get; set; } + } + + public class DoctorGroupConsistentSubjectView: ConsistentCommonView + { + + public List SubjectTaskVisitList => VisitTaskList.GroupBy(t => new { t.SubjectId, t.VisitTaskNum }).Where(g => g.Count() == 2).Select(g => g.First()).OrderBy(t=>t.VisitTaskNum).ToList(); + + public List VisitTaskList { get; set; } = new List(); + } + + public class VisitTaskGroupSimpleDTO + { + public Guid TrialId { get; set; } + + public Guid SubjectId { get; set; } + + + public string TaskName { get; set; } + public string TaskBlindName { get; set; } + + public decimal VisitTaskNum { get; set; } + + public ReadingCategory ReadingCategory { get; set; } + + //任务阅片状态 + public ReadingTaskState ReadingTaskState { get; set; } + + public TaskState TaskState { get; set; } + + public Guid? DoctorUserId { get; set; } + public Arm ArmEnum { get; set; } + + + public Guid? SourceSubjectVisitId { get; set; } + public Guid? SouceReadModuleId { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + + public string BlindSubjectCode { get; set; } = string.Empty; + public string BlindTrialSiteCode { get; set; } = string.Empty; + + public bool IsNeedClinicalDataSign { get; set; } + public bool IsClinicalDataSign { get; set; } + } + + public class VisitTaskSimpleDTO :VisitTaskGroupSimpleDTO + { + public Guid? Id { get; set; } + + + public string TaskCode { get; set; } + + + //可以不查询 + public List GlobalVisitTaskList { get; set; } + + //也可以不查询 + public bool IsHaveGeneratedTask { get; set; } + + + + + + + //public Guid? TaskConsistentRuleId { get; set; } + + } + + + //public class DoctorConsistentRuleSubjectView + //{ + // public Guid TrialId => VisitTaskList.Select(t => t.TrialId).First(); + // public Guid SiteId => VisitTaskList.Select(t => t.SiteId).First(); + + // public String TrialSiteCode => VisitTaskList.Select(t => t.TrialSiteCode).First(); + // public string SubjectCode => VisitTaskList.Select(t => t.SubjectCode).First(); + + // public Guid SubjectId { get; set; } + + // public bool IsHaveGeneratedTask { get; set; } + // public int? ValidTaskCount { get; set; } + + // public int? ValidVisitCount => VisitTaskList.Select(t => t.TaskName).Distinct().Count(); + + // public List VisitTaskList { get; set; } + + // //public List HistoryDoctorUserList => VisitTaskList.SelectMany(t => t.RelationDoctorUserList).DistinctBy(t=>t.UserId).ToList(); + //} + + public class InfluenceTaskInfo + { + public Guid Id { get; set; } + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + + public Guid SubjectId { get; set; } + + public Arm ArmEnum { get; set; } + public string TaskCode { get; set; } + + public string TaskName { get; set; } + public string TaskBlindName { get; set; } + + public decimal VisitTaskNum { get; set; } + + public ReadingCategory ReadingCategory { get; set; } + + //任务阅片状态 + public ReadingTaskState ReadingTaskState { get; set; } + + public TaskState TaskState { get; set; } + + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + + public UserSimpleInfo DoctorUser { get; set; } + + public ReReadingOrBackOptType OptType { get; set; } + + /// + /// 是否是一致性分析产生 + /// + public bool IsAnalysisCreate { get; set; } + + public bool? IsSelfAnalysis { get; set; } + + public string TrialReadingCriterionName { get; set; } + + } + + + public class VisitTaskSimpleView + { + public Guid Id { get; set; } + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + + public Guid SubjectId { get; set; } + + public Arm ArmEnum { get; set; } + public string TaskCode { get; set; } + + public string TaskName { get; set; } + public string TaskBlindName { get; set; } + + public decimal VisitTaskNum { get; set; } + + public ReadingCategory ReadingCategory { get; set; } + + //任务阅片状态 + public ReadingTaskState ReadingTaskState { get; set; } + + public TaskState TaskState { get; set; } + + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + + public UserSimpleInfo DoctorUser { get; set; } + + public List GlobalVisitTaskList { get; set; } + + //[JsonIgnore] + //public List RelationDoctorUserList { get; set; } = new List(); + + [JsonIgnore] + public Guid? DoctorUserId { get; set; } + [JsonIgnore] + public Guid? TaskConsistentRuleId { get; set; } + + public bool IsHaveGeneratedTask { get; set; } + } + + + + + ///TaskConsistentRuleQuery 列表查询参数模型 + public class TaskConsistentRuleQuery + { + [NotDefault] + public Guid TrialId { get; set; } + + public bool IsSelfAnalysis { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + + } + + /// TaskConsistentRuleAddOrEdit 列表查询参数模型 + public class TaskConsistentRuleAddOrEdit + { + public Guid? Id { get; set; } + public bool IsEnable { get; set; } + public string Note { get; set; } = string.Empty; + public Guid TrialId { get; set; } + public int PlanSubjectCount { get; set; } + public int PlanVisitCount { get; set; } + public int IntervalWeeks { get; set; } + public bool IsHaveReadingPeriod { get; set; } + public bool IsGenerateGlobalTask { get; set; } + + public bool IsSelfAnalysis { get; set; } + public int BlindSubjectNumberOfPlaces { get; set; } + public string BlindTrialSiteCode { get; set; } = string.Empty; + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + + public class TaskConsistentRuleBatchAddOrEdit : TaskConsistentRuleAddOrEdit + { + + public bool IsBatchAdd { get; set; } + } + +} + + diff --git a/IRaCIS.Core.Application/Service/Allocation/DTO/TaskMedicalReviewRuleViewModel.cs b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskMedicalReviewRuleViewModel.cs new file mode 100644 index 0000000..ed3a95d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskMedicalReviewRuleViewModel.cs @@ -0,0 +1,95 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-29 13:36:46 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Application.ViewModel +{ + /// TaskTaskMedicalReviewRuleView 列表视图模型 + public class TaskMedicalReviewRuleView : TaskMedicalReviewRuleAddOrEdit + { + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + public UserSimpleInfo DoctorUser { get; set; } + + + + [JsonIgnore] + public List ActualVisitTaskList { get; set; } + [JsonIgnore] + public List ActualJudgeTaskList { get; set; } + [JsonIgnore] + public List ActualGlobalTaskList { get; set; } + [JsonIgnore] + public List ActualTumorTaskList { get; set; } + + + + + [JsonIgnore] + public List GeneratedVisitTaskList { get; set; } + [JsonIgnore] + public List GeneratedJudgeTaskList { get; set; } + [JsonIgnore] + public List GeneratedGlobalTaskList { get; set; } + [JsonIgnore] + public List GeneratedTumorTaskList { get; set; } + + + public int GeneratedVisitCount => GeneratedVisitTaskList.Count; + public int GeneratedJudgeCount => GeneratedJudgeTaskList.Count; + public int GeneratedGlobalCount => GeneratedGlobalTaskList.Count; + public int GeneratedTumorCount => GeneratedTumorTaskList.Count; + + + + + public int ActualVisitCount => ActualVisitTaskList.Count; + public int ActualJudgeCount => ActualJudgeTaskList.Count; + public int ActualGlobalCount => ActualGlobalTaskList.Count; + public int ActualTumorCount => ActualTumorTaskList.Count; + + } + + + public class TaskBasicIdView + { + public Guid TaskId { get; set; } + public Guid TrialId { get; set; } + public Guid? DoctorUserId { get; set; } + } + ///TaskTaskMedicalReviewRuleQuery 列表查询参数模型 + public class TaskMedicalReviewRuleQuery + { + [NotDefault] + public Guid TrialId { get; set; } + + } + + /// TaskTaskMedicalReviewRuleAddOrEdit 列表查询参数模型 + public class TaskMedicalReviewRuleAddOrEdit + { + public Guid? Id { get; set; } + public Guid DoctorUserId { get; set; } + public bool IsEnable { get; set; } + public string Note { get; set; } + public Guid TrialId { get; set; } + public int PlanVisitCount { get; set; } + public int PlanJudgeCount { get; set; } + public int PlanGlobalCount { get; set; } + public int PlanTumorCount { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Allocation/DTO/TaskMedicalReviewViewModel.cs b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskMedicalReviewViewModel.cs new file mode 100644 index 0000000..4386a6a --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskMedicalReviewViewModel.cs @@ -0,0 +1,191 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-29 10:59:50 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Core.Application.ViewModel +{ + /// TaskMedicalReviewView 列表视图模型 + public class TaskMedicalReviewView : VisitTaskViewBasic + { + + public Guid VisitTaskId { get; set; } + public string MedicalNo { get; set; } = string.Empty; + + public int AuditState { get; set; } + public DateTime? AuditSignTime { get; set; } + public MedicalReviewDoctorUserIdea DoctorUserIdeaEnum { get; set; } + public Guid? MedicalManagerUserId { get; set; } + + /// + /// 审核建议 + /// + public AuditAdvice AuditAdviceEnum { get; set; } + + + public bool IsHaveQuestion { get; set; } + + + + public UserSimpleInfo DoctorUser { get; set; } + + public UserSimpleInfo MedicalManagerUser { get; set; } + + + /// + /// 医学审核对话关闭原因 + /// + public MedicalDialogClose MedicalDialogCloseEnum { get; set; } + + + /// + /// 无效的 为True无效 + /// + public bool IsInvalid { get; set; } + } + + public class GenerateMedicalReviewTaskView : VisitTaskViewBasic + { + //开始读片时间 + public DateTime? FirstReadingTime { get; set; } + + public string MedicalNo { get; set; } = string.Empty; + + //读片时间差 + public TimeSpan? ReadingDurationTimeSpan { get; set; } + + /// + /// 全局是否有更新 + /// + public bool? IsGlobalHaveUpdate { get; set; } = false; + + //时间差 换成字符串 + public string ReadingDuration + { + get + { + if (!ReadingDurationTimeSpan.HasValue) + return ""; + else return string.Format("{0}天{1}小时{2}分钟", (SignTime - FirstReadingTime)?.Days, (SignTime - FirstReadingTime)?.Hours, (SignTime - FirstReadingTime)?.Minutes) + /*string.Format("{0}分钟", (ReadingDurationTimeSpan)?.TotalMinutes)*/; + } + } + + //是否产生裁判 + public bool IsGeneratedJudge { get; set; } + + //审核次数 + public int? GeneratedMedicalReviewCount { get; set; } + public UserSimpleInfo DoctorUser { get; set; } + } + + public class GetNextMedicalReviewTaskInDto + { + [NotDefault] + public Guid MedicalReviewId { get; set; } + } + + + ///TaskMedicalReviewQuery 列表查询参数模型 + public class TaskMedicalReviewQuery : PageInput + { + public Guid TrialId { get; set; } + public Guid? SiteId { get; set; } + + /// + /// 传了Id 就不查询这条数据 + /// + public Guid? Id { get; set; } + + public Guid? SubjectId { get; set; } + + + public bool IsGetBeRead { get; set; } = false; + + public string SubjectCode { get; set; } = String.Empty; + + public string TrialSiteCode { get; set; } = String.Empty; + public string TaskName { get; set; } = String.Empty; + + public bool? IsUrgent { get; set; } + public Guid? DoctorUserId { get; set; } + + + public TaskState? TaskState { get; set; } + + public MedicalReviewAuditState? AuditState { get; set; } + + + public ReadingCategory? ReadingCategory { get; set; } + + public ReadingTaskState? ReadingTaskState { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + } + + + public class SetMedicalReviewInvalidCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + public List MedicalReviewIdList { get; set; } + } + + public class GenerateMedicalReviewTaskQuery : TaskMedicalReviewQuery + { + public bool? IsGeneratedJudge { get; set; } + + public bool? IsGlobalHaveUpdate { get; set; } + } + + + + public class GenerateMedicalReviewTaskCommand + { + public Guid TrialId { get; set; } + + + } + + + + public class ManuallyGeneratedAndAssignedMedicalReviewCommand + { + [NotDefault] + public Guid TrialId { get; set; } + [NotDefault] + public Guid MedicalManagerUserId { get; set; } + + + public List TaskIdList { get; set; } + } + + public class AssignMedicalReviewTaskCommand + { + public List IdList { get; set; } + + + public Guid? MedicalManagerUserId { get; set; } + + public MedicalReviewTaskOptType TaskOptType { get; set; } + } + + public enum MedicalReviewTaskOptType + { + Assign = 1, + + ReAssign = 2, + + + CancelAssign = 3, + } + +} + + diff --git a/IRaCIS.Core.Application/Service/Allocation/DTO/VisitTaskViewModel.cs b/IRaCIS.Core.Application/Service/Allocation/DTO/VisitTaskViewModel.cs new file mode 100644 index 0000000..1e323a4 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/DTO/VisitTaskViewModel.cs @@ -0,0 +1,804 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 14:10:54 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Application.ViewModel +{ + + public class VisitTaskViewBasic + { + public Guid Id { get; set; } + public Guid TrialId { get; set; } + + public string TaskCode { get; set; } + + public string TaskName { get; set; } + public string TaskBlindName { get; set; } + + public decimal VisitTaskNum { get; set; } + + public bool IsUrgent { get; set; } + + /// + /// 加急类型 + /// + public TaskUrgentType? TaskUrgentType { get; set; } + + + public string TaskUrgentRemake { get; set; } = string.Empty; + + public ReadingCategory ReadingCategory { get; set; } + + public TaskAllocationState TaskAllocationState { get; set; } + public TaskState TaskState { get; set; } + + public DateTime? SignTime { get; set; } + + public DateTime? AllocateTime { get; set; } + public Guid SubjectId { get; set; } + + public Arm ArmEnum { get; set; } + public Guid? DoctorUserId { get; set; } + + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + + public Guid SiteId { get; set; } + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + public bool IsPMSetBack { get; set; } + + #region 标准配置 + public Guid TrialReadingCriterionId { get; set; } + + public string TrialReadingCriterionName { get; set; } + + + public bool IsNeedClinicalDataSign { get; set; } + + public bool IsClinicalDataSign { get; set; } + + + public CompleteClinicalDataEnum CompleteClinicalDataEnum => + (IsNeedClinicalDataSign && IsClinicalDataSign) ? CompleteClinicalDataEnum.Complete : (IsNeedClinicalDataSign && IsClinicalDataSign == false) ? CompleteClinicalDataEnum.NotComplete : CompleteClinicalDataEnum.NA; + + + + /// + /// 阅片工具 + /// + public ReadingTool? ReadingTool { get; set; } + + /// + /// 任务展示检查批次 读片任务显示是否顺序 + /// + public bool IsReadingTaskViewInOrder { get; set; } = true; + + + /// + /// 阅片是否显示患者信息 + /// + public bool IsReadingShowSubjectInfo { get; set; } = false; + + /// + /// IR阅片页面是否可以查看既往任务结果 + /// + public bool IsReadingShowPreviousResults { get; set; } = false; + + public int? DigitPlaces { get; set; } = 2; + + public bool IseCRFShowInDicomReading { get; set; } = false; + + public CriterionType CriterionType { get; set; } + + + + ///// + ///// 仲裁对象 + ///// + //public ArbitrationRule ArbitrationRule { get; set; } = ArbitrationRule.Reading; + + + ///// + ///// 阅片模式 + ///// + //public ReadingMethod ReadingType { get; set; } = ReadingMethod.Double; + + ///// + ///// 全局阅片 + ///// + //public bool IsGlobalReading { get; set; } = true; + + ///// + ///// 仲裁阅片 + ///// + //public bool IsArbitrationReading { get; set; } = true; + + + ///// + ///// 肿瘤学阅片 原字段 IsClinicalReading + ///// + //public bool IsOncologyReading { get; set; } + #endregion + + } + + + /// VisitTaskView 列表视图模型 + public class VisitTaskView : VisitTaskViewBasic + { + + public string UserCode { get; set; } + public string UserName { get; set; } + public string FullName { get; set; } + + public string UserTypeShortName { get; set; } + + public bool IsCanEditUrgentState { get; set; } + + + } + + public class UserSimpleInfo + { + public Guid UserId { get; set; } + public string UserCode { get; set; } + public string UserName { get; set; } + public string FullName { get; set; } + + //public string UserTypeShortName { get; set; } + } + + + public class JudgeVisitTaskView : VisitTaskView + { + + public List HistoryReadingDoctorUserList { get; set; } + + } + + + public class ReadingTaskView : VisitTaskView + { + + public ReadingTaskState ReadingTaskState { get; set; } + + public ReReadingApplyState ReReadingApplyState { get; set; } + public DateTime? SuggesteFinishedTime { get; set; } + + //任务来源检查批次Id 方便回更检查批次读片状态 + public Guid? SourceSubjectVisitId { get; set; } + public Guid? SouceReadModuleId { get; set; } + + + } + + + public class AnalysisTaskView : ReadingTaskView + { + public bool? IsSelfAnalysis { get; set; } + + public bool IsReReadingOrBackInfluenceAnalysis { get; set; } + } + + + + public class ReReadingTaskView + { + + //重阅原始编号 + //public string ReReadingOriginalTaskCode { get; set; } + + + + public Guid Id { get; set; } + + public AnalysisTaskView OriginalReReadingTask { get; set; } + + public string? ReReadingNewTaskCode { get; set; } + + + + public ReadingTaskState ReadingTaskState { get; set; } + + public RequestReReadingType RequestReReadingType { get; set; } + + public string RequestReReadingRejectReason { get; set; } = string.Empty; + + public DateTime? RequestReReadingTime { get; set; } + + public string RequestReReadingReason { get; set; } = string.Empty; + + public DateTime? SuggesteFinishedTime { get; set; } + + public RequestReReadingResult RequestReReadingResultEnum { get; set; } + + //重阅原始任务Id 重阅会产生新的任务 + public Guid OriginalReReadingTaskId { get; set; } + + //重阅申请 产生的新任务Id + public Guid? NewReReadingTaskId { get; set; } + + + //产生重阅的根任务Id + public Guid RootReReadingTaskId { get; set; } + + + } + + + public class IRHaveReadView : VisitTaskViewBasic + { + public ReadingTaskState ReadingTaskState { get; set; } + + public ReReadingApplyState ReReadingApplyState { get; set; } + public DateTime? SuggesteFinishedTime { get; set; } + + } + + public class IRUnReadSubjectView + { + public int Index { get; set; } = 0; + + public Guid SubjectId { get; set; } + public string SubjectCode { get; set; } = String.Empty; + + public bool IsUrgent => UnReadTaskList.Any(t => t.IsUrgent); + + public int UnReadTaskCount { get; set; } + + public int UnReadCanReadTaskCount { get; set; } + + public int HaveReadTaskCount { get; set; } + + public int TotalTaskCount { get; set; } + + public bool ExistReadingApply { get; set; } + + public DateTime? SuggesteFinishedTime { get; set; } + + public List UnReadCanReadTaskList => UnReadTaskList.Where(y => y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true)).ToList(); + + public List UnReadTaskList { get; set; } = new List(); + } + + public class IRUnreadTaskView + { + public Guid Id { get; set; } + + public ReadingCategory ReadingCategory { get; set; } + + /// + /// 是否是一致性分析产生 + /// + public bool IsAnalysisCreate { get; set; } + + public bool IsUrgent { get; set; } + + public decimal VisitNum { get; set; } + + public string TaskBlindName { get; set; } + + public Arm ArmEnum { get; set; } + + public Guid? VisistId { get; set; } + public DateTime? SuggesteFinishedTime { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + + public bool IsNeedClinicalDataSign { get; set; } + + public bool IsClinicalDataSign { get; set; } + + public bool IsFrontTaskNeedSignButNotSign { get; set; } + } + + + public class HistoryReadingDoctorUser + { + + public string? JudgeTaskCode { get; set; } + public Guid DoctorUserId { get; set; } + + public string UserCode { get; set; } + public string UserName { get; set; } + public string FullName { get; set; } + } + + + public class GetOrderReadingIQueryableInDto + { + public Guid TrialId { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + + public string? SubjectCode { get; set; } = null; + + public PageInput? Page { get; set; } = null; + + } + + public class VisitTaskQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public Guid? SiteId { get; set; } + + public Guid? SubjectId { get; set; } + + public string SubjectCode { get; set; } = String.Empty; + + public bool? IsUrgent { get; set; } + + public string TaskName { get; set; } = String.Empty; + + public Guid? DoctorUserId { get; set; } + + public ReadingCategory? ReadingCategory { get; set; } + + public ReadingTaskState? ReadingTaskState { get; set; } + public TaskAllocationState? TaskAllocationState { get; set; } + + public TaskState? TaskState { get; set; } + public DateTime? BeginAllocateDate { get; set; } + + public DateTime? EndAllocateDate { get; set; } + + public Guid? RootReReadingTaskId { get; set; } + + public string? TaskCode { get; set; } + + public String? TrialSiteCode { get; set; } + + public ReReadingApplyState? ReReadingApplyState { get; set; } + + public RequestReReadingType? RequestReReadingType { get; set; } + + + public bool? IsSelfAnalysis { get; set; } + + public Arm? ArmEnum { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + public CompleteClinicalDataEnum? CompleteClinicalDataEnum { get; set; } + + public RequestReReadingResult? RequestReReadingResultEnum { get; set; } + + + public DateTime? BeginSignTime { get; set; } + + public DateTime? EndSignTime { get; set; } + + + + } + + + + + + + + + + public class IRUnReadSubjectQuery : PageInput + { + public Guid TrialId { get; set; } + + public string SubjectCode { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + } + + public class IRUnReadOutDto + { + /// + /// 未完成阅片量 + /// + public int UnReadTaskCount { get; set; } + + /// + /// 完成阅片量 + /// + public int FinishTaskCount { get; set; } + + /// + /// 未完成裁判任务数量 + /// + public int UnReadJudgeTaskCount { get; set; } + + /// + /// 完成裁判任务数量 + /// + public int FinishJudgeTaskCount { get; set; } + + /// + /// 建议完成时间 + /// + public DateTime? SuggesteFinishedTime { get; set; } + + } + + public class SubjectAssignQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public Guid? SiteId { get; set; } + + public Guid? SubjectId { get; set; } + + public string SubjectCode { get; set; } = String.Empty; + + public bool IsJudgeDoctor { get; set; } + + + public Guid? DoctorUserId { get; set; } + + public bool? IsHaveAssigned { get; set; } + + public bool? IsAssignConfirmed { get; set; } + + } + + public class SubjectAssignStatQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public Guid? SiteId { get; set; } + + public Guid? SubjectId { get; set; } + + public Guid? DoctorUserId { get; set; } + + + public string SubjectCode { get; set; } = String.Empty; + + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + + } + + public class BatchAssignDoctorToSubjectCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + public List SubjectIdList { get; set; } + + public List DoctorArmList { get; set; } + + } + + + + + + + + + public class SubjectAssignStat + { + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + + public Guid SubjectId { get; set; } + + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + public int? VisitTaskTypeCount { get; set; } + public int? GlobalTaskTypeCount { get; set; } + public int? JudgeTaskTypeCount { get; set; } + + public int? OncologyTaskTypeCount { get; set; } + + [JsonIgnore] + public List DoctorUserList { get; set; } + + public List PreviousDoctorUserList => DoctorUserList.Where(t => t.OrignalSubjectUserId != null).ToList(); + + public List NowDoctorUserList => DoctorUserList.Where(t => t.OrignalSubjectUserId == null).ToList(); + + + + } + + public class SubjectUserView + { + public Guid TrialReadingCriterionId { get; set; } + public Guid Id { get; set; } + public DateTime? AssignTime { get; set; } + + public Guid DoctorUserId { get; set; } + public Arm ArmEnum { get; set; } + + public bool IsConfirmed { get; set; } + + //该属性有值 说明该医生被替换了 分配的时候 要过滤掉 + public Guid? OrignalSubjectUserId { get; set; } + + public UserSimpleInfo DoctorUser { get; set; } + } + + + public class SubjectUserDTO : SubjectUserView + { + public bool IsHaveReading { get; set; } + } + + + public class CancelSubjectAssignedDoctorCommand + { + public Guid Id { get; set; } + + + public Guid SubjectId { get; set; } + public Guid DoctorUserId { get; set; } + public Arm ArmEnum { get; set; } + + + public bool IsCancelAssign { get; set; } + + } + + + public class CancelSubjectDoctorCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + public List CancelList { get; set; } + + public string Note { get; set; } = string.Empty; + } + + + + public class SubjectAssignView + { + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + + public Guid SubjectId { get; set; } + + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + + public bool IsAssignedDoctorUser { get; set; } + + [JsonIgnore] + public bool IsJudge { get; set; } + + public bool IsConfirmed => DoctorUserList.Where(t => IsJudge ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).All(t => t.IsConfirmed) + && DoctorUserList.Where(t => IsJudge ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Count() > 0; + + public List DoctorUserIdList => DoctorUserList.Select(t => t.DoctorUserId).ToList(); + + //.ForEach(t => t.HistoryDoctorList = TotalDoctorUserList.Where(c => (Guid?)t.Id == c.OrignalSubjectUserId).ToList()) + public List DoctorUserList => TotalDoctorUserList.Where(t => t.OrignalSubjectUserId == null).ToList() + ; + + [JsonIgnore] + public List TotalDoctorUserList { get; set; } = new List(); + } + + + + public class AssignDoctorView + { + public Guid Id { get; set; } + public Guid DoctorUserId { get; set; } + + public string UserCode { get; set; } + public string UserName { get; set; } + public string FullName { get; set; } + + public string UserTypeShortName { get; set; } + + public DateTime? AssignTime { get; set; } + + public Arm ArmEnum { get; set; } + + public bool IsConfirmed { get; set; } + + //该属性有值 说明该医生被替换了 分配的时候 要过滤掉 + public Guid? OrignalSubjectUserId { get; set; } + + //public List HistoryDoctorList { get; set; } + + } + + + + public class AssginSubjectDoctorCommand + { + public Guid TrialId { get; set; } + + public bool IsReAssign { get; set; } + + public List SubjectIdList { get; set; } + + public bool IsJudgeDoctor { get; set; } + + public List DoctorUserIdArmList { get; set; } = new List(); + } + + public class DoctorArm + { + public Guid DoctorUserId { get; set; } + + public Arm ArmEnum { get; set; } + } + + public class CancelSubjectAssignCommand + { + public Guid TrialId { get; set; } + + public bool IsJudgeDoctor { get; set; } + + public List SubjectIdList { get; set; } + } + + public class AutoAssignResultDTO + { + public Guid DoctorUserId { get; set; } + + public int PlanReadingRatio { get; set; } + + public List SubjectArmList { get; set; } = new List(); + + public int SubjectCount { get; set; } + + public int Weight => (SubjectCount - SubjectArmList.Count) * PlanReadingRatio; + + } + + public class SubjectArm + { + public Guid SubjectId { get; set; } + + public Arm ArmEnum { get; set; } + } + + + public class ApplySubjectCommand + { + public Guid TrialId { get; set; } + + public bool IsJudgeDoctor { get; set; } + + public List SubjectIdList { get; set; } = new List(); + } + + + public class AssignConfirmCommand + { + public Guid TrialId { get; set; } + + public bool IsJudgeDoctor { get; set; } + + public List SubjectDoctorUserList { get; set; } = new List(); + } + + public class SubjectDoctorCommand + { + public Guid SubjectId { get; set; } + + public List DoctorUserIdArmList { get; set; } = new List(); + } + + public class AutoSubjectAssignCommand + { + public Guid TrialId { get; set; } + + public bool IsJudgeDoctor { get; set; } + } + + public class AssignSubjectTaskToDoctorCommand + { + public Guid Id { get; set; } + + public Guid TrialId { get; set; } + + public Guid SubjectId { get; set; } + + public bool IsJudgeDoctor { get; set; } + + public Guid? DoctorUserId { get; set; } + + public TaskOptType TaskOptType { get; set; } + + } + + + + public class AIRReReadingCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + + public class ApplyReReadingCommand + { + + + public List TaskIdList { get; set; } + + public Guid TrialId { get; set; } + + public bool IsCopyOrigenalForms { get; set; } + public bool IsCopyFollowForms { get; set; } + + public RequestReReadingType RequestReReadingType { get; set; } + + public string RequestReReadingReason { get; set; } = string.Empty; + } + + + public class ConfirmReReadingCommand + { + public Guid TrialId { get; set; } + + public List ConfirmReReadingList { get; set; } + + public string RequestReReadingRejectReason { get; set; } = string.Empty; + public RequestReReadingResult RequestReReadingResultEnum { get; set; } + } + + public class ConfirmReReadingDTO + { + [NotDefault] + public Guid Id { get; set; } + + //重阅原始任务Id 重阅会产生新的任务 + + [NotDefault] + public Guid OriginalReReadingTaskId { get; set; } + + } + + + public enum TaskOptType + { + Assign = 1, + + ReAssign = 2, + + Confirm = 3, + + CancelAssign = 4, + } + +} + + diff --git a/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskAllocationRuleService.cs b/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskAllocationRuleService.cs new file mode 100644 index 0000000..1395a47 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskAllocationRuleService.cs @@ -0,0 +1,25 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 13:13:19 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// ITaskAllocationRuleService + /// + public interface ITaskAllocationRuleService + { + + + //Task> GetTaskAllocationRuleList(TaskAllocationRuleQuery queryTaskAllocationRule); + + Task AddOrUpdateTaskAllocationRule(TaskAllocationRuleAddOrEdit addOrEditTaskAllocationRule); + + Task DeleteTaskAllocationRule(Guid taskAllocationRuleId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskConsistentRuleService.cs b/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskConsistentRuleService.cs new file mode 100644 index 0000000..8e2cc36 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskConsistentRuleService.cs @@ -0,0 +1,25 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-07-01 15:35:00 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// ITaskConsistentRuleService + /// + public interface ITaskConsistentRuleService + { + + + //Task> GetTaskConsistentRuleList(TaskConsistentRuleQuery inQuery); + + Task AddOrUpdateTaskConsistentRule(TaskConsistentRuleAddOrEdit addOrEditTaskConsistentRule); + + Task DeleteTaskConsistentRule(Guid taskConsistentRuleId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskMedicalReviewRuleService.cs b/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskMedicalReviewRuleService.cs new file mode 100644 index 0000000..e8063f1 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskMedicalReviewRuleService.cs @@ -0,0 +1,25 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-29 13:37:37 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// ITaskTaskMedicalReviewRuleService + /// + public interface ITaskMedicalReviewRuleService + { + + + //Task> GetTaskMedicalReviewRuleList(TaskMedicalReviewRuleQuery queryTaskTaskMedicalReviewRule); + + Task AddOrUpdateTaskMedicalReviewRule(TaskMedicalReviewRuleAddOrEdit addOrEditTaskTaskMedicalReviewRule); + + Task DeleteTaskMedicalReviewRule(Guid taskTaskMedicalReviewRuleId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskMedicalReviewService.cs b/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskMedicalReviewService.cs new file mode 100644 index 0000000..81a77a4 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/Interface/ITaskMedicalReviewService.cs @@ -0,0 +1,23 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-29 10:59:32 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + + +using IRaCIS.Core.Application.ViewModel; + +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// ITaskMedicalReviewService + /// + public interface ITaskMedicalReviewService + { + + //Task> GetTaskMedicalReviewList(TaskMedicalReviewQuery queryTaskMedicalReview); + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Allocation/Interface/IVisitTaskHelpeService.cs b/IRaCIS.Core.Application/Service/Allocation/Interface/IVisitTaskHelpeService.cs new file mode 100644 index 0000000..ac847dc --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/Interface/IVisitTaskHelpeService.cs @@ -0,0 +1,20 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 14:10:49 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + + +using IRaCIS.Core.Application.ViewModel; + +namespace IRaCIS.Core.Application.Service +{ + public interface IVisitTaskHelpeService + { + Task GenerateVisitTaskAsync(Guid trialId, List subjectVisitIdList, bool isAssignSubjectToDoctor = false); + + Task AddTaskAsync(GenerateTaskCommand generateTaskCommand); + + Task BaseCritrionGenerateVisitTask(Guid trialId, Guid confirmedTrialReadingCriterionId); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Allocation/Interface/IVisitTaskService.cs b/IRaCIS.Core.Application/Service/Allocation/Interface/IVisitTaskService.cs new file mode 100644 index 0000000..c9a5a55 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/Interface/IVisitTaskService.cs @@ -0,0 +1,21 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 14:10:49 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + + +using IRaCIS.Core.Application.ViewModel; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Service +{ + public interface IVisitTaskService + { + Task ApplyReReading(ApplyReReadingCommand applyReReadingCommand); + + Task ConfirmReReading(ConfirmReReadingCommand agreeReReadingCommand, [FromServices] IVisitTaskHelpeService _visitTaskCommonService); + + Task<(int, List)> GetOrderReadingIQueryable(GetOrderReadingIQueryableInDto inDto); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Allocation/TaskAllocationRuleService.cs b/IRaCIS.Core.Application/Service/Allocation/TaskAllocationRuleService.cs new file mode 100644 index 0000000..65c966e --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/TaskAllocationRuleService.cs @@ -0,0 +1,223 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 13:14:38 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Core.Application.Service +{ + /// + /// 分配规则 + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TaskAllocationRuleService : BaseService, ITaskAllocationRuleService + { + + private readonly IRepository _taskAllocationRuleRepository; + private readonly IRepository _userRepository; + + private readonly IRepository _trialRepository; + + private readonly IRepository _subjectCanceDoctorRepository; + + + + public TaskAllocationRuleService(IRepository taskAllocationRuleRepository, IRepository userRepository, IRepository trialRepository, IRepository subjectCanceDoctorRepository) + { + _taskAllocationRuleRepository = taskAllocationRuleRepository; + _userRepository = userRepository; + _trialRepository = trialRepository; + _subjectCanceDoctorRepository = subjectCanceDoctorRepository; + } + + + + + /// + /// 获取计划列表 医生带阅片类型 + /// + /// + /// + public async Task<(List,object)> GetDoctorPlanAllocationRuleList(Guid trialId) + { + + var list = await _taskAllocationRuleRepository.Where(t => t.TrialId == trialId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + //所有标准都是一样 后台只返回任意一个标准的就好了 + var trialTaskConfig = _repository.Where(t => t.TrialId == trialId && t.IsConfirm).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + + return (list, trialTaskConfig); + } + + + + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task AddOrUpdateTaskAllocationRule(TaskAllocationRuleAddOrEdit addOrEditTaskAllocationRule) + { + + + //冗余 存 + + var enrollId = _repository.Where(t => t.TrialId == addOrEditTaskAllocationRule.TrialId && t.DoctorUserId == addOrEditTaskAllocationRule.DoctorUserId).Select(t => t.Id).FirstOrDefault(); + + if (enrollId == Guid.Empty) + { + return ResponseOutput.NotOk("错误,未在入组表中找到该医生得账号Id"); + } + + addOrEditTaskAllocationRule.EnrollId = enrollId; + + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = t => t.DoctorUserId == addOrEditTaskAllocationRule.DoctorUserId && t.TrialId == addOrEditTaskAllocationRule.TrialId, + VerifyMsg = "已有该医生配置,不允许继续增加" + }; + + var entity = await _taskAllocationRuleRepository.InsertOrUpdateAsync(addOrEditTaskAllocationRule, true, verifyExp1); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + [HttpDelete("{taskAllocationRuleId:guid}")] + public async Task DeleteTaskAllocationRule(Guid taskAllocationRuleId) + { + if (await _taskAllocationRuleRepository.Where(t => t.Id == taskAllocationRuleId).AnyAsync(t => t.DoctorUser.VisitTaskList.Where(u => u.TrialId == t.TrialId).Any())) + { + return ResponseOutput.NotOk("已分配任务给该医生,不允许删除"); + } + + var success = await _taskAllocationRuleRepository.DeleteFromQueryAsync(t => t.Id == taskAllocationRuleId, true); + + + return ResponseOutput.Ok(); + } + + + //public async Task AddSubjectCancelDoctorNote(CancelDoctorCommand command) + //{ + // await _subjectCanceDoctorRepository.InsertOrUpdateAsync(command, true); + + // return ResponseOutput.Ok(); + //} + + public async Task> GetSubjectCancelDoctorHistoryList(Guid subjectId) + { + var list = await _subjectCanceDoctorRepository.Where(t => t.SubjectId == subjectId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + return list; + } + + + + + + + /// + /// 获取项目下 医生账户信息下拉 + /// + /// + /// + /// + [HttpGet("{trialId:guid}")] + public async Task> GetDoctorUserSelectList(Guid trialId, [FromServices] IRepository _enrollRepository) + { + var query = from enroll in _enrollRepository.Where(t => t.TrialId == trialId && t.EnrollStatus >= EnrollStatus.ConfirmIntoGroup) + join user in _userRepository.AsQueryable() on enroll.DoctorUserId equals user.Id + select new TrialDoctorUserSelectView() + { + TrialId = enroll.TrialId, + DoctorUserId = user.Id, + FullName = user.FullName, + UserCode = user.UserCode, + UserName = user.UserName, + UserTypeEnum = user.UserTypeRole.UserTypeEnum, + ReadingCategoryList = enroll.EnrollReadingCategoryList.Select(t => t.ReadingCategory).ToList() + + }; + + var list= await query.ToListAsync(); + + return list; + } + + + [HttpPost] + public async Task> GetDoctorSelectList(DoctorSelectQuery selectQuery, [FromServices] IRepository _enrollRepository) + { + + var query = from allocationRule in _taskAllocationRuleRepository.Where(t => t.TrialId == selectQuery.TrialId && t.IsEnable) + .WhereIf(selectQuery.ReadingCategory != null && selectQuery.TrialReadingCriterionId == null, t => t.Enroll.EnrollReadingCategoryList.Any(t => t.ReadingCategory == selectQuery.ReadingCategory)) + .WhereIf(selectQuery.TrialReadingCriterionId != null && selectQuery.ReadingCategory == null, t => t.Enroll.EnrollReadingCategoryList.Any(t => t.TrialReadingCriterionId == selectQuery.TrialReadingCriterionId)) + .WhereIf(selectQuery.TrialReadingCriterionId != null && selectQuery.ReadingCategory !=null, + t => t.Enroll.EnrollReadingCategoryList.Any(t => t.TrialReadingCriterionId == selectQuery.TrialReadingCriterionId && t.ReadingCategory==selectQuery.ReadingCategory)) + join user in _userRepository.AsQueryable() on allocationRule.DoctorUserId equals user.Id + select new TrialDoctorUserSelectView() + { + TrialId = allocationRule.TrialId, + DoctorUserId = user.Id, + FullName = user.FullName, + UserCode = user.UserCode, + UserName = user.UserName, + UserTypeEnum = user.UserTypeRole.UserTypeEnum, + + ReadingCategoryList = selectQuery.TrialReadingCriterionId == null ? + + allocationRule.Enroll.EnrollReadingCategoryList.Where(t=> (selectQuery.ReadingCategory == null ?true: t.ReadingCategory == selectQuery.ReadingCategory) ).Select(t => t.ReadingCategory).OrderBy(t => t).ToList() : + + allocationRule.Enroll.EnrollReadingCategoryList + .Where(t => t.TrialReadingCriterionId == selectQuery.TrialReadingCriterionId && (selectQuery.ReadingCategory == null?true : t.ReadingCategory == selectQuery.ReadingCategory) ) + .Select(t => t.ReadingCategory).OrderBy(t => t).ToList() + }; + + return await query.ToListAsync(); + } + + + #region 废弃 + /// + /// 获取检查批次任务 应用Subject后 医生比率情况 + /// + /// + [HttpPost] + [Obsolete] + public async Task> GetSubjectApplyDoctorTaskStatList(ApplySubjectCommand assignConfirmCommand) + { + var taskAllocationRuleQueryable = _taskAllocationRuleRepository.Where(t => t.TrialId == assignConfirmCommand.TrialId && t.IsJudgeDoctor == assignConfirmCommand.IsJudgeDoctor) + .ProjectTo(_mapper.ConfigurationProvider, new { subjectIdList = assignConfirmCommand.SubjectIdList, isJudgeDoctor = assignConfirmCommand.IsJudgeDoctor }); + + return await taskAllocationRuleQueryable.ToListAsync(); + } + + + + [HttpPost] + [Obsolete] + public async Task> GetTaskAllocationRuleList(TaskAllocationRuleQuery queryTaskAllocationRule) + { + var taskAllocationRuleQueryable = _taskAllocationRuleRepository.Where(t => t.TrialId == queryTaskAllocationRule.TrialId /*&& t.IsJudgeDoctor == queryTaskAllocationRule.IsJudgeDoctor*/) + .ProjectTo(_mapper.ConfigurationProvider); + + + //var trialTaskConfig = _trialRepository.Where(t => t.Id == queryTaskAllocationRule.TrialId).ProjectTo(_mapper.ConfigurationProvider, new { isJudgeDoctor = queryTaskAllocationRule.IsJudgeDoctor }).FirstOrDefault(); + + return await taskAllocationRuleQueryable.ToListAsync(); + } + + #endregion + + + } +} diff --git a/IRaCIS.Core.Application/Service/Allocation/TaskConsistentRuleService.cs b/IRaCIS.Core.Application/Service/Allocation/TaskConsistentRuleService.cs new file mode 100644 index 0000000..78af17e --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/TaskConsistentRuleService.cs @@ -0,0 +1,773 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-07-01 15:33:04 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Domain.Share; +using System.Linq.Expressions; +using IRaCIS.Core.Infra.EFCore.Common; +using System.Linq; +using Nito.AsyncEx; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Core.Application.Service +{ + /// + /// 一致性分析配置表 + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TaskConsistentRuleService : BaseService, ITaskConsistentRuleService + { + + private readonly IRepository _taskConsistentRuleRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _subjectUserRepository; + private readonly IRepository _subjectRepository; + private readonly IRepository _enrollRepository; + + private readonly AsyncLock _mutex = new AsyncLock(); + + public TaskConsistentRuleService(IRepository visitTaskRepository, IRepository enrollRepository, IRepository taskConsistentRuleRepository, IRepository subjectUserRepository, IRepository subjectRepository) + { + _taskConsistentRuleRepository = taskConsistentRuleRepository; + _visitTaskRepository = visitTaskRepository; + _subjectUserRepository = subjectUserRepository; + _subjectRepository = subjectRepository; + _enrollRepository = enrollRepository; + } + + /// + /// 设置一致性分析任务失效 + /// + /// + /// + [HttpPost] + public async Task SetAnalysisTaskInvalid(List taskIdList) + { + + + await _visitTaskRepository.UpdatePartialFromQueryAsync(t => taskIdList.Contains(t.Id), u => new VisitTask() { TaskState = TaskState.NotEffect },true); + + await _visitTaskRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + + } + + + /// + /// 一致性分析列表 (自身 组内 最后勾选 产生的任务) + /// + /// + /// + [HttpPost] + public async Task> GetAnalysisTaskList(VisitTaskQuery queryVisitTask) + { + var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == queryVisitTask.TrialId) + .Where(t => t.IsAnalysisCreate) + + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + + .WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId) + .WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) + .WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.IsSelfAnalysis != null, t => t.IsSelfAnalysis == queryVisitTask.IsSelfAnalysis) + .WhereIf(queryVisitTask.ArmEnum != null, t => t.ArmEnum == queryVisitTask.ArmEnum) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => (t.Subject.Code.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider); + + var defalutSortArray = new string[] { nameof(VisitTask.IsUrgent) + " desc", nameof(VisitTask.SubjectId), nameof(VisitTask.VisitTaskNum) }; + + var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray); + + //var trialTaskConfig = _repository.Where(t => t.Id == queryVisitTask.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + return pageList; + } + + + /// + /// 为自身一致性分析医生,选择Subejct 列表 + /// + /// + /// + [HttpPost] + public async Task> GetDoctorSelfConsistentRuleSubjectList(ConsistentQuery inQuery) + { + var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.Id == inQuery.TaskConsistentRuleId); + + var pagedList = await GetIQueryableDoctorSelfConsistentSubjectView(filterObj, inQuery.DoctorUserId).ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(DoctorSelfConsistentSubjectView.SubjectCode) : inQuery.SortField, inQuery.Asc); + + return pagedList; + } + + + + /// + /// 确认生成自身一致性分析任务 + /// + /// + /// + /// + [HttpPost] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task ConfirmGenerateSelfConsistentTask(ConsistentConfirmGenerateCommand inCommand, [FromServices] IVisitTaskHelpeService _visitTaskCommonService) + { + + + var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.Id == inCommand.TaskConsistentRuleId); + var doctorUserId = inCommand.DoctorUserId; + var trialReadingCriterionId = filterObj.TrialReadingCriterionId; + + var list = await GetIQueryableDoctorSelfConsistentSubjectView(filterObj, doctorUserId, inCommand.SubejctIdList).ToListAsync(); + + //var (group, query) = GetIQueryableDoctorSelfConsistentRuleSubjectView(filterObj, inCommand.SubejctIdList); + + //var list = query.OrderByDescending(t => t.IsHaveGeneratedTask).ToList(); + + using (await _mutex.LockAsync()) + { + int maxCodeInt = 0; + + foreach (var subject in list) + { + //处理 Subject 编号 + + var blindSubjectCode = string.Empty; + + var subjectTask = _visitTaskRepository.Where(t => t.SubjectId == subject.SubjectId && t.TrialReadingCriterionId==trialReadingCriterionId && t.IsSelfAnalysis == true).OrderByDescending(t => t.BlindSubjectCode).FirstOrDefault(); + + if (subjectTask!=null && subjectTask.BlindSubjectCode != String.Empty) + { + blindSubjectCode = subjectTask.BlindSubjectCode; + } + else + { + var maxCodeStr = _visitTaskRepository.Where(t => t.TrialId == subject.TrialId && t.TrialReadingCriterionId == trialReadingCriterionId && t.IsSelfAnalysis == true).OrderByDescending(t => t.BlindSubjectCode).Select(t => t.BlindSubjectCode).FirstOrDefault(); + + if ( !string.IsNullOrEmpty(maxCodeStr)) + { + int.TryParse(maxCodeStr.Substring(maxCodeStr.Length - filterObj.BlindSubjectNumberOfPlaces), out maxCodeInt); + + } + + blindSubjectCode = filterObj.BlindTrialSiteCode + (maxCodeInt + 1).ToString($"D{filterObj.BlindSubjectNumberOfPlaces}"); + } + + subject.VisitTaskList = subject.VisitTaskList.Take(filterObj.PlanVisitCount).ToList(); + + subject.VisitTaskList.ForEach(t => + { + t.DoctorUserId = doctorUserId; + //t.TaskConsistentRuleId = filterObj.Id; + t.BlindTrialSiteCode = filterObj.BlindTrialSiteCode; + t.BlindSubjectCode = blindSubjectCode; + }); + + + //最后一个检查批次添加全局 + if (filterObj.IsGenerateGlobalTask) + { + var lastTask = (subject.VisitTaskList.Take(filterObj.PlanVisitCount).Last()).Clone(); + + var existGlobal = _visitTaskRepository.Where(t => t.SubjectId == lastTask.SubjectId &&t.TrialReadingCriterionId==trialReadingCriterionId && t.TaskState == TaskState.Effect && t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum == lastTask.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + + + if (existGlobal == null) + { + existGlobal = new VisitTaskSimpleDTO() + { + SubjectId = lastTask.SubjectId, + TrialId = lastTask.TrialId, + ArmEnum = lastTask.ArmEnum, + ReadingCategory = ReadingCategory.Global, + TaskName = lastTask.TaskName + "_Global", + TaskBlindName = lastTask.TaskBlindName + "_Global", + TrialReadingCriterionId=trialReadingCriterionId, + }; + } + + + existGlobal.DoctorUserId = doctorUserId; + existGlobal.BlindSubjectCode = lastTask.BlindSubjectCode; + existGlobal.BlindTrialSiteCode = lastTask.BlindTrialSiteCode; + + subject.VisitTaskList.Add(existGlobal); + } + + + + await _visitTaskCommonService.AddTaskAsync(new GenerateTaskCommand() + { + TrialId = filterObj.TrialId, + + ReadingCategory = GenerateTaskCategory.SelfConsistent, + + + //产生的过滤掉已经生成的 + GenerataConsistentTaskList = subject.VisitTaskList.Where(t => t.IsHaveGeneratedTask == false).ToList() + }); + + await _visitTaskRepository.SaveChangesAsync(); + + } + } + + return ResponseOutput.Ok(); + + + } + + + /// + /// 组间一致性分析 选择Subejct 列表 + /// + /// + /// + [HttpPost] + public async Task> GetGroupConsistentRuleSubjectList(GroupConsistentQuery inQuery) + { + var trialId = inQuery.TrialId; + + var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.TrialReadingCriterionId==inQuery.TrialReadingCriterionId && t.IsSelfAnalysis == false); + + + if (filterObj == null) + { + return new PageOutput(); + } + + var query = await GetGroupConsistentQueryAsync(filterObj); + + + var pagedList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(DoctorSelfConsistentSubjectView.SubjectCode) : inQuery.SortField, inQuery.Asc); + + return pagedList; + } + + + + /// + /// 确认生成组间一致性分析任务 + /// + /// + /// + /// + [HttpPost] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task ConfirmGenerateGroupConsistentTask(GroupConsistentConfirmGenrateCommand inCommand, [FromServices] IVisitTaskHelpeService _visitTaskCommonService) + { + var trialId = inCommand.TrialId; + + var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.IsSelfAnalysis == false); + + var trialReadingCriterionId = filterObj.TrialReadingCriterionId; + + + var query = await GetGroupConsistentQueryAsync(filterObj, inCommand.SubejctIdList); + + var subjectList = query.ToList(); + + var doctorUserIdQuery = from enroll in _repository.Where(t => t.TrialId == trialId).Where(t => t.EnrollReadingCategoryList.Where(t=>t.TrialReadingCriterionId==trialReadingCriterionId).Any(c => c.ReadingCategory == ReadingCategory.Global || c.ReadingCategory == ReadingCategory.Visit)) + join user in _repository.Where() on enroll.DoctorId equals user.DoctorId + select user.Id; + + var configDoctorUserIdList = await doctorUserIdQuery.ToListAsync(); + + using (await _mutex.LockAsync()) + { + int maxCodeInt = 0; + + foreach (var subject in subjectList.Where(t => t.IsHaveGeneratedTask == false)) + { + + //组间一致性分析 也用盲态SubjectCode + //处理 Subject 编号 + + var blindSubjectCode = string.Empty; + + var subjectTask = _visitTaskRepository.Where(t => t.SubjectId == subject.SubjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.IsSelfAnalysis==false).OrderByDescending(t => t.BlindSubjectCode).FirstOrDefault(); + if (subjectTask != null && subjectTask.BlindSubjectCode != String.Empty) + { + blindSubjectCode = subjectTask.BlindSubjectCode; + } + else + { + var maxCodeStr = _visitTaskRepository.Where(t => t.TrialId == subject.TrialId && t.TrialReadingCriterionId == trialReadingCriterionId && t.IsSelfAnalysis == false).OrderByDescending(t => t.BlindSubjectCode).Select(t => t.BlindSubjectCode).FirstOrDefault(); + + if (!string.IsNullOrEmpty(maxCodeStr)) + { + int.TryParse(maxCodeStr.Substring(maxCodeStr.Length - filterObj.BlindSubjectNumberOfPlaces), out maxCodeInt); + + } + + blindSubjectCode = filterObj.BlindTrialSiteCode + (maxCodeInt + 1).ToString($"D{filterObj.BlindSubjectNumberOfPlaces}"); + } + + + + + + + var subjectAddTaskList = new List(); + + + //需要处理的医生 + + var needAddDoctorUserIdList = configDoctorUserIdList.Except(subject.VisitTaskList.Select(t => (Guid)t.DoctorUserId)).ToList(); + + if (needAddDoctorUserIdList.Count == 0) + { + throw new BusinessValidationFailedException("请配置一致性分析的医生"); + } + + + foreach (var needAddDoctorUserId in needAddDoctorUserIdList) + { + + //每个医生 都生成处理的任务 + foreach (var task in subject.SubjectTaskVisitList.Take(filterObj.PlanVisitCount)) + { + + subjectAddTaskList.Add(new VisitTaskGroupSimpleDTO() + { + ReadingCategory = task.ReadingCategory, + ReadingTaskState = task.ReadingTaskState, + TaskBlindName = task.TaskBlindName, + TaskName = task.TaskName, + TaskState = task.TaskState, + SubjectId = task.SubjectId, + VisitTaskNum = task.VisitTaskNum, + TrialId = task.TrialId, + DoctorUserId = needAddDoctorUserId, + ArmEnum = Arm.GroupConsistentArm, + SouceReadModuleId = task.SouceReadModuleId, + SourceSubjectVisitId = task.SourceSubjectVisitId, + + TrialReadingCriterionId = task.TrialReadingCriterionId, + + BlindSubjectCode=blindSubjectCode, + BlindTrialSiteCode=filterObj.BlindTrialSiteCode + + + }); + + } + + //最后一个检查批次添加全局 + + if (filterObj.IsGenerateGlobalTask) + { + var lastTask = (subjectAddTaskList.Take(filterObj.PlanVisitCount).Last()).Clone(); + + + var existGlobal = _visitTaskRepository.Where(t => t.SubjectId == lastTask.SubjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.TaskState == TaskState.Effect && t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum == lastTask.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + + + if (existGlobal == null) + { + existGlobal = new VisitTaskSimpleDTO() + { + SubjectId = lastTask.SubjectId, + TrialId = lastTask.TrialId, + ReadingCategory = ReadingCategory.Global, + TaskName = lastTask.TaskName + "_Global", + TaskBlindName = lastTask.TaskBlindName + "_Global", + + TrialReadingCriterionId = trialReadingCriterionId, + + BlindSubjectCode = blindSubjectCode, + BlindTrialSiteCode = filterObj.BlindTrialSiteCode + + }; + } + + + existGlobal.BlindSubjectCode = blindSubjectCode; + existGlobal.BlindTrialSiteCode = filterObj.BlindTrialSiteCode; + existGlobal.ArmEnum = Arm.GroupConsistentArm; + existGlobal.DoctorUserId = needAddDoctorUserId; + + subjectAddTaskList.Add(existGlobal); + } + + } + + + await _visitTaskCommonService.AddTaskAsync(new GenerateTaskCommand() + { + TrialId = filterObj.TrialId, + + ReadingCategory = GenerateTaskCategory.GroupConsistent, + + + GenerataGroupConsistentTaskList = subjectAddTaskList + }); + + + await _taskConsistentRuleRepository.SaveChangesAsync(); + } + + } + + + + return ResponseOutput.Ok(); + + + } + + + + + /// + /// 仅仅自身一致性时使用( + /// + /// + /// + /// + /// + private IQueryable GetIQueryableDoctorSelfConsistentSubjectView(TaskConsistentRule filterObj, Guid doctorUserId, List? subejctIdList = null) + { + var trialId = filterObj.TrialId; + + var trialReadingCriterionId = filterObj.TrialReadingCriterionId; + + #region Subejct 维度 + + Expression> comonTaskFilter = u => u.TrialId == trialId && u.IsAnalysisCreate == false && u.TaskState == TaskState.Effect && u.ReadingTaskState == ReadingTaskState.HaveSigned && u.TrialReadingCriterionId== trialReadingCriterionId && + u.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < DateTime.Now && (u.ReReadingApplyState == ReReadingApplyState.Default || u.ReReadingApplyState == ReReadingApplyState.Reject) && u.DoctorUserId == doctorUserId; + + + + + if (subejctIdList != null && subejctIdList?.Count > 0) + { + comonTaskFilter = comonTaskFilter.And(t => subejctIdList.Contains(t.SubjectId)); + } + + + Expression> visitTaskFilter = comonTaskFilter.And(t => t.ReadingCategory == ReadingCategory.Visit); + + ////所选检查批次数量 的检查批次 其中必有一个检查批次后有全局任务 + //if (filterObj.IsHaveReadingPeriod == true) + //{ + // //这里的过滤条件 不能用 where(comonTaskFilter) 会报错,奇怪的问题 只能重新写一遍 + // visitTaskFilter = visitTaskFilter.And(c => c.Subject.SubjectVisitTaskList.Any(t => t.VisitTaskNum == c.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global] && t.ReadingCategory == ReadingCategory.Global && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && t.ReadingTaskState == ReadingTaskState.HaveSigned && + // t.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < DateTime.Now && (t.ReReadingApplyState == ReReadingApplyState.Default || t.ReReadingApplyState == ReReadingApplyState.Reject))); + + //} + + + var subjectQuery = _subjectRepository.Where(t => t.TrialId == trialId && + t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).Count() >= filterObj.PlanVisitCount) + .WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).OrderBy(t => t.VisitTaskNum).Take(filterObj.PlanVisitCount + 1).Any(t => t.ReadingCategory == ReadingCategory.Global)) + ; + + + + var query = subjectQuery.Select(t => new DoctorSelfConsistentSubjectView() + { + TrialId = t.TrialId, + SiteId = t.SiteId, + SubjectCode = t.Code, + TrialSiteCode = t.TrialSite.TrialSiteCode, + SubjectId = t.Id, + + IsReReadingOrBackInfluenceAnalysis=t.IsReReadingOrBackInfluenceAnalysis, + + BlindSubjectCode = t.SubjectVisitTaskList.Where(t => t.IsAnalysisCreate && t.TrialReadingCriterionId == trialReadingCriterionId).OrderByDescending(t => t.BlindSubjectCode).Select(t => t.BlindSubjectCode).FirstOrDefault(), + + IsHaveGeneratedTask = t.SubjectVisitTaskList.Any(c => c.DoctorUserId == doctorUserId && c.IsSelfAnalysis == true && c.TrialReadingCriterionId==trialReadingCriterionId), + + + ValidVisitCount = t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).Count(), + + VisitTaskList = t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).OrderBy(t => t.VisitTaskNum).Select(c => new VisitTaskSimpleDTO() + { + Id = c.Id, + ReadingCategory = c.ReadingCategory, + ReadingTaskState = c.ReadingTaskState, + TaskBlindName = c.TaskBlindName, + TaskCode = c.TaskCode, + TaskName = c.TaskName, + TaskState = c.TaskState, + ArmEnum = c.ArmEnum, + SubjectId = c.SubjectId, + VisitTaskNum = c.VisitTaskNum, + TrialId = c.TrialId, + SourceSubjectVisitId = c.SourceSubjectVisitId, + SouceReadModuleId = c.SouceReadModuleId, + + + + TrialReadingCriterionId=c.TrialReadingCriterionId, + IsClinicalDataSign = c.IsClinicalDataSign, + IsNeedClinicalDataSign = c.IsNeedClinicalDataSign, + + //自身一致性才有意义 + //IsHaveGeneratedTask = c.Subject.SubjectVisitTaskList.Any(t => t.ConsistentAnalysisOriginalTaskId == c.Id), + + GlobalVisitTaskList = c.Subject.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.VisitTaskNum == c.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]).Select(c => new VisitTaskSimpleDTO() + { + Id = c.Id, + ReadingCategory = c.ReadingCategory, + ReadingTaskState = c.ReadingTaskState, + TaskBlindName = c.TaskBlindName, + TaskCode = c.TaskCode, + TaskName = c.TaskName, + TaskState = c.TaskState, + ArmEnum = c.ArmEnum, + SubjectId = c.SubjectId, + VisitTaskNum = c.VisitTaskNum, + TrialId = c.TrialId, + SourceSubjectVisitId = c.SourceSubjectVisitId, + SouceReadModuleId = c.SouceReadModuleId, + + TrialReadingCriterionId = c.TrialReadingCriterionId, + IsClinicalDataSign = c.IsClinicalDataSign, + IsNeedClinicalDataSign = c.IsNeedClinicalDataSign, + }).ToList(), + + }).ToList() + }); + + return query.OrderByDescending(t => t.IsHaveGeneratedTask); + + #endregion + } + + + + + private async Task> GetGroupConsistentQueryAsync(TaskConsistentRule filterObj, List? subejctIdList = null) + { + + var trialId = filterObj.TrialId; + var trialReadingCriterionId = filterObj.TrialReadingCriterionId; + + //var trialConfig = (await _repository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.ReadingType, t.IsReadingTaskViewInOrder }).FirstOrDefaultAsync()).IfNullThrowException(); + + + + Expression> comonTaskFilter = u => u.TrialId == trialId && u.IsAnalysisCreate == false && u.TaskState == TaskState.Effect && u.ReadingTaskState == ReadingTaskState.HaveSigned && u.TrialReadingCriterionId == trialReadingCriterionId + && (u.ReReadingApplyState == ReReadingApplyState.Default || u.ReReadingApplyState == ReReadingApplyState.Reject); + + + if (subejctIdList != null && subejctIdList?.Count > 0) + { + comonTaskFilter = comonTaskFilter.And(t => subejctIdList.Contains(t.SubjectId)); + } + + Expression> visitTaskFilter = comonTaskFilter.And(t => t.ReadingCategory == ReadingCategory.Visit); + + + ////所选检查批次数量 的检查批次 其中必有一个检查批次后有全局任务 + //if (filterObj.IsHaveReadingPeriod == true) + //{ + // //visitTaskFilter = visitTaskFilter.And(t => t.Subject.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Any(u => u.VisitTaskNum == t.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global] && u.ReadingCategory == ReadingCategory.Global)); + + // //这里的过滤条件 不能用 where(comonTaskFilter) 会报错,奇怪的问题 只能重新写一遍 + // visitTaskFilter = visitTaskFilter.And(c => c.Subject.SubjectVisitTaskList.Any(t => t.VisitTaskNum == c.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global] && t.ReadingCategory == ReadingCategory.Global && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && t.ReadingTaskState == ReadingTaskState.HaveSigned && + // t.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < DateTime.Now && (t.ReReadingApplyState == ReReadingApplyState.Default || t.ReReadingApplyState == ReReadingApplyState.Reject))); + + //} + + + IQueryable subjectQuery = default; + + //单重阅片没有组件一致性 + + subjectQuery = _subjectRepository.Where(t => t.TrialId == trialId && + t.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).Select(t => t.DoctorUserId).Distinct().Count() == 2 && + t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).GroupBy(t => new { t.SubjectId, t.VisitTaskNum }).Where(g => g.Count() == 2).Count() >= filterObj.PlanVisitCount + ) + .WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).OrderBy(t => t.VisitTaskNum).Take(filterObj.PlanVisitCount * 2 + 2).Any(t => t.ReadingCategory == ReadingCategory.Global)) + + ; + + + + var query = subjectQuery.Select(t => new DoctorGroupConsistentSubjectView() + { + TrialId = t.TrialId, + SiteId = t.SiteId, + SubjectCode = t.Code, + TrialSiteCode = t.TrialSite.TrialSiteCode, + SubjectId = t.Id, + IsReReadingOrBackInfluenceAnalysis = t.IsReReadingOrBackInfluenceAnalysis, + + IsHaveGeneratedTask = t.SubjectVisitTaskList.Any(c => c.IsSelfAnalysis == false && c.TrialReadingCriterionId==trialReadingCriterionId), + + + ValidVisitCount = t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).GroupBy(t => new { t.SubjectId, t.VisitTaskNum }).Where(g => g.Count() == 2).Count(), + + VisitTaskList = t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter) + .Select(c => new VisitTaskGroupSimpleDTO() + { + ReadingCategory = c.ReadingCategory, + ReadingTaskState = c.ReadingTaskState, + TaskBlindName = c.TaskBlindName, + TaskName = c.TaskName, + TaskState = c.TaskState, + SubjectId = c.SubjectId, + VisitTaskNum = c.VisitTaskNum, + TrialId = c.TrialId, + DoctorUserId = c.DoctorUserId, + + SourceSubjectVisitId = c.SourceSubjectVisitId, + SouceReadModuleId = c.SouceReadModuleId, + + TrialReadingCriterionId = c.TrialReadingCriterionId, + IsClinicalDataSign = c.IsClinicalDataSign, + IsNeedClinicalDataSign = c.IsNeedClinicalDataSign, + + }).ToList() + + // + }); + + query = query.OrderByDescending(t => t.IsHaveGeneratedTask); + + return query; + } + + + + + [HttpPost] + public async Task GetConsistentRule(TaskConsistentRuleQuery inQuery) + { + return await _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsSelfAnalysis == inQuery.IsSelfAnalysis && t.TrialReadingCriterionId==inQuery.TrialReadingCriterionId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + } + + /// + /// 自身一致性分配 配置+ 统计已经生成数量统计表 + /// + /// + /// + [HttpPost] + public async Task> GetSelfConsistentDoctorStatList(TaskConsistentRuleQuery inQuery) + { + var trialId = inQuery.TrialId; + + var taskConsistentRuleQueryable = from enroll in _repository.Where(t => t.TrialId == trialId && t.EnrollStatus==EnrollStatus.ConfirmIntoGroup) + join user in _repository.Where() on enroll.DoctorId equals user.DoctorId + join taskConsistentRule in _repository.Where(t => t.TrialId == trialId &&t.TrialReadingCriterionId==inQuery.TrialReadingCriterionId && t.IsSelfAnalysis) on enroll.TrialId equals taskConsistentRule.TrialId + select new TaskConsistentRuleView() + { + Id = taskConsistentRule.Id, + CreateTime = taskConsistentRule.CreateTime, + BlindTrialSiteCode = taskConsistentRule.BlindTrialSiteCode, + BlindSubjectNumberOfPlaces = taskConsistentRule.BlindSubjectNumberOfPlaces, + CreateUserId = taskConsistentRule.CreateUserId, + IntervalWeeks = taskConsistentRule.IntervalWeeks, + IsEnable = taskConsistentRule.IsEnable, + PlanSubjectCount = taskConsistentRule.PlanSubjectCount, + Note = taskConsistentRule.Note, + TrialId = taskConsistentRule.TrialId, + UpdateTime = taskConsistentRule.UpdateTime, + UpdateUserId = taskConsistentRule.UpdateUserId, + IsGenerateGlobalTask = taskConsistentRule.IsGenerateGlobalTask, + IsHaveReadingPeriod = taskConsistentRule.IsHaveReadingPeriod, + PlanVisitCount = taskConsistentRule.PlanVisitCount, + + GeneratedSubjectCount = taskConsistentRule.Trial.VisitTaskList.Where(t => t.IsAnalysisCreate && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.IsSelfAnalysis == true && t.DoctorUserId == user.Id).Select(t => t.SubjectId).Distinct().Count(), + + AnalysisDoctorUser = new UserSimpleInfo() + { + UserId = user.Id, + UserCode = user.UserCode, + FullName = user.FullName, + UserName = user.UserName + } + }; + + + + //if (await _taskConsistentRuleRepository.AnyAsync(t => t.TrialId == inQuery.TrialId)) + //{ + // var rule = await _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstAsync(); + + // rule.IsBatchAdd = true; + + // await BatchAddOrUpdateTaskConsistentRule(rule); + //} + + //#endregion + + //var taskConsistentRuleQueryable = _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId) + // .ProjectTo(_mapper.ConfigurationProvider); + + return await taskConsistentRuleQueryable.ToListAsync(); + } + + + + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task AddOrUpdateTaskConsistentRule(TaskConsistentRuleAddOrEdit addOrEditTaskConsistentRule) + { + + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = t => t.TrialId == addOrEditTaskConsistentRule.TrialId && t.IsSelfAnalysis == addOrEditTaskConsistentRule.IsSelfAnalysis && t.TrialReadingCriterionId==addOrEditTaskConsistentRule.TrialReadingCriterionId, + VerifyMsg = "已有该项目配置,不允许继续增加" + }; + + if (await _visitTaskRepository.AnyAsync(t => t.IsSelfAnalysis == addOrEditTaskConsistentRule.IsSelfAnalysis && t.TrialId == addOrEditTaskConsistentRule.TrialId && t.TrialReadingCriterionId == addOrEditTaskConsistentRule.TrialReadingCriterionId)) + { + return ResponseOutput.NotOk("该标准已有Subject 生成了任务,不允许修改配置"); + } + + var entity = await _taskConsistentRuleRepository.InsertOrUpdateAsync(addOrEditTaskConsistentRule, true, verifyExp1); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + [HttpDelete("{taskConsistentRuleId:guid}")] + public async Task DeleteTaskConsistentRule(Guid taskConsistentRuleId) + { + var config = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.Id == taskConsistentRuleId); + + + if (await _visitTaskRepository.AnyAsync(t => t.IsAnalysisCreate && t.TrialId == config.TrialId && t.IsSelfAnalysis == config.IsSelfAnalysis && t.TrialReadingCriterionId==config.TrialReadingCriterionId)) + { + throw new BusinessValidationFailedException("该标准已产生一致性分析任务,不允许删除"); + } + + + + var success = await _taskConsistentRuleRepository.DeleteFromQueryAsync(t => t.Id == taskConsistentRuleId, true); + return ResponseOutput.Ok(); + } + + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Allocation/TaskMedicalReviewRuleService.cs b/IRaCIS.Core.Application/Service/Allocation/TaskMedicalReviewRuleService.cs new file mode 100644 index 0000000..fae8083 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/TaskMedicalReviewRuleService.cs @@ -0,0 +1,195 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-29 13:38:40 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Core.Application.Service +{ + /// + /// 医学审核生成规则 废弃 + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TaskMedicalReviewRuleService : BaseService, ITaskMedicalReviewRuleService + { + + private readonly IRepository _taskMedicalReviewRuleRepository; + private readonly IRepository _taskMedicalReviewRepository; + public TaskMedicalReviewRuleService(IRepository taskMedicalReviewRuleRepository,IRepository taskMedicalReviewRepository) + { + _taskMedicalReviewRuleRepository = taskMedicalReviewRuleRepository; + _taskMedicalReviewRepository = taskMedicalReviewRepository; + } + + + [HttpPost] + public async Task<(List,object)> GetTaskMedicalReviewRuleList(TaskMedicalReviewRuleQuery inQuery) + { + + + var taskTaskMedicalReviewRuleQueryable = _taskMedicalReviewRuleRepository.Where(t => t.TrialId == inQuery.TrialId) + .ProjectTo(_mapper.ConfigurationProvider); + + var isHaveMIM = await _repository.AnyAsync(t => t.User.UserTypeEnum == Domain.Share.UserTypeEnum.MIM && t.TrialId==inQuery.TrialId); + + return (await taskTaskMedicalReviewRuleQueryable.ToListAsync(),new {IsHaveMIM=isHaveMIM}); + } + + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task AddOrUpdateTaskMedicalReviewRule(TaskMedicalReviewRuleAddOrEdit addOrEditTaskTaskMedicalReviewRule) + { + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = t => t.DoctorUserId == addOrEditTaskTaskMedicalReviewRule.DoctorUserId && t.TrialId == addOrEditTaskTaskMedicalReviewRule.TrialId, + VerifyMsg = "已有该医生配置,不允许继续增加" + }; + + var entity = await _taskMedicalReviewRuleRepository.InsertOrUpdateAsync(addOrEditTaskTaskMedicalReviewRule, true, verifyExp1); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + + [HttpDelete("{taskMedicalReviewRuleId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task DeleteTaskMedicalReviewRule(Guid taskMedicalReviewRuleId) + { + + if(await _taskMedicalReviewRuleRepository.Where(t=>t.Id== taskMedicalReviewRuleId).AnyAsync(t => t.TaskMedicalReviewList.Any())) + { + return ResponseOutput.NotOk("已产生医学审核任务"); + } + + var success = await _taskMedicalReviewRuleRepository.DeleteFromQueryAsync(t => t.Id == taskMedicalReviewRuleId,true); + + return ResponseOutput.Ok(); + } + + /// + /// 产生医学审核 + /// + /// + [Obsolete] + public async Task GenerateMedicalReviewTask(GenerateMedicalReviewTaskCommand generateCommand) + { + var trialId = generateCommand.TrialId; + + //var mimUserList = await _trialUserRepository.Where(t => t.User.UserTypeEnum == Domain.Share.UserTypeEnum.MIM && t.TrialId == trialId).Select(t => t.User).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + + Guid? defalutMIMUserId = null /*mimUserList.FirstOrDefault()?.UserId*/; + + //获取当前医生数据 已经生成的,和配置的数量 + var taskTaskMedicalReviewRuleList = await _taskMedicalReviewRuleRepository.Where(t => t.TrialId == trialId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + foreach (var item in taskTaskMedicalReviewRuleList) + { + + if (item.IsEnable) + { + + if (item.PlanGlobalCount > item.GeneratedGlobalCount) + { + var needGenerateCount = item.PlanGlobalCount - item.GeneratedGlobalCount; + + var canGenerateCount = item.ActualGlobalCount - item.GeneratedGlobalCount; + + var toGenerateCount = canGenerateCount > needGenerateCount ? needGenerateCount : canGenerateCount; + + //分配给MIM + + if (toGenerateCount > 0) + { + var toGenerateTaskList = item.ActualGlobalTaskList.ExceptBy(item.GeneratedGlobalTaskList.Select(t => t.TaskId), t => t.TaskId).Take(toGenerateCount).ToList(); + + foreach (var toGenerateTask in toGenerateTaskList) + { + await _taskMedicalReviewRepository.AddAsync(new TaskMedicalReview() { DoctorUserId = toGenerateTask.DoctorUserId!.Value, TrialId = toGenerateTask.TrialId, VisitTaskId = toGenerateTask.TaskId, MedicalManagerUserId = defalutMIMUserId }); + } + } + + + } + if (item.PlanJudgeCount > item.GeneratedJudgeCount) + { + var needGenerateCount = item.PlanJudgeCount - item.GeneratedJudgeCount; + + var canGenerateCount = item.ActualJudgeCount - item.GeneratedGlobalCount; + + var toGenerateCount = canGenerateCount > needGenerateCount ? needGenerateCount : canGenerateCount; + + if (toGenerateCount > 0) + { + var toGenerateTaskList = item.ActualJudgeTaskList.ExceptBy(item.GeneratedJudgeTaskList.Select(t => t.TaskId), t => t.TaskId).Take(toGenerateCount).ToList(); + + foreach (var toGenerateTask in toGenerateTaskList) + { + await _taskMedicalReviewRepository.AddAsync(new TaskMedicalReview() { DoctorUserId = toGenerateTask.DoctorUserId!.Value, TrialId = toGenerateTask.TrialId, VisitTaskId = toGenerateTask.TaskId, MedicalManagerUserId = defalutMIMUserId }); + } + } + + } + if (item.PlanTumorCount > item.GeneratedTumorCount) + { + var needGenerateCount = item.PlanTumorCount - item.GeneratedTumorCount; + + var canGenerateCount = item.ActualTumorCount - item.GeneratedGlobalCount; + + var toGenerateCount = canGenerateCount > needGenerateCount ? needGenerateCount : canGenerateCount; + + if (toGenerateCount > 0) + { + var toGenerateTaskList = item.ActualTumorTaskList.ExceptBy(item.GeneratedTumorTaskList.Select(t => t.TaskId), t => t.TaskId).Take(toGenerateCount).ToList(); + + foreach (var toGenerateTask in toGenerateTaskList) + { + await _taskMedicalReviewRepository.AddAsync(new TaskMedicalReview() { DoctorUserId = toGenerateTask.DoctorUserId!.Value, TrialId = toGenerateTask.TrialId, VisitTaskId = toGenerateTask.TaskId, MedicalManagerUserId = defalutMIMUserId }); + } + } + + } + if (item.PlanVisitCount > item.GeneratedVisitCount) + { + var needGenerateCount = item.PlanVisitCount - item.GeneratedVisitCount; + + var canGenerateCount = item.ActualVisitCount - item.GeneratedGlobalCount; + + var toGenerateCount = canGenerateCount > needGenerateCount ? needGenerateCount : canGenerateCount; + + if (toGenerateCount > 0) + { + + var toGenerateTaskList = item.ActualVisitTaskList.ExceptBy(item.GeneratedVisitTaskList.Select(t => t.TaskId), t => t.TaskId).Take(toGenerateCount).ToList(); + + foreach (var toGenerateTask in toGenerateTaskList) + { + await _taskMedicalReviewRepository.AddAsync(new TaskMedicalReview() { DoctorUserId = toGenerateTask.DoctorUserId!.Value, TrialId = toGenerateTask.TrialId, VisitTaskId = toGenerateTask.TaskId, MedicalManagerUserId = defalutMIMUserId }); + } + } + + } + + } + + } + + + await _taskMedicalReviewRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Allocation/TaskMedicalReviewService.cs b/IRaCIS.Core.Application/Service/Allocation/TaskMedicalReviewService.cs new file mode 100644 index 0000000..120215c --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/TaskMedicalReviewService.cs @@ -0,0 +1,302 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-29 10:59:39 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Core.Application.Service +{ + /// + /// 任务医学审核 + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TaskMedicalReviewService : BaseService, ITaskMedicalReviewService + { + + private readonly IRepository _taskMedicalReviewRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _trialUserRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _taskMedicalReviewRuleRepository; + + public TaskMedicalReviewService(IRepository taskMedicalReviewRepository, + IRepository trialRepository, + IRepository trialUserRepository, + IRepository readingQuestionCriterionTrialRepository, + IRepository visitTaskRepository, + IRepository taskMedicalReviewRuleRepository) + { + _taskMedicalReviewRepository = taskMedicalReviewRepository; + this._trialRepository = trialRepository; + _trialUserRepository = trialUserRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + _visitTaskRepository = visitTaskRepository; + _taskMedicalReviewRuleRepository = taskMedicalReviewRuleRepository; + + } + + /// + /// PM 医学审核(挑选任务生成后的列表) + /// + /// + /// + [HttpPost] + public async Task> GetMedicalReviewTaskList(TaskMedicalReviewQuery inQuery) + { + + var taskMedicalReviewQueryable = _taskMedicalReviewRepository.Where(t => t.VisitTask.TrialId == inQuery.TrialId) + .WhereIf(inQuery.SiteId != null, t => t.VisitTask.Subject.SiteId == inQuery.SiteId) + .WhereIf(inQuery.SubjectId != null, t => t.VisitTask.SubjectId == inQuery.SubjectId) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.VisitTask.Subject.Code.Contains(inQuery.SubjectCode)) + .WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.VisitTask.TaskName.Contains(inQuery.TaskName) || t.VisitTask.TaskBlindName.Contains(inQuery.TaskName)) + .WhereIf(inQuery.IsUrgent != null, t => t.VisitTask.IsUrgent == inQuery.IsUrgent) + .WhereIf(inQuery.DoctorUserId != null, t => t.VisitTask.DoctorUserId == inQuery.DoctorUserId) + .WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => (t.VisitTask.BlindTrialSiteCode.Contains(inQuery.TrialSiteCode) && t.VisitTask.IsAnalysisCreate) || (t.VisitTask.Subject.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode) && t.VisitTask.IsAnalysisCreate == false)) + .WhereIf(inQuery.ReadingCategory != null, t => t.VisitTask.ReadingCategory == inQuery.ReadingCategory) + .WhereIf(inQuery.TaskState != null, t => t.VisitTask.TaskState == inQuery.TaskState) + .WhereIf(inQuery.AuditState != null, t => t.AuditState == inQuery.AuditState) + + .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.VisitTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) + .ProjectTo(_mapper.ConfigurationProvider); + + var pageList = await taskMedicalReviewQueryable.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(TaskMedicalReviewView.Id) : inQuery.SortField, inQuery.Asc); + + return pageList; + } + + + /// + /// 产生医学审核列表 + /// + /// + /// + [HttpPost] + public async Task> GetGenerateMedicalReviewTaskList(GenerateMedicalReviewTaskQuery inQuery) + { + var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId) + .Where(t => t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && t.ReadingTaskState == ReadingTaskState.HaveSigned) + + .WhereIf(inQuery.SiteId != null, t => t.Subject.SiteId == inQuery.SiteId) + .WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId) + .WhereIf(inQuery.IsUrgent != null, t => t.IsUrgent == inQuery.IsUrgent) + .WhereIf(inQuery.DoctorUserId != null, t => t.DoctorUserId == inQuery.DoctorUserId) + .WhereIf(inQuery.ReadingCategory != null, t => t.ReadingCategory == inQuery.ReadingCategory) + .WhereIf(inQuery.ReadingTaskState != null, t => t.ReadingTaskState == inQuery.ReadingTaskState) + .WhereIf(inQuery.IsGeneratedJudge != null, t => t.JudgeVisitTaskId != null) + .WhereIf(inQuery.IsGlobalHaveUpdate != null, t => t.IsGlobalHaveUpdate == inQuery.IsGlobalHaveUpdate) + + .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) + .WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.TaskName.Contains(inQuery.TaskName) || t.TaskBlindName.Contains(inQuery.TaskName)) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.Subject.Code.Contains(inQuery.SubjectCode)) + + .ProjectTo(_mapper.ConfigurationProvider); + + var defalutSortArray = new string[] { nameof(TaskMedicalReviewView.IsUrgent) + " desc", nameof(TaskMedicalReviewView.SubjectId) }; + var pageList = await visitTaskQueryable.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); + + + return pageList; + } + + /// + /// 手动生成并分配医学审核 + /// + /// + /// + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ManuallyGeneratedAndAssignedMedicalReview(ManuallyGeneratedAndAssignedMedicalReviewCommand command) + { + + foreach (var taskId in command.TaskIdList) + { + await _taskMedicalReviewRepository.AddAsync(new TaskMedicalReview() { TrialId = command.TrialId, VisitTaskId = taskId, MedicalManagerUserId = command.MedicalManagerUserId, AllocateTime = DateTime.Now }); + } + + await _taskMedicalReviewRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + /// + /// 获取下一个医学审核任务 + /// + /// + /// + /// + [HttpPost] + public async Task GetNextMedicalReviewTask(GetNextMedicalReviewTaskInDto inDto) + { + var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.MedicalReviewId).Include(x=>x.VisitTask).FirstNotNullAsync(); + + var subjectReview = await GetMIMMedicalReviewTaskList(new TaskMedicalReviewQuery() + { + + TrialId = medicalReview.TrialId, + IsGetBeRead = true, + Id=inDto.MedicalReviewId, + SubjectId= medicalReview.VisitTask.SubjectId, + TrialReadingCriterionId = medicalReview.VisitTask.TrialReadingCriterionId, + PageIndex=1, + PageSize=1, + }); + + if (subjectReview.Item1.CurrentPageData.Count == 1) + { + return subjectReview.Item1.CurrentPageData[0]; + } + + var data = await GetMIMMedicalReviewTaskList(new TaskMedicalReviewQuery() + { + + TrialId = medicalReview.TrialId, + IsGetBeRead = true, + Id = inDto.MedicalReviewId, + TrialReadingCriterionId = medicalReview.VisitTask.TrialReadingCriterionId, + PageIndex = 1, + PageSize = 1, + }); + + if (data.Item1.CurrentPageData.Count == 1) + { + return data.Item1.CurrentPageData[0]; + } + else + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_Finish"]); + } + + + } + + + + /// + /// MIM 医学审核 + /// + /// + /// + [HttpPost] + public async Task<(PageOutput, object)> GetMIMMedicalReviewTaskList(TaskMedicalReviewQuery inQuery) + { + + var taskMedicalReviewQueryable = _taskMedicalReviewRepository.Where(t => t.VisitTask.TrialId == inQuery.TrialId && t.MedicalManagerUserId == _userInfo.Id&&t.VisitTask.TrialReadingCriterionId==inQuery.TrialReadingCriterionId) + .WhereIf(inQuery.SubjectId != null, t => t.VisitTask.SubjectId == inQuery.SubjectId) + .WhereIf(inQuery.SiteId != null, t => t.VisitTask.Subject.SiteId == inQuery.SiteId) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.VisitTask.Subject.Code.Contains(inQuery.SubjectCode)) + .WhereIf(inQuery.Id != null, t => t.Id != inQuery.Id) + .WhereIf(inQuery.IsUrgent != null, t => t.VisitTask.IsUrgent == inQuery.IsUrgent) + .WhereIf(inQuery.AuditState != null, t => t.AuditState == inQuery.AuditState) + .WhereIf(inQuery.TaskState != null, t => t.VisitTask.TaskState == inQuery.TaskState) + .WhereIf(inQuery.DoctorUserId != null, t => t.VisitTask.DoctorUserId == inQuery.DoctorUserId) + .WhereIf(inQuery.ReadingCategory != null, t => t.VisitTask.ReadingCategory == inQuery.ReadingCategory) + .WhereIf(inQuery.ReadingTaskState != null, t => t.VisitTask.ReadingTaskState == inQuery.ReadingTaskState) + .WhereIf(inQuery.IsGetBeRead,x=>!x.IsInvalid&&x.AuditState!= MedicalReviewAuditState.HaveSigned) + .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.VisitTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) + .ProjectTo(_mapper.ConfigurationProvider); + + var pageList = await taskMedicalReviewQueryable.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(TaskMedicalReviewView.Id) : inQuery.SortField, inQuery.Asc); + + return (pageList, new + { + + IsConfirmMedicineQuestion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inQuery.TrialReadingCriterionId).Select(x => x.IsConfirmMedicineQuestion).FirstOrDefaultAsync() + }); + } + + + /// + /// 设置医学审核失效 + /// + /// + /// + [HttpPost] + public async Task SetMedicalReviewInvalid(SetMedicalReviewInvalidCommand command) + { + + if (await _taskMedicalReviewRepository.Where(t => command.MedicalReviewIdList.Contains(t.Id)).AnyAsync(t => t.AuditState == MedicalReviewAuditState.HaveSigned)) + { + return ResponseOutput.NotOk("已签名的不允许设置为失效"); + } + + await _taskMedicalReviewRepository.UpdatePartialFromQueryAsync(t => command.MedicalReviewIdList.Contains(t.Id), c => new TaskMedicalReview() { IsInvalid = true },true); + + return ResponseOutput.Ok(); + + } + + + + + + /// + /// 123 分别是 分配,重新分配,取消分配 + /// + /// + /// + [UnitOfWork] + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task AssignMedicalReviewTask(AssignMedicalReviewTaskCommand command) + { + + foreach (var id in command.IdList) + { + var task = await _taskMedicalReviewRepository.FirstOrDefaultAsync(t => t.Id == id); + + if (command.TaskOptType == MedicalReviewTaskOptType.Assign) + { + if (task.MedicalManagerUserId != null && task.MedicalManagerUserId != command.MedicalManagerUserId) + { + throw new BusinessValidationFailedException("当前有任务已分配给其他人,不允许分配,请刷新列表"); + } + + task.MedicalManagerUserId = command.MedicalManagerUserId; + task.AllocateTime = DateTime.Now; + + } + else if (command.TaskOptType == MedicalReviewTaskOptType.ReAssign) + { + task.MedicalManagerUserId = command.MedicalManagerUserId; + task.AllocateTime = DateTime.Now; + } + + else if (command.TaskOptType == MedicalReviewTaskOptType.CancelAssign) + { + task.MedicalManagerUserId = null; + task.AllocateTime = null; + } + } + + + + await _taskMedicalReviewRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + /// + /// 获取医学经理列表 + /// + /// + [HttpGet("{trialId:guid}")] + public async Task> GetMIMUserList(Guid trialId) + { + var query = _trialUserRepository.Where(t => t.User.UserTypeEnum == Domain.Share.UserTypeEnum.MIM && t.TrialId == trialId).Select(t => t.User).ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToListAsync(); + } + + } +} diff --git a/IRaCIS.Core.Application/Service/Allocation/VisitTaskHelpeService.cs b/IRaCIS.Core.Application/Service/Allocation/VisitTaskHelpeService.cs new file mode 100644 index 0000000..ea6b8f5 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/VisitTaskHelpeService.cs @@ -0,0 +1,1531 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 14:10:49 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using EasyCaching.Core; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using AutoMapper; +using MassTransit; +using IRaCIS.Core.Infra.EFCore.Common; +using System.Linq.Expressions; +using IRaCIS.Core.Domain.Share.Reading; + +namespace IRaCIS.Core.Application.Service +{ + + + /// + /// 检查批次读片任务 + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class VisitTaskHelpeService : IVisitTaskHelpeService + { + + private readonly IRepository _visitTaskRepository; + private readonly IRepository _trialRepository; + private readonly IEasyCachingProvider _provider; + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _readingJudgeInfoRepository; + private readonly IRepository _taskAllocationRuleRepository; + private readonly IRepository _subjectUserRepository; + private readonly IRepository _readModuleRepository; + + private readonly IMapper _mapper; + private readonly IUserInfo _userInfo; + private readonly IRepository _visitTaskReReadingRepository; + + private readonly IRepository _trialReadingCriterionRepository; + + private readonly IRepository _trialClinicalDataSetRepository; + private readonly IRepository _readingClinicalDataRepository; + + + public VisitTaskHelpeService(IRepository visitTaskRepository, IRepository subjectUserRepository, IRepository trialRepository, IEasyCachingProvider provider, + IRepository subjectVisitRepository, + IRepository readModuleRepository, + IRepository readingJudgeInfoRepository, + IRepository taskAllocationRuleRepository, IMapper mapper, IUserInfo userInfo, IRepository visitTaskReReadingRepository, + IRepository trialReadingCriterionRepository, IRepository trialClinicalDataSetRepository, IRepository readingClinicalDataRepository) + { + _readingClinicalDataRepository = readingClinicalDataRepository; + _trialClinicalDataSetRepository = trialClinicalDataSetRepository; + + _visitTaskRepository = visitTaskRepository; + _trialRepository = trialRepository; + this._readModuleRepository = readModuleRepository; + _provider = provider; + _subjectVisitRepository = subjectVisitRepository; + this._readingJudgeInfoRepository = readingJudgeInfoRepository; + _taskAllocationRuleRepository = taskAllocationRuleRepository; + _subjectUserRepository = subjectUserRepository; + _mapper = mapper; + _userInfo = userInfo; + _visitTaskReReadingRepository = visitTaskReReadingRepository; + + _trialReadingCriterionRepository = trialReadingCriterionRepository; + } + + //查询列表的时候,一致性核查通过未产生任务的 自动产生任务 如果是一致性核查,那么还会自动分配 + public async Task GenerateVisitTaskAsync(Guid trialId, List subjectVisitIdList, bool isAssignSubjectToDoctor = false) + { + + if (subjectVisitIdList.Count == 0) + { + return; + } + + var subjectVisitList = _subjectVisitRepository.Where(t => subjectVisitIdList.Contains(t.Id)).ProjectTo(_mapper.ConfigurationProvider).Distinct().ToList(); + + + await AddTaskAsync(new GenerateTaskCommand() { TrialId = trialId, IsAssignSubjectToDoctor = isAssignSubjectToDoctor, VisitGenerataTaskList = subjectVisitList, ReadingCategory = GenerateTaskCategory.Visit }); + + + } + + + //基于标准签名 产生任务 + public async Task BaseCritrionGenerateVisitTask(Guid trialId, Guid confirmedTrialReadingCriterionId) + { + //找到一致性核查通过且没有产生任务的检查批次 + var needGenerateVisit = await _subjectVisitRepository.Where(t => t.TrialId == trialId && t.CheckState == CheckStateEnum.CVPassed && + !t.VisitTaskList.Any(u => u.TrialReadingCriterionId == confirmedTrialReadingCriterionId && u.SourceSubjectVisitId == t.Id && u.TaskState==TaskState.Effect && u.IsAnalysisCreate==false)).ToListAsync(); + + + var trialReadingCriterionConfig = await _trialReadingCriterionRepository.Where(t => t.Id == confirmedTrialReadingCriterionId).Select(t => new { TrialReadingCriterionId = t.Id, t.ReadingTool, t.ReadingType, t.IsReadingTaskViewInOrder, t.IsFollowVisitAutoAssign, t.IsFollowGlobalVisitAutoAssign, t.FollowGlobalVisitAutoAssignDefaultState, t.FollowVisitAutoAssignDefaultState, t.TaskAllocateObjEnum, t.CriterionType }).FirstOrDefaultAsync(); + + //获取确认的临床数据配置 + var clinicalDataConfirmList = _trialClinicalDataSetRepository.Where(t => t.TrialId == trialId && t.IsConfirm).Include(t => t.TrialClinicalDataSetCriteriaList).ToList(); + + var visitBlindConfig = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.BlindBaseLineName, t.BlindFollowUpPrefix }).FirstOrDefault(); + + var dbMaxCode = _visitTaskRepository.Where(t => t.TrialId == trialId).Select(t => t.Code).DefaultIfEmpty().Max(); + + var cacheMaxCodeInt = _provider.Get($"{trialId}_{StaticData.CacheKey.TaskMaxCode}").Value; + + int currentMaxCodeInt = cacheMaxCodeInt > dbMaxCode ? cacheMaxCodeInt : dbMaxCode; + + + + foreach (var subjectGroup in needGenerateVisit.GroupBy(t => t.SubjectId).Select(g => new { SubjectId = g.Key, SubjectVisitList = g.ToList() })) + { + var assignConfigList = await _subjectUserRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectGroup.SubjectId && t.OrignalSubjectUserId == null && t.IsConfirmed).Select(u => new { u.DoctorUserId, u.ArmEnum }).ToListAsync(); + + + + var visitNumList = _subjectVisitRepository.Where(t => t.SubjectId == subjectGroup.SubjectId && t.IsLostVisit == false).OrderBy(t => t.VisitNum).Select(t => t.VisitNum).ToList(); + + foreach (var subjectVisit in subjectGroup.SubjectVisitList) + { + //var blindTaskName = string.Empty; + + var blindTaskName = subjectVisit.VisitName; + + var isNeedClinicalDataSign = IsNeedClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList); + var isClinicalDataSign = IsClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList, subjectVisit.Id, trialId); + + //if (visitNumList.IndexOf(subjectVisit.VisitNum) == 0) + //{ + // blindTaskName = visitBlindConfig.BlindBaseLineName; + //} + //else + //{ + // if (trialReadingCriterionConfig.IsReadingTaskViewInOrder) + // { + // blindTaskName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(subjectVisit.VisitNum); + // } + // else + // { + // blindTaskName = visitBlindConfig.BlindFollowUpPrefix; + // } + //} + + //每个检查批次 根据项目配置生成任务 双审生成两个 + + TaskUrgentType? taskUrgentType = null; + + if (subjectVisit.PDState == PDStateEnum.PDProgress) + { + taskUrgentType = TaskUrgentType.PDProgress; + } + else if (subjectVisit.IsEnrollmentConfirm) + { + taskUrgentType = TaskUrgentType.EnrollmentConfirm; + } + else if (subjectVisit.IsUrgent) + { + taskUrgentType = TaskUrgentType.VisitUrgent; + } + + bool isCanEditUrgentState = taskUrgentType == TaskUrgentType.EnrollmentConfirm || taskUrgentType == TaskUrgentType.PDProgress ? false : true; + + if (trialReadingCriterionConfig.ReadingType == ReadingMethod.Double) + { + //未防止脏数据 这里也多判断一次 + var existCurrentVisitTaskList = _visitTaskRepository.Where(t => t.TrialId == trialId && t.SubjectId == subjectVisit.SubjectId + && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.TaskState == TaskState.Effect + && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.SourceSubjectVisitId == subjectVisit.Id).ToList(); + + VisitTask? task1 = null; + VisitTask? task2 = null; + + if (!existCurrentVisitTaskList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1)) + { + currentMaxCodeInt = currentMaxCodeInt + 1; + + + + + task1 = await _visitTaskRepository.AddAsync(new VisitTask() + { + TrialId = trialId, + SubjectId = subjectVisit.SubjectId, + IsUrgent = subjectVisit.IsUrgent, + TaskBlindName = blindTaskName, + TaskName = subjectVisit.VisitName, + VisitTaskNum = subjectVisit.VisitNum, + TaskUrgentType = taskUrgentType, + IsCanEditUrgentState= isCanEditUrgentState, + //CheckPassedTime = subjectVisit.CheckPassedTime, + ArmEnum = Arm.DoubleReadingArm1,//特殊 + Code = currentMaxCodeInt, + SourceSubjectVisitId = subjectVisit.Id, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt, nameof(VisitTask)), + ReadingCategory = ReadingCategory.Visit, + + TrialReadingCriterionId = trialReadingCriterionConfig.TrialReadingCriterionId, + IsNeedClinicalDataSign = isNeedClinicalDataSign, + IsClinicalDataSign = isClinicalDataSign + }) ; + + } + + if (!existCurrentVisitTaskList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2)) + { + currentMaxCodeInt = currentMaxCodeInt + 1; + + + task2 = await _visitTaskRepository.AddAsync(new VisitTask() + { + TrialId = trialId, + SubjectId = subjectVisit.SubjectId, + IsUrgent = subjectVisit.IsUrgent, + TaskBlindName = blindTaskName, + TaskName = subjectVisit.VisitName, + TaskUrgentType = taskUrgentType, + IsCanEditUrgentState = isCanEditUrgentState, + VisitTaskNum = subjectVisit.VisitNum, + //CheckPassedTime = subjectVisit.CheckPassedTime, + ArmEnum = Arm.DoubleReadingArm2,//特殊 + Code = currentMaxCodeInt, + SourceSubjectVisitId = subjectVisit.Id, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt, nameof(VisitTask)), + ReadingCategory = ReadingCategory.Visit, + + TrialReadingCriterionId = trialReadingCriterionConfig.TrialReadingCriterionId, + IsNeedClinicalDataSign = isNeedClinicalDataSign, + IsClinicalDataSign = isClinicalDataSign + }); + + + } + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + + + + + var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated; + + if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null) + { + task1.TaskAllocationState = defaultState; + //分配给对应Arm的人 + task1.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm1).DoctorUserId; + task1.AllocateTime = DateTime.Now; + + task1.SuggesteFinishedTime = GetSuggessFinishTime(true,UrgentType.NotUrget); + } + + if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null) + { + task2.TaskAllocationState = defaultState; + task2.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm2).DoctorUserId; + task2.AllocateTime = DateTime.Now; + task2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + } + + } + else if (trialReadingCriterionConfig.ReadingType == ReadingMethod.Single) + { + + var singleTask = await _visitTaskRepository.AddAsync(new VisitTask() + { + TrialId = trialId, + SubjectId = subjectVisit.SubjectId, + IsUrgent = subjectVisit.IsUrgent, + TaskBlindName = blindTaskName, + TaskName = subjectVisit.VisitName, + VisitTaskNum = subjectVisit.VisitNum, + TaskUrgentType = taskUrgentType, + IsCanEditUrgentState = isCanEditUrgentState, + //CheckPassedTime = subjectVisit.CheckPassedTime, + ArmEnum = Arm.SingleReadingArm, //特殊 + Code = currentMaxCodeInt + 1, + SourceSubjectVisitId = subjectVisit.Id, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), + ReadingCategory = ReadingCategory.Visit, + TrialReadingCriterionId = trialReadingCriterionConfig.TrialReadingCriterionId, + IsNeedClinicalDataSign = isNeedClinicalDataSign, + IsClinicalDataSign = isClinicalDataSign + }); + + + currentMaxCodeInt = currentMaxCodeInt + 1; + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + + + + var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated; + + if (assignConfigList.Any(t => t.ArmEnum == Arm.SingleReadingArm)) + { + singleTask.TaskAllocationState = defaultState; + + singleTask.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.SingleReadingArm).DoctorUserId; + + singleTask.AllocateTime = DateTime.Now; + + singleTask.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + } + + + + + } + await _visitTaskRepository.SaveChangesAsync(); + + } + + } + + + } + + + + private bool IsNeedClinicalDataSign(ReadingCategory readingCategory, bool isbaseLine, Guid trialReadingCriterionId, List trialClinicalDataSetList) + { + //检查批次 + if (readingCategory == ReadingCategory.Visit) + { + if (isbaseLine) + { + //return trialClinicalDataSetList.Where(t=>t.CriterionEnumList.Any(c=>c==(int)criterionType) && (t.ClinicalDataLevel == ClinicalLevel.Subject || t.ClinicalDataLevel == ClinicalLevel.SubjectVisit)).Count()> 0; + + return trialClinicalDataSetList.Any(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCriterionId) && (t.ClinicalDataLevel == ClinicalLevel.Subject || t.ClinicalDataLevel == ClinicalLevel.SubjectVisit)); + } + else + { + return trialClinicalDataSetList.Any(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCriterionId) && t.ClinicalDataLevel == ClinicalLevel.SubjectVisit); + } + } + + else if (readingCategory == ReadingCategory.Judge) + { + return false; + } + //阅片期 + else if (readingCategory == ReadingCategory.Global) + { + return trialClinicalDataSetList.Any(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCriterionId) && t.ClinicalDataLevel == ClinicalLevel.ImageRead); + } + // 肿瘤学 + else if (readingCategory == ReadingCategory.Oncology) + { + return trialClinicalDataSetList.Any(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCriterionId) && t.ClinicalDataLevel == ClinicalLevel.OncologyRead); + } + else + { + return false; + } + + } + + + // 有可能在任务生成之前 就签名完了临床数据 + private bool IsClinicalDataSign(ReadingCategory readingCategory, bool isbaseLine, Guid trialReadingCriterionId, List trialClinicalDataSetList, Guid readingId, Guid trialId) + { + + //不需要签名的 直接返回 + if (IsNeedClinicalDataSign(readingCategory, isbaseLine, trialReadingCriterionId, trialClinicalDataSetList) == false) + { + return false; + } + + + var isClinicalDataSign = false; + + var needSignCount = 0; + + //IC 的自动签名 不用管 只用处理PM的就好 + var haveSignedCount = _readingClinicalDataRepository + .Where(t => t.TrialId == trialId && t.IsSign && t.ReadingClinicalDataState == ReadingClinicalDataStatus.HaveSigned && t.ReadingId == readingId && t.ClinicalDataTrialSet.UploadRole == UploadRole.PM).Count(); + + //检查批次 + if (readingCategory == ReadingCategory.Visit) + { + if (isbaseLine) + { + //IC 的自动签名 不用管 只用处理PM 的就好 + needSignCount = trialClinicalDataSetList.Where(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCriterionId) && (t.ClinicalDataLevel == ClinicalLevel.Subject || t.ClinicalDataLevel == ClinicalLevel.SubjectVisit) && t.UploadRole == UploadRole.PM).Count(); + } + else + { + needSignCount = trialClinicalDataSetList.Where(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCriterionId) && t.ClinicalDataLevel == ClinicalLevel.SubjectVisit && t.UploadRole == UploadRole.PM).Count(); + + } + } + + else if (readingCategory == ReadingCategory.Judge) + { + + } + //阅片期 + else if (readingCategory == ReadingCategory.Global) + { + needSignCount = trialClinicalDataSetList.Where(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCriterionId) && t.ClinicalDataLevel == ClinicalLevel.ImageRead && t.UploadRole == UploadRole.PM).Count(); + + } + // 肿瘤学 + else if (readingCategory == ReadingCategory.Oncology) + { + return trialClinicalDataSetList.Any(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCriterionId) && t.ClinicalDataLevel == ClinicalLevel.OncologyRead); + } + else + { + needSignCount = trialClinicalDataSetList.Where(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCriterionId) && t.ClinicalDataLevel == ClinicalLevel.OncologyRead && t.UploadRole == UploadRole.PM).Count(); + } + + //可能仅仅IC 基线 没有PM + if (needSignCount == haveSignedCount /*&& needSignCount != 0*/) + { + isClinicalDataSign = true; + + } + + + + return isClinicalDataSign; + } + + + public DateTime GetSuggessFinishTime(bool isInOrder, UrgentType urgentType ) + { + + var datetime= new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day , 22, 0, 0).AddDays(7); + + + //switch (urgentType) + //{ + // case UrgentType.NotUrget: + + // datetime= new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day + 7, 22, 0, 0); + + // break; + // case UrgentType.EnrollConfirm: + + // datetime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day , 22, 0, 0); + // break; + + // case UrgentType.EnrollConfirmOtherNotUrgent: + + // datetime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day+5, 22, 0, 0); + // break; + + // case UrgentType.PDVProgressVisitOrGlobal: + // datetime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 22, 0, 0); + // break; + // case UrgentType.PDVProgressJudge: + // datetime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day+1, 22, 0, 0); + // break; + + // //不加急 7天 + // case UrgentType.NormalUrgent: + + // datetime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day + 7, 22, 0, 0); + // break; + + // default: + // break; + //} + + return datetime; + + + + } + + + public async Task AddTaskAsync(GenerateTaskCommand generateTaskCommand) + { + + var trialId = generateTaskCommand.TrialId; + var isAssignSubjectToDoctor = generateTaskCommand.IsAssignSubjectToDoctor; + + var trialReadingCriterionConfigList = _trialReadingCriterionRepository.Where(t => t.TrialId == trialId && t.ReadingInfoSignTime != null).Select(t => new { TrialReadingCriterionId = t.Id, t.ReadingTool, t.ReadingType, t.IsReadingTaskViewInOrder, t.IsFollowVisitAutoAssign, t.IsFollowGlobalVisitAutoAssign, t.FollowGlobalVisitAutoAssignDefaultState, t.FollowVisitAutoAssignDefaultState, t.TaskAllocateObjEnum, t.CriterionType }).ToList(); + + var visitBlindConfig = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.BlindBaseLineName, t.BlindFollowUpPrefix }).FirstOrDefault(); + + //获取确认的临床数据配置 + var clinicalDataConfirmList = _trialClinicalDataSetRepository.Where(t => t.TrialId == trialId && t.IsConfirm).Include(t => t.TrialClinicalDataSetCriteriaList).ToList(); + + + var dbMaxCode = _visitTaskRepository.Where(t => t.TrialId == trialId).Select(t => t.Code).DefaultIfEmpty().Max(); + + var cacheMaxCodeInt = _provider.Get($"{trialId}_{StaticData.CacheKey.TaskMaxCode}").Value; + + int currentMaxCodeInt = cacheMaxCodeInt > dbMaxCode ? cacheMaxCodeInt : dbMaxCode; + + + + + switch (generateTaskCommand.ReadingCategory) + { + case GenerateTaskCategory.Visit: + + //每个Subject 的每个检查批次 都要根据每个标准进行任务的生成 和分配(考虑回退后 的分配) + + + foreach (var subjectGroup in generateTaskCommand.VisitGenerataTaskList.GroupBy(t => t.SubjectId).Select(g => new { SubjectId = g.Key, SubjectVisitList = g.ToList() })) + { + + + foreach (var subjectVisit in subjectGroup.SubjectVisitList) + { + + var subjectVisitInfo = await _subjectVisitRepository.Where(x => x.Id == subjectVisit.Id).Select(x => new + { + x.PDState, + x.IsEnrollmentConfirm, + x.IsUrgent, + }).FirstNotNullAsync(); + + TaskUrgentType? taskUrgentType = null; + + if (subjectVisitInfo.PDState == PDStateEnum.PDProgress) + { + taskUrgentType = TaskUrgentType.PDProgress; + } + else if (subjectVisitInfo.IsEnrollmentConfirm) + { + taskUrgentType = TaskUrgentType.EnrollmentConfirm; + } + else if (subjectVisitInfo.IsUrgent) + { + taskUrgentType = TaskUrgentType.VisitUrgent; + } + + bool isCanEditUrgentState = taskUrgentType == TaskUrgentType.EnrollmentConfirm || taskUrgentType == TaskUrgentType.PDProgress ? false : true; + + var visitNumList = _subjectVisitRepository.Where(t => t.SubjectId == subjectGroup.SubjectId && t.IsLostVisit == false).OrderBy(t => t.VisitNum).Select(t => t.VisitNum).ToList(); + + + foreach (var trialReadingCriterionConfig in trialReadingCriterionConfigList) + { + + var assignConfigList = await _subjectUserRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.OrignalSubjectUserId == null && t.IsConfirmed).Select(u => new { u.DoctorUserId, u.ArmEnum }).ToListAsync(); + + + var blindTaskName = subjectVisit.VisitName; + + var isNeedClinicalDataSign = IsNeedClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList); + var isClinicalDataSign = IsClinicalDataSign(ReadingCategory.Visit, subjectVisit.IsBaseLine, trialReadingCriterionConfig.TrialReadingCriterionId, clinicalDataConfirmList, subjectVisit.Id, trialId); + + + //if (visitNumList.IndexOf(subjectVisit.VisitNum) == 0) + //{ + // blindTaskName = visitBlindConfig.BlindBaseLineName; + //} + //else + //{ + // if (trialReadingCriterionConfig.IsReadingTaskViewInOrder) + // { + // blindTaskName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(subjectVisit.VisitNum); + // } + // else + // { + // blindTaskName = visitBlindConfig.BlindFollowUpPrefix; + // } + //} + + + //每个检查批次 根据项目配置生成任务 双审生成两个 + + //双重 可能有一个人的任务没分配,然后影像回退不会影响未分配的任务,所以生成的时候 要进行判断 不能每次都生成两个 + if (trialReadingCriterionConfig.ReadingType == ReadingMethod.Double) + { + + var existCurrentVisitTaskList = _visitTaskRepository.Where(t => t.TrialId == trialId && t.SubjectId == subjectVisit.SubjectId + && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.TaskState == TaskState.Effect + && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.SourceSubjectVisitId == subjectVisit.Id).ToList(); + + VisitTask? task1 = null; + VisitTask? task2 = null; + + + if (!existCurrentVisitTaskList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1)) + { + currentMaxCodeInt = currentMaxCodeInt + 1; + + task1 = await _visitTaskRepository.AddAsync(new VisitTask() + { + TrialId = trialId, + SubjectId = subjectVisit.SubjectId, + TaskBlindName = blindTaskName, + + IsUrgent = subjectVisit.IsUrgent, + TaskUrgentType = taskUrgentType, + IsCanEditUrgentState = isCanEditUrgentState, + TaskName = subjectVisit.VisitName, + VisitTaskNum = subjectVisit.VisitNum, + //CheckPassedTime = subjectVisit.CheckPassedTime, + ArmEnum = Arm.DoubleReadingArm1,//特殊 + Code = currentMaxCodeInt, + SourceSubjectVisitId = subjectVisit.Id, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt, nameof(VisitTask)), + ReadingCategory = ReadingCategory.Visit, + + TrialReadingCriterionId = trialReadingCriterionConfig.TrialReadingCriterionId, + IsNeedClinicalDataSign = isNeedClinicalDataSign, + IsClinicalDataSign = isClinicalDataSign + }); + } + + if (!existCurrentVisitTaskList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2)) + { + currentMaxCodeInt = currentMaxCodeInt + 1; + task2 = await _visitTaskRepository.AddAsync(new VisitTask() + { + TrialId = trialId, + SubjectId = subjectVisit.SubjectId, + TaskBlindName = blindTaskName, + TaskName = subjectVisit.VisitName, + IsUrgent = subjectVisit.IsUrgent, + + TaskUrgentType = taskUrgentType, + IsCanEditUrgentState = isCanEditUrgentState, + VisitTaskNum = subjectVisit.VisitNum, + //CheckPassedTime = subjectVisit.CheckPassedTime, + ArmEnum = Arm.DoubleReadingArm2,//特殊 + Code = currentMaxCodeInt, + SourceSubjectVisitId = subjectVisit.Id, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt, nameof(VisitTask)), + ReadingCategory = ReadingCategory.Visit, + + TrialReadingCriterionId = trialReadingCriterionConfig.TrialReadingCriterionId, + IsNeedClinicalDataSign = isNeedClinicalDataSign, + IsClinicalDataSign = isClinicalDataSign + }); + } + + + + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + + + #region 分配 + if (isAssignSubjectToDoctor) + { + + + if (trialReadingCriterionConfig.TaskAllocateObjEnum == TaskAllocateObj.Subject) + { + var allocateSubjectArmList = _visitTaskRepository.Where(t => t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.TrialId == trialId && t.DoctorUserId != null && t.ArmEnum != Arm.JudgeArm) + .Select(t => new { t.DoctorUserId, t.ArmEnum }).Distinct().ToList(); + + //当前任务没有分配医生,初次分配 不处理 只生成任务,后续根据生成的任务 再进行分配 + if (allocateSubjectArmList.Count == 0) + { + + } + else + { + //并且配置了医生 + if (assignConfigList.Count > 0 && trialReadingCriterionConfig.IsFollowVisitAutoAssign) + { + + #region 后续检查批次 未分配的进行再次分配,重置的或者失效的 需要重新生成新的任务 (PM 有序退回 或者PM 有序 申请重阅) + + + if (trialReadingCriterionConfig.IsReadingTaskViewInOrder) + { + //之前有回退到影像上传的检查批次 那么当前检查批次一致性核查通过的时候,当前检查批次生成但是不分配出去(排除失访的) + + var beforeBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum < subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync(); + + //之前有回退的,那么当前检查批次任务生成但是不分配 + if (beforeBackVisitTask != null) + { + //不用进行额外处理 + + //检查批次2 PM 回退 基线回退 检查批次2先一致性核查通过,生成检查批次2任务,但是不分配 + } + else + { + #region 当前检查批次根据配置规则分配出去 + + var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated; + + if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null) + { + task1.TaskAllocationState = defaultState; + //分配给对应Arm的人 + task1.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm1).DoctorUserId; + task1.AllocateTime = DateTime.Now; + + task1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + } + + if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null) + { + task2.TaskAllocationState = defaultState; + task2.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm2).DoctorUserId; + task2.AllocateTime = DateTime.Now; + task2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + } + + #endregion + } + + //后续最近的未一致性核查通过的检查批次任务 + var followBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync(); + + //大于当前检查批次 同时小于最近的未一致性核查通过的检查批次任务分配 或者生成 + + //存在退回检查批次1 又退回基线 这种情况 生成任务 考虑基线先一致性核查通过,但是检查批次1还未通过时 生成任务 + var followVisitTaskList = await _visitTaskRepository + .Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == trialReadingCriterionConfig.TrialReadingCriterionId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.SourceSubjectVisit.CheckState == CheckStateEnum.CVPassed && t.ReadingCategory == ReadingCategory.Visit && t.IsAnalysisCreate == false, true) + .WhereIf(followBackVisitTask != null, t => t.VisitTaskNum < followBackVisitTask.VisitTaskNum) + .ToListAsync(); + + var followVisitGroup = followVisitTaskList.GroupBy(t => t.VisitTaskNum); + + //每个检查批次去判断 是分配还是生成(因为影响哪里有些是取消分配,有些是重阅重置需要重新生成) + foreach (var visitGroup in followVisitGroup) + { + + var visit = await _subjectVisitRepository.Where(x => x.Id == visitGroup.First().SourceSubjectVisitId).Select(x => new + { + x.PDState, + x.IsEnrollmentConfirm, + x.IsUrgent, + }).FirstNotNullAsync(); + + + TaskUrgentType? urgentType = null; + + if (subjectVisitInfo.PDState == PDStateEnum.PDProgress) + { + urgentType = TaskUrgentType.PDProgress; + } + else if (subjectVisitInfo.IsEnrollmentConfirm) + { + urgentType = TaskUrgentType.EnrollmentConfirm; + } + else if (subjectVisitInfo.IsUrgent) + { + urgentType = TaskUrgentType.VisitUrgent; + } + + bool isCanEdit = urgentType == TaskUrgentType.EnrollmentConfirm || urgentType == TaskUrgentType.PDProgress ? false : true; + + //如果后续检查批次已分配有效 就不用处理 + if (visitGroup.Any(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.ArmEnum == Arm.DoubleReadingArm1)) + { + //不做处理 + } + else + { + var arm1 = visitGroup.FirstOrDefault(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.ArmEnum == Arm.DoubleReadingArm1); + + if (arm1 != null) + { + + //有可能仅仅只分配了一个Subject 未分配 那么 + if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null) + { + arm1.IsUrgent = visit.IsUrgent; + arm1.TaskUrgentType = urgentType; + arm1.IsCanEditUrgentState = isCanEdit; + arm1.TaskAllocationState = TaskAllocationState.Allocated; + arm1.AllocateTime = DateTime.Now; + arm1.DoctorUserId = task1.DoctorUserId; + arm1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + } + + } + else + { + var latestTask = visitGroup.Where(t => t.ArmEnum == Arm.DoubleReadingArm1).OrderByDescending(t => t.CreateTime).First(); + + + + + var taskOne = await _visitTaskRepository.AddAsync(new VisitTask() + { + TrialId = trialId, + SubjectId = subjectVisit.SubjectId, + IsUrgent = visit.IsUrgent, + TaskUrgentType = urgentType, + IsCanEditUrgentState = isCanEdit, + ArmEnum = Arm.DoubleReadingArm1,//特殊 + Code = currentMaxCodeInt + 1, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), + ReadingCategory = ReadingCategory.Visit, + + SourceSubjectVisitId = latestTask.SourceSubjectVisitId, + VisitTaskNum = latestTask.VisitTaskNum, + TaskBlindName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(latestTask.VisitTaskNum), + TaskName = latestTask.TaskName, + + BlindSubjectCode = latestTask.BlindSubjectCode, + BlindTrialSiteCode = latestTask.BlindTrialSiteCode, + IsAnalysisCreate = latestTask.IsAnalysisCreate, + IsSelfAnalysis = latestTask.IsSelfAnalysis, + TaskAllocationState = TaskAllocationState.Allocated, + AllocateTime = DateTime.Now, + DoctorUserId = task1.DoctorUserId, + SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget), + TrialReadingCriterionId = latestTask.TrialReadingCriterionId, + IsNeedClinicalDataSign = latestTask.IsNeedClinicalDataSign, + IsClinicalDataSign = latestTask.IsClinicalDataSign + }); + + currentMaxCodeInt = currentMaxCodeInt + 1; + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + } + + } + + //如果后续检查批次已分配有效 就不用处理 + if (visitGroup.Any(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.ArmEnum == Arm.DoubleReadingArm2)) + { + //不做处理 + } + else + { + var arm2 = visitGroup.FirstOrDefault(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.ArmEnum == Arm.DoubleReadingArm2); + if (arm2 != null) + { + //有可能仅仅只分配了一个Subject + if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null) + { + arm2.IsUrgent = visit.IsUrgent; + arm2.TaskUrgentType = urgentType; + arm2.IsCanEditUrgentState = isCanEdit; + arm2.TaskAllocationState = TaskAllocationState.Allocated; + arm2.AllocateTime = DateTime.Now; + arm2.DoctorUserId = task2.DoctorUserId; + arm2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + } + + } + else + { + var latestTask = visitGroup.Where(t => t.ArmEnum == Arm.DoubleReadingArm2).OrderByDescending(t => t.CreateTime).First(); + + var taskTwo = await _visitTaskRepository.AddAsync(new VisitTask() + { + TrialId = trialId, + SubjectId = subjectVisit.SubjectId, + IsUrgent = visit.IsUrgent, + TaskUrgentType = urgentType, + IsCanEditUrgentState = isCanEdit, + + //CheckPassedTime = subjectVisit.CheckPassedTime, + ArmEnum = Arm.DoubleReadingArm2,//特殊 + Code = currentMaxCodeInt + 1, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), + ReadingCategory = ReadingCategory.Visit, + + SourceSubjectVisitId = latestTask.SourceSubjectVisitId, + VisitTaskNum = latestTask.VisitTaskNum, + TaskBlindName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(latestTask.VisitTaskNum), + TaskName = latestTask.TaskName, + + BlindSubjectCode = latestTask.BlindSubjectCode, + BlindTrialSiteCode = latestTask.BlindTrialSiteCode, + IsAnalysisCreate = latestTask.IsAnalysisCreate, + IsSelfAnalysis = latestTask.IsSelfAnalysis, + TaskAllocationState = TaskAllocationState.Allocated, + + AllocateTime = DateTime.Now, + DoctorUserId = task2.DoctorUserId, + SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget), + + TrialReadingCriterionId = latestTask.TrialReadingCriterionId, + IsNeedClinicalDataSign = latestTask.IsNeedClinicalDataSign, + IsClinicalDataSign = latestTask.IsClinicalDataSign + }); + + currentMaxCodeInt = currentMaxCodeInt + 1; + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + } + } + + } + + + } + //无序的时候 生成任务并分配出去 + else + { + var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated; + + if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm1) && task1 != null) + { + task1.TaskAllocationState = defaultState; + //分配给对应Arm的人 + task1.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm1).DoctorUserId; + task1.AllocateTime = DateTime.Now; + + task1.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + } + + if (assignConfigList.Any(t => t.ArmEnum == Arm.DoubleReadingArm2) && task2 != null) + { + task2.TaskAllocationState = defaultState; + task2.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.DoubleReadingArm2).DoctorUserId; + task2.AllocateTime = DateTime.Now; + task2.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + } + + } + + + #endregion + + } + else + //后续检查批次不自动分配,或者配置的医生数量不足,就不进行分配 + { + + } + + } + + + } + + + } + #endregion + + } + else if (trialReadingCriterionConfig.ReadingType == ReadingMethod.Single) + { + + + var singleTask = await _visitTaskRepository.AddAsync(new VisitTask() + { + TrialId = trialId, + SubjectId = subjectVisit.SubjectId, + IsUrgent = subjectVisit.IsUrgent, + TaskBlindName = blindTaskName, + TaskName = subjectVisit.VisitName, + TaskUrgentType = taskUrgentType, + IsCanEditUrgentState = isCanEditUrgentState, + VisitTaskNum = subjectVisit.VisitNum, + //CheckPassedTime = subjectVisit.CheckPassedTime, + ArmEnum = Arm.SingleReadingArm, //特殊 + Code = currentMaxCodeInt + 1, + SourceSubjectVisitId = subjectVisit.Id, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), + ReadingCategory = ReadingCategory.Visit, + + TrialReadingCriterionId = trialReadingCriterionConfig.TrialReadingCriterionId, + IsNeedClinicalDataSign = isNeedClinicalDataSign, + IsClinicalDataSign = isClinicalDataSign + }); + + + currentMaxCodeInt = currentMaxCodeInt + 1; + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + + #region 分配 + + if (isAssignSubjectToDoctor) + { + + if (trialReadingCriterionConfig.TaskAllocateObjEnum == TaskAllocateObj.Subject) + { + + if (trialReadingCriterionConfig.IsFollowVisitAutoAssign) + { + + + //该Subject 之前是否有已分配的 如果改变配置 可能会出现 一个Subject 分配的同一个医生 有的在Arm1 有的在Arm2 + var allocateSubjectArmList = _visitTaskRepository.Where(t => t.SubjectId == subjectVisit.SubjectId && t.TrialId == trialId && t.DoctorUserId != null && t.ArmEnum != Arm.JudgeArm) + .Select(t => new { t.DoctorUserId, t.ArmEnum }).Distinct().ToList(); + + //不是初次分配 + if (allocateSubjectArmList.Count != 0) + { + //if (_taskAllocationRuleRepository.Where(t => t.TrialId == trialId && t.IsEnable).Count() < 2) + //{ + // throw new BusinessValidationFailedException("能参与读片的医生数量必须>=2,自动分配任务中止"); + + //} + + //配置了医生 + if (assignConfigList.Count > 0) + { + + #region 重阅/退回的时候,需要将取消分配的检查批次类型的 任务重新分配 + + if (trialReadingCriterionConfig.IsReadingTaskViewInOrder) + { + //之前有回退到影像上传的检查批次 那么当前检查批次一致性核查通过的时候,当前检查批次生成但是不分配出去(排除失访的) + var beforeBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum < subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync(); + + + if (beforeBackVisitTask == null) + { + #region 检查批次2 PM 回退 基线回退 检查批次2先一致性核查通过,生成检查批次2任务,但是不分配 + + var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated; + + + if (assignConfigList.Any(t => t.ArmEnum == Arm.SingleReadingArm)) + { + singleTask.TaskAllocationState = defaultState; + + singleTask.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.SingleReadingArm).DoctorUserId; + + singleTask.AllocateTime = DateTime.Now; + + singleTask.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + } + + + #endregion + + + var followBackVisitTask = await _visitTaskRepository.Where(t => t.TrialId == trialId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.ReadingCategory == ReadingCategory.Visit && t.SourceSubjectVisit.CheckState != CheckStateEnum.CVPassed && t.SourceSubjectVisit.IsLostVisit == false).OrderBy(t => t.VisitTaskNum).FirstOrDefaultAsync(); + + //存在退回检查批次1 又退回基线 这种情况 生成任务 考虑基线先一致性核查通过,但是检查批次1还未通过时 生成任务 + var followVisitTaskList = await _visitTaskRepository + .Where(t => t.TrialId == trialId && t.SubjectId == subjectVisit.SubjectId && t.VisitTaskNum > subjectVisit.VisitNum && t.SourceSubjectVisit.CheckState == CheckStateEnum.CVPassed && t.ReadingCategory == ReadingCategory.Visit && t.IsAnalysisCreate == false, true) + .WhereIf(followBackVisitTask != null, t => t.VisitTaskNum < followBackVisitTask.VisitTaskNum) + .ToListAsync(); + + var followVisitGroup = followVisitTaskList.GroupBy(t => t.VisitTaskNum); + + //每个检查批次去判断 是分配还是生成 + + + foreach (var visitGroup in followVisitGroup) + { + //如果后续检查批次已分配有效 就不用处理 + if (visitGroup.Any(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.ArmEnum == Arm.SingleReadingArm)) + { + //不做处理 + } + + else + { + var arm = visitGroup.FirstOrDefault(t => t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.DoctorUserId == null && t.ArmEnum == Arm.SingleReadingArm); + if (arm != null) + { + arm.TaskAllocationState = TaskAllocationState.Allocated; + arm.AllocateTime = DateTime.Now; + arm.DoctorUserId = singleTask.DoctorUserId; + arm.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + } + else + { + var latestTask = visitGroup.Where(t => t.ArmEnum == Arm.SingleReadingArm).OrderByDescending(t => t.CreateTime).First(); + + var taskOne = await _visitTaskRepository.AddAsync(new VisitTask() + { + TrialId = trialId, + SubjectId = subjectVisit.SubjectId, + IsUrgent = subjectVisit.IsUrgent, + ArmEnum = Arm.SingleReadingArm,//特殊 + Code = currentMaxCodeInt + 1, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), + ReadingCategory = ReadingCategory.Visit, + TaskUrgentType = latestTask.TaskUrgentType, + SourceSubjectVisitId = latestTask.SourceSubjectVisitId, + VisitTaskNum = latestTask.VisitTaskNum, + TaskBlindName = visitBlindConfig.BlindFollowUpPrefix + " " + visitNumList.IndexOf(latestTask.VisitTaskNum), + TaskName = latestTask.TaskName, + + BlindSubjectCode = latestTask.BlindSubjectCode, + BlindTrialSiteCode = latestTask.BlindTrialSiteCode, + IsAnalysisCreate = latestTask.IsAnalysisCreate, + IsSelfAnalysis = latestTask.IsSelfAnalysis, + TaskAllocationState = TaskAllocationState.Allocated, + AllocateTime = DateTime.Now, + DoctorUserId = singleTask.DoctorUserId, + SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget), + + TrialReadingCriterionId = latestTask.TrialReadingCriterionId, + IsNeedClinicalDataSign = latestTask.IsNeedClinicalDataSign, + IsClinicalDataSign = latestTask.IsClinicalDataSign + + }); + + currentMaxCodeInt = currentMaxCodeInt + 1; + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + } + } + + + } + + } + //之前有回退的 后续检查批次不生成或者分配 当前检查批次生成但是不分配出去 + else + { + + //不用进行额外处理 + } + + + + } + //无序的时候 生成任务并分配出去 + else + { + var defaultState = trialReadingCriterionConfig.FollowVisitAutoAssignDefaultState == TaskAllocateDefaultState.InitAllocated ? TaskAllocationState.InitAllocated : TaskAllocationState.Allocated; + + + if (assignConfigList.Any(t => t.ArmEnum == Arm.SingleReadingArm)) + { + singleTask.TaskAllocationState = defaultState; + + singleTask.DoctorUserId = assignConfigList.FirstOrDefault(t => t.ArmEnum == Arm.SingleReadingArm).DoctorUserId; + + singleTask.AllocateTime = DateTime.Now; + + singleTask.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + } + + + } + + #endregion + + } + } + } + else + { + //后续Subect 不自动分配 不处理 + } + + + + + } + + + } + #endregion + + + } + + + } + + + + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trialId && t.Id == subjectVisit.Id, u => new SubjectVisit() { IsVisitTaskGenerated = true }); + } + + + } + + + break; + + + case GenerateTaskCategory.ReReading: + + var reReadingVisitTask = generateTaskCommand.ReReadingTask; + + Guid? subjectVisitId = Guid.Empty; + + if (reReadingVisitTask.SourceSubjectVisitId == null) + { + subjectVisitId= _readModuleRepository.Where(t=>t.Id==reReadingVisitTask.SouceReadModuleId).Select(t=>t.SubjectVisitId).FirstOrDefault(); + } + else + { + subjectVisitId = reReadingVisitTask.SourceSubjectVisitId; + } + + var subjectVisitUrgentInfo = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Select(x => new + { + x.PDState, + x.IsEnrollmentConfirm, + x.IsUrgent, + }).FirstNotNullAsync(); + + TaskUrgentType? taskUrgent = null; + + if (subjectVisitUrgentInfo.PDState == PDStateEnum.PDProgress) + { + taskUrgent = TaskUrgentType.PDProgress; + } + else if (subjectVisitUrgentInfo.IsEnrollmentConfirm) + { + taskUrgent = TaskUrgentType.EnrollmentConfirm; + } + else if (subjectVisitUrgentInfo.IsUrgent) + { + taskUrgent = TaskUrgentType.VisitUrgent; + } + bool isCanEditUrgent = taskUrgent == TaskUrgentType.EnrollmentConfirm || taskUrgent == TaskUrgentType.PDProgress ? false : true; + var newTask = await _visitTaskRepository.AddAsync(new VisitTask() + { + + TrialId = reReadingVisitTask.TrialId, + SubjectId = reReadingVisitTask.SubjectId, + ArmEnum = reReadingVisitTask.ArmEnum, + TaskName = reReadingVisitTask.TaskName, + TaskBlindName = reReadingVisitTask.TaskBlindName, + + IsUrgent = reReadingVisitTask.IsAnalysisCreate?false: subjectVisitUrgentInfo.IsUrgent, + TaskUrgentType = reReadingVisitTask.IsAnalysisCreate ? null : taskUrgent, + IsCanEditUrgentState = isCanEditUrgent, + VisitTaskNum = reReadingVisitTask.VisitTaskNum, + ReadingCategory = reReadingVisitTask.ReadingCategory, + SourceSubjectVisitId = reReadingVisitTask.SourceSubjectVisitId, + SouceReadModuleId = reReadingVisitTask.SouceReadModuleId, + + IsReReadingCreate = true, + TaskState = TaskState.Effect, + Code = currentMaxCodeInt + 1, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), + + IsAnalysisCreate = reReadingVisitTask.IsAnalysisCreate, + IsSelfAnalysis = reReadingVisitTask.IsSelfAnalysis, + BlindSubjectCode = reReadingVisitTask.BlindSubjectCode, + BlindTrialSiteCode = reReadingVisitTask.BlindTrialSiteCode, + + + TrialReadingCriterionId = reReadingVisitTask.TrialReadingCriterionId, + IsNeedClinicalDataSign = reReadingVisitTask.IsNeedClinicalDataSign, + IsClinicalDataSign = reReadingVisitTask.IsClinicalDataSign, + + // TaskAllocationState = reReadingVisitTask.TaskAllocationState, + // AllocateTime = DateTime.Now, + //DoctorUserId = reReadingVisitTask.DoctorUserId, + + }); + + generateTaskCommand.Action(newTask); + + + if (reReadingVisitTask.ReadingCategory == ReadingCategory.Judge) + { + var judgeRecord = await _readingJudgeInfoRepository.Where(t => t.JudgeTaskId == reReadingVisitTask.Id).FirstOrDefaultAsync(); + + var origenalTaskIdList = new Guid[] { judgeRecord.TaskIdOne, judgeRecord.TaskIdTwo }; + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => origenalTaskIdList.Contains(x.Id), x => new VisitTask() + { + JudgeVisitTaskId = newTask.Id + }); + + await _readingJudgeInfoRepository.AddAsync(new ReadingJudgeInfo() + { + JudgeTaskId = newTask.Id, + SubjectId = judgeRecord.SubjectId, + TrialId = judgeRecord.TrialId, + TaskIdOne = judgeRecord.TaskIdOne, + TaskIdTwo = judgeRecord.TaskIdTwo + }); + } + + + //是否增加任务类别 + + currentMaxCodeInt = currentMaxCodeInt + 1; + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + + //} + + break; + case GenerateTaskCategory.SelfConsistent: + + foreach (var task in generateTaskCommand.GenerataConsistentTaskList) + { + var consistentTask = new VisitTask() + { + TrialId = task.TrialId, + SubjectId = task.SubjectId, + ArmEnum = task.ArmEnum, + TaskName = task.TaskName, + TaskBlindName = task.TaskBlindName, + + VisitTaskNum = task.VisitTaskNum, + ReadingCategory = task.ReadingCategory, + + TaskState = TaskState.Effect, + Code = currentMaxCodeInt + 1, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), + + DoctorUserId = task.DoctorUserId, + TaskAllocationState = TaskAllocationState.Allocated, + AllocateTime = DateTime.Now, + SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget), + + SouceReadModuleId = task.SouceReadModuleId, + SourceSubjectVisitId = task.SourceSubjectVisitId, + + ConsistentAnalysisOriginalTaskId = task.Id, + IsAnalysisCreate = true, + IsSelfAnalysis = true, + + TrialReadingCriterionId = task.TrialReadingCriterionId, + BlindTrialSiteCode = task.BlindTrialSiteCode, + BlindSubjectCode = task.BlindSubjectCode, + IsNeedClinicalDataSign = task.IsNeedClinicalDataSign, + IsClinicalDataSign = task.IsClinicalDataSign + }; + + await _visitTaskRepository.AddAsync(consistentTask); + + currentMaxCodeInt = currentMaxCodeInt + 1; + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + } + + break; + + case GenerateTaskCategory.GroupConsistent: + + foreach (var task in generateTaskCommand.GenerataGroupConsistentTaskList) + { + var consistentTask = new VisitTask() + { + TrialId = task.TrialId, + SubjectId = task.SubjectId, + ArmEnum = task.ArmEnum, + TaskName = task.TaskName, + TaskBlindName = task.TaskBlindName, + + VisitTaskNum = task.VisitTaskNum, + ReadingCategory = task.ReadingCategory, + + TaskState = TaskState.Effect, + Code = currentMaxCodeInt + 1, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), + + SouceReadModuleId = task.SouceReadModuleId, + SourceSubjectVisitId = task.SourceSubjectVisitId, + + DoctorUserId = task.DoctorUserId, + TaskAllocationState = TaskAllocationState.Allocated, + AllocateTime = DateTime.Now, + SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget), + + IsAnalysisCreate = true, + IsSelfAnalysis = false, + + TrialReadingCriterionId = task.TrialReadingCriterionId, + BlindTrialSiteCode = task.BlindTrialSiteCode, + BlindSubjectCode = task.BlindSubjectCode, + IsNeedClinicalDataSign = task.IsNeedClinicalDataSign, + IsClinicalDataSign = task.IsClinicalDataSign + + }; + + await _visitTaskRepository.AddAsync(consistentTask); + + currentMaxCodeInt = currentMaxCodeInt + 1; + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + } + + break; + + case GenerateTaskCategory.Judge: + var firstTask = await _visitTaskRepository.Where(x => generateTaskCommand.JudgeVisitTaskIdList.Contains(x.Id)).FirstOrDefaultAsync(); + + var subjectUser = await _subjectUserRepository.Where(x => x.SubjectId == firstTask.SubjectId && x.ArmEnum == Arm.JudgeArm && x.IsConfirmed && x.TrialReadingCriterionId == firstTask.TrialReadingCriterionId).FirstOrDefaultAsync(); + + VisitTask visitTask = new VisitTask() + { + ArmEnum = Arm.JudgeArm, + Id = NewId.NextGuid(), + SubjectId = firstTask.SubjectId, + ReadingTaskState = ReadingTaskState.WaitReading, + TaskName = firstTask.TaskName, + ReadingCategory = ReadingCategory.Judge, + VisitTaskNum = firstTask.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Judge], + TrialId = firstTask.TrialId, + Code = currentMaxCodeInt + 1, + IsCanEditUrgentState= firstTask.IsCanEditUrgentState, + SourceSubjectVisitId = firstTask.SourceSubjectVisitId, + SouceReadModuleId = firstTask.SouceReadModuleId, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), + TaskState = TaskState.Effect, + TaskBlindName = firstTask.TaskBlindName, + DoctorUserId = subjectUser == null ? null : subjectUser.DoctorUserId, + TaskAllocationState = subjectUser == null ? TaskAllocationState.NotAllocate : TaskAllocationState.Allocated, + AllocateTime = subjectUser == null ? null : DateTime.Now, + SuggesteFinishedTime = subjectUser == null ? null : GetSuggessFinishTime(true, UrgentType.NotUrget), + + TrialReadingCriterionId = firstTask.TrialReadingCriterionId, + + }; + await _visitTaskRepository.AddAsync(visitTask); + currentMaxCodeInt = currentMaxCodeInt + 1; + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => generateTaskCommand.JudgeVisitTaskIdList.Contains(x.Id), x => new VisitTask() + { + JudgeVisitTaskId = visitTask.Id + }); + + await _readingJudgeInfoRepository.AddAsync(new ReadingJudgeInfo() + { + JudgeTaskId = visitTask.Id, + SubjectId = firstTask.SubjectId, + TrialId = firstTask.TrialId, + TaskIdOne = generateTaskCommand.JudgeVisitTaskIdList[0], + TaskIdTwo = generateTaskCommand.JudgeVisitTaskIdList[1] + }); + break; + + //case ReadingCategory.ReadingPeriod: + case GenerateTaskCategory.Global: + + + var originalTaskInfo = await _visitTaskRepository.Where(x => x.Id == generateTaskCommand.OriginalVisitId).Include(x => x.TrialReadingCriterion).FirstNotNullAsync(); + + //var criterionType = await _trialReadingCriterionRepository.Where(t => t.Id == originalTaskInfo.TrialReadingCriterionId).Select(t => t.CriterionType).FirstOrDefaultAsync(); + + // 需要添加全局任务再添加 + if (originalTaskInfo.TrialReadingCriterion.IsGlobalReading) + { + var visitNumList = _subjectVisitRepository.Where(t => t.SubjectId == originalTaskInfo.SubjectId && t.IsLostVisit == false).OrderBy(t => t.VisitNum).Select(t => t.VisitNum).ToList(); + foreach (var item in generateTaskCommand.ReadingGenerataTaskList) + { + item.VisitNum = originalTaskInfo.VisitTaskNum; + var task1 = await _visitTaskRepository.AddAsync(new VisitTask() + { + TrialId = trialId, + SubjectId = item.SubjectId, + IsUrgent = originalTaskInfo.IsUrgent, + VisitTaskNum = item.VisitNum + ReadingCommon.TaskNumDic[ReadingCategory.Global], + TaskName = item.ReadingName, + ArmEnum = originalTaskInfo.ArmEnum,//特殊 + TaskUrgentType= originalTaskInfo.TaskUrgentType, + IsCanEditUrgentState= originalTaskInfo.IsCanEditUrgentState, + TaskUrgentRemake = originalTaskInfo.TaskUrgentRemake, + DoctorUserId = originalTaskInfo.DoctorUserId, + AllocateTime = DateTime.Now, + TaskAllocationState = TaskAllocationState.Allocated, + SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget), + + Code = currentMaxCodeInt + 1, + TaskBlindName = "G-"+ visitNumList.IndexOf(originalTaskInfo.VisitTaskNum), + SouceReadModuleId = item.ReadModuleId, + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), + ReadingCategory = item.ReadingCategory, + + TrialReadingCriterionId = originalTaskInfo.TrialReadingCriterionId, + IsNeedClinicalDataSign = IsNeedClinicalDataSign(ReadingCategory.Global, false, originalTaskInfo.TrialReadingCriterionId, clinicalDataConfirmList), + IsClinicalDataSign = IsClinicalDataSign(ReadingCategory.Global, false, originalTaskInfo.TrialReadingCriterionId, clinicalDataConfirmList, item.ReadModuleId, trialId) + }); + + currentMaxCodeInt = currentMaxCodeInt + 1; + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + } + } + + + break; + case GenerateTaskCategory.Oncology: + + originalTaskInfo = await _visitTaskRepository.Where(x => x.Id == generateTaskCommand.OriginalVisitId).FirstNotNullAsync(); + + //criterionType = await _trialReadingCriterionRepository.Where(t => t.Id == originalTaskInfo.TrialReadingCriterionId).Select(t => t.CriterionType).FirstOrDefaultAsync(); + + foreach (var item in generateTaskCommand.ReadingGenerataTaskList) + { + //需要 根据标准筛选 + var oncologySubjectUser = await _subjectUserRepository.Where(x => x.SubjectId == item.SubjectId && x.ArmEnum == Arm.TumorArm && x.IsConfirmed && x.TrialReadingCriterionId == originalTaskInfo.TrialReadingCriterionId).FirstOrDefaultAsync(); + + item.VisitNum = await _readModuleRepository.Where(x => x.Id == item.ReadModuleId).Select(x => x.SubjectVisit.VisitNum).FirstOrDefaultAsync(); + + + var singleTask = await _visitTaskRepository.AddAsync(new VisitTask() + { + TrialId = trialId, + SubjectId = item.SubjectId, + IsUrgent = originalTaskInfo.IsUrgent, + TaskName = item.ReadingName, + // 原任务是全局任务 加0.03 就好 + VisitTaskNum = originalTaskInfo.VisitTaskNum + 0.03m, + ArmEnum = Arm.TumorArm, //特殊 + Code = currentMaxCodeInt + 1, + SouceReadModuleId = item.ReadModuleId, + TaskBlindName = item.ReadingName, + + DoctorUserId = oncologySubjectUser == null ? null : oncologySubjectUser.DoctorUserId, + AllocateTime = oncologySubjectUser == null ? null : DateTime.Now, + TaskAllocationState = oncologySubjectUser == null ? TaskAllocationState.NotAllocate : TaskAllocationState.Allocated, + SuggesteFinishedTime = oncologySubjectUser == null ? null : GetSuggessFinishTime(true, UrgentType.NotUrget), + + TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), + ReadingCategory = item.ReadingCategory, + + TrialReadingCriterionId = originalTaskInfo.TrialReadingCriterionId, + + IsNeedClinicalDataSign = IsNeedClinicalDataSign(ReadingCategory.Oncology, false, originalTaskInfo.TrialReadingCriterionId, clinicalDataConfirmList), + IsClinicalDataSign = IsClinicalDataSign(ReadingCategory.Oncology, false, originalTaskInfo.TrialReadingCriterionId, clinicalDataConfirmList, item.ReadModuleId, trialId) + + }); + + singleTask.AllocateTime = DateTime.Now; + + currentMaxCodeInt = currentMaxCodeInt + 1; + + _provider.Set($"{trialId}_{StaticData.CacheKey.TaskMaxCode}", currentMaxCodeInt, TimeSpan.FromMinutes(30)); + } + break; + } + + + + await _visitTaskRepository.SaveChangesAsync(); + } + + + } + + + +} diff --git a/IRaCIS.Core.Application/Service/Allocation/VisitTaskService.cs b/IRaCIS.Core.Application/Service/Allocation/VisitTaskService.cs new file mode 100644 index 0000000..dfe5485 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/VisitTaskService.cs @@ -0,0 +1,2932 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 14:10:49 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Infra.EFCore.Common; +using System.Linq.Expressions; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Filter; +using DocumentFormat.OpenXml.Office2010.Word; +using System.Linq.Dynamic.Core; +using System.Linq; +using DocumentFormat.OpenXml.Bibliography; +using Org.BouncyCastle.Crypto; +using IRaCIS.Core.Domain.Share.Reading; + +namespace IRaCIS.Core.Application.Service.Allocation +{ + /// + /// 检查批次读片任务 + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class VisitTaskService : BaseService, IVisitTaskService + { + + private readonly IRepository _visitTaskRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _taskAllocationRuleRepository; + private readonly IRepository _subjectRepository; + private readonly IRepository _subjectUserRepository; + + private readonly IRepository _readModuleRepository; + + private readonly IRepository _visitTaskReReadingRepository; + private readonly IRepository _taskMedicalReviewRepository; + private readonly IRepository _readingTaskQuestionAnswerRepository; + private readonly IRepository _trialReadingCriterionRepository; + + private readonly IRepository _readingClinicalDataReposiotry; + + + public VisitTaskService(IRepository subjectVisitRepository, IRepository visitTaskRepository, IRepository trialRepository, + IRepository subjectRepository, IRepository subjectUserRepository, IRepository taskAllocationRuleRepository, + IRepository readModuleRepository, IRepository visitTaskReReadingRepository, + IRepository taskMedicalReviewRepository, + IRepository readingTaskQuestionAnswerRepository + , IRepository trialReadingCriterionRepository, + IRepository readingClinicalDataReposiotry + ) + { + _readingClinicalDataReposiotry = readingClinicalDataReposiotry; + _taskAllocationRuleRepository = taskAllocationRuleRepository; + _visitTaskRepository = visitTaskRepository; + _trialRepository = trialRepository; + _subjectVisitRepository = subjectVisitRepository; + _subjectRepository = subjectRepository; + _subjectUserRepository = subjectUserRepository; + _readModuleRepository = readModuleRepository; + _visitTaskReReadingRepository = visitTaskReReadingRepository; + _taskMedicalReviewRepository = taskMedicalReviewRepository; + _readingTaskQuestionAnswerRepository = readingTaskQuestionAnswerRepository; + + _trialReadingCriterionRepository = trialReadingCriterionRepository; + } + + /// + /// 设置任务加急 + /// + /// + /// + [HttpPost] + public async Task SetTaskUrgent(SetTaskUrgentInDto inDto) + { + await _visitTaskRepository.UpdatePartialFromQueryAsync(inDto.VisitTaskId, x => new VisitTask() + { + IsUrgent = inDto.IsUrgent, + TaskUrgentType = inDto.TaskUrgentType, + TaskUrgentRemake = inDto.TaskUrgentRemake, + + }); + + return await _visitTaskRepository.SaveChangesAsync(); + } + + public async Task> GetTrialCriterionList(Guid trialId, bool isHaveSigned = true) + { + var list = await _repository.Where(t => t.TrialId == trialId && t.IsConfirm) + .OrderBy(t => t.ShowOrder) + .Select(t => new TrialReadingCriterionDto() { TrialReadingCriterionId = t.Id, TrialReadingCriterionName = t.CriterionName, CriterionType = t.CriterionType, ReadingType = t.ReadingType, ReadingInfoSignTime = t.ReadingInfoSignTime }) + .ToListAsync(); + + if (list.Count == 0) + { + throw new BusinessValidationFailedException("该项目还未确认任何一个阅片标准"); + + } + + + + + return list.AsQueryable().WhereIf(isHaveSigned == true, t => t.ReadingInfoSignTime != null).ToList(); + } + + + /// + /// Subject 任务类型 统计 +分配情况 + /// + /// + [HttpPost] + public async Task<(PageOutput, object?)> GetSubjectAssignAndTaskStatList(SubjectAssignStatQuery querySubjectAssign) + { + var subjectQuery = _subjectRepository.Where(t => t.TrialId == querySubjectAssign.TrialId && t.SubjectVisitTaskList.Any()) + .WhereIf(querySubjectAssign.SiteId != null, t => t.SiteId == querySubjectAssign.SiteId) + .WhereIf(querySubjectAssign.SubjectId != null, t => t.Id == querySubjectAssign.SubjectId) + .WhereIf(querySubjectAssign.DoctorUserId != null, t => t.SubjectDoctorList.Any(t => t.DoctorUserId == querySubjectAssign.DoctorUserId && t.TrialReadingCriterionId == querySubjectAssign.TrialReadingCriterionId)) + .WhereIf(!string.IsNullOrEmpty(querySubjectAssign.SubjectCode), t => t.Code.Contains(querySubjectAssign.SubjectCode)) + + .ProjectTo(_mapper.ConfigurationProvider, new { trialReadingCriterionId = querySubjectAssign.TrialReadingCriterionId }); + + + var pageList = await subjectQuery.ToPagedListAsync(querySubjectAssign.PageIndex, querySubjectAssign.PageSize, string.IsNullOrWhiteSpace(querySubjectAssign.SortField) ? nameof(querySubjectAssign.SubjectId) : querySubjectAssign.SortField, querySubjectAssign.Asc); + + + + var criterionConfig = (await _trialReadingCriterionRepository.Where(x => x.Id == querySubjectAssign.TrialReadingCriterionId).Select(x => new { x.ReadingTool, x.IsReadingTaskViewInOrder, x.ReadingType, x.IsArbitrationReading, x.IsOncologyReading, x.IsGlobalReading }).FirstOrDefaultAsync()).IfNullThrowException(); + + return (pageList, criterionConfig); + } + + /// + /// 一次性分配所有医生 批量分配(添加),后端现在没限制 + /// + /// + /// + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task BatchAssignDoctorToSubject(BatchAssignDoctorToSubjectCommand command) + { + //var inOrder = _trialRepository.Where(t => t.Id == command.TrialId).Select(t => t.IsReadingTaskViewInOrder).FirstOrDefault(); + + //var inOrder = _trialReadingCriterionRepository.Where(t => t.Id == command.TrialReadingCriterionId).Select(t => t.IsReadingTaskViewInOrder).FirstOrDefault(); + + + foreach (var subjectId in command.SubjectIdList) + { + foreach (var doctorArm in command.DoctorArmList) + { + if (!await _subjectUserRepository.Where(t => t.TrialReadingCriterionId == command.TrialReadingCriterionId).AnyAsync(t => t.SubjectId == subjectId && t.DoctorUserId == doctorArm.DoctorUserId && t.ArmEnum == doctorArm.ArmEnum && t.IsConfirmed && t.OrignalSubjectUserId == null)) + { + await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = command.TrialId, TrialReadingCriterionId = command.TrialReadingCriterionId, ArmEnum = doctorArm.ArmEnum, DoctorUserId = doctorArm.DoctorUserId, SubjectId = subjectId, AssignTime = DateTime.Now }); + + //task.SuggesteFinishedTime = task.IsUrgent ? DateTime.Now.AddDays(2) : DateTime.Now.AddDays(7); + + } + + + Expression> updateWhere = t => t.TrialReadingCriterionId == command.TrialReadingCriterionId && t.SubjectId == subjectId && t.ArmEnum == doctorArm.ArmEnum && t.TrialId == command.TrialId && t.TaskAllocationState == TaskAllocationState.NotAllocate && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false; + + //if (inOrder) + //{ + // //针对有序阅片 只分配< 最小的 不是一致性核查通过状态 和不是失访 的检查批次 的任务 + // if (await _subjectVisitRepository.AnyAsync(t => t.SubjectId == subjectId && t.CheckState != CheckStateEnum.CVPassed && t.IsLostVisit == false)) + // { + // var notCheckPassedMinVisitNum = await _subjectVisitRepository.Where(t => t.SubjectId == subjectId && t.CheckState != CheckStateEnum.CVPassed).OrderBy(t => t.VisitNum).Select(t => t.VisitNum).FirstOrDefaultAsync(); + + // updateWhere = updateWhere.And(t => t.VisitTaskNum < notCheckPassedMinVisitNum); + + // } + + //} + + + await _visitTaskRepository + .UpdatePartialFromQueryAsync(updateWhere, + u => new VisitTask() + { + AllocateTime = DateTime.Now, + DoctorUserId = doctorArm.DoctorUserId, + TaskAllocationState = TaskAllocationState.Allocated, + + SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget), + }); + } + } + + await _subjectUserRepository.SaveChangesAsync(); + + + + + + return ResponseOutput.Ok(); + } + + + + /// + /// 阅片人维度 Subject统计表 + /// + /// + /// + /// + public async Task<(List, object)> GetDoctorSubjectStat(Guid trialId, Guid trialReadingCriterionId) + { + var list = await _taskAllocationRuleRepository.Where(t => t.TrialId == trialId) + .Where(t => t.Enroll.EnrollReadingCategoryList.Any(t => t.TrialReadingCriterionId == trialReadingCriterionId)) + .ProjectTo(_mapper.ConfigurationProvider, new { trialReadingCriterionId = trialReadingCriterionId }).ToListAsync(); + + var criterionConfig = (await _trialReadingCriterionRepository.Where(x => x.Id == trialReadingCriterionId).Select(x => new { x.ReadingTool, x.IsReadingTaskViewInOrder, x.ReadingType, x.IsArbitrationReading, x.IsOncologyReading }).FirstOrDefaultAsync()).IfNullThrowException(); + + return (list, criterionConfig); + } + + + /// + /// 获取Subject 分配医生情况 + /// + /// + /// + /// + public async Task<(List, object)> GetSubjectAssignedDoctorList(Guid subjectId, Guid trialReadingCriterionId) + { + var list = await _subjectUserRepository.Where(t => t.SubjectId == subjectId && t.OrignalSubjectUserId == null && t.IsConfirmed && t.TrialReadingCriterionId == trialReadingCriterionId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var criterionConfig = (await _trialReadingCriterionRepository.Where(x => x.Id == trialReadingCriterionId).Select(x => new { x.ReadingTool, x.IsReadingTaskViewInOrder, x.ReadingType, x.IsGlobalReading, x.IsOncologyReading, x.IsArbitrationReading }).FirstOrDefaultAsync()).IfNullThrowException(); + + return (list, criterionConfig); + } + + + /// + /// 取消Subject 分配的医生 + /// + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task CancelSubjectAssignedDoctor(CancelSubjectDoctorCommand cancelCommand) + { + foreach (var command in cancelCommand.CancelList.Where(t => t.IsCancelAssign)) + { + if (await _visitTaskRepository.AnyAsync(t => t.TrialReadingCriterionId == cancelCommand.TrialReadingCriterionId && t.SubjectId == command.SubjectId && t.DoctorUserId == command.DoctorUserId && t.ArmEnum == command.ArmEnum && t.ReadingTaskState != ReadingTaskState.WaitReading)) + { + throw new BusinessValidationFailedException("当前医生已开始做该Subject 该标准的任务,不允许取消分配"); + } + + await _subjectUserRepository.DeleteFromQueryAsync(t => t.Id == command.Id); + + await _visitTaskRepository.UpdatePartialFromQueryAsync(t => t.TrialReadingCriterionId == cancelCommand.TrialReadingCriterionId && t.SubjectId == command.SubjectId && t.DoctorUserId == command.DoctorUserId && t.ArmEnum == command.ArmEnum && t.ReadingTaskState == ReadingTaskState.WaitReading && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false, u => new VisitTask() + { + AllocateTime = null, + DoctorUserId = null, + TaskAllocationState = TaskAllocationState.NotAllocate, + SuggesteFinishedTime = null + }); + } + + var subjectId = cancelCommand.CancelList.First().SubjectId; + + await _repository.AddAsync(new SubjectCanceDoctor() { SubjectId = subjectId, Note = cancelCommand.Note }); + + await _visitTaskRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + + + /// + /// 任务 手动分配 重新分配 确认 取消分配 + /// 分配 + /// + /// + [HttpPost] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AssignSubjectTaskToDoctor(AssignSubjectTaskToDoctorCommand assignSubjectTaskToDoctorCommand) + { + var visitTask = await _visitTaskRepository.FirstOrDefaultAsync(t => t.Id == assignSubjectTaskToDoctorCommand.Id); + + if (assignSubjectTaskToDoctorCommand.TaskOptType == TaskOptType.Assign || assignSubjectTaskToDoctorCommand.TaskOptType == TaskOptType.ReAssign) + { + //手动分配验证规则 + + if (visitTask.SourceSubjectVisitId != null) + { + + if (await _visitTaskRepository.AnyAsync(t => t.SourceSubjectVisitId == visitTask.SourceSubjectVisitId && t.TaskAllocationState == TaskAllocationState.Allocated && t.DoctorUserId == assignSubjectTaskToDoctorCommand.DoctorUserId && t.Id != visitTask.Id)) + { + return ResponseOutput.NotOk("其中一个任务已分配给该医生,不允许分配"); + } + } + else if (visitTask.SouceReadModuleId != null) + { + if (await _visitTaskRepository.AnyAsync(t => t.SouceReadModuleId == visitTask.SouceReadModuleId && t.TaskAllocationState == TaskAllocationState.Allocated && t.DoctorUserId == assignSubjectTaskToDoctorCommand.DoctorUserId && t.Id != visitTask.Id)) + { + return ResponseOutput.NotOk("其中一个任务已分配给该医生,不允许分配"); + } + } + else + { + throw new BusinessValidationFailedException("出现脏数据 任务来源字段没有值"); + } + + //PM 回退了 但是还没生成任务 当前任务编号前有检查批次进行了回退就不允许分配 + if (await _subjectVisitRepository.AnyAsync(t => t.SubjectId == visitTask.SubjectId && t.IsPMBackOrReReading && t.VisitNum <= visitTask.VisitTaskNum)) + { + return ResponseOutput.NotOk("该患者有检查批次进入了退回流程,还未经过一致性核查通过,不允许分配"); + } + + + visitTask.AllocateTime = DateTime.Now; + visitTask.DoctorUserId = assignSubjectTaskToDoctorCommand.DoctorUserId; + visitTask.TaskAllocationState = TaskAllocationState.Allocated; + + visitTask.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + + + + //await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == visitTask.SourceSubjectVisitId, u => new SubjectVisit() { ReadingStatus = ReadingStatusEnum.ImageReading }); + + //await _readModuleRepository.BatchUpdateNoTrackingAsync(t => t.Id == visitTask.SouceReadModuleId, u => new ReadModule() { ReadingStatus = ReadingStatusEnum.ImageReading }); + } + + else if (assignSubjectTaskToDoctorCommand.TaskOptType == TaskOptType.ReAssign) + { + //验证 是不是两个任务都给了同一个医生 + + + + //是否删除配置规则表里的 Subject 医生绑定关系 重新添加绑定关系 + + //是否其该Subject 其他检查批次 绑定的医生 也同时变更? + + + } + + else if (assignSubjectTaskToDoctorCommand.TaskOptType == TaskOptType.Confirm) + { + visitTask.TaskAllocationState = TaskAllocationState.Allocated; + } + else if (assignSubjectTaskToDoctorCommand.TaskOptType == TaskOptType.CancelAssign) + { + visitTask.AllocateTime = null; + visitTask.DoctorUserId = null; + visitTask.TaskAllocationState = TaskAllocationState.NotAllocate; + + //await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == visitTask.SourceSubjectVisitId, u => new SubjectVisit() { ReadingStatus = ReadingStatusEnum.TaskAllocate }); + + //await _readModuleRepository.BatchUpdateNoTrackingAsync(t => t.Id == visitTask.SouceReadModuleId, u => new ReadModule() { ReadingStatus = ReadingStatusEnum.TaskAllocate }); + } + await _visitTaskRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } + + + + + /// + /// 获取手动分配 未分配的Subject列表(IsHaveAssigned 传递false) + /// + /// + [HttpPost] + public async Task> GetSubjectAssignList(SubjectAssignQuery querySubjectAssign) + { + var subjectQuery = _subjectRepository.Where(t => t.TrialId == querySubjectAssign.TrialId) + .WhereIf(querySubjectAssign.IsJudgeDoctor == false, t => t.SubjectVisitTaskList.Where(t => t.ArmEnum != Arm.JudgeArm).Any()) + .WhereIf(querySubjectAssign.IsJudgeDoctor, t => t.SubjectVisitTaskList.Where(t => t.ArmEnum == Arm.JudgeArm).Any()) + + .WhereIf(querySubjectAssign.SiteId != null, t => t.SiteId == querySubjectAssign.SiteId) + .WhereIf(querySubjectAssign.SubjectId != null, t => t.Id == querySubjectAssign.SubjectId) + + .WhereIf(querySubjectAssign.IsHaveAssigned != null && querySubjectAssign.IsHaveAssigned == true && querySubjectAssign.IsJudgeDoctor == false, t => t.SubjectDoctorList.Where(t => t.ArmEnum != Arm.JudgeArm).Any()) + .WhereIf(querySubjectAssign.IsHaveAssigned != null && querySubjectAssign.IsHaveAssigned == true && querySubjectAssign.IsJudgeDoctor, t => t.SubjectDoctorList.Where(t => t.ArmEnum == Arm.JudgeArm).Any()) + .WhereIf(querySubjectAssign.IsHaveAssigned != null && querySubjectAssign.IsHaveAssigned == false && querySubjectAssign.IsJudgeDoctor == false, t => !t.SubjectDoctorList.Where(t => t.ArmEnum != Arm.JudgeArm).Any()) + .WhereIf(querySubjectAssign.IsHaveAssigned != null && querySubjectAssign.IsHaveAssigned == false && querySubjectAssign.IsJudgeDoctor, t => !t.SubjectDoctorList.Where(t => t.ArmEnum == Arm.JudgeArm).Any()) + + + .WhereIf(querySubjectAssign.IsAssignConfirmed != null && querySubjectAssign.IsAssignConfirmed == true && querySubjectAssign.IsJudgeDoctor == false, t => t.SubjectDoctorList.Where(t => t.ArmEnum != Arm.JudgeArm).All(u => u.IsConfirmed)) + .WhereIf(querySubjectAssign.IsAssignConfirmed != null && querySubjectAssign.IsAssignConfirmed == true && querySubjectAssign.IsJudgeDoctor, t => t.SubjectDoctorList.Where(t => t.ArmEnum == Arm.JudgeArm).All(u => u.IsConfirmed)) + + .WhereIf(querySubjectAssign.IsAssignConfirmed != null && querySubjectAssign.IsAssignConfirmed == false && querySubjectAssign.IsJudgeDoctor == false, t => t.SubjectDoctorList.Where(t => t.ArmEnum != Arm.JudgeArm).Any(u => u.IsConfirmed == false)) + .WhereIf(querySubjectAssign.IsAssignConfirmed != null && querySubjectAssign.IsAssignConfirmed == false && querySubjectAssign.IsJudgeDoctor, t => t.SubjectDoctorList.Where(t => t.ArmEnum == Arm.JudgeArm).Any(u => u.IsConfirmed == false)) + + .WhereIf(querySubjectAssign.DoctorUserId != null && querySubjectAssign.IsJudgeDoctor == false, t => t.SubjectDoctorList.Where(t => t.ArmEnum != Arm.JudgeArm).Any(t => t.DoctorUserId == querySubjectAssign.DoctorUserId)) + .WhereIf(querySubjectAssign.DoctorUserId != null && querySubjectAssign.IsJudgeDoctor, t => t.SubjectDoctorList.Where(t => t.ArmEnum == Arm.JudgeArm).Any(t => t.DoctorUserId == querySubjectAssign.DoctorUserId)) + .ProjectTo(_mapper.ConfigurationProvider, new { isJudgeDoctor = querySubjectAssign.IsJudgeDoctor }); + + var pageList = await subjectQuery.ToPagedListAsync(querySubjectAssign.PageIndex, querySubjectAssign.PageSize, string.IsNullOrWhiteSpace(querySubjectAssign.SortField) ? nameof(querySubjectAssign.SubjectId) : querySubjectAssign.SortField, querySubjectAssign.Asc); + + return pageList; + } + + + /// + /// 批量为 多个Subject 分配医生 手动分配 IsReAssign 为true 批量删除 重新分配 + /// + /// + /// + [HttpPost] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AssignSubjectDoctor(AssginSubjectDoctorCommand assginSubjectDoctorCommand) + { + var trialId = assginSubjectDoctorCommand.TrialId; + var doctorUserIdList = assginSubjectDoctorCommand.DoctorUserIdArmList.Select(t => t.DoctorUserId).ToList(); + + if (assginSubjectDoctorCommand.IsJudgeDoctor) + { + if (assginSubjectDoctorCommand.IsReAssign) + { + + //if (await _visitTaskRepository.AnyAsync(t => assginSubjectDoctorCommand.SubjectIdList.Contains(t.SubjectId) && t.DoctorUserId != null && t.ArmEnum == Arm.JudgeArm)) + //{ + // throw new BusinessValidationFailedException("有Subject任务已应用,不允许重新分配"); + //} + + + await _subjectUserRepository.BatchDeleteNoTrackingAsync(t => doctorUserIdList.Contains(t.DoctorUserId) && assginSubjectDoctorCommand.SubjectIdList.Contains(t.SubjectId) && t.ArmEnum == Arm.JudgeArm); + } + + + } + else + { + if (assginSubjectDoctorCommand.IsReAssign) + { + + //if (await _visitTaskRepository.AnyAsync(t => assginSubjectDoctorCommand.SubjectIdList.Contains(t.SubjectId) && t.DoctorUserId != null && t.ArmEnum != Arm.JudgeArm)) + //{ + // throw new BusinessValidationFailedException("有Subject任务已应用,不允许重新分配"); + //} + + + await _subjectUserRepository.BatchDeleteNoTrackingAsync(t => doctorUserIdList.Contains(t.DoctorUserId) && assginSubjectDoctorCommand.SubjectIdList.Contains(t.SubjectId) && t.ArmEnum != Arm.JudgeArm); + } + + + + } + foreach (var subjectId in assginSubjectDoctorCommand.SubjectIdList) + { + foreach (var doctorUserId in doctorUserIdList) + { + + var armEnum = assginSubjectDoctorCommand.DoctorUserIdArmList.Where(t => t.DoctorUserId == doctorUserId).First().ArmEnum; + + + if (await _subjectUserRepository.AnyAsync(t => t.TrialId == trialId && t.SubjectId == subjectId && t.DoctorUserId == doctorUserId && t.ArmEnum != armEnum && t.OrignalSubjectUserId == null)) + { + throw new BusinessValidationFailedException("有Subject 在其他Arm组已有该医生,不允许在新的组添加该医生"); + } + + if (await _subjectUserRepository.AnyAsync(t => t.TrialId == trialId && t.SubjectId == subjectId && t.DoctorUserId == doctorUserId && t.ArmEnum == armEnum && t.OrignalSubjectUserId == null)) + { + throw new BusinessValidationFailedException("有Subject 已有该Arm组的医生,不允许继续分配,请刷新页面,确认页面数据是否过期"); + } + else + { + await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = trialId, SubjectId = subjectId, DoctorUserId = doctorUserId, ArmEnum = armEnum, AssignTime = DateTime.Now }); + + } + + } + + } + + + await _subjectUserRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + /// + /// 批量取消Subject 分配的医生 + /// + /// 数量 + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task CancelSubjectAssignDoctor(CancelSubjectAssignCommand cancelSubjectAssignCommand) + { + if (cancelSubjectAssignCommand.IsJudgeDoctor) + { + foreach (var subjectId in cancelSubjectAssignCommand.SubjectIdList) + { + if (await _visitTaskRepository.AnyAsync(t => t.SubjectId == subjectId && t.DoctorUserId != null && t.ArmEnum == Arm.JudgeArm)) + { + throw new BusinessValidationFailedException("有Subject任务已应用,不允许取消分配"); + } + + await _subjectUserRepository.DeleteFromQueryAsync(t => t.SubjectId == subjectId && t.ArmEnum == Arm.JudgeArm); + } + } + else + { + foreach (var subjectId in cancelSubjectAssignCommand.SubjectIdList) + { + if (await _visitTaskRepository.AnyAsync(t => t.SubjectId == subjectId && t.DoctorUserId != null && t.ArmEnum != Arm.JudgeArm)) + { + throw new BusinessValidationFailedException("有Subject任务已应用,不允许取消分配"); + } + + await _subjectUserRepository.DeleteFromQueryAsync(t => t.SubjectId == subjectId && t.ArmEnum != Arm.JudgeArm); + } + } + + await _subjectUserRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + /// + /// 手动分配确认 绑定该Subject的已存在的任务给医生 + /// + /// + /// + [HttpPost] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ManualAssignDoctorApplyTask(AssignConfirmCommand assignConfirmCommand) + { + var trialId = assignConfirmCommand.TrialId; + + //获取项目配置 判断应该分配几个医生 + //var trialConfig = (await _trialRepository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.ReadingType, t.IsFollowVisitAutoAssign, t.IsFollowGlobalVisitAutoAssign, t.FollowGlobalVisitAutoAssignDefaultState, t.TaskAllocateObjEnum }).FirstOrDefaultAsync()).IfNullThrowException(); + + //需要确认的Subject + var subjectIdList = assignConfirmCommand.SubjectDoctorUserList.Select(t => t.SubjectId).ToList(); + + //将关系确认 + if (assignConfirmCommand.SubjectDoctorUserList.Count == 0) + { + await _subjectUserRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trialId && t.IsConfirmed == false && t.OrignalSubjectUserId == null, u => new SubjectUser() { IsConfirmed = true }); + } + else + { + await _subjectUserRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trialId && t.IsConfirmed == false && t.OrignalSubjectUserId == null + && subjectIdList.Contains(t.SubjectId), u => new SubjectUser() { IsConfirmed = true }); + } + + + var taskList = _visitTaskRepository.Where(t => t.TrialId == assignConfirmCommand.TrialId && t.DoctorUserId == null, true) + .WhereIf(subjectIdList.Count() > 0 && assignConfirmCommand.IsJudgeDoctor == false, t => subjectIdList.Contains(t.SubjectId) && t.Subject.SubjectDoctorList.Where(t => t.ArmEnum != Arm.JudgeArm).Any()) + .WhereIf(subjectIdList.Count() > 0 && assignConfirmCommand.IsJudgeDoctor, t => subjectIdList.Contains(t.SubjectId) && t.Subject.SubjectDoctorList.Where(t => t.ArmEnum == Arm.JudgeArm).Any()) + //过滤掉 那些回退的subject + .Where(t => !t.Subject.SubjectVisitList.Any(t => t.IsPMBackOrReReading)) + + .ToList(); + + + foreach (var subjectTaskGroup in taskList.GroupBy(t => t.SubjectId)) + { + var subjectId = subjectTaskGroup.Key; + + + + //如果数据为空 那么就是确认所有已分配的 + List subjectDoctorIdArmList = new List(); + + if (assignConfirmCommand.SubjectDoctorUserList.Count == 0) + { + subjectDoctorIdArmList = _subjectUserRepository.Where(t => t.SubjectId == subjectId && t.OrignalSubjectUserId == null).Select(t => new DoctorArm() { DoctorUserId = t.DoctorUserId, ArmEnum = t.ArmEnum }).ToList(); + } + else + { + subjectDoctorIdArmList = assignConfirmCommand.SubjectDoctorUserList.Where(t => t.SubjectId == subjectId).First().DoctorUserIdArmList; + } + + + foreach (var task in subjectTaskGroup.OrderBy(t => t.ArmEnum).ToList()) + { + + var subjectDoctorArm = subjectDoctorIdArmList.Where(t => t.ArmEnum == task.ArmEnum).FirstOrDefault(); + + if (subjectDoctorArm != null) + { + task.DoctorUserId = subjectDoctorArm.DoctorUserId; + task.AllocateTime = DateTime.Now; + task.TaskAllocationState = TaskAllocationState.Allocated; + + task.SuggesteFinishedTime = /*task.IsUrgent ? DateTime.Now.AddDays(2) : DateTime.Now.AddDays(7)*/ GetSuggessFinishTime(true, UrgentType.NotUrget); + + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == task.SourceSubjectVisitId, u => new SubjectVisit() { ReadingStatus = ReadingStatusEnum.ImageReading }); + + await _readModuleRepository.BatchUpdateNoTrackingAsync(t => t.Id == task.SouceReadModuleId, u => new ReadModule() { ReadingStatus = ReadingStatusEnum.ImageReading }); + + + } + else + { + throw new BusinessValidationFailedException("在配置表中未找到配置的医生,无法应用绑定,请核对数据"); + } + + } + } + + + + await _visitTaskRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + + + + + /// + /// 检查批次任务 + /// + /// + /// + /// + [HttpPost] + public async Task> GetVisitTaskList(VisitTaskQuery queryVisitTask, [FromServices] IVisitTaskHelpeService _visitTaskCommonService) + { + //以前检查批次未产生任务的,在查询这里要产生 + var svIdList = await _subjectVisitRepository.Where(t => t.TrialId == queryVisitTask.TrialId && t.CheckState == CheckStateEnum.CVPassed && t.IsVisitTaskGenerated == false).Select(t => t.Id).ToListAsync(); + + //之前没有生成任务的逻辑 但是现在加了,任务要自动生成 + await _visitTaskCommonService.GenerateVisitTaskAsync(queryVisitTask.TrialId, svIdList); + + + var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == queryVisitTask.TrialId && t.IsAnalysisCreate == false) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.ReadingCategory == null, t => t.ReadingCategory != ReadingCategory.Judge) + + .WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + .WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId) + .WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) + .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) + + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => (t.Subject.Code.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider); + + var defalutSortArray = new string[] { nameof(VisitTask.IsUrgent) + " desc", nameof(VisitTask.SubjectId), nameof(VisitTask.VisitTaskNum) }; + + var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray); + + //var trialTaskConfig = _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + return pageList; + } + + + + /// + /// 裁判任务 + /// + /// + /// + [HttpPost] + public async Task/*, object)*/> GetJudgeVisitTaskList(VisitTaskQuery queryVisitTask) + { + + var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == queryVisitTask.TrialId && t.IsAnalysisCreate == false) + .Where(t => t.ReadingCategory == ReadingCategory.Judge) + + .WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId) + .WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) + .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => (t.Subject.Code.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider); + + var defalutSortArray = new string[] { nameof(VisitTask.IsUrgent) + " desc", nameof(VisitTask.SubjectId), nameof(VisitTaskView.VisitTaskNum) }; + + var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray); + + return pageList; + //var trialTaskConfig = _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + + //return (pageList, trialTaskConfig); + } + + + + + + /// + /// PM阅片跟踪 + /// + /// + /// + [HttpPost] + public async Task<(PageOutput, object?)> GetReadingTaskList(VisitTaskQuery queryVisitTask) + { + var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == queryVisitTask.TrialId && t.IsAnalysisCreate == false) + //.Where(t => t.IsAnalysisCreate == false && t.DoctorUserId != null) + + .WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + .WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId) + .WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) + .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + .WhereIf(queryVisitTask.ReReadingApplyState != null, t => t.ReReadingApplyState == queryVisitTask.ReReadingApplyState) + + .WhereIf(queryVisitTask.CompleteClinicalDataEnum == CompleteClinicalDataEnum.Complete, t => t.IsClinicalDataSign && t.IsNeedClinicalDataSign == true) + .WhereIf(queryVisitTask.CompleteClinicalDataEnum == CompleteClinicalDataEnum.NotComplete, t => t.IsClinicalDataSign == false && t.IsNeedClinicalDataSign == true) + .WhereIf(queryVisitTask.CompleteClinicalDataEnum == CompleteClinicalDataEnum.NA, t => t.IsNeedClinicalDataSign == false) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => (t.Subject.Code.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime >= queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime <= queryVisitTask.EndAllocateDate) + .WhereIf(queryVisitTask.TaskCode != null, t => t.TaskCode.Contains(queryVisitTask.TaskCode) ) + .WhereIf(queryVisitTask.BeginSignTime != null, t => t.SignTime >= queryVisitTask.BeginSignTime) + .WhereIf(queryVisitTask.EndSignTime != null, t => t.SignTime <= queryVisitTask.EndSignTime) + .ProjectTo(_mapper.ConfigurationProvider); + + var defalutSortArray = new string[] { nameof(VisitTask.IsUrgent) + " desc", nameof(VisitTask.SubjectId), nameof(VisitTask.VisitTaskNum) }; + + var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray); + + var trialTaskConfig = _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + + return (pageList,trialTaskConfig); + } + + + + + /// + /// PM 重阅追踪 + /// + /// + /// + [HttpPost] + public async Task> GetReReadingTaskList(VisitTaskQuery queryVisitTask) + { + + + var visitTaskQueryable = _visitTaskReReadingRepository + .Where(t => t.OriginalReReadingTask.TrialId == queryVisitTask.TrialId /*&& t.OriginalReReadingTask.IsAnalysisCreate == false*/) + .WhereIf(queryVisitTask.RootReReadingTaskId != null, t => t.RootReReadingTaskId == queryVisitTask.RootReReadingTaskId || t.OriginalReReadingTaskId == queryVisitTask.RootReReadingTaskId) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskCode), t => t.OriginalReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!) || t.RootReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!)) + .WhereIf(queryVisitTask.SiteId != null, t => t.OriginalReReadingTask.Subject.SiteId == queryVisitTask.SiteId) + .WhereIf(queryVisitTask.TaskState != null, t => t.OriginalReReadingTask.TaskState == queryVisitTask.TaskState) + .WhereIf(queryVisitTask.ReReadingApplyState != null, t => t.OriginalReReadingTask.ReReadingApplyState == queryVisitTask.ReReadingApplyState) + .WhereIf(queryVisitTask.RequestReReadingType != null, t => t.RequestReReadingType == queryVisitTask.RequestReReadingType) + .WhereIf(queryVisitTask.SubjectId != null, t => t.OriginalReReadingTask.SubjectId == queryVisitTask.SubjectId) + .WhereIf(queryVisitTask.IsUrgent != null, t => t.OriginalReReadingTask.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.OriginalReReadingTask.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.OriginalReReadingTask.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.OriginalReReadingTask.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.OriginalReReadingTask.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.OriginalReReadingTask.ReadingCategory == queryVisitTask.ReadingCategory) + + .WhereIf(queryVisitTask.RequestReReadingResultEnum != null, t => t.RequestReReadingResultEnum == queryVisitTask.RequestReReadingResultEnum) + + + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.OriginalReReadingTask.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate) || (t.OriginalReReadingTask.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.OriginalReReadingTask.TaskName.Contains(queryVisitTask.TaskName) || t.OriginalReReadingTask.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => (t.OriginalReReadingTask.Subject.Code.Contains(queryVisitTask.SubjectCode) && t.OriginalReReadingTask.IsAnalysisCreate == false) || (t.OriginalReReadingTask.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.OriginalReReadingTask.IsAnalysisCreate)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider); + + //var defalutSortArray = new string[] { nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.IsUrgent) + " desc", nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.SubjectId), nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.VisitTaskNum) }; + + var defalutSortArray = new string[] { nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.TaskState), + nameof(ReReadingTaskView.RequestReReadingResultEnum) , + nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.IsUrgent) + " desc", + nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.SubjectId),nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.VisitTaskNum) }; + + + + var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray); + + return pageList; + } + + + /// + /// 获取IR 重阅影像阅片列表 + /// + /// + /// + [HttpPost] + public async Task> GetIRReReadingTaskList(VisitTaskQuery queryVisitTask) + { + + var visitTaskQueryable = _visitTaskReReadingRepository + .Where(t => t.RequestReReadingType == RequestReReadingType.DocotorApply) + .Where(t => t.OriginalReReadingTask.DoctorUserId == _userInfo.Id) + .Where(t => t.OriginalReReadingTask.TrialId == queryVisitTask.TrialId) + + .WhereIf(queryVisitTask.RootReReadingTaskId != null, t => t.RootReReadingTaskId == queryVisitTask.RootReReadingTaskId || t.OriginalReReadingTaskId == queryVisitTask.RootReReadingTaskId) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskCode), t => t.OriginalReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!) || t.RootReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!)) + .WhereIf(queryVisitTask.TaskState != null, t => t.OriginalReReadingTask.TaskState == queryVisitTask.TaskState) + .WhereIf(queryVisitTask.ReReadingApplyState != null, t => t.OriginalReReadingTask.ReReadingApplyState == queryVisitTask.ReReadingApplyState) + .WhereIf(queryVisitTask.SiteId != null, t => t.OriginalReReadingTask.Subject.SiteId == queryVisitTask.SiteId) + .WhereIf(queryVisitTask.SubjectId != null, t => t.OriginalReReadingTask.SubjectId == queryVisitTask.SubjectId) + .WhereIf(queryVisitTask.IsUrgent != null, t => t.OriginalReReadingTask.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.OriginalReReadingTask.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.OriginalReReadingTask.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.OriginalReReadingTask.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.OriginalReReadingTask.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + + + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.OriginalReReadingTask.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate) || (t.OriginalReReadingTask.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.OriginalReReadingTask.TaskName.Contains(queryVisitTask.TaskName) || t.NewReReadingTask.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => (t.OriginalReReadingTask.Subject.Code.Contains(queryVisitTask.SubjectCode) && t.OriginalReReadingTask.IsAnalysisCreate == false) || (t.OriginalReReadingTask.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.OriginalReReadingTask.IsAnalysisCreate)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider); + + + + var defalutSortArray = new string[] { nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.IsUrgent) + " desc", nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.SubjectId), nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.VisitTaskNum) }; + + var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray); + + return pageList; + } + + + //SPM 能看到项目组申请记录 + [HttpPost] + public async Task> GetSPMReReadingTaskList(VisitTaskQuery queryVisitTask) + { + var visitTaskQueryable = _visitTaskReReadingRepository.Where(t => t.RequestReReadingType == RequestReReadingType.TrialGroupApply && t.OriginalReReadingTask.IsAnalysisCreate == false) + .Where(t => t.OriginalReReadingTask.TrialId == queryVisitTask.TrialId) + .WhereIf(queryVisitTask.RootReReadingTaskId != null, t => t.RootReReadingTaskId == queryVisitTask.RootReReadingTaskId || t.OriginalReReadingTaskId == queryVisitTask.RootReReadingTaskId) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskCode), t => t.OriginalReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!) || t.RootReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!)) + .WhereIf(queryVisitTask.TaskState != null, t => t.OriginalReReadingTask.TaskState == queryVisitTask.TaskState) + .WhereIf(queryVisitTask.SiteId != null, t => t.OriginalReReadingTask.Subject.SiteId == queryVisitTask.SiteId) + .WhereIf(queryVisitTask.SubjectId != null, t => t.OriginalReReadingTask.SubjectId == queryVisitTask.SubjectId) + .WhereIf(queryVisitTask.IsUrgent != null, t => t.OriginalReReadingTask.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.OriginalReReadingTask.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.OriginalReReadingTask.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.OriginalReReadingTask.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.OriginalReReadingTask.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.OriginalReReadingTask.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate) || (t.OriginalReReadingTask.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate == false)) + + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.OriginalReReadingTask.TaskName.Contains(queryVisitTask.TaskName) || t.NewReReadingTask.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => (t.OriginalReReadingTask.Subject.Code.Contains(queryVisitTask.SubjectCode) && t.OriginalReReadingTask.IsAnalysisCreate == false) || (t.OriginalReReadingTask.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.OriginalReReadingTask.IsAnalysisCreate)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider); + + var defalutSortArray = new string[] { nameof(ReReadingTaskView.RequestReReadingTime) }; + + //var defalutSortArray = new string[] { nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.IsUrgent) + " desc", nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.SubjectId), nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.VisitTaskNum) }; + + var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray); + + return pageList; + } + + + + /// + /// IR 待阅片任务列表 + /// + /// + [HttpPost] + public async Task<(PageOutput, object)> GetIRUnReadSubjectTaskList(IRUnReadSubjectQuery iRUnReadSubjectQuery) + { + var trialId = iRUnReadSubjectQuery.TrialId; + + var trialReadingCriterionId = iRUnReadSubjectQuery.TrialReadingCriterionId; + + var criterionConfig = await _trialReadingCriterionRepository.Where(x => x.Id == iRUnReadSubjectQuery.TrialReadingCriterionId).FirstNotNullAsync(); + + var readingTool = criterionConfig.ReadingTool; + var isReadingTaskViewInOrder = criterionConfig.IsReadingTaskViewInOrder; + + var visitTaskListInfo = await GetOrderReadingIQueryable(new GetOrderReadingIQueryableInDto() + { + TrialId = trialId, + TrialReadingCriterionId = trialReadingCriterionId, + SubjectCode = iRUnReadSubjectQuery.SubjectCode, + Page = new PageInput() + { + PageIndex = iRUnReadSubjectQuery.PageIndex, + PageSize = iRUnReadSubjectQuery.PageSize, + Asc = iRUnReadSubjectQuery.Asc, + SortField = iRUnReadSubjectQuery.SortField, + + } + + }); + + + var totalCount = visitTaskListInfo.Item1; + var currentPageData = visitTaskListInfo.Item2; + + + + var result = new PageOutput() + { + PageSize = iRUnReadSubjectQuery.PageSize, + PageIndex = iRUnReadSubjectQuery.PageIndex, + TotalCount = totalCount, + CurrentPageData = currentPageData, + }; + + + #region 按照Subject 维度 + if (isReadingTaskViewInOrder) + { + + + // 封装的方法有问题 + //var result = await visitQuery.ToPagedListAsync(iRUnReadSubjectQuery.PageIndex, iRUnReadSubjectQuery.PageSize, String.IsNullOrEmpty(iRUnReadSubjectQuery.SortField) ? nameof(IRUnReadSubjectView.SubjectId) : iRUnReadSubjectQuery.SortField, iRUnReadSubjectQuery.Asc); + return (result, new + { + RandomReadInfo = new IRUnReadOutDto(), + IsReadingTaskViewInOrder = isReadingTaskViewInOrder, + ReadingTool = readingTool, + IseCRFShowInDicomReading = criterionConfig.IseCRFShowInDicomReading, + IsReadingShowSubjectInfo = criterionConfig.IsReadingShowSubjectInfo, + IsReadingShowPreviousResults = criterionConfig.IsReadingShowPreviousResults, + DigitPlaces = criterionConfig.DigitPlaces, + CriterionType = criterionConfig.CriterionType, + }); + } + else + { + + var taskQuery = _visitTaskRepository.Where(x => x.TrialId == iRUnReadSubjectQuery.TrialId && x.DoctorUserId == _userInfo.Id && x.TaskState == TaskState.Effect && x.TrialReadingCriterionId == trialReadingCriterionId) + // .Where(x=>x.Subject.ClinicalDataList.Any(c => c.IsSign && (c.ReadingId == x.SouceReadModuleId || c.ReadingId == x.SourceSubjectVisitId))) + .Where(x => !x.Subject.IsDeleted); + + IRUnReadOutDto iRUnReadOut = new IRUnReadOutDto() + { + FinishJudgeTaskCount = await taskQuery.Where(x => x.ReadingCategory == ReadingCategory.Judge && x.ReadingTaskState == ReadingTaskState.HaveSigned).CountAsync(), + FinishTaskCount = await taskQuery.Where(x => x.ReadingCategory != ReadingCategory.Judge && x.ReadingTaskState == ReadingTaskState.HaveSigned).CountAsync(), + SuggesteFinishedTime = await taskQuery.Where(x => x.ReadingTaskState != ReadingTaskState.HaveSigned).MaxAsync(x => x.SuggesteFinishedTime), + UnReadJudgeTaskCount = await taskQuery.Where(x => x.ReadingCategory == ReadingCategory.Judge && x.ReadingTaskState != ReadingTaskState.HaveSigned).CountAsync(), + UnReadTaskCount = await taskQuery.Where(x => x.ReadingCategory != ReadingCategory.Judge && x.ReadingTaskState != ReadingTaskState.HaveSigned).CountAsync(), + }; + + + + + + return (result, new + { + IsReadingTaskViewInOrder = isReadingTaskViewInOrder, + RandomReadInfo = iRUnReadOut, + ReadingTool = readingTool, + IseCRFShowInDicomReading = criterionConfig.IseCRFShowInDicomReading, + IsReadingShowSubjectInfo = criterionConfig.IsReadingShowSubjectInfo, + IsReadingShowPreviousResults = criterionConfig.IsReadingShowPreviousResults, + DigitPlaces = criterionConfig.DigitPlaces, + CriterionType = criterionConfig.CriterionType, + }); + + } + + + + #endregion + + + } + + /// + /// 获取有序阅片IQuery对象 + /// + /// + + /// + public async Task<(int, List)> GetOrderReadingIQueryable(GetOrderReadingIQueryableInDto inDto) + { + var trialReadingCriterionId = inDto.TrialReadingCriterionId; + + //Expression> visitTaskLambda = x => x.DoctorUserId == _userInfo.Id && x.TaskState == TaskState.Effect && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId; + + + var visitQuery = _visitTaskRepository.Where(x => x.TrialId == inDto.TrialId && x.DoctorUserId == _userInfo.Id + && x.TaskState == TaskState.Effect /*&& x.TrialReadingCriterionId== inDto.TrialReadingCriterionId*/) + + //.Where(t => t.VisitTaskNum < t.Subject.SubjectVisitList.Where(t => t.CheckState != CheckStateEnum.CVPassed).Min(t => t.VisitNum) ) + .Where(t => t.Subject.SubjectVisitList.Any(t => t.CheckState != CheckStateEnum.CVPassed) ? t.VisitTaskNum < t.Subject.SubjectVisitList.Where(t => t.CheckState != CheckStateEnum.CVPassed).Min(t => t.VisitNum) : true) + //满足前序检查批次不存在 需要签署但是未签署 sql 相当复杂 同时想查询所有未读的统计数字 就无法统计 byzhouhang + //但是加字段 IsFrontTaskNeedSignButNotSign 那么签名临床数据的时候,要对该subject 该标准的有效的任务 这个字段需要在签名的时候维护 采取这种方式 统计数字灵活 + //.Where(t => t.Subject.SubjectVisitTaskList.AsQueryable().Where(visitTaskLambda).Any(c => c.IsNeedClinicalDataSign == true && c.IsClinicalDataSign == false && c.VisitTaskNum < t.VisitTaskNum)) + .WhereIf(!string.IsNullOrEmpty(inDto.SubjectCode), t => (t.Subject.Code.Contains(inDto.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(inDto.SubjectCode) && t.IsAnalysisCreate)); + + + var visitGroupQuery = visitQuery.GroupBy(x => new { x.SubjectId, x.Subject.Code, x.BlindSubjectCode }); + + + + var visitTaskQuery = visitGroupQuery.Select(x => new IRUnReadSubjectView() + { + SubjectId = x.Key.SubjectId, + SubjectCode = x.Key.BlindSubjectCode == string.Empty ? x.Key.Code : x.Key.BlindSubjectCode, + + SuggesteFinishedTime = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).Min(x => x.SuggesteFinishedTime), + + //未读任务量 + UnReadTaskCount = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).Count(), + + //未读 里可读任务量 + UnReadCanReadTaskCount = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned && y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true) + //不能对包含聚合或子查询的表达式执行聚合函数 + //&& !x.Any(t => t.ReadingTaskState != ReadingTaskState.HaveSigned && t.IsNeedClinicalDataSign == true && t.IsClinicalDataSign == false && t.VisitTaskNum y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState == ReadingTaskState.HaveSigned).Count(), + + ExistReadingApply = x.Any(y => y.ReReadingApplyState == ReReadingApplyState.DocotorHaveApplyed || y.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed), + + //查出所有未读的 未读的可读的 在这个列表基础上 过滤下 y.IsFrontTaskNeedSignButNotSign==false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true) 这样容易排错 确认这三个字段是否维护有误 + UnReadTaskList = x.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned).OrderBy(x => x.VisitTaskNum) + .Select(u => new IRUnreadTaskView() + { + Id = u.Id, + IsUrgent = u.IsUrgent, + VisitNum = u.VisitTaskNum, + TaskBlindName = u.TaskBlindName, + VisistId = u.SourceSubjectVisitId, + SuggesteFinishedTime = u.SuggesteFinishedTime, + ReadingCategory = u.ReadingCategory, + IsAnalysisCreate = u.IsAnalysisCreate, + ArmEnum = u.ArmEnum, + TrialReadingCriterionId = u.TrialReadingCriterionId, + IsNeedClinicalDataSign = u.IsNeedClinicalDataSign, + IsClinicalDataSign = u.IsClinicalDataSign, + IsFrontTaskNeedSignButNotSign = u.IsFrontTaskNeedSignButNotSign + }) + .ToList(), + }).Where(x => x.UnReadCanReadTaskCount > 0)/*.OrderBy(x => x.UnReadCanReadTaskCount)*/; + // 有序阅片需要找到最小需要 + + + // 不这样写会有问题 + var count = visitQuery.Where(y => y.TrialReadingCriterionId == trialReadingCriterionId && y.ReadingTaskState != ReadingTaskState.HaveSigned && y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true)) + .GroupBy(x => new { x.SubjectId, x.Subject.Code, x.BlindSubjectCode }).Count(); + var result = new List(); + + var propName = string.IsNullOrWhiteSpace(inDto.Page.SortField) ? "UnReadCanReadTaskCount" : inDto.Page.SortField; + + var visitTaskOrderQuery = inDto.Page.Asc ? visitTaskQuery.OrderBy(propName) : visitTaskQuery.OrderBy(propName + " desc"); + if (inDto.Page != null) + { + result = await visitTaskOrderQuery + .Skip((inDto.Page.PageIndex - 1) * inDto.Page.PageSize) + .Take(inDto.Page.PageSize).ToListAsync(); + } + else + { + result = await visitTaskOrderQuery.ToListAsync(); + } + + + return (count, result); + } + + + /// + /// IR 已阅片任务 + /// + /// + /// + [HttpPost] + public async Task> GetIRHaveReadTaskList(VisitTaskQuery queryVisitTask) + { + + + var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == queryVisitTask.TrialId) + .Where(t => t.DoctorUserId == _userInfo.Id && t.ReadingTaskState == ReadingTaskState.HaveSigned)//该医生 已经签名的数据 + + + .WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId) + .WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) + .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + + + .WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => (t.Subject.Code.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider); + + var defalutSortArray = new string[] { nameof(VisitTask.IsUrgent) + " desc", nameof(VisitTask.SubjectId), nameof(VisitTask.VisitTaskNum) }; + + var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray); + + return pageList; + } + + + + [HttpPost] + [UnitOfWork] + public async Task AIRReReading(AIRReReadingCommand command, [FromServices] IVisitTaskHelpeService _visitTaskCommonService) + { + var baseLineTaskList = await _visitTaskRepository.Where(t => t.TrialId == command.TrialId && t.TrialReadingCriterionId == command.TrialReadingCriterionId && t.DoctorUserId == _userInfo.Id + && t.TaskState == TaskState.Effect && t.ReadingCategory == ReadingCategory.Visit && t.ReadingTaskState == ReadingTaskState.HaveSigned && t.SourceSubjectVisit.IsBaseLine == true).ToListAsync(); + + + var baseLineTaskIdList = baseLineTaskList.Select(t => t.Id).ToList(); + + if (baseLineTaskIdList.Count == 0) + { + return ResponseOutput.Ok(); + } + + //if (baseLineTaskList == null) + //{ + // return ResponseOutput.NotOk("基线任务未阅完,不允许重阅基线任务"); + //} + + await ApplyReReading(new ApplyReReadingCommand() { IsCopyFollowForms = false, IsCopyOrigenalForms = false, TaskIdList = baseLineTaskIdList, TrialId = command.TrialId, RequestReReadingReason = "AIR自动重阅基线", RequestReReadingType = RequestReReadingType.DocotorApply }); + + + + var requestRecordList = await _visitTaskReReadingRepository.Where(t => baseLineTaskIdList.Contains(t.OriginalReReadingTaskId) && t.RequestReReadingUserId == _userInfo.Id && t.RequestReReadingReason == "AIR自动重阅基线").ToListAsync(); + + if (requestRecordList.Count() != baseLineTaskIdList.Count()) + { + return ResponseOutput.NotOk("后台数据有错误"); + } + + await ConfirmReReading(new ConfirmReReadingCommand() + { + TrialId = command.TrialId, + RequestReReadingResultEnum = RequestReReadingResult.Agree, + //ConfirmReReadingList = new List() { new ConfirmReReadingDTO() { Id = requestRecord.Id, OriginalReReadingTaskId = task.Id } } + ConfirmReReadingList = requestRecordList.Select(t => new ConfirmReReadingDTO() { Id = t.Id, OriginalReReadingTaskId = t.OriginalReReadingTaskId }).ToList() + }, _visitTaskCommonService); + + return ResponseOutput.Ok(); + + } + + + + /// + /// 申请重阅 1:IR 2:PM + /// + /// + /// + [HttpPost] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task ApplyReReading(ApplyReReadingCommand applyReReadingCommand) + { + + + var taskList = await _visitTaskRepository.Where(t => applyReReadingCommand.TaskIdList.Contains(t.Id), true).ToListAsync(); + + var trialReadingCriterionId = taskList.First()!.TrialReadingCriterionId; + + var criterionConfig = (await _trialReadingCriterionRepository.Where(x => x.Id == trialReadingCriterionId).Select(x => new { x.ReadingTool, x.IsReadingTaskViewInOrder }).FirstOrDefaultAsync()).IfNullThrowException(); + + foreach (var task in taskList) + { + + + if (task.ReadingTaskState != ReadingTaskState.HaveSigned || task.TaskState != TaskState.Effect) + { + throw new BusinessValidationFailedException("未阅片完成,或者未生效的任务不允许申请重阅"); + } + + if (task.ReReadingApplyState == ReReadingApplyState.DocotorHaveApplyed || task.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed || task.ReReadingApplyState == ReReadingApplyState.Agree) + { + throw new BusinessValidationFailedException("重阅已申请,或者重阅已同意状态下不允许申请重阅"); + } + + + + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager) + { + if (task.IsAnalysisCreate) + { + throw new BusinessValidationFailedException("PM 不允许对一致性分析任务进行申请重阅"); + } + + if (task.ReadingCategory != ReadingCategory.Visit) + { + throw new BusinessValidationFailedException("PM 仅仅允许对检查批次类型的任务申请重阅"); + } + + + // 有序 一个患者检查批次重阅未处理完,不能申请其他的 + if (criterionConfig.IsReadingTaskViewInOrder) + { + if (await _visitTaskReReadingRepository.AnyAsync(t => t.OriginalReReadingTask.TrialId == task.TrialId && t.OriginalReReadingTask.SubjectId == task.SubjectId && t.OriginalReReadingTask.TaskState == TaskState.Effect + && t.OriginalReReadingTask.TrialReadingCriterionId == task.TrialReadingCriterionId + && t.OriginalReReadingTask.ReadingTaskState == ReadingTaskState.HaveSigned && t.RequestReReadingType == RequestReReadingType.TrialGroupApply && t.RequestReReadingResultEnum == RequestReReadingResult.Default)) + { + return ResponseOutput.NotOk("当前为有序阅片,该患者已有检查批次已申请重阅还未处理(项目组申请),暂不能继续申请重阅"); + } + } + + + task.ReReadingApplyState = ReReadingApplyState.TrialGroupHaveApplyed; + + + } + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IndependentReviewer) + { + task.ReReadingApplyState = ReReadingApplyState.DocotorHaveApplyed; + + // 有序 + if (criterionConfig.IsReadingTaskViewInOrder) + { + + if (await _visitTaskReReadingRepository.AnyAsync(t => t.OriginalReReadingTask.TrialId == task.TrialId && t.OriginalReReadingTask.SubjectId == task.SubjectId && t.OriginalReReadingTask.TaskState == TaskState.Effect + && t.OriginalReReadingTask.TrialReadingCriterionId == task.TrialReadingCriterionId + && t.OriginalReReadingTask.ReadingTaskState == ReadingTaskState.HaveSigned && t.RequestReReadingType == RequestReReadingType.DocotorApply && t.RequestReReadingResultEnum == RequestReReadingResult.Default)) + { + return ResponseOutput.NotOk("当前为有序阅片,该患者已有检查批次已申请重阅还未处理,暂不能继续申请重阅"); + } + + + //在PM 的申请重阅的影响列表里也不能申请重阅 + + var pmApply = await _visitTaskReReadingRepository.Where(t => t.OriginalReReadingTask.TrialId == task.TrialId && t.OriginalReReadingTask.SubjectId == task.SubjectId && t.OriginalReReadingTask.TaskState == TaskState.Effect && t.OriginalReReadingTask.ReadingCategory == ReadingCategory.Visit + && t.OriginalReReadingTask.ReadingTaskState == ReadingTaskState.HaveSigned && t.RequestReReadingType == RequestReReadingType.TrialGroupApply && t.RequestReReadingResultEnum == RequestReReadingResult.Default).Include(t => t.OriginalReReadingTask).FirstOrDefaultAsync(); + + if (pmApply != null) + { + var originalTask = pmApply.OriginalReReadingTask; + + //PM 有序影响列表 + if (await _visitTaskRepository.Where(t => t.TrialId == originalTask.TrialId && t.SubjectId == originalTask.SubjectId && t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated && t.IsAnalysisCreate == false && t.TrialReadingCriterionId == originalTask.TrialReadingCriterionId && t.VisitTaskNum > originalTask.VisitTaskNum).AnyAsync(t => t.VisitTaskNum == task.VisitTaskNum)) + { + return ResponseOutput.NotOk("当前为有序阅片,影像存在问题,项目组已申请回退,暂不能申请重阅"); + } + + } + + + + Expression> filterExpression = t => t.TrialId == task.TrialId && t.SubjectId == task.SubjectId && t.TaskState == TaskState.Effect && t.TrialReadingCriterionId == task.TrialReadingCriterionId + && t.DoctorUserId == task.DoctorUserId && t.IsAnalysisCreate == false && t.VisitTaskNum > task.VisitTaskNum; + + + + if (task.ReadingCategory == ReadingCategory.Global && await _visitTaskRepository.AnyAsync(filterExpression.And(t => t.ReadingCategory == ReadingCategory.Global))) + { + throw new BusinessValidationFailedException("有序阅片,只允许申请该患者阅片人最后一次完成全局任务重阅"); + } + + if (task.ReadingCategory == ReadingCategory.Oncology && await _visitTaskRepository.AnyAsync(filterExpression.And(t => t.ReadingCategory == ReadingCategory.Oncology))) + { + throw new BusinessValidationFailedException("有序阅片,只允许申请该患者阅片人最后一次完成肿瘤学任务重阅"); + } + + if (task.ReadingCategory == ReadingCategory.Judge && await _visitTaskRepository.AnyAsync(filterExpression.And(t => t.ReadingCategory == ReadingCategory.Judge))) + { + throw new BusinessValidationFailedException("有序阅片,只允许申请该患者阅片人最后一次完成裁判的任务重阅"); + } + + } + else + { + if (task.ReadingCategory != ReadingCategory.Visit && task.ReadingCategory != ReadingCategory.Global) + { + throw new BusinessValidationFailedException("无序阅片,仅仅允许IR 申请 全局和检查批次类型类别的任务进行重阅"); + } + } + + } + + //AIR 不加验证 + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.AIR) + { + task.ReReadingApplyState = ReReadingApplyState.DocotorHaveApplyed; + } + + + + var rootReReadingTaskId = _visitTaskReReadingRepository.Where(t => t.NewReReadingTaskId == task.Id).Select(u => u.RootReReadingTaskId).FirstOrDefault(); + + //添加申请记录 + var visitTaskReReading = await _visitTaskReReadingRepository.AddAsync(new VisitTaskReReading() + { + + RootReReadingTaskId = rootReReadingTaskId == Guid.Empty ? task.Id : rootReReadingTaskId, + OriginalReReadingTaskId = task.Id, + RequestReReadingTime = DateTime.Now, + RequestReReadingUserId = _userInfo.Id, + IsCopyOrigenalForms = applyReReadingCommand.IsCopyOrigenalForms, + IsCopyFollowForms = applyReReadingCommand.IsCopyFollowForms, + RequestReReadingReason = applyReReadingCommand.RequestReReadingReason, + RequestReReadingType = applyReReadingCommand.RequestReReadingType, + + }); + } + + + await _visitTaskRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + /// + /// 重阅原任务跟踪处理 只会在同意的时候调用这个 + /// + /// + /// + private void ReReadingTaskTrackingDeal(VisitTask origenalTask, ConfirmReReadingCommand agreeReReadingCommand) + { + if (origenalTask.ReReadingApplyState == ReReadingApplyState.DocotorHaveApplyed || origenalTask.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed) + { + origenalTask.ReReadingApplyState = agreeReReadingCommand.RequestReReadingResultEnum == RequestReReadingResult.Agree ? ReReadingApplyState.Agree : ReReadingApplyState.Reject; + origenalTask.TaskState = agreeReReadingCommand.RequestReReadingResultEnum == RequestReReadingResult.Agree ? TaskState.HaveReturned : origenalTask.TaskState; + } + else + { + throw new BusinessValidationFailedException("当前重阅任务状态不为已申请状态,不允许进行处理,请刷新页面"); + } + + } + + private async Task SetMedicalReviewInvalidAsync(List influenceTaskList, bool isPMApply = true) + { + //将医学审核设置为失效 + var taskIdList = influenceTaskList.Select(t => t.Id).ToList(); + + //PM 申请 医学审核任务状态为待审核、审核中的,设置为失效。 + + //IR 申请 当前任务进入医学审核,医学审核任务未签名且结论中是否有问题项,答案为否的,设置为失效 + + if (isPMApply) + { + await _taskMedicalReviewRepository.UpdatePartialFromQueryAsync(t => taskIdList.Contains(t.VisitTaskId) && t.AuditState != MedicalReviewAuditState.HaveSigned, u => new TaskMedicalReview() { IsInvalid = true }); + + } + else + { + + await _taskMedicalReviewRepository.UpdatePartialFromQueryAsync(t => taskIdList.Contains(t.VisitTaskId) && t.IsHaveQuestion == false && t.AuditState != MedicalReviewAuditState.HaveSigned, u => new TaskMedicalReview() { IsInvalid = true }); + } + } + + /// + /// PM 申请重阅 被同意 或者 PM 直接退回的时候影响 + /// + /// + /// + private async Task SetReReadingOrBackInfluenceAnalysisAsync(Guid subjectId) + { + if (await _repository.AnyAsync(t => t.IsAnalysisCreate && t.SubjectId == subjectId)) + { + await _repository.UpdatePartialFromQueryAsync(t => t.Id == subjectId, u => new Subject() { IsReReadingOrBackInfluenceAnalysis = true }); + + } + + } + + public DateTime GetSuggessFinishTime(bool isInOrder, UrgentType urgentType) + { + + var datetime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 22, 0, 0).AddDays(7); + + return datetime; + } + + /// + /// 确认重阅与否 1同意 2 拒绝 + /// + /// + /// + /// + [HttpPost] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task ConfirmReReading(ConfirmReReadingCommand agreeReReadingCommand, [FromServices] IVisitTaskHelpeService _visitTaskCommonService) + { + + var trialId = agreeReReadingCommand.TrialId; + + foreach (var item in agreeReReadingCommand.ConfirmReReadingList) + { + + var origenalTask = (await _visitTaskRepository.Where(t => item.OriginalReReadingTaskId == t.Id).FirstOrDefaultAsync()).IfNullThrowException(); + + + if (origenalTask.TaskState != TaskState.Effect) + { + return ResponseOutput.NotOk("当前申请重阅任务的状态,已被其他任务重阅已影响,不允许对该状态下的任务进行重阅同意与否操作"); + } + + + var criterionConfig = await _trialReadingCriterionRepository.Where(x => x.Id == origenalTask.TrialReadingCriterionId).Select(x => new { x.ReadingTool, x.IsReadingTaskViewInOrder }).FirstOrDefaultAsync(); + + + //更新申请信息 + var visitTaskReReadingAppply = await _visitTaskReReadingRepository.FirstOrDefaultAsync(t => t.Id == item.Id); + visitTaskReReadingAppply.RequestReReadingConfirmUserId = _userInfo.Id; + visitTaskReReadingAppply.RequestReReadingResultEnum = agreeReReadingCommand.RequestReReadingResultEnum; + visitTaskReReadingAppply.RequestReReadingRejectReason = agreeReReadingCommand.RequestReReadingRejectReason; + + + Expression> filterExpression = t => t.TrialId == trialId && t.SubjectId == origenalTask.SubjectId && t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated; + + //是否是一致性分析任务 正常申请 会影响一致性分析任务 + filterExpression = filterExpression.And(t => t.IsAnalysisCreate == origenalTask.IsAnalysisCreate); + + + if (agreeReReadingCommand.RequestReReadingResultEnum == RequestReReadingResult.Agree) + { + //PM申请 SPM / CPM审批 回退检查批次,在此不生成检查批次任务 影响多个标准的任务 + if (visitTaskReReadingAppply.RequestReReadingType == RequestReReadingType.TrialGroupApply && (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM)) + { + #region PM 申请两个IR 同一检查批次,其他人的申请记录也设置为同意 不会出现,因为在未处理前 一个Subject只能申请一次 + + + // await _visitTaskReReadingRepository.BatchUpdateNoTrackingAsync(t => t.OriginalReReadingTask.SubjectId == origenalTask.SubjectId && + // t.OriginalReReadingTask.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed && + // t.RequestReReadingType == RequestReReadingType.TrialGroupApply && + //t.OriginalReReadingTask.VisitTaskNum == origenalTask.VisitTaskNum && + //t.Id != item.Id, u => new VisitTaskReReading() + //{ + // RequestReReadingConfirmUserId = _userInfo.Id, + // RequestReReadingResultEnum = RequestReReadingResult.Agree, + //}); + + // //只更新 PM 申请 同一检查批次的数据 + // await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == origenalTask.SubjectId && + // t.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed && + // t.TrialReadingCriterionId == origenalTask.TrialReadingCriterionId && + // t.IsAnalysisCreate == origenalTask.IsAnalysisCreate && + // t.VisitTaskNum == origenalTask.VisitTaskNum && + // t.Id != origenalTask.Id, u => new VisitTask() + // { + // ReReadingApplyState = ReReadingApplyState.Agree + // }); + + + #endregion + + // 不管有序 无序 都会 回退检查批次 + if (origenalTask.ReadingCategory == ReadingCategory.Visit) + { + //执行类似一致性核查回退流程 + await VisitBackAsync(origenalTask.SourceSubjectVisitId); + } + else + { + throw new BusinessValidationFailedException("仅允许同意检查批次类型的任务重阅"); + } + + //有序阅片 + if (criterionConfig.IsReadingTaskViewInOrder) + { + + + //检查批次影响当前以及当前之后的 两个阅片人的 + filterExpression = filterExpression.And(t => t.VisitTaskNum >= origenalTask.VisitTaskNum); + + + #region 影响的任务 + + + + var influenceTaskList = await _visitTaskRepository.Where(filterExpression, true).ToListAsync(); + + var trakingOrigenalTask = influenceTaskList.Where(t => t.Id == origenalTask.Id).FirstOrDefault(); + + foreach (var influenceTask in influenceTaskList) + { + //处理申请的任务 + if (influenceTask.Id == origenalTask.Id) + { + ReReadingTaskTrackingDeal(influenceTask, agreeReReadingCommand); + + //influenceTaskList.ForEach(t => + //{ + // //记录实际影像的任务 + + // influenceTask.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = t.Id }); + //}); + + await SetReReadingOrBackInfluenceAnalysisAsync(origenalTask.SubjectId); + + await SetMedicalReviewInvalidAsync(influenceTaskList); + + } + + //申请的检查批次 要不是重阅重置,要不就是失效 不会存在取消分配 + if (influenceTask.ReadingCategory == ReadingCategory.Visit && influenceTask.VisitTaskNum != origenalTask.VisitTaskNum) + { + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.TaskState = TaskState.HaveReturned; + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Return }); + } + else if (influenceTask.ReadingTaskState == ReadingTaskState.Reading) + { + influenceTask.TaskState = TaskState.Adbandon; + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Abandon }); + } + else + { + influenceTask.DoctorUserId = null; + influenceTask.AllocateTime = null; + influenceTask.SuggesteFinishedTime = null; + influenceTask.TaskAllocationState = TaskAllocationState.NotAllocate; + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.CancelAssign }); + } + } + //当前检查批次 + else + { + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.TaskState = TaskState.HaveReturned; + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Return }); + } + else + { + influenceTask.TaskState = TaskState.Adbandon; + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Abandon }); + } + + } + } + #endregion + } + //无序阅片 没有 全局 肿瘤学 + else + { + + // 1.当前任务及裁判任务 + // 2.影响所有阅片人的任务 + + var judegTaskNum = origenalTask.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Judge]; + + filterExpression = filterExpression.And(t => t.VisitTaskNum == origenalTask.VisitTaskNum || t.VisitTaskNum == judegTaskNum); + + + var influenceTaskList = await _visitTaskRepository.Where(filterExpression, true).ToListAsync(); + + //影像列表为空就为null + var trakingOrigenalTask = influenceTaskList.Where(t => t.Id == origenalTask.Id).FirstOrDefault(); + + foreach (var influenceTask in influenceTaskList) + { + //处理申请的任务 + if (influenceTask.Id == origenalTask.Id) + { + ReReadingTaskTrackingDeal(influenceTask, agreeReReadingCommand); + + //influenceTaskList.ForEach(t => + //{ + // //记录实际影像的任务 + + // influenceTask.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = t.Id }); + //}); + + await SetReReadingOrBackInfluenceAnalysisAsync(origenalTask.SubjectId); + + await SetMedicalReviewInvalidAsync(influenceTaskList); + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Return }); + } + else + { + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.TaskState = TaskState.HaveReturned; + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Return }); + } + else + { + influenceTask.TaskState = TaskState.Adbandon; + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Abandon }); + } + } + } + + } + + + + } + //IR申请 PM 审批 注意这里有一致性分析的申请同意 不会回退检查批次,在此要生成影响的检查批次任务 + else if (visitTaskReReadingAppply.RequestReReadingType == RequestReReadingType.DocotorApply && (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.AIR)) + { + #region 两个IR 申请同一检查批次,其他人的申请记录也设置为同意 + + + await _visitTaskReReadingRepository.BatchUpdateNoTrackingAsync(t => t.OriginalReReadingTask.SubjectId == origenalTask.SubjectId && + t.OriginalReReadingTask.ReReadingApplyState == ReReadingApplyState.DocotorHaveApplyed && + t.RequestReReadingType == RequestReReadingType.DocotorApply && + t.OriginalReReadingTask.VisitTaskNum == origenalTask.VisitTaskNum && + t.Id != item.Id, u => new VisitTaskReReading() + { + RequestReReadingConfirmUserId = _userInfo.Id, + RequestReReadingResultEnum = RequestReReadingResult.Agree, + }); + + //await _visitTaskRepository.UpdatePartialFromQueryAsync(t => t.Id == origenalTask.Id, u => new VisitTask() + //{ + // ReReadingApplyState = ReReadingApplyState.Agree + //}); + + + #endregion + + + //影响申请标准的任务 + filterExpression = filterExpression.And(t => t.TrialReadingCriterionId == origenalTask.TrialReadingCriterionId); + + //有序阅片 + if (criterionConfig.IsReadingTaskViewInOrder) + { + + #region 有序 IR 申请 重阅 影响的其他检查批次查询 + + + + switch (origenalTask.ReadingCategory) + { + case ReadingCategory.Visit: + //影响后续检查批次已经读完的,正在读的,未读的不做处理 以及其他类型任务 + filterExpression = filterExpression.And(t => t.VisitTaskNum >= origenalTask.VisitTaskNum && + ((t.DoctorUserId == origenalTask.DoctorUserId && ((t.ReadingCategory == ReadingCategory.Visit && t.ReadingTaskState != ReadingTaskState.WaitReading) || t.ReadingCategory == ReadingCategory.Global)) + || + t.ReadingCategory == ReadingCategory.Judge || t.ReadingCategory == ReadingCategory.Oncology) + ); + break; + + + //不影响后续检查批次: (t.ReadingCategory == ReadingCategory.Visit && t.ReadingTaskState != ReadingTaskState.WaitReading) || + case ReadingCategory.Global: + + filterExpression = filterExpression.And(t => t.VisitTaskNum > origenalTask.VisitTaskNum && + ((t.DoctorUserId == origenalTask.DoctorUserId && t.ReadingCategory == ReadingCategory.Global) + || (t.ReadingCategory == ReadingCategory.Oncology) || (t.ReadingCategory == ReadingCategory.Judge)) || t.Id == origenalTask.Id); + break; + + case ReadingCategory.Oncology: + + //仅仅影响自己 后续任务如果是检查批次任务、全局任务或裁判任务,均不处理 + filterExpression = filterExpression.And(t => t.Id == origenalTask.Id); + break; + + case ReadingCategory.Judge: + + //裁判的影响自己 和后续肿瘤学阅片(不是自己做的) + filterExpression = filterExpression.And(t => t.Id == origenalTask.Id || t.VisitTaskNum > origenalTask.VisitTaskNum && t.ReadingCategory == ReadingCategory.Oncology); + + break; + + + default: + throw new BusinessValidationFailedException("不支持重阅的任务类型"); + + } + + #endregion + + + + + #region 这里时影响其他的任务 /*不包括申请的任务 申请的任务,在上面会统一处理*/ + + var influenceTaskList = await _visitTaskRepository.Where(filterExpression, true).ToListAsync(); + + var trakingOrigenalTask = influenceTaskList.Where(t => t.Id == origenalTask.Id).FirstOrDefault(); + + foreach (var influenceTask in influenceTaskList) + { + + //处理申请的任务 + if (influenceTask.Id == origenalTask.Id) + { + ReReadingTaskTrackingDeal(influenceTask, agreeReReadingCommand); + + + await SetMedicalReviewInvalidAsync(influenceTaskList, false); + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Return }); + } + //处理其他任务 + else + { + + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.TaskState = TaskState.HaveReturned; + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Return }); + } + else + { + influenceTask.TaskState = TaskState.Adbandon; + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Abandon }); + } + + + if (/*influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned &&*/ influenceTask.ReadingCategory == ReadingCategory.Visit) + { + await _visitTaskCommonService.AddTaskAsync(new GenerateTaskCommand() + { + TrialId = trialId, + + ReadingCategory = GenerateTaskCategory.ReReading, + + ReReadingTask = influenceTask, + + //同步才可以 + Action = (newTask) => + { + //申请表 设置新任务Id + visitTaskReReadingAppply.NewReReadingTaskId = newTask.Id; + + //生成的任务分配给原始医生 + newTask.DoctorUserId = origenalTask.DoctorUserId; + newTask.TaskAllocationState = TaskAllocationState.Allocated; + newTask.AllocateTime = DateTime.Now; + newTask.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + + //拷贝后续表单 + if (visitTaskReReadingAppply.IsCopyFollowForms && origenalTask.VisitTaskNum != influenceTask.VisitTaskNum) + { + if (origenalTask.ReadingCategory == ReadingCategory.Visit) + { + var list = _readingTaskQuestionAnswerRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList(); + + foreach (var item in list) + { + item.Id = Guid.Empty; + item.VisitTaskId = newTask.Id; + } + + _ = _readingTaskQuestionAnswerRepository.AddRangeAsync(list).Result; + } + //else if (origenalTask.ReadingCategory == ReadingCategory.Global) + //{ + // var list = _repository.Where(t => t.GlobalTaskId == origenalTask.Id).ToList(); + + // foreach (var item in list) + // { + // item.Id = Guid.Empty; + // item.GlobalTaskId = newTask.Id; + // } + + // _ = _repository.AddRangeAsync(list).Result; + //} + } + + } + }); + } + + } + + + } + + #endregion + + + + + } + //无序阅片 只会申请检查批次类型和裁判类型的任务 注意这里有一致性分析的申请同意 + else + { + //1.当前任务及裁判任务 + //2.影响当前阅片人的任务 + filterExpression = filterExpression.And(t => t.Id == origenalTask.Id || t.Id == origenalTask.JudgeVisitTaskId); + + + var influenceTaskList = await _visitTaskRepository.Where(filterExpression, true).ToListAsync(); + + var trakingOrigenalTask = influenceTaskList.Where(t => t.Id == origenalTask.Id).FirstOrDefault(); + + foreach (var influenceTask in influenceTaskList) + { + //申请原任务处理 + if (influenceTask.Id == origenalTask.Id) + { + ReReadingTaskTrackingDeal(influenceTask, agreeReReadingCommand); + + await SetMedicalReviewInvalidAsync(influenceTaskList, false); + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Return }); + } + else + { + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.TaskState = TaskState.HaveReturned; + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Return }); + } + else + { + influenceTask.TaskState = TaskState.Adbandon; + + trakingOrigenalTask?.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Abandon }); + } + } + + } + + } + + + + #region 申请任务 重新生成 拷贝表单 + + + await _visitTaskCommonService.AddTaskAsync(new GenerateTaskCommand() + { + TrialId = trialId, + + ReadingCategory = GenerateTaskCategory.ReReading, + + ReReadingTask = origenalTask, + + //同步才可以 + Action = (newTask) => + { + //申请表 设置新任务Id + visitTaskReReadingAppply.NewReReadingTaskId = newTask.Id; + + //生成的任务分配给原始医生 + newTask.DoctorUserId = origenalTask.DoctorUserId; + newTask.TaskAllocationState = TaskAllocationState.Allocated; + newTask.AllocateTime = DateTime.Now; + newTask.SuggesteFinishedTime = GetSuggessFinishTime(true, UrgentType.NotUrget); + + + //裁判任务 需要进行特殊处理 在重阅逻辑里面处理 + + + //拷贝表单 + if (visitTaskReReadingAppply.IsCopyOrigenalForms) + { + if (origenalTask.ReadingCategory == ReadingCategory.Visit) + { + var list = _readingTaskQuestionAnswerRepository.Where(t => t.VisitTaskId == origenalTask.Id).ToList(); + + foreach (var item in list) + { + item.Id = Guid.Empty; + item.VisitTaskId = newTask.Id; + } + + _ = _readingTaskQuestionAnswerRepository.AddRangeAsync(list).Result; + } + //else if (origenalTask.ReadingCategory == ReadingCategory.Global) + //{ + // var list = _repository.Where(t => t.GlobalTaskId == origenalTask.Id).ToList(); + + // foreach (var item in list) + // { + // item.Id = Guid.Empty; + // item.GlobalTaskId = newTask.Id; + // } + + // _ = _repository.AddRangeAsync(list).Result; + //} + + + } + + } + }); + + #endregion + + + } + else + { + throw new BusinessValidationFailedException("不符合 PM申请 SPM / CPM审批 | IR申请 PM 审批 "); + } + + } + else if (agreeReReadingCommand.RequestReReadingResultEnum == RequestReReadingResult.Reject) + { + + if (origenalTask.ReReadingApplyState == ReReadingApplyState.DocotorHaveApplyed || origenalTask.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed) + { + await _visitTaskRepository.UpdatePartialFromQueryAsync(t => t.Id == origenalTask.Id, u => new VisitTask() + { + ReReadingApplyState = ReReadingApplyState.Reject + }); + } + else + { + throw new BusinessValidationFailedException("当前重阅任务状态不为已申请状态,不允许进行处理,请刷新页面"); + } + + } + } + + + + + + await _visitTaskRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + /// + /// PM 设置任务 退回 + /// + /// + [HttpPut("{trialId:guid}/{taskId:guid}")] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task PMSetTaskBack(Guid trialId, Guid taskId) + { + + //var trialConfig = (await _trialRepository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.IsReadingTaskViewInOrder, t.ReadingType }).FirstOrDefaultAsync()).IfNullThrowException(); + + + var task = (await _visitTaskRepository.Where(t => t.Id == taskId).FirstOrDefaultAsync()).IfNullThrowException(); + + var criterionConfig = (await _trialReadingCriterionRepository.Where(x => x.Id == task.TrialReadingCriterionId).Select(x => new { x.ReadingTool, x.IsReadingTaskViewInOrder }).FirstOrDefaultAsync()).IfNullThrowException(); + + if (task.TaskState != TaskState.Effect || task.ReadingCategory != ReadingCategory.Visit || task.ReadingTaskState == ReadingTaskState.HaveSigned) + { + return ResponseOutput.NotOk("仅仅允许针对生效、未完成的检查批次任务进行退回操作,请刷新页面数据"); + } + + + if( await _subjectVisitRepository.AnyAsync(t=>t.Id==task.SourceSubjectVisitId && t.CheckState!= CheckStateEnum.CVPassed)) + { + return ResponseOutput.NotOk("当前检查批次已回退到影像上传,不允许继续回退!"); + } + + + if (task.IsAnalysisCreate) + { + return ResponseOutput.NotOk("一致性分析的任务,不允许设置退回"); + } + + //PM 才允许操作 + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager) + { + + #region 有序 无序公用流程 + + + //执行类似一致性核查回退流程 回退检查批次到影像上传流程 + await VisitBackAsync(task.SourceSubjectVisitId); + + #endregion + + + + //有序 + if (criterionConfig.IsReadingTaskViewInOrder) + { + + Expression> filterExpression = t => t.TrialId == trialId && t.SubjectId == task.SubjectId && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false && t.TaskAllocationState == TaskAllocationState.Allocated; + + + //另一个阅片人的任务根据任务进度自动进入PM退回或PM申请重阅 + filterExpression = filterExpression.And(t => t.VisitTaskNum >= task.VisitTaskNum); + + + var influenceTaskList = await _visitTaskRepository.Where(filterExpression, true).ToListAsync(); + + + + + #region 方式一 + //foreach (var influenceTask in influenceTaskList) + //{ + // //申请任务的阅片人 后续任务肯定没做, 只有检查批次任务,没有其他任务 取消分配 + // if (influenceTask.DoctorUserId == task.DoctorUserId) + // { + + // switch (influenceTask.ReadingCategory) + // { + // case ReadingCategory.Visit: + + // influenceTask.DoctorUserId = null; + // influenceTask.AllocateTime = null; + // influenceTask.SuggesteFinishedTime = null; + // influenceTask.TaskAllocationState = TaskAllocationState.NotAllocate; + // break; + + // case ReadingCategory.Global: + // case ReadingCategory.Judge: + // case ReadingCategory.Oncology: + + // throw new BusinessValidationFailedException("不支持回退任务类型"); + // } + // } + // //另外一个阅片人 + // else + // { + // //另外一个阅片人 有序PM 申请重阅流程 + // if (otherReviewerTask.ReadingTaskState == ReadingTaskState.HaveSigned) + // { + // switch (influenceTask.ReadingCategory) + // { + // case ReadingCategory.Visit: + + // if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + // { + // influenceTask.TaskState = TaskState.HaveReturned; + // } + // else if (influenceTask.ReadingTaskState == ReadingTaskState.Reading) + // { + // influenceTask.TaskState = TaskState.Adbandon; + // } + // else + // { + // influenceTask.DoctorUserId = null; + // influenceTask.AllocateTime = null; + // influenceTask.SuggesteFinishedTime = null; + // influenceTask.TaskAllocationState = TaskAllocationState.NotAllocate; + // } + // break; + // case ReadingCategory.Global: + // case ReadingCategory.Oncology: + + // if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + // { + // influenceTask.TaskState = TaskState.HaveReturned; + // } + // else + // { + // influenceTask.TaskState = TaskState.Adbandon; + // } + + // break; + + // case ReadingCategory.Judge: + // default: + + // throw new BusinessValidationFailedException("不支持回退任务类型"); + // } + // } + // //另外一个阅片人 有序 PM 回退流程 + // else + // { + // switch (influenceTask.ReadingCategory) + // { + // case ReadingCategory.Visit: + + // influenceTask.DoctorUserId = null; + // influenceTask.AllocateTime = null; + // influenceTask.SuggesteFinishedTime = null; + // influenceTask.TaskAllocationState = TaskAllocationState.NotAllocate; + // break; + + // case ReadingCategory.Global: + // case ReadingCategory.Judge: + // case ReadingCategory.Oncology: + + // throw new BusinessValidationFailedException("不支持回退任务类型"); + // } + // } + + // } + //} + + + + #endregion + + + + #region 方式二 + + var origenalTask = influenceTaskList.Where(t => t.Id == task.Id).FirstOrDefault(); + + foreach (var influenceTask in influenceTaskList) + { + + //同意的检查批次 因为要记录具体的操作,所以废弃 + if (influenceTask.Id == task.Id) + { + //influenceTaskList.ForEach(t => + //{ + // //记录实际影像的任务 + + // influenceTask.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = t.Id }); + //}); + + await SetReReadingOrBackInfluenceAnalysisAsync(influenceTask.SubjectId); + + await SetMedicalReviewInvalidAsync(influenceTaskList); + + + influenceTask.IsPMSetBack = true; + } + + //申请的检查批次 要不是重阅重置,要不就是失效 不会存在取消分配 + if (influenceTask.ReadingCategory == ReadingCategory.Visit && influenceTask.VisitTaskNum != task.VisitTaskNum) + { + //后续检查批次处理检查批次 + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.TaskState = TaskState.HaveReturned; + + origenalTask.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Return }); + } + else if (influenceTask.ReadingTaskState == ReadingTaskState.Reading) + { + influenceTask.TaskState = TaskState.Adbandon; + origenalTask.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Abandon }); + } + else + { + influenceTask.DoctorUserId = null; + influenceTask.AllocateTime = null; + influenceTask.SuggesteFinishedTime = null; + influenceTask.TaskAllocationState = TaskAllocationState.NotAllocate; + + origenalTask.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.CancelAssign }); + } + } + else + { + //申请的检查批次 全局肿瘤学 + + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.TaskState = TaskState.HaveReturned; + origenalTask.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Return }); + } + else + { + influenceTask.TaskState = TaskState.Adbandon; + origenalTask.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Abandon }); + } + + } + } + + #endregion + + + + } + + else + { + //无序 无序阅片没有 全局 肿瘤学 + + + //// 当前任务标为失效 + //task.TaskState = TaskState.Adbandon; + + ////考虑该检查批次 另外一个阅片人的任务 + //var otherReviewerTask = await _visitTaskRepository.FirstOrDefaultAsync(t => t.SourceSubjectVisitId == task.SourceSubjectVisitId && t.Id != task.Id && t.TaskState == TaskState.Effect); + + + //if (otherReviewerTask.ReadingTaskState == ReadingTaskState.HaveSigned) + //{ + // //另外阅片人完成阅片了 就设置为重阅重置 + // otherReviewerTask.TaskState = TaskState.HaveReturned; + //} + //else + //{ + // otherReviewerTask.TaskState = TaskState.Adbandon; + //} + + + // 申请该检查批次的任务 申请人失效 另外一个人重阅重置或者失效 + + var currentVisitList = await _visitTaskRepository.Where(t => t.SourceSubjectVisitId == task.SourceSubjectVisitId && t.ReadingCategory == ReadingCategory.Visit && t.TaskState == TaskState.Effect && t.VisitTaskNum == task.VisitTaskNum, true).ToListAsync(); + + await SetMedicalReviewInvalidAsync(currentVisitList); + + var origenalTask = currentVisitList.Where(t => t.Id == task.Id).First(); + + foreach (var influenceTask in currentVisitList) + { + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + //另外阅片人完成阅片了 就设置为重阅重置 + influenceTask.TaskState = TaskState.HaveReturned; + + origenalTask.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Return }); + } + else + { + influenceTask.TaskState = TaskState.Adbandon; + + origenalTask.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = influenceTask.Id, OptType = ReReadingOrBackOptType.Abandon }); + } + + ////同意的检查批次 + //if (influenceTask.Id == task.Id) + //{ + // currentVisitList.ForEach(t => + // { + // //记录实际影像的任务 + + // influenceTask.TaskInfluenceList.Add(new TaskInfluence() { InfluenceTaskId = t.Id }); + // }); + //} + } + + } + + } + else + { + return ResponseOutput.NotOk("仅PM 可以进行回退操作"); + } + + await _visitTaskRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + //包括临床数据签名状态 + private async Task VisitBackAsync(Guid? subjectVisitId) + { + var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); + + + //需要重新产生任务 + sv.IsVisitTaskGenerated = false; + sv.IsPMBackOrReReading = true; + + sv.AuditState = AuditStateEnum.None; + sv.SubmitState = SubmitStateEnum.ToSubmit; + sv.ReadingStatus = ReadingStatusEnum.ImageNotSubmit; + + //回退后,回退状态恢复 + sv.RequestBackState = RequestBackStateEnum.NotRequest; + sv.IsCheckBack = false; + sv.CheckBackTime = null; + sv.CheckState = CheckStateEnum.None; + sv.CheckChallengeState = CheckChanllengeTypeEnum.None; + + sv.SVENDTC = null; + sv.SVSTDTC = null; + + sv.PreliminaryAuditTime = null; + sv.SubmitTime = null; + sv.ReviewAuditTime = null; + sv.CurrentActionUserExpireTime = null; + + + sv.IsTake = false; + sv.CurrentActionUserId = null; + sv.PreliminaryAuditUserId = null; + sv.ReviewAuditUserId = null; + + + if (sv.IsBaseLine) + { + await _readingClinicalDataReposiotry.UpdatePartialFromQueryAsync(t => t.ReadingId == sv.Id && (t.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Subject || t.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.SubjectVisit), c => new ReadingClinicalData() { IsSign = false, ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded }); + + } + else + { + await _readingClinicalDataReposiotry.UpdatePartialFromQueryAsync(t => t.ReadingId == sv.Id && t.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.SubjectVisit, c => new ReadingClinicalData() + { + IsSign = false, + ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded, + IsBlind = null, + IsComplete = null + }); + } + + + + //await _repository.AddAsync(new CheckChallengeDialog() { SubjectVisitId = subjectVisitId, TalkContent = "PM/APM同意一致性核查回退。", UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt }); + + await _repository.BatchDeleteAsync(t => t.SubjectVisitId == subjectVisitId); + await _repository.BatchDeleteAsync(t => t.DicomSerie.IsDeleted); + await _repository.BatchDeleteAsync(t => t.IsDeleted); + + var success = await _subjectVisitRepository.SaveChangesAsync(); + + } + + + /// + /// 影响提示列表 重阅仅仅针对已完成的任务申请 退回针对的是未完成的(退回仅仅针对是检查批次类型的) + /// + /// + /// 重阅还是直接回退 + /// 申请记录的Id + /// + [HttpGet("{taskId:guid}/{isReReading:bool}")] + public async Task<(List, object)> GetReReadingOrBackInfluenceTaskList(Guid taskId, bool isReReading, Guid? applyId) + { + var isIRAppyTaskInfluenced = false; + + var filterObj = (await _visitTaskRepository.FirstOrDefaultNoTrackingAsync(t => t.Id == taskId)).IfNullThrowException(); + var trialId = filterObj.TrialId; + + //var trialConfig = (await _trialRepository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.IsReadingTaskViewInOrder, t.ReadingType }).FirstOrDefaultAsync()).IfNullThrowException(); + + var criterionConfig = (await _trialReadingCriterionRepository.Where(x => x.Id == filterObj.TrialReadingCriterionId).Select(x => new { x.ReadingTool, x.IsReadingTaskViewInOrder }).FirstOrDefaultAsync()).IfNullThrowException(); + + Expression> filterExpression = t => t.TrialId == trialId && t.SubjectId == filterObj.SubjectId && t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated; + + //是否是一致性分析任务 (一致性分析的任务 不会产生裁判 肿瘤学 仅仅有生成的检查批次和全局) + + filterExpression = filterExpression.And(t => t.IsAnalysisCreate == filterObj.IsAnalysisCreate); + + //重阅影响 + if (isReReading) + { + + + + //IR 申请 PM 同意 仅仅影响自己 + + if ((_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager && applyId != null && await _visitTaskReReadingRepository.AnyAsync(t => t.Id == applyId && t.CreateUser.UserTypeEnum == UserTypeEnum.IndependentReviewer)) + || (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IndependentReviewer && applyId == null)) + { + filterExpression = filterExpression.And(t => t.TrialReadingCriterionId == filterObj.TrialReadingCriterionId); + + //当前任务及其之后的所有检查批次任务、全局任务、裁判任务、肿瘤学阅片任务 + + //有序 + if (criterionConfig.IsReadingTaskViewInOrder) + { + + switch (filterObj.ReadingCategory) + { + case ReadingCategory.Visit: + //影响当前医生 以及当前医生之后的 1、检查批次任务 已经读完的 + //2、后续任务如果是全局、肿瘤学阅片任务,状态为阅片完成标记为重阅重置;若在阅片中,则标记为失效;若为待阅片,则标记为失效; + //3、当前任务、后续检查批次任务或者全局任务触发了裁判任务,若裁判任务状态为阅片完成,则标记为重阅重置;若在阅片中或待阅片,则标记为失效 + // filterExpression = filterExpression.And(t => t.VisitTaskNum >= filterObj.VisitTaskNum && + //((t.DoctorUserId == filterObj.DoctorUserId && ((t.ReadingCategory == ReadingCategory.Visit && t.ReadingTaskState == ReadingTaskState.HaveSigned) || t.ReadingCategory == ReadingCategory.Global)) + // || + // t.ReadingCategory == ReadingCategory.Judge || t.ReadingCategory == ReadingCategory.Oncology) + // ); + + + filterExpression = filterExpression.And(t => t.VisitTaskNum >= filterObj.VisitTaskNum && + ( + (((t.ReadingCategory == ReadingCategory.Visit && t.ReadingTaskState != ReadingTaskState.WaitReading) || t.ReadingCategory == ReadingCategory.Global) && t.DoctorUserId == filterObj.DoctorUserId) + // 裁判 肿瘤学是另外的医生做 + || t.ReadingCategory == ReadingCategory.Judge + || t.ReadingCategory == ReadingCategory.Oncology + ) + ); + + + + break; + + case ReadingCategory.Global: + + // 1、后续任务如果是检查批次任务,均不处理; + //2、后续任务如果是全局、状态为阅片完成则标记为重阅重置,若阅片中或待阅片则不处理; + //3、当前任务或者全局任务触发了裁判任务,若裁判任务状态为阅片完成,则标记为重阅重置;若在阅片中或待阅片,则标记为失效 + //4、后续任务为肿瘤学阅片任务,状态为阅片完成标记为重阅重置;若在阅片中,则标记为失效;若为待阅片,则标记为失效; + + //filterExpression = filterExpression.And(t => t.VisitTaskNum >= filterObj.VisitTaskNum && + //((t.DoctorUserId == filterObj.DoctorUserId && ((t.ReadingCategory == ReadingCategory.Visit && t.ReadingTaskState == ReadingTaskState.HaveSigned) || t.ReadingCategory == ReadingCategory.Global)) || (t.ReadingCategory == ReadingCategory.Oncology) || (t.ReadingCategory == ReadingCategory.Judge))); + + + filterExpression = filterExpression.And(t => t.VisitTaskNum > filterObj.VisitTaskNum && + ( + (t.DoctorUserId == filterObj.DoctorUserId && t.ReadingCategory == ReadingCategory.Global) + || (t.ReadingCategory == ReadingCategory.Oncology) + || (t.ReadingCategory == ReadingCategory.Judge) + ) || t.Id == filterObj.Id); + + break; + + //1、后续任务如果是检查批次任务、全局任务或裁判任务,均不处理; + case ReadingCategory.Oncology: + + //仅仅影响自己 + filterExpression = filterExpression.And(t => t.Id == filterObj.Id); + break; + + //(只允许申请该阅片人最后一次完成裁判的任务重阅)申请的时候做了限制 + case ReadingCategory.Judge: + + // 1、后续任务如果是检查批次任务、全局任务,均不处理; + //2、后续若有肿瘤学阅片,若肿瘤学阅片任务状态为阅片完成,则标记为重阅重置;若为阅片中则标记为失效,如为待阅片,则取消分配 + + //裁判的影响自己 和后续肿瘤学阅片(不是自己做的) + filterExpression = filterExpression.And(t => t.Id == filterObj.Id || t.VisitTaskNum > filterObj.VisitTaskNum && t.ReadingCategory == ReadingCategory.Oncology); + + break; + + + default: + throw new BusinessValidationFailedException("不支持重阅的任务类型"); + } + } + //无序 + else + { + //1.当前任务及裁判任务 + //2.影响当前阅片人的任务 + filterExpression = filterExpression.And(t => t.Id == filterObj.Id || t.Id == filterObj.JudgeVisitTaskId); + } + + + //throw new BusinessValidationFailedException("仅允许PM 同意 IR 申请的任务"); + } + //PM 影响所有阅片人 仅仅针对检查批次 SPM CPM 掉用 + + else if (((_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM) && applyId != null && await _visitTaskReReadingRepository.AnyAsync(t => t.Id == applyId && t.CreateUser.UserTypeEnum == UserTypeEnum.ProjectManager) && filterObj.IsAnalysisCreate == false && filterObj.ReadingCategory == ReadingCategory.Visit) + || (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager && applyId == null)) + { + //有序 + if (criterionConfig.IsReadingTaskViewInOrder) + { + switch (filterObj.ReadingCategory) + { + case ReadingCategory.Visit: + + //检查批次影响当前以及当前之后的 两个阅片人的 + filterExpression = filterExpression.And(t => t.VisitTaskNum >= filterObj.VisitTaskNum); + + break; + + default: + throw new BusinessValidationFailedException("不支持重阅的任务类型"); + } + + if (await _visitTaskReReadingRepository.AnyAsync(t => t.RequestReReadingType == RequestReReadingType.DocotorApply && t.RequestReReadingResultEnum == RequestReReadingResult.Default && + t.OriginalReReadingTask.VisitTaskNum >= filterObj.VisitTaskNum && t.OriginalReReadingTask.SubjectId == filterObj.SubjectId && t.OriginalReReadingTask.TrialReadingCriterionId == filterObj.TrialReadingCriterionId && t.OriginalReReadingTask.IsAnalysisCreate == filterObj.IsAnalysisCreate)) + { + isIRAppyTaskInfluenced = true; + } + } + //无序 + else + { + // 1.当前任务及裁判任务 + // 2.影响所有阅片人的任务 + + var judegTaskNum = filterObj.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Judge]; + + filterExpression = filterExpression.And(t => t.VisitTaskNum == filterObj.VisitTaskNum || t.VisitTaskNum == judegTaskNum); + } + + //throw new BusinessValidationFailedException("仅允许SPM CPM 同意 PM 申请的非 一致性分析的检查批次任务"); + } + else + { + throw new BusinessValidationFailedException("当前用户查看列表未定义"); + } + + + + } + + + //退回影响 仅仅针对是检查批次类型的 影响多个标准的任务 + else + { + + if (filterObj.IsAnalysisCreate) + { + throw new BusinessValidationFailedException("不允许退回一致性分析任务"); + } + + if (filterObj.ReadingCategory == ReadingCategory.Visit && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager) + { + //有序 + if (criterionConfig.IsReadingTaskViewInOrder) + { + // 当前任务及其之后的所有检查批次任务 两个阅片人的 + + // 1.后续检查批次不处理 + //2.当前任务未完成,不会产生全局任务。后续任务均为检查批次任务,且均为待阅片,取消分配; + filterExpression = filterExpression.And(t => t.VisitTaskNum >= filterObj.VisitTaskNum); + + + if (await _visitTaskReReadingRepository.AnyAsync(t => t.RequestReReadingType == RequestReReadingType.DocotorApply && t.RequestReReadingResultEnum == RequestReReadingResult.Default && + t.OriginalReReadingTask.VisitTaskNum >= filterObj.VisitTaskNum && t.OriginalReReadingTask.SubjectId == filterObj.SubjectId && t.OriginalReReadingTask.TrialReadingCriterionId == filterObj.TrialReadingCriterionId && t.OriginalReReadingTask.IsAnalysisCreate == filterObj.IsAnalysisCreate)) + { + isIRAppyTaskInfluenced = true; + } + } + //无序 + else + { + //自己和另一个人的当前任务 退回针对的是未完成的肯定不会有裁判 + filterExpression = filterExpression.And(t => t.VisitTaskNum == filterObj.VisitTaskNum); + } + } + else + { + throw new BusinessValidationFailedException("仅仅检查批次类型的任务支持PM退回"); + } + + } + + var list = await _visitTaskRepository.Where(filterExpression) + //IR 申请的时候,仅仅看到影响自己的 + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IndependentReviewer, t => t.DoctorUserId == _userInfo.Id) + .OrderBy(t => t.VisitTaskNum).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + #region 影响后的操作 + + foreach (var influenceTask in list) + { + + if (isReReading) + { + + if ((_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager && applyId != null) || (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IndependentReviewer && applyId == null)) + { + //有序 + if (criterionConfig.IsReadingTaskViewInOrder) + { + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.OptType = ReReadingOrBackOptType.Return; + } + else + { + influenceTask.OptType = ReReadingOrBackOptType.Abandon; + } + //else if (influenceTask.ReadingTaskState == ReadingTaskState.Reading) + //{ + // influenceTask.OptType = ReReadingOrBackOptType.Abandon; + //} + //else + //{ + // throw new BusinessValidationFailedException("IR 申请重阅,不会影响到后续未读的任务,当前影响列表有未读的任务,请核查"); + //} + } + else + { + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.OptType = ReReadingOrBackOptType.Return; + } + else + { + influenceTask.OptType = ReReadingOrBackOptType.Abandon; + } + } + } + if (((_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM) && applyId != null) || (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager && applyId == null)) + { + //有序 + if (criterionConfig.IsReadingTaskViewInOrder) + { + //申请的检查批次 要不是重阅重置,要不就是失效 不会存在取消分配 + if (influenceTask.ReadingCategory == ReadingCategory.Visit && influenceTask.VisitTaskNum != filterObj.VisitTaskNum) + { + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.OptType = ReReadingOrBackOptType.Return; + } + else if (influenceTask.ReadingTaskState == ReadingTaskState.Reading) + { + influenceTask.OptType = ReReadingOrBackOptType.Abandon; + } + else + { + influenceTask.OptType = ReReadingOrBackOptType.CancelAssign; + } + } + else + { + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.OptType = ReReadingOrBackOptType.Return; + } + else + { + influenceTask.OptType = ReReadingOrBackOptType.Abandon; + } + } + } + else + { + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.OptType = ReReadingOrBackOptType.Return; + } + else + { + influenceTask.OptType = ReReadingOrBackOptType.Abandon; + } + } + } + + } + //PM退回 + else + { + //有序 + if (criterionConfig.IsReadingTaskViewInOrder) + { + //申请的检查批次 要不是重阅重置,要不就是失效 不会存在取消分配 + if (influenceTask.ReadingCategory == ReadingCategory.Visit && influenceTask.VisitTaskNum != filterObj.VisitTaskNum) + { + //后续检查批次处理检查批次 + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.OptType = ReReadingOrBackOptType.Return; + } + else if (influenceTask.ReadingTaskState == ReadingTaskState.Reading) + { + influenceTask.OptType = ReReadingOrBackOptType.Abandon; + } + else + { + influenceTask.OptType = ReReadingOrBackOptType.CancelAssign; + } + } + else + { + //申请的检查批次 全局肿瘤学 + + if (influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned) + { + influenceTask.OptType = ReReadingOrBackOptType.Return; + } + else + { + influenceTask.OptType = ReReadingOrBackOptType.Abandon; + } + } + } + //无序 + else + { + //重阅重置或者失效 + influenceTask.OptType = influenceTask.ReadingTaskState == ReadingTaskState.HaveSigned ? ReReadingOrBackOptType.Return : ReReadingOrBackOptType.Abandon; + } + } + + + } + #endregion + + + return (list, new { IsIRAppyTaskInfluenced = isIRAppyTaskInfluenced }); + + } + + + /// + /// 获取已影响的列表 + /// + /// + public async Task> GetInfluencedTaskList(Guid taskId) + { + var list = await _repository.Where(t => t.OriginalTaskId == taskId)/*.Select(t => t.InfluenceTask)*/.ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + return list; + } + + + #region 暂时废弃 + + + ///// + ///// 自动一次性分配所有未分配的 Subject 给医生 暂时废弃 + ///// + ///// + ///// + //[HttpPost] + //[UnitOfWork] + //[Obsolete] + //public async Task AutoSubjectAssignDoctor(AutoSubjectAssignCommand autoSubjectAssignCommand) + //{ + // //自动分配的话,需要把手动分配的给删掉 + + + // var trialId = autoSubjectAssignCommand.TrialId; + // var isJudge = autoSubjectAssignCommand.IsJudgeDoctor; + + + // //获取项目配置 判断应该分配几个医生 + // var trialConfig = (await _trialRepository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id/*, t.ReadingType*/, t.IsFollowVisitAutoAssign, t.IsFollowGlobalVisitAutoAssign, t.FollowGlobalVisitAutoAssignDefaultState, t.TaskAllocateObjEnum }).FirstOrDefaultAsync()).IfNullThrowException(); + + + // //获取 已产生任务的Subject 目前分配情况 + // var subjectList = _subjectRepository.Where(t => t.TrialId == trialId) + // .WhereIf(isJudge == false, t => t.SubjectVisitTaskList.Where(t => t.ArmEnum != Arm.JudgeArm).Any()) + // .WhereIf(isJudge, t => t.SubjectVisitTaskList.Where(t => t.ArmEnum == Arm.JudgeArm).Any()) + // ////过滤掉 那些回退的subject + // //.Where(t => !t.SubjectVisitList.Any(t => t.IsPMBackOrReReading)) + // .Select(t => new + // { + // SubjectId = t.Id, + + // //给Subject分配医生的时候, 未确认绑定关系的 + // DoctorUserList = t.SubjectDoctorList.Where(t => isJudge ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Where(t => t.OrignalSubjectUserId == null).Select(t => new { t.DoctorUserId, t.ArmEnum }), + // //IsApplyed = t.SubjectDoctorList.Where(t => isJudge ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Where(t => t.OrignalSubjectUserId == null).SelectMany(t => t.SubjectArmVisitTaskList).Any(c => c.DoctorUserId != null) + // IsApplyed = false + // }).ToList(); + + // //已产生任务的Subject数量(裁判情况下,就是产生裁判任务Subject 的数量) + // var subjectCount = subjectList.Count; + + // //获取医生列表(裁判是裁判的医生列表) + // var waitAllocationDoctorList = _taskAllocationRuleRepository.Where(t => t.TrialId == trialId && t.IsEnable) + // .Where(t => t.IsJudgeDoctor == isJudge) + // .Select(t => new AutoAssignResultDTO() { DoctorUserId = t.DoctorUserId, PlanReadingRatio = t.PlanReadingRatio, SubjectCount = subjectCount }) + // .ToList(); + + // if (waitAllocationDoctorList.Count == 0) + // { + // throw new BusinessValidationFailedException("启用的医生数量为0"); + // } + + + // //已分配的 医生的情况 + // var haveAssignedSubjectDoctorList = subjectList.Clone().SelectMany(t => t.DoctorUserList.Select(c => new { t.SubjectId, c.DoctorUserId, c.ArmEnum })).ToList(); + + // //将目前已分配的情况 换到医生的维度 + // foreach (var waitAllocationDoctor in waitAllocationDoctorList) + // { + // waitAllocationDoctor.SubjectArmList = haveAssignedSubjectDoctorList.Where(t => t.DoctorUserId == waitAllocationDoctor.DoctorUserId) + // .Select(g => new SubjectArm() + // { + // SubjectId = g.SubjectId, + // ArmEnum = g.ArmEnum + // }).ToList(); + // } + + + // #region 完全按照Subject 遍历去分 + + + // //仅仅分配未应用的 而且 没有分配医生的 + // foreach (var subject in subjectList.Where(t => t.IsApplyed == false && !t.DoctorUserList.Any())) + // { + // //该Subject 已经分配的医生数量 + // var hasAssignDoctorCount = subject.DoctorUserList.Count(); + + // if (isJudge) + // { + // if (hasAssignDoctorCount > 1) + // { + // throw new BusinessValidationFailedException("当前有Subject裁判绑定医生数量大于1"); + + // } + + // var allocateDoctor = waitAllocationDoctorList.OrderByDescending(t => t.Weight).ThenByDescending(t => t.PlanReadingRatio).FirstOrDefault(); + + // //将分配结果记录 + // waitAllocationDoctorList.FirstOrDefault(t => t.DoctorUserId == allocateDoctor.DoctorUserId).SubjectArmList.Add(new SubjectArm() + // { + // SubjectId = subject.SubjectId, + // ArmEnum = Arm.JudgeArm + // }); + + // await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = trialId, SubjectId = subject.SubjectId, DoctorUserId = allocateDoctor.DoctorUserId, ArmEnum = Arm.JudgeArm, AssignTime = DateTime.Now }); + + // } + // else + // { + + // //分配两个医生 + // if (trialConfig.ReadingType == ReadingMethod.Double) + // { + + + // if (hasAssignDoctorCount > 2) + // { + // throw new BusinessValidationFailedException("双重阅片当前有Subject绑定医生数量大于2"); + + // } + + // var allocateDoctorList = waitAllocationDoctorList.OrderByDescending(t => t.Weight).ThenByDescending(t => t.PlanReadingRatio).Take(2).ToList(); + + + // #region 看阅片人之前在Subject哪个组做的多 + + // var preferredDoctor1Arm = waitAllocationDoctorList.Where(t => t.DoctorUserId == allocateDoctorList[0].DoctorUserId) + // .SelectMany(t => t.SubjectArmList).GroupBy(t => t.ArmEnum) + // .Select(g => new { ArmEnum = g.Key, SubjectCount = g.Count() }) + // .OrderByDescending(t => t.SubjectCount).FirstOrDefault()?.ArmEnum; + + // var preferredDoctor2Arm = waitAllocationDoctorList.Where(t => t.DoctorUserId == allocateDoctorList[1].DoctorUserId) + // .SelectMany(t => t.SubjectArmList).GroupBy(t => t.ArmEnum) + // .Select(g => new { ArmEnum = g.Key, SubjectCount = g.Count() }) + // .OrderByDescending(t => t.SubjectCount).FirstOrDefault()?.ArmEnum; + + // //存放医生分配的Arm + // var doctor1Arm = Arm.DoubleReadingArm1; + // var doctor2Arm = Arm.DoubleReadingArm2; + + // if (preferredDoctor1Arm == null && preferredDoctor2Arm == null || + // preferredDoctor1Arm == null && preferredDoctor2Arm == Arm.DoubleReadingArm2 || + // preferredDoctor1Arm == Arm.DoubleReadingArm1 && preferredDoctor2Arm == null || + // preferredDoctor1Arm == Arm.DoubleReadingArm1 && preferredDoctor2Arm == Arm.DoubleReadingArm2 + // ) + // { + // doctor1Arm = Arm.DoubleReadingArm1; + // doctor2Arm = Arm.DoubleReadingArm2; + // } + // else if (preferredDoctor1Arm == null && preferredDoctor2Arm == Arm.DoubleReadingArm1 || + // preferredDoctor1Arm == Arm.DoubleReadingArm2 && preferredDoctor2Arm == Arm.DoubleReadingArm1 || + // preferredDoctor1Arm == Arm.DoubleReadingArm2 && preferredDoctor2Arm == null) + // { + // doctor1Arm = Arm.DoubleReadingArm2; + // doctor2Arm = Arm.DoubleReadingArm1; + // } + + // else if (preferredDoctor1Arm == Arm.DoubleReadingArm1 && preferredDoctor2Arm == Arm.DoubleReadingArm1) + // { + // doctor1Arm = Arm.DoubleReadingArm1; + // doctor2Arm = Arm.DoubleReadingArm2; + // } + // else if (preferredDoctor1Arm == Arm.DoubleReadingArm2 && preferredDoctor2Arm == Arm.DoubleReadingArm2) + // { + // doctor1Arm = Arm.DoubleReadingArm2; + // doctor2Arm = Arm.DoubleReadingArm1; + // } + + // #endregion + + + // await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = trialId, SubjectId = subject.SubjectId, DoctorUserId = allocateDoctorList[0].DoctorUserId, ArmEnum = doctor1Arm, AssignTime = DateTime.Now }); + + + // //将分配结果记录 + // waitAllocationDoctorList.FirstOrDefault(t => t.DoctorUserId == allocateDoctorList[0].DoctorUserId).SubjectArmList.Add(new SubjectArm() + // { + // SubjectId = subject.SubjectId, + // ArmEnum = doctor1Arm + // }); + + + // await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = trialId, SubjectId = subject.SubjectId, DoctorUserId = allocateDoctorList[1].DoctorUserId, ArmEnum = doctor2Arm, AssignTime = DateTime.Now }); + + // //将分配结果记录 + // waitAllocationDoctorList.FirstOrDefault(t => t.DoctorUserId == allocateDoctorList[1].DoctorUserId).SubjectArmList.Add(new SubjectArm() + // { + // SubjectId = subject.SubjectId, + // ArmEnum = doctor2Arm + // }); + // } + // else if (trialConfig.ReadingType == ReadingMethod.Single) + // { + // if (hasAssignDoctorCount > 1) + // { + // throw new BusinessValidationFailedException("单重阅片当前有Subject绑定医生数量大于1"); + // } + + // var allocateDoctor = waitAllocationDoctorList.OrderByDescending(t => t.Weight).ThenByDescending(t => t.PlanReadingRatio).FirstOrDefault(); + + // //将分配结果记录 + // waitAllocationDoctorList.FirstOrDefault(t => t.DoctorUserId == allocateDoctor.DoctorUserId).SubjectArmList.Add(new SubjectArm() + // { + // SubjectId = subject.SubjectId, + // ArmEnum = Arm.SingleReadingArm + // }); + + // await _subjectUserRepository.AddAsync(new SubjectUser() { TrialId = trialId, SubjectId = subject.SubjectId, DoctorUserId = allocateDoctor.DoctorUserId, ArmEnum = Arm.SingleReadingArm, AssignTime = DateTime.Now }); + + // } + + // } + + // } + + + // #endregion + + + + + + + + // await _subjectUserRepository.SaveChangesAsync(); + // return ResponseOutput.Ok(); + + //} + + + #endregion + } +} diff --git a/IRaCIS.Core.Application/Service/Allocation/_MapConfig.cs b/IRaCIS.Core.Application/Service/Allocation/_MapConfig.cs new file mode 100644 index 0000000..70f2702 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Allocation/_MapConfig.cs @@ -0,0 +1,314 @@ +using AutoMapper; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Service +{ + public class AllocationConfig : Profile + { + public AllocationConfig() + { + + + CreateMap() + .ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode)) + .ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)) + .ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName)) + .ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName)); + + List subjectIdList = new List(); + bool isJudgeDoctor = false; + + + CreateMap() + .ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode)) + .ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)) + .ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName)) + .ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName)) + //.ForMember(o => o.ArmList, t => t.MapFrom(u => u.DoctorVisitTaskList.Where(c => c.TrialId == u.TrialId).Select(t => t.ArmEnum).Distinct())) + .ForMember(o => o.TotalTaskCount, t => t.MapFrom(u => u.Trial.VisitTaskList.Count())) + .ForMember(o => o.SelfUndoTaskCount, t => t.MapFrom(u => u.Trial.VisitTaskList.Count(t => t.DoctorUserId == u.DoctorUserId && t.ReadingTaskState != ReadingTaskState.HaveSigned))) + .ForMember(o => o.TotalSubjectCount, t => t.MapFrom(u => u.Trial.SubjectList.Count())) + .ForMember(o => o.SelfApplyedTaskCount, t => t.MapFrom(u => u.Trial.VisitTaskList.Count(t => t.DoctorUserId == u.DoctorUserId))) + .ForMember(o => o.ApplyedTotalTaskCount, t => t.MapFrom(u => u.Trial.VisitTaskList.Count(t => t.DoctorUserId != null))) + + //.ForMember(o => o.SelfApplyedSubjectCount, t => t.MapFrom(u => u.Trial.SubjectDoctorUserList.Where(t => t.DoctorUserId == u.DoctorUserId && t.SubjectArmVisitTaskList.Any(c => c.DoctorUserId != null)).Count())) + .ForMember(o => o.SelfSubjectCount, t => t.MapFrom(u => u.Trial.SubjectDoctorUserList.Where(t => t.DoctorUserId == u.DoctorUserId).Count())) + + .ForMember(o => o.ApplyedTotalSubjectCount, t => t.MapFrom(u => u.Trial.SubjectList.Count(c => c.SubjectVisitTaskList.Any(d => d.DoctorUserId != null)))) + + + + //该医生未应用Subject 数量 + //.ForMember(o => o.WaitApplySelfSubjectCount, t => t.MapFrom(u => + // subjectIdList.Count == 0 ? u.Trial.SubjectDoctorUserList.Where(t => t.DoctorUserId == u.DoctorUserId && t.SubjectArmVisitTaskList.Where(t => isJudgeDoctor ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Any(c => c.DoctorUserId == null)).Count() + // : u.Trial.SubjectDoctorUserList.Where(t => t.DoctorUserId == u.DoctorUserId && subjectIdList.Contains(t.SubjectId) && t.SubjectArmVisitTaskList.Where(t => isJudgeDoctor ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Any(c => c.DoctorUserId == null)).Count() + // )) + + + .ForMember(o => o.WaitApplyTotalSubjectCount, t => t.MapFrom(u => + subjectIdList.Count == 0 ? u.Trial.SubjectList.Where(t => t.SubjectVisitTaskList.Where(t => isJudgeDoctor ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Any(c => c.DoctorUserId == null)).Count() + : u.Trial.SubjectList.Where(t => subjectIdList.Contains(t.Id) && t.SubjectVisitTaskList.Where(t => isJudgeDoctor ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Any(c => c.DoctorUserId == null)).Count() + )) + + //.ForMember(o => o.WaitApplySelfTaskCount, t => t.MapFrom(u => + //subjectIdList.Count == 0 ? u.Trial.SubjectDoctorUserList.Where(d => d.DoctorUserId == u.DoctorUserId).SelectMany(t => t.SubjectArmVisitTaskList.Where(t => isJudgeDoctor ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Where(t => t.DoctorUserId == null)).Count() + //: u.Trial.SubjectDoctorUserList.Where(d => d.DoctorUserId == u.DoctorUserId && subjectIdList.Contains(d.SubjectId)).SelectMany(t => t.SubjectArmVisitTaskList.Where(t => isJudgeDoctor ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Where(t => t.DoctorUserId == null)).Count() + // )) + + .ForMember(o => o.WaitApplyTotalTaskCount, t => t.MapFrom(u => + subjectIdList.Count == 0 ? u.Trial.VisitTaskList.Where(t => isJudgeDoctor ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Where(t => t.DoctorUserId == null).Count() + : u.Trial.VisitTaskList.Where(t => isJudgeDoctor ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Where(t => t.DoctorUserId == null && subjectIdList.Contains(t.SubjectId)).Count())) + + ; + + var trialReadingCriterionId = Guid.Empty; + CreateMap() + .ForMember(o => o.DoctorUser, t => t.MapFrom(u => u.Enroll.DoctorUser)) + .ForMember(o => o.CriterionReadingCategoryList, t => t.MapFrom(u => u.Enroll.EnrollReadingCategoryList.Select(t => new TrialCriterionReadingCategory() { EnrollId = t.EnrollId, ReadingCategory = t.ReadingCategory, TrialReadingCriterionId = t.TrialReadingCriterionId }))) + .ForMember(o => o.TrialReadingCriterionList, t => t.MapFrom(u => u.Trial.ReadingQuestionCriterionTrialList.Where(t => t.IsConfirm))) + .ForMember(o => o.ReadingCategoryList, t => t.MapFrom(u => u.Enroll.EnrollReadingCategoryList.Where(t=>t.TrialReadingCriterionId== trialReadingCriterionId).OrderBy(t => t.ReadingCategory).Select(t => t.ReadingCategory).ToList())) + ; + + CreateMap() + .ForMember(t => t.TrialReadingCriterionId, u => u.MapFrom(c => c.Id)) + .ForMember(t => t.TrialReadingCriterionName, u => u.MapFrom(c => c.CriterionName)); + + CreateMap().IncludeBase() + .ForMember(o => o.AssignedSubjectCount, t => t.MapFrom(u => u.DoctorUser.VisitTaskList.Where(t => t.TrialId == u.TrialId).Select(t => t.SubjectId).Distinct().Count())) + .ForMember(o => o.WaitDealTrialTaskCount, t => t.MapFrom(u => u.DoctorUser.VisitTaskList.Where(t => t.TrialId == u.TrialId).Where(t => t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskState == TaskState.Effect).Count())) + .ForMember(o => o.WaitDealAllTaskCount, t => t.MapFrom(u => u.DoctorUser.VisitTaskList.Where(t => t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskState == TaskState.Effect).Count())); + + + CreateMap() + + .ForMember(o => o.SubjectId, t => t.MapFrom(u => u.Id)) + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.Code)) + .ForMember(o => o.VisitTaskTypeCount, t => t.MapFrom(u => u.SubjectVisitTaskList.Where(t => t.ReadingCategory == ReadingCategory.Visit && t.TrialReadingCriterionId==trialReadingCriterionId && t.TaskState==TaskState.Effect).Select(t => t.VisitTaskNum).Distinct().Count())) + .ForMember(o => o.GlobalTaskTypeCount, t => t.MapFrom(u => u.SubjectVisitTaskList.Where(t => t.ReadingCategory == ReadingCategory.Global && t.TrialReadingCriterionId == trialReadingCriterionId && t.TaskState == TaskState.Effect).Select(t => t.VisitTaskNum).Distinct().Count())) + .ForMember(o => o.OncologyTaskTypeCount, t => t.MapFrom(u => u.SubjectVisitTaskList.Where(t => t.ReadingCategory == ReadingCategory.Oncology && t.TrialReadingCriterionId == trialReadingCriterionId && t.TaskState == TaskState.Effect).Select(t => t.VisitTaskNum).Distinct().Count())) + .ForMember(o => o.JudgeTaskTypeCount, t => t.MapFrom(u => u.SubjectVisitTaskList.Where(t => t.ReadingCategory == ReadingCategory.Judge && t.TrialReadingCriterionId == trialReadingCriterionId && t.TaskState == TaskState.Effect).Select(t => t.VisitTaskNum).Distinct().Count())) + .ForMember(o => o.DoctorUserList, t => t.MapFrom(u => u.SubjectDoctorList.Where(t=>t.TrialReadingCriterionId== trialReadingCriterionId))); + + CreateMap() + .ForMember(o => o.DoctorUser, t => t.MapFrom(u => u.DoctorUser)); + + + CreateMap().IncludeBase() + .ForMember(o => o.IsHaveReading, t => t.MapFrom(u => u.Subject.SubjectVisitTaskList.Any(t => t.ReadingTaskState != ReadingTaskState.WaitReading && t.TrialReadingCriterionId==u.TrialReadingCriterionId && t.DoctorUserId==u.DoctorUserId))); + + + CreateMap(); + + + CreateMap() + .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName)) + .ForMember(o => o.ReadingTool, t => t.MapFrom(u => u.TrialReadingCriterion.ReadingTool)) + .ForMember(o => o.IsReadingTaskViewInOrder, t => t.MapFrom(u => u.TrialReadingCriterion.IsReadingTaskViewInOrder)) + .ForMember(o => o.IsReadingShowSubjectInfo, t => t.MapFrom(u => u.TrialReadingCriterion.IsReadingShowSubjectInfo)) + .ForMember(o => o.IsReadingShowPreviousResults, t => t.MapFrom(u => u.TrialReadingCriterion.IsReadingShowPreviousResults)) + .ForMember(o => o.DigitPlaces, t => t.MapFrom(u => u.TrialReadingCriterion.DigitPlaces)) + .ForMember(o => o.IseCRFShowInDicomReading, t => t.MapFrom(u => u.TrialReadingCriterion.IseCRFShowInDicomReading)) + .ForMember(o => o.CriterionType, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionType)); + + CreateMap().IncludeBase() + + + .ForMember(o => o.SiteId, t => t.MapFrom(u => u.Subject.SiteId)) + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.IsAnalysisCreate == true ? u.BlindTrialSiteCode : u.Subject.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.IsAnalysisCreate == true ? u.BlindSubjectCode : u.Subject.Code)) + .ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode)) + .ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)) + .ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName)) + .ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName)) + //.ForMember(o => o.IsClinicalDataSigned, t => t.MapFrom(u => u.Subject.ClinicalDataList.Any(c => c.IsSign && (c.ReadingId == u.SouceReadModuleId || c.ReadingId == u.SourceSubjectVisitId)))) + ; + + + + + CreateMap().IncludeBase() + + + .ForMember(o => o.HistoryReadingDoctorUserList, t => t.MapFrom(u => u.JudgeVisitList)); + + CreateMap().IncludeBase() + + ; + + CreateMap().IncludeBase() + .ForMember(o => o.IsReReadingOrBackInfluenceAnalysis, t => t.MapFrom(u => u.Subject.IsReReadingOrBackInfluenceAnalysis)); + + + + CreateMap(); + + CreateMap(); + + + + + CreateMap() + .ForMember(o => o.ReReadingNewTaskCode, t => t.MapFrom(u => u.NewReReadingTask.TaskCode)) + .ForMember(o => o.OriginalReReadingTask, t => t.MapFrom(u => u.OriginalReReadingTask)) + ; + + + + + CreateMap().IncludeBase() + .ForMember(o => o.SiteId, t => t.MapFrom(u => u.Subject.SiteId)) + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.IsAnalysisCreate == true ? u.BlindTrialSiteCode : u.Subject.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.IsAnalysisCreate == true ? u.BlindSubjectCode : u.Subject.Code)); + + + + + CreateMap() + .ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode)) + .ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)) + .ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName)) + .ForMember(o => o.JudgeTaskCode, t => t.MapFrom(u => u.TaskCode)) + .ForMember(o => o.DoctorUserId, t => t.MapFrom(u => u.DoctorUser.Id)); + + + CreateMap() + .ForMember(o => o.SubjectId, t => t.MapFrom(u => u.Id)) + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.Code)) + .ForMember(o => o.IsJudge, t => t.MapFrom(u => isJudgeDoctor)) + .ForMember(o => o.IsAssignedDoctorUser, t => t.MapFrom(u => u.SubjectDoctorList.Where(t => isJudgeDoctor ? t.ArmEnum == Arm.JudgeArm : t.ArmEnum != Arm.JudgeArm).Any())) + + + .ForMember(o => o.TotalDoctorUserList, t => t.MapFrom(u => u.SubjectDoctorList.Where(t => isJudgeDoctor ? true : t.ArmEnum != Arm.JudgeArm).OrderBy(t => t.ArmEnum))); + + CreateMap() + //.ForMember(o => o.AssignTime, t => t.MapFrom(u => u.AssignTime)) + .ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode)) + .ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)) + .ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName)) + .ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName)); + + + + CreateMap(); + + + CreateMap() + .ForMember(o => o.DoctorUserId, t => t.MapFrom(u => u.Id)) + .ForMember(o => o.FullName, t => t.MapFrom(u => u.FullName)); + + CreateMap().IncludeMembers(t => t.VisitTask) + .ForMember(o => o.TrialReadingCriterionId, t => t.MapFrom(u => u.VisitTask.TrialReadingCriterion.Id)) + .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.VisitTask.TrialReadingCriterion.CriterionName)) + .ForMember(o => o.ReadingTool, t => t.MapFrom(u => u.VisitTask.TrialReadingCriterion.ReadingTool)) + .ForMember(o => o.IsReadingTaskViewInOrder, t => t.MapFrom(u => u.VisitTask.TrialReadingCriterion.IsReadingTaskViewInOrder)) + .ForMember(o => o.IsReadingShowSubjectInfo, t => t.MapFrom(u => u.VisitTask.TrialReadingCriterion.IsReadingShowSubjectInfo)) + .ForMember(o => o.IsReadingShowPreviousResults, t => t.MapFrom(u => u.VisitTask.TrialReadingCriterion.IsReadingShowPreviousResults)) + .ForMember(o => o.DigitPlaces, t => t.MapFrom(u => u.VisitTask.TrialReadingCriterion.DigitPlaces)) + .ForMember(o => o.IseCRFShowInDicomReading, t => t.MapFrom(u => u.VisitTask.TrialReadingCriterion.IseCRFShowInDicomReading)) + .ForMember(o => o.CriterionType, t => t.MapFrom(u => u.VisitTask.TrialReadingCriterion.CriterionType)) + + .ForMember(o => o.Id, t => t.MapFrom(u => u.Id)) + .ForMember(o => o.MedicalNo, t => t.MapFrom(u => u.VisitTask.Subject.MedicalNo)) + .ForMember(o => o.DoctorUser, t => t.MapFrom(u => u.VisitTask.DoctorUser)) + .ForMember(o => o.MedicalManagerUser, t => t.MapFrom(u => u.MedicalManagerUser)); + + CreateMap() + .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName)) + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.IsAnalysisCreate == true ? u.BlindTrialSiteCode : u.Subject.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.IsAnalysisCreate == true ? u.BlindSubjectCode : u.Subject.Code)); + + CreateMap() + .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName)) + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.IsAnalysisCreate == true ? u.BlindTrialSiteCode : u.Subject.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.IsAnalysisCreate == true ? u.BlindSubjectCode : u.Subject.Code)) + .ForMember(o => o.GeneratedMedicalReviewCount, t => t.MapFrom(u => u.TaskMedicalReviewList.Count())) + .ForMember(o => o.MedicalNo, t => t.MapFrom(u => u.Subject.MedicalNo)) + .ForMember(o => o.IsGeneratedJudge, t => t.MapFrom(u => u.JudgeVisitTaskId != null)) + .ForMember(o => o.ReadingDurationTimeSpan, t => t.MapFrom(u => u.SignTime - u.FirstReadingTime)) + + + ; + + CreateMap(); + CreateMap(); + + + + CreateMap() + + .ForMember(o => o.UserId, t => t.MapFrom(u => u.Id)) + .ForMember(o => o.UserCode, t => t.MapFrom(u => u.UserCode)) + .ForMember(o => o.UserName, t => t.MapFrom(u => u.UserName)) + .ForMember(o => o.FullName, t => t.MapFrom(u => u.FullName)); + //.ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.UserTypeRole.UserTypeShortName)); + + + + CreateMap() + .ForMember(o => o.DoctorUser, t => t.MapFrom(u => u.DoctorUser)) + .ForMember(o => o.ActualVisitTaskList, t => t.MapFrom(u => u.DoctorTrialVisitTaskList.Where(t => t.ReadingCategory == ReadingCategory.Visit && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect + && t.ReadingTaskState == ReadingTaskState.HaveSigned).OrderBy(t => t.SignTime).Select(t => new TaskBasicIdView() { TaskId = t.Id, TrialId = t.TrialId, DoctorUserId = t.DoctorUserId }))) + + .ForMember(o => o.ActualJudgeTaskList, t => t.MapFrom(u => u.DoctorTrialVisitTaskList.Where(t => t.ReadingCategory == ReadingCategory.Judge && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && + t.ReadingTaskState == ReadingTaskState.HaveSigned).OrderBy(t => t.SignTime).Select(t => new TaskBasicIdView() { TaskId = t.Id, TrialId = t.TrialId, DoctorUserId = t.DoctorUserId }))) + + .ForMember(o => o.ActualGlobalTaskList, t => t.MapFrom(u => u.DoctorTrialVisitTaskList.Where(t => t.ReadingCategory == ReadingCategory.Global && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && + t.ReadingTaskState == ReadingTaskState.HaveSigned).OrderBy(t => t.SignTime).Select(t => new TaskBasicIdView() { TaskId = t.Id, TrialId = t.TrialId, DoctorUserId = t.DoctorUserId }))) + + .ForMember(o => o.ActualTumorTaskList, t => t.MapFrom(u => u.DoctorTrialVisitTaskList.Where(t => t.ReadingCategory == ReadingCategory.Oncology && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && + t.ReadingTaskState == ReadingTaskState.HaveSigned).OrderBy(t => t.SignTime).Select(t => new TaskBasicIdView() { TaskId = t.Id, TrialId = t.TrialId, DoctorUserId = t.DoctorUserId }))) + + .ForMember(o => o.GeneratedGlobalTaskList, t => t.MapFrom(u => u.TaskMedicalReviewList.Where(t => t.VisitTask.ReadingCategory == ReadingCategory.Global && t.VisitTask.IsAnalysisCreate == false && t.VisitTask.TaskState == TaskState.Effect && t.VisitTask.ReadingTaskState == ReadingTaskState.HaveSigned).OrderBy(t => t.VisitTask.SignTime).Select(t => new TaskBasicIdView() { TaskId = t.VisitTaskId, TrialId = t.TrialId, DoctorUserId = t.DoctorUserId }))) + + .ForMember(o => o.GeneratedJudgeTaskList, t => t.MapFrom(u => u.TaskMedicalReviewList.Where(t => t.VisitTask.ReadingCategory == ReadingCategory.Judge && t.VisitTask.IsAnalysisCreate == false && t.VisitTask.TaskState == TaskState.Effect && t.VisitTask.ReadingTaskState == ReadingTaskState.HaveSigned).OrderBy(t => t.VisitTask.SignTime).Select(t => new TaskBasicIdView() { TaskId = t.VisitTaskId, TrialId = t.TrialId, DoctorUserId = t.DoctorUserId }))) + + .ForMember(o => o.GeneratedTumorTaskList, t => t.MapFrom(u => u.TaskMedicalReviewList.Where(t => t.VisitTask.ReadingCategory == ReadingCategory.Oncology && t.VisitTask.IsAnalysisCreate == false && t.VisitTask.TaskState == TaskState.Effect && t.VisitTask.ReadingTaskState == ReadingTaskState.HaveSigned).OrderBy(t => t.VisitTask.SignTime).Select(t => new TaskBasicIdView() { TaskId = t.VisitTaskId, TrialId = t.TrialId, DoctorUserId = t.DoctorUserId }))) + + .ForMember(o => o.GeneratedVisitTaskList, t => t.MapFrom(u => u.TaskMedicalReviewList.Where(t => t.VisitTask.ReadingCategory == ReadingCategory.Visit && t.VisitTask.IsAnalysisCreate == false && t.VisitTask.TaskState == TaskState.Effect && t.VisitTask.ReadingTaskState == ReadingTaskState.HaveSigned).OrderBy(t => t.VisitTask.SignTime).Select(t => new TaskBasicIdView() { TaskId = t.VisitTaskId, TrialId = t.TrialId, DoctorUserId = t.DoctorUserId }))); + + CreateMap(); + + + //CreateMap().IncludeMembers(t => t.TaskConsistentRule) + // .ForMember(o => o.AnalysisDoctorUser, t => t.MapFrom(u => u.do)); + + + //CreateMap() + // .ForMember(o => o.GeneratedSubjectCount, t => t.MapFrom(u => u.DoctorVisitTaskList.Where(t => t.IsAnalysisCreate && t.TaskConsistentRuleId == u.Id).Select(t => t.SubjectId).Distinct().Count())) ; + + CreateMap(); + + CreateMap(); + + CreateMap().ForMember(t => t.Id, u => u.Ignore()).ReverseMap(); + + + CreateMap() + //.ForMember(o => o.DoctorUser, t => t.MapFrom(u => u.DoctorUser)) + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.Subject.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.Subject.Code)); + + CreateMap() + .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName)) + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.IsAnalysisCreate == true ? u.BlindTrialSiteCode : u.Subject.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.IsAnalysisCreate == true ? u.BlindSubjectCode : u.Subject.Code)) + .ForMember(o => o.OptType, t => t.Ignore()); + + + CreateMap().IncludeMembers(c => c.InfluenceTask) + .ForMember(o => o.OptType, t => t.MapFrom(u => u.OptType)) + .ForMember(o => o.Id, t => t.MapFrom(u => u.InfluenceTask.Id)); + + + + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/Common/CommonDocumentService.cs b/IRaCIS.Core.Application/Service/Common/CommonDocumentService.cs new file mode 100644 index 0000000..0328e08 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/CommonDocumentService.cs @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-31 13:18:56 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Application.Helper; + +namespace IRaCIS.Core.Application.Service +{ + /// + /// 系统模板文档配置表 + /// + [ApiExplorerSettings(GroupName = "Common")] + public class CommonDocumentService : BaseService, ICommonDocumentService + { + + private readonly IRepository _commonDocumentRepository; + + public CommonDocumentService(IRepository commonDocumentRepository) + { + _commonDocumentRepository = commonDocumentRepository; + } + + + [HttpPost] + public async Task> GetCommonDocumentList(CommonDocumentQuery queryCommonDocument) + { + + var commonDocumentQueryable = _commonDocumentRepository.AsQueryable(true) + .WhereIf(queryCommonDocument.FileTypeEnum != null, t => t.FileTypeEnum == queryCommonDocument.FileTypeEnum) + + .WhereIf(queryCommonDocument.CriterionTypeEnum != null, t => t.CriterionTypeEnum == queryCommonDocument.CriterionTypeEnum) + .WhereIf(queryCommonDocument.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == queryCommonDocument.BusinessScenarioEnum) + .WhereIf(string.IsNullOrEmpty(queryCommonDocument.Code), t => t.Code.Contains(queryCommonDocument.Code)) + .WhereIf(string.IsNullOrEmpty(queryCommonDocument.Name), t => t.Name.Contains(queryCommonDocument.Name)) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken, userId = _userInfo.Id }); + + return await commonDocumentQueryable.ToPagedListAsync(queryCommonDocument.PageIndex, queryCommonDocument.PageSize, String.IsNullOrEmpty(queryCommonDocument.SortField) ? nameof(CommonDocument.Code) : queryCommonDocument.SortField, queryCommonDocument.Asc); ; + } + + + public async Task AddOrUpdateCommonDocument(CommonDocumentAddOrEdit addOrEditCommonDocument) + { + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = t => t.Code == addOrEditCommonDocument.Code, + VerifyMsg = "文档的Code不能够重复。" + }; + + //var verifyExp3 = new EntityVerifyExp() + //{ + // VerifyExp = t => t.Code == addOrEditCommonDocument.Code && t.CriterionTypeEnum==addOrEditCommonDocument.CriterionTypeEnum, + // VerifyMsg = "标准邮件文档的Code不能够重复。", + // IsVerify= addOrEditCommonDocument.CriterionTypeEnum != null + //}; + + var verifyExp2 = new EntityVerifyExp() + { + VerifyExp = t => t.CriterionTypeEnum == addOrEditCommonDocument.CriterionTypeEnum && t.BusinessScenarioEnum==addOrEditCommonDocument.BusinessScenarioEnum && + t.IsDeleted==addOrEditCommonDocument.IsDeleted, + VerifyMsg = "一个场景一个标准只允许有一个模板文档", + IsVerify=addOrEditCommonDocument.CriterionTypeEnum !=null && addOrEditCommonDocument.IsDeleted==false + }; + + + if (addOrEditCommonDocument.CriterionTypeEnum != null && addOrEditCommonDocument.CriterionTypeEnum != null) + { + var testValue = new Dictionary() + { + ["SponsorName"] = "Test", + }; + + var templatePhyicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, addOrEditCommonDocument.Path); + + + if (File.Exists(templatePhyicalPath)) + { + + try + { + MemoryStream memoryStream = new MemoryStream(); + + MiniSoftware.MiniWord.SaveAsByTemplate(memoryStream, templatePhyicalPath, testValue); + + memoryStream.Seek(0, SeekOrigin.Begin); + + + } + catch (Exception ) + { + + return ResponseOutput.NotOk("读取模板内容失败, 请将文件另存为docx格式尝试!"); + } + + + } + } + + var entity = await _commonDocumentRepository.InsertOrUpdateAsync(addOrEditCommonDocument, true, verifyExp1, verifyExp2); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + } +} diff --git a/IRaCIS.Core.Application/Service/Common/DTO/CommonDocumentViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/CommonDocumentViewModel.cs new file mode 100644 index 0000000..25a9212 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/DTO/CommonDocumentViewModel.cs @@ -0,0 +1,68 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-31 13:18:48 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.ViewModel +{ + + public class DocumentTypeDto + { + public Guid DictionaryId { get; set; } + + public Guid DictionaryValue { get; set; } + + public Guid DictionaryValueCN { get; set; } + } + + + /// CommonDocumentView 列表视图模型 + public class CommonDocumentView : CommonDocumentAddOrEdit + { + + public string FullFilePath { get; set; } = String.Empty; + public DateTime? DeletedTime { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + + } + + ///CommonDocumentQuery 列表查询参数模型 + public class CommonDocumentQuery : PageInput + { + public CriterionType? CriterionTypeEnum { get; set; } + public CommonDocumentFileType? FileTypeEnum { get; set; } + public CommonDocumentBusinessScenario? BusinessScenarioEnum { get; set; } + + public string Name { get; set; } = String.Empty; + + public string Code { get; set; } = String.Empty; + + } + + /// CommonDocumentAddOrEdit 列表查询参数模型 + public class CommonDocumentAddOrEdit + { + public Guid? Id { get; set; } + public string Name { get; set; } = String.Empty; + public string Path { get; set; } = String.Empty; + public string Description { get; set; } = String.Empty; + public bool IsDeleted { get; set; } + public string Code { get; set; } = String.Empty; + + public CriterionType? CriterionTypeEnum { get; set; } + public CommonDocumentFileType FileTypeEnum { get; set; } + public CommonDocumentBusinessScenario BusinessScenarioEnum { get; set; } + + + //public Guid FileTypeId { get; set; } + //public Guid ModuleTypeId { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Common/DTO/DictionaryModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/DictionaryModel.cs new file mode 100644 index 0000000..60b8aba --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/DTO/DictionaryModel.cs @@ -0,0 +1,268 @@ +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Application.Contracts +{ + + + public class BasicDicView : AddOrEditBasicDic + { + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + public string ConfigType { get; set; } = String.Empty; + + } + + public class GetTrialConfigDictionaryListOutDto + { + + public Guid Id { get; set; } + + public Guid DictionaryId { get; set; } + + public int ShowOrder { get; set; } + + public string Description { get; set; } = string.Empty; + + public string Code { get; set; } + + public string ParentCode { get; set; } + + public string ChildGroup { get; set; } + + public string Value { get; set; } = string.Empty; + + public string ValueCN { get; set; } = string.Empty; + } + + + public class AddOrEditBasicDic + { + public Guid? Id { get; set; } + + public string Code { get; set; } = String.Empty; + + + public string Description { get; set; } = String.Empty; + + + public string Value { get; set; } = String.Empty; + + public string ValueCN { get; set; } = String.Empty; + + + public string ChildGroup { get; set; } = String.Empty; + + public int ChildCodeEnum { get; set; } + + + public int ShowOrder { get; set; } + + + //有父亲 就有值 + public Guid? ParentId { get; set; } + + public bool IsEnable { get; set; } + + + + public DicDataTypeEnum DataTypeEnum { get; set; } + + //是配置的话,就有值 + public Guid? ConfigTypeId { get; set; } + + } + + public class AddBasicDicAndChild + { + [NotDefault] + public Guid ConfigTypeId { get; set; } + + public bool IsEnable { get; set; } + + public string Code { get; set; } = String.Empty; + + public int ShowOrder { get; set; } + public string Description { get; set; } = String.Empty; + + public DicDataTypeEnum DataTypeEnum { get; set; } + + + public List ChildList { get; set;}=new List(); + } + + + public class AddBasicDicChild + { + public string Code { get; set; } = String.Empty; + public string Value { get; set; } = String.Empty; + + public string ValueCN { get; set; } = String.Empty; + + public string ChildGroup { get; set; } = String.Empty; + + public int ShowOrder { get; set; } + + public string Description { get; set; } = String.Empty; + + public bool IsEnable { get; set; } = true; + } + + + + public class GetCriterionDictionaryInDto + { + public Guid? ReadingCriterionId { get; set; } + + public string DictionaryCode { get; set; } + } + + public class GetBasicDataAllSelectInDto + { + public Guid? TrialReadingCriterionId { get; set; } + } + + + public class GetTrialCriterionDictionaryListInDto + { + [NotDefault] + public Guid TrialCriterionId { get; set; } + } + public class GetCriterionDictionaryListInDto + { + [NotDefault] + public Guid SystemCriterionId { get; set; } + } + public class GetCriterionDictionaryListOutDto + { + public Guid Id { get; set; } + public string Code { get; set; } = string.Empty; + + public int Count { get; set; } + public int ShowOrder { get; set; } + + public string Description { get; set; } = string.Empty; + } + + + public class BasicDicSelectCopy : BasicDicSelect + { + + } + public class BasicDicSelect + { + public Guid Id { get; set; } + public string Code { get; set; } = string.Empty; + + public string ValueCN { get; set; } = string.Empty; + public string Value { get; set; } = string.Empty; + + public string Description { get; set; } = string.Empty; + + public int ShowOrder { get; set; } + + public Guid? ParentId { get; set; } + public string ParentCode { get; set; } = string.Empty; + public int? ParentChildCodeEnum { get; set; } + + + public string ChildGroup { get; set; } = string.Empty; + + public DicDataTypeEnum DataTypeEnum { get; set; } + + public bool IsEnumInt => System.Text.RegularExpressions.Regex.IsMatch(Code, @"^[-+]?\d*$") && DataTypeEnum == DicDataTypeEnum.Enum; + + + } + + public class BasicDicQuery : PageInput + { + public string? keyInfo { get; set; } + + public string? Code { get; set; } + + + public DicDataTypeEnum? DataTypeEnum { get; set; } + + public Guid? ConfigTypeId { get; set; } + + } + + + + + + + + + + + + + + + + + + + + + + + + + public class DicViewModelDTO : AddOrUpdateDicDTO + { + + } + + + public class AddOrUpdateDicDTO + { + public Guid? Id { get; set; } + public string KeyName { get; set; } = String.Empty; + public string Value { get; set; } = String.Empty; + public string Description { get; set; } = String.Empty; + public string ValueCN { get; set; } = String.Empty; + public int ShowOrder { get; set; } + public string Type { get; set; } = String.Empty; + } + + public class DicQueryDTO : PageInput + { + public string KeyName { get; set; } = String.Empty; + } + + public class KeyNameType + { + public Guid KeyId { get; set; } + public string KeyName { get; set; } = String.Empty; + public string Type { get; set; } = String.Empty; + } + + public class DicResultDTO + { + public Dictionary> DicList = new Dictionary>(); + } + + + public class TrialDictionaryView + { + public Guid? Id { get; set; } + public string KeyName { get; set; } = String.Empty; + public string Value { get; set; } = String.Empty; + public int ShowOrder { get; set; } + } + + public class TrialDicSelect + { + public TrialDictionaryView[] Phase { get; set; } = new TrialDictionaryView[0]; + public TrialDictionaryView[] IndicationType { get; set; } = new TrialDictionaryView[0]; + public TrialDictionaryView[] DeclarationType { get; set; } = new TrialDictionaryView[0]; + } +} diff --git a/IRaCIS.Core.Application/Service/Common/DTO/EmailNoticeConfigViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/EmailNoticeConfigViewModel.cs new file mode 100644 index 0000000..ae2bfca --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/DTO/EmailNoticeConfigViewModel.cs @@ -0,0 +1,77 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-02-15 11:55:57 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; +namespace IRaCIS.Core.Application.Contracts +{ + /// EmailNoticeConfigView 列表视图模型 + public class EmailNoticeConfigView : EmailNoticeConfigAddOrEdit + { + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public Guid UpdateUserId { get; set; } + + public DateTime UpdateTime { get; set; } + + + + //[JsonIgnore] + //public SystemBasicDataSelect Scenario { get; set; } + ////public Guid? ScenarioParentId => Scenario.ParentId; + //public string ScenarioName => Scenario.Value; + //public string ScenarioNameCN => Scenario.ValueCN; + //public List CriteriaEnumList { get; set; } + + } + + ///EmailNoticeConfigQuery 列表查询参数模型 + public class EmailNoticeConfigQuery:PageInput + { + //public Guid? ScenarioId { get; set; } + + public CommonDocumentBusinessScenario? BusinessScenarioEnum { get; set; } + + public bool? IsReturnRequired { get; set; } + public bool? IsUrgent { get; set; } + public bool? IsEnable { get; set; } + + } + + /// EmailNoticeConfigAddOrEdit 列表查询参数模型 + public class EmailNoticeConfigAddOrEdit + { + public Guid? Id { get; set; } + public string Code { get; set; } = String.Empty; + + + public CommonDocumentBusinessScenario BusinessScenarioEnum { get; set; } + + /// + /// 是否区分标准 + /// + public bool IsDistinguishCriteria { get; set; } + + + //public string AuthorizationCode { get; set; } = String.Empty; + //public Guid ScenarioId { get; set; } + //public string Title { get; set; } = String.Empty; + //public string Body { get; set; } = String.Empty; + //public string FromEmail { get; set; } = String.Empty; + //public string ReceiveEmail { get; set; } = String.Empty; + //public string CopyEmail { get; set; } = String.Empty; + public bool IsReturnRequired { get; set; } + public bool IsUrgent { get; set; } + public bool IsEnable { get; set; } + public bool IsAutoSend { get; set; } + + public bool IsDeleted { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Common/DTO/FileModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/FileModel.cs new file mode 100644 index 0000000..e23569a --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/DTO/FileModel.cs @@ -0,0 +1,18 @@ + +namespace IRaCIS.Application.Contracts +{ + + + public class UploadFileInfoDTO + { + public Guid Id { get; set; } + public string FilePath { get; set; } = string.Empty; + + //[JsonIgnore] + //public string FullFilePathNoToken => FilePath; + public string FullFilePath { get; set; } = string.Empty; + + } + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Common/DTO/FrontAuditConfigViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/FrontAuditConfigViewModel.cs new file mode 100644 index 0000000..d34d74d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/DTO/FrontAuditConfigViewModel.cs @@ -0,0 +1,304 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-28 16:43:52 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using IRaCIS.Core.Infra.EFCore.Common.Dto; + +namespace IRaCIS.Core.Application.ViewModel +{ + /// FrontAuditConfigView 列表视图模型 + public class FrontAuditConfigView:FrontAuditConfig + { + public string ChildrenTypeValue { get; set; } = string.Empty; + public string ChildrenTypeValueCN { get; set; } = string.Empty; + + public string ModuleTypeValue { get; set; } = string.Empty; + public string ModuleTypeValueCN { get; set; } = string.Empty; + + public string OptTypeValue { get; set; } = string.Empty; + public string OptTypeValueCN { get; set; } = string.Empty; + + + + public string ObjectTypeValue { get; set; } = string.Empty; + + public string ObjectTypeValueCN { get; set; } = string.Empty; + + + + } + + public class BatchAddFrontAudit + { + [NotDefault] + public Guid ParentId { get; set; } + + public List Columns { get; set; } + } + + public class GetChildrenItem + { + + + public Guid Id { get; set; } + } + + /// + /// 完全复制对象 + /// + public class FullyReplicated + { + /// + /// 数据来源对象 + /// + public Guid FromItemId { get; set; } + + /// + /// 要赋值到的对象 + /// + public Guid ToItemId { get; set; } + + + } + + /// + /// 复制其他对象到当前对象 + /// + public class CopyOtherToThisItem + { + /// + /// 数据源Guids + /// + public List DataSourceGuids { get; set; }=new List(){ }; + + /// + /// 添加对象的Guid + /// + public Guid AddItemGuid { get; set; } + } + + public class GetDescriptionByModuleTypeDto + { + public Guid ModuleTypeId { get; set; } + } + + ///FrontAuditConfigQuery 列表查询参数模型 + public class FrontAuditConfigQuery + { + /// Value + public string Value { get; set; } = string.Empty; + + /// ValueCN + public string ValueCN { get; set; } = string.Empty; + + /// Description + public string Description { get; set; } = string.Empty; + + /// Code + public string Code { get; set; } = string.Empty; + + /// OptTypeId + public Guid? OptTypeId { get; set; } + + /// ChildrenTypeId + public Guid? ChildrenTypeId { get; set; } + + public Guid? ModuleTypeId { get; set; } + + public Guid? ObjectTypeId { get; set; } + + public string ConfigType { get; set; } = string.Empty; + + } + + public class ChangeFrontAuditSortDto + { + public List SortDataList { get; set; } + } + + public class FrontAuditSort + { + public Guid Id { get; set; } + + public int Sort { get; set; } + + } + + + + public class FrontAuditConfigDTO: FrontAuditConfigAddOrEdit + { + + } + + + /// FrontAuditConfigAddOrEdit 列表查询参数模型 + public class FrontAuditConfigAddOrEdit + { + public Guid? Id { get; set; } + public string Value { get; set; } = string.Empty; + public string ValueCN { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + public string Code { get; set; } = string.Empty; + public Guid? ParentId { get; set; } + public bool IsEnable { get; set; } + public Guid? ModuleTypeId { get; set; } + public Guid? OptTypeId { get; set; } + public Guid? ChildrenTypeId { get; set; } + + public string ConfigType { get; set; } = string.Empty; + + public int? IsShowParent { get; set; } + + public int? Sort { get; set; } + + public string EnumList { get; set; } = string.Empty; + + + public string EnumType { get; set; } = string.Empty; + + public Guid? ObjectTypeId { get; set; } + + public bool? IsShowByTrialConfig { get; set; } + + public string TrialConfigRelyFieldName { get; set; } = string.Empty; + + + /// + /// 是否向前站位 + /// + public bool? IsForwardPosition { get; set; } + + /// + /// 标识 + /// + public string Identification { get; set; } = string.Empty; + + + + /// + /// 是否有签名 + /// + public bool? IsHaveSign { get; set; } + + /// + /// 是否有原因 + /// + public bool? IsHaveReason { get; set; } + + /// + /// 是否完成 + /// + public bool? IsFinish { get; set; } + + /// + /// 是否加入计划 + /// + public bool? IsJoinPlan { get; set; } + + + + /// + /// 数据类型 + /// + + public string DataType { get; set; } + + /// + /// 子数据Lable + /// + + public string ChildDataLabel { get; set; } + + /// + /// 子数据Value + /// + + public string ChildDataValue { get; set; } + + + + + /// + /// 日期类型 + /// + + public string DateType { get; set; }=string.Empty; + + + /// + /// 字典Code + /// + + public string DictionaryCode { get; set; } = string.Empty; + + /// + /// 字典Type + /// + + public string DictionaryType { get; set; } = string.Empty; + + + /// + /// 外键Table + /// + + public string ForeignKeyTableName { get; set; } = string.Empty; + + /// + /// 外键Ke + /// + + public string ForeignKeyValue { get; set; } = string.Empty; + + /// + ///外键Text + /// + + public string ForeignKeyText { get; set; } = string.Empty; + + + /// + /// 接口名 + /// + + public string InterfaceName { get; set; } = string.Empty; + + + + + + /// + /// 是否为特殊类型 + /// + + public bool? IsSpecialType { get; set; } + public bool IsConfig { get; set; } + public string DictionaryKey { get; set; } = string.Empty; + + + + + //byzhouahng + + public string TableConfigJsonStr { get; set; } = String.Empty; + public string UrlConfigJsonStr { get; set; } = String.Empty; + + public UrlConfig UrlConfig { get; set; } = new UrlConfig(); + public List TableConfigList { get; set; } = new List(); + + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Common/DTO/SysMessageModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/SysMessageModel.cs new file mode 100644 index 0000000..24f7f56 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/DTO/SysMessageModel.cs @@ -0,0 +1,16 @@ +using System; + +namespace IRaCIS.Application.Contracts +{ + public class SysMessageDTO + { + public int Id { get; set; } + public int ToDoctorId { get; set; } + public int FromUserId { get; set; } + public string Title { get; set; } = string.Empty; + public string Content { get; set; } = string.Empty; + public string MessageTime { get; set; } = string.Empty; + public bool HasRead { get; set; } + public string Memo { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Application/Service/Common/DTO/SystemBasicDataViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/SystemBasicDataViewModel.cs new file mode 100644 index 0000000..7758d3f --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/DTO/SystemBasicDataViewModel.cs @@ -0,0 +1,66 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-02-15 15:46:00 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.Contracts +{ + /// SystemBasicDataView 列表视图模型 + public class SystemBasicDataView: SystemBasicDataAddOrEdit + { + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + } + + + public class SystemBasicDataSelect + { + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public string Value { get; set; } = string.Empty; + public string Code { get; set; } = string.Empty; + + public Guid? ParentId { get; set; } + public string ParentCode { get; set; } = string.Empty; + } + + + ///SystemBasicDataQuery 列表查询参数模型 + public class SystemBasicDataQuery:PageInput + { + /// Name + public string? Name { get; set; } + + /// Code + public string? Code { get; set; } + + } + + /// SystemBasicDataAddOrEdit 列表查询参数模型 + public class SystemBasicDataAddOrEdit + { + public Guid? Id { get; set; } + public string Name { get; set; } = string.Empty; + public string Value { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public int ShowOrder { get; set; } + public string Code { get; set; } = string.Empty; + public Guid? ParentId { get; set; } + + public string ValueCN { get; set; } = string.Empty; + + public bool IsEnable { get; set; }=true; + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Common/DTO/SystemLogModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/SystemLogModel.cs new file mode 100644 index 0000000..32819a6 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/DTO/SystemLogModel.cs @@ -0,0 +1,99 @@ +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + public class SystemLogDTO + { + public Guid Id { get; set; } + public string ApiPath { get; set; } = string.Empty; + public string Params { get; set; } = string.Empty; + public string Result { get; set; } = string.Empty; + public DateTime RequestTime { get; set; } = DateTime.Now; + public long ElapsedMilliseconds { get; set; } = 0; + public Guid OptUserId { get; set; } = Guid.Empty; + public string OptUserName { get; set; } = string.Empty; + public string ClientIP { get; set; } = string.Empty; + public bool Status { get; set; } = true; + public string Message { get; set; } = string.Empty; + public string LogCategory { get; set; } = string.Empty; + } + + public class QueryLogQueryDTO : PageInput + { + public string Keyword { get; set; } = string.Empty; + public string LogCategory { get; set; } = string.Empty; + public DateTime? BeginTime { get; set; } + public DateTime? EndTime { get; set; } + } + + + public class AuditQueryDTO : PageInput + { + public Guid TrialId { get; set; } + + public Guid? StudyId { get; set; } + + public Guid? SubjectId { get; set; } + + public int? AuditType { get; set; } + + public string SubjectInfo { get; set; } = string.Empty; + + public Guid? OptUserId { get; set; } + + + public DateTime? StartDate { get; set; } + + public DateTime? EndDate { get; set; } + } + + + public class AuditDTO + { + public Guid Id { get; set; } + + public int AuditType { get; set; } + + public Guid TrialId { get; set; } + + public Guid StudyId { get; set; } + + public Guid? SubjectId { get; set; } + + public string SubjectName { get; set; } = string.Empty; + + public string SubjectCode { get; set; } = string.Empty; + + public Guid OptUserId { get; set; } + + public string OptUser { get; set; } = string.Empty; + + public DateTime OptTime { get; set; } = DateTime.Now; + + public string Note { get; set; } = string.Empty; + + public string Detail { get; set; } = string.Empty; + + public string TrialCode { get; set; } = string.Empty; + + public string TrialIndication { get; set; } = string.Empty; + } + + + public class OptUserDto + { + public Guid OptUserId { get; set; } + public string OptUser { get; set; } = string.Empty; + } + + public class AuditSubjectSelectDto + { + public Guid? SubjectId { get; set; } + + public string SubjectCode { get; set; } = string.Empty; + + public string SubjectName { get; set; } = string.Empty; + + } + +} diff --git a/IRaCIS.Core.Application/Service/Common/DictionaryService.cs b/IRaCIS.Core.Application/Service/Common/DictionaryService.cs new file mode 100644 index 0000000..8913d4b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/DictionaryService.cs @@ -0,0 +1,523 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Infra.EFCore.Common; + +namespace IRaCIS.Application.Services +{ + /// + /// 数据字典-基础数据维护 + /// + + [ApiExplorerSettings(GroupName = "Common")] + public class DictionaryService : BaseService, IDictionaryService + { + private readonly IRepository _dicRepository; + private readonly IRepository _doctorDictionaryRepository; + private readonly IRepository _trialDictionaryRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _systemCriterionDictionaryCodeRepository; + private readonly IRepository _trialCriterionDictionaryCodeRepository; + private readonly IRepository _readingCriterionDictionaryRepository; + private readonly IRepository _readingQuestionCriterionSystem; + private readonly IRepository _readingQuestionCriterionTrial; + private readonly IReadingQuestionService _readingQuestionService; + + public DictionaryService(IRepository sysDicRepository, IRepository doctorDictionaryRepository, IRepository trialDictionaryRepository, + IRepository doctorRepository, IRepository trialRepository, + + IRepository systemCriterionDictionaryCodeRepository, + IRepository trialCriterionDictionaryCodeRepository, + IRepository readingCriterionDictionaryRepository, + IRepository readingQuestionCriterionSystem, + IRepository readingQuestionCriterionTrial, + IReadingQuestionService readingQuestionService + + + ) + { + _dicRepository = sysDicRepository; + _doctorDictionaryRepository = doctorDictionaryRepository; + _trialDictionaryRepository = trialDictionaryRepository; + _doctorRepository = doctorRepository; + _trialRepository = trialRepository; + this._systemCriterionDictionaryCodeRepository = systemCriterionDictionaryCodeRepository; + this._trialCriterionDictionaryCodeRepository = trialCriterionDictionaryCodeRepository; + this._readingCriterionDictionaryRepository = readingCriterionDictionaryRepository; + this._readingQuestionCriterionSystem = readingQuestionCriterionSystem; + this._readingQuestionCriterionTrial = readingQuestionCriterionTrial; + this._readingQuestionService = readingQuestionService; + } + + /// + /// 添加bool + /// + /// + /// + [HttpPost] + public async Task AddBoolDic(AddOrEditBasicDic addOrEditBasic) + { + + var entity = await _dicRepository.InsertFromDTOAsync(addOrEditBasic); + + + + var child1 = entity.Clone(); + child1.Id = Guid.Empty; + child1.Code = "true"; + child1.ParentId = entity.Id; + + entity.ChildList.Add(child1); + + var child2 = entity.Clone(); + child2.Id = Guid.Empty; + child2.Code = "false"; + child2.ParentId = entity.Id; + + entity.ChildList.Add(child2); + + await _dicRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id.ToString()); + } + + /// + /// New 查询条件 + /// + /// + /// + [HttpPost] + public async Task> GetBasicDicList(BasicDicQuery basicDicQuery) + { + + var systemBasicDataQueryable = _dicRepository.Where(t => t.ParentId == null) + .WhereIf(!string.IsNullOrEmpty(basicDicQuery.Code), t => t.Code.Contains(basicDicQuery.Code!)) + .WhereIf(!string.IsNullOrEmpty(basicDicQuery.keyInfo), t => t.Description.Contains(basicDicQuery.keyInfo!) + || t.Code.Contains(basicDicQuery.keyInfo!) + || t.ChildList.Any(u => u.ValueCN.Contains(basicDicQuery.keyInfo!)) + || t.ChildList.Any(u => u.Description.Contains(basicDicQuery.keyInfo!)) + || t.ChildList.Any(u => u.Value.Contains(basicDicQuery.keyInfo!))) + + .WhereIf(basicDicQuery.ConfigTypeId != null, t => t.ConfigTypeId == basicDicQuery.ConfigTypeId!) + .WhereIf(basicDicQuery.DataTypeEnum != null, t => t.DataTypeEnum == basicDicQuery.DataTypeEnum) + .WhereIf(basicDicQuery.DataTypeEnum == null, t => t.DataTypeEnum != DicDataTypeEnum.Config) + + .ProjectTo(_mapper.ConfigurationProvider); + + return await systemBasicDataQueryable.ToPagedListAsync(basicDicQuery.PageIndex, basicDicQuery.PageSize, String.IsNullOrEmpty(basicDicQuery.SortField) ? nameof(BasicDicView.ConfigType) : basicDicQuery.SortField, basicDicQuery.Asc, String.IsNullOrEmpty(basicDicQuery.SortField), new[] { nameof(BasicDicView.ConfigType), nameof(BasicDicView.ShowOrder) }); + } + + + /// + /// 添加和编辑 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateBasicDic(AddOrEditBasicDic addOrEditBasic) + { + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = t => t.Code == addOrEditBasic.Code&&t.ParentId== addOrEditBasic.ParentId, + VerifyMsg = $"已有{addOrEditBasic.Code}名称的字典", + IsVerify= addOrEditBasic.ParentId ==null + }; + + + + if (addOrEditBasic.Id != null && addOrEditBasic.ParentId==null) + { + await _dicRepository.UpdatePartialFromQueryAsync(t => t.ParentId == addOrEditBasic.Id, c => new Dictionary() { DataTypeEnum = addOrEditBasic.DataTypeEnum }); + + //await _dicRepository.BatchUpdateNoTrackingAsync(t => t.ParentId == addOrEditBasic.Id, c => new Dictionary() { DataTypeEnum = addOrEditBasic.DataTypeEnum }); + } + + var entity = await _dicRepository.InsertOrUpdateAsync(addOrEditBasic, true, verifyExp1); + + + + await _dicRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id.ToString()); + } + + + + /// + /// 添加字典 的同时 一起添加子项 --New + /// + /// + /// + [HttpPost] + public async Task AddBasicDicAndChild(AddBasicDicAndChild addBasicDicAndChild) + { + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = t => t.Code == addBasicDicAndChild.Code && t.ParentId == null, + VerifyMsg = $"已有{addBasicDicAndChild.Code}名称的字典", + IsVerify = true + }; + + + var entity = await _dicRepository.InsertFromDTOAsync(addBasicDicAndChild, false, verifyExp1); + + var childList = _mapper.Map>(addBasicDicAndChild.ChildList); + + foreach (var item in childList) + { + item.DataTypeEnum = addBasicDicAndChild.DataTypeEnum; + item.ParentId = entity.Id; + } + + await _dicRepository.AddRangeAsync(childList); + + await _dicRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id.ToString()); + } + + + + /// + /// 获取子项数组 + /// + /// + /// + [HttpGet("{parentId:guid}")] + public async Task> GetChildList(Guid parentId) + { + return await _dicRepository.Where(t => t.ParentId == parentId) + .OrderBy(t => t.ShowOrder).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + + + /// 删除字典数据 + [HttpDelete("{id:guid}")] + public async Task DeleteDictionary(Guid id) + { + if (await _readingCriterionDictionaryRepository.AnyAsync(x => x.DictionaryId == id)) + { + return ResponseOutput.NotOk("当前字典在标准中被引用,不允许删除!"); + } + + if (await _dicRepository.AnyAsync(t => t.ParentId == id)) + { + return ResponseOutput.NotOk("有子项数据,不允许直接删除!"); + } + + if ((await _doctorDictionaryRepository.AnyAsync(t => t.DictionaryId == id)) || + (await _doctorRepository.AnyAsync(t => t.SpecialityId == id || t.PositionId == id || t.DepartmentId == id || t.RankId == id)) + + ) + { + return ResponseOutput.NotOk("当前条目已经在阅片人的简历中被引用。"); + } + + if (await _trialDictionaryRepository.AnyAsync(t => t.DictionaryId == id) || + await _trialRepository.AnyAsync(t => t.ReviewModeId == id)) + { + return ResponseOutput.NotOk("当前条目已经在项目信息中被引用。"); + } + + var success = await _dicRepository.BatchDeleteNoTrackingAsync(t => t.Id == id); + return ResponseOutput.Result(success); + } + + /// + /// 传递父亲 code 字符串 数组 返回多个下拉框数据 + /// + /// + /// + [HttpPost] + public async Task>> GetBasicDataSelect(string[] searchArray) + { + + var searchList = await _dicRepository.Where(t => searchArray.Contains(t.Parent.Code) && t.ParentId != null && t.IsEnable).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + return searchList.GroupBy(t => t.ParentCode).ToDictionary(g => g.Key, g => g.OrderBy(t => t.ShowOrder).ToList()); + + } + + /// + /// 根据父亲Code 获取单个下拉框数据 + /// + /// + /// + public async Task> GetBasicDataSelect(string searchKey) + { + var searchList = await _dicRepository.Where(t => t.Parent.Code == searchKey && t.ParentId != null && t.IsEnable).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + return searchList; + } + + + /// + /// 根据父亲字典分组 获取子项 + /// + /// + /// + public async Task> GetBasicConfigSelect(string searchKey) + { + var searchList = await _dicRepository.Where(t => t.ConfigDictionary.Code == searchKey && t.ParentId == null && t.IsEnable).ProjectTo(_mapper.ConfigurationProvider).OrderBy(x=>x.ShowOrder).ToListAsync(); + + return searchList; + } + + /// + /// 获取标准字典 + /// + /// + public async Task> GetCriterionDictionaryList(GetCriterionDictionaryListInDto inDto) + { + var criterionCodes= await _systemCriterionDictionaryCodeRepository.Where(x => x.SystemCriterionId == inDto.SystemCriterionId).ToListAsync(); + + var parentCodes = await _readingCriterionDictionaryRepository.Where(x => x.CriterionId== inDto.SystemCriterionId).Select(x => x.ParentCode).ToListAsync(); + + + var codes = criterionCodes.Select(x=>x.Code).ToList(); + + var dictionaryList = await _dicRepository.Where(x => codes.Contains(x.Code) && x.ParentId == null) + .OrderBy(x => x.ShowOrder).Select(x => new GetCriterionDictionaryListOutDto() + { + Code = x.Code, + ShowOrder = x.ShowOrder, + Description = x.Description + }).ToListAsync(); + + dictionaryList.ForEach(x => { + x.Count = parentCodes.Count(y => y == x.Code); + x.Id = criterionCodes.Where(y => y.Code == x.Code).Select(x => x.Id).FirstOrDefault(); + }); + + return dictionaryList; + } + + + /// + /// 获取标准字典 + /// + /// + public async Task> GetTrialCriterionDictionaryList(GetTrialCriterionDictionaryListInDto inDto) + { + var criterionCodes = await _trialCriterionDictionaryCodeRepository.Where(x => x.TrialCriterionId == inDto.TrialCriterionId).ToListAsync(); + + var parentCodes = await _readingCriterionDictionaryRepository.Where(x => x.CriterionId == inDto.TrialCriterionId).Select(x => x.ParentCode).ToListAsync(); + + + var codes = criterionCodes.Select(x => x.Code).ToList(); + + var dictionaryList = await _dicRepository.Where(x => codes.Contains(x.Code) && x.ParentId == null) + .OrderBy(x => x.ShowOrder).Select(x => new GetCriterionDictionaryListOutDto() + { + Code = x.Code, + ShowOrder = x.ShowOrder, + Description = x.Description + }).ToListAsync(); + + dictionaryList.ForEach(x => { + x.Count = parentCodes.Count(y => y == x.Code); + x.Id = criterionCodes.Where(y => y.Code == x.Code).Select(x => x.Id).FirstOrDefault(); + }); + + return dictionaryList; + } + + /// + /// 获取标准指定字典 + /// + /// + /// + [AllowAnonymous] + [HttpPost] + public async Task>> GetCriterionDictionary(GetCriterionDictionaryInDto inDto) + { + var searchList = await _dicRepository.Where(t => t.ParentId != null && t.IsEnable) + .WhereIf(!inDto.DictionaryCode.IsNullOrEmpty(), x =>x.Parent.Code==inDto.DictionaryCode) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var result = searchList.GroupBy(t => t.ParentCode).ToDictionary(g => g.Key, g => g.OrderBy(t => t.ShowOrder).ToList()); + if (inDto.ReadingCriterionId == null) + { + return result; + } + else + { + List selectCode = new List(); + + var isSystemCriterion = await _readingQuestionCriterionSystem.AnyAsync(x => x.Id == inDto.ReadingCriterionId); + if (isSystemCriterion) + { + selectCode = await _systemCriterionDictionaryCodeRepository.Where(x => x.SystemCriterionId == inDto.ReadingCriterionId).Select(x => x.Code).ToListAsync(); + } + else + { + selectCode = await _trialCriterionDictionaryCodeRepository.Where(x => x.TrialCriterionId == inDto.ReadingCriterionId).Select(x => x.Code).ToListAsync(); + } + + + var criterionCode = await _dicRepository.Where(x => x.ConfigDictionary.Code == "Reading_eCRF_Criterion").Select(x => x.Code).ToListAsync(); + + + foreach (var item in criterionCode) + { + if (result.ContainsKey(item)) + { + result[item] = new List (); + } + } + + + var criterionDictionList = await _readingCriterionDictionaryRepository.Where(x => x.CriterionId == inDto.ReadingCriterionId).Select(x => new BasicDicSelect() + { + ChildGroup = x.Dictionary.ChildGroup, + Code = x.Dictionary.Code, + DataTypeEnum = x.Dictionary.DataTypeEnum, + ParentChildCodeEnum = x.Dictionary.Parent.ChildCodeEnum, + ShowOrder = x.Dictionary.ShowOrder, + ParentCode = x.ParentCode, + Id = x.DictionaryId, + ParentId = x.Dictionary.ParentId, + Value = x.Dictionary.Value, + ValueCN = x.Dictionary.ValueCN + + }).ToListAsync(); + + criterionDictionList = criterionDictionList.Where(x => selectCode.Contains(x.ParentCode)).ToList(); + + var criterionDic = criterionDictionList.GroupBy(x => x.ParentCode).ToDictionary(g => g.Key, g => g.OrderBy(t => t.ShowOrder).ToList()); + + foreach (var item in criterionDic) + { + if (result.ContainsKey(item.Key)) + { + result[item.Key] = item.Value; + } + + + } + + return result; + } + + } + + + /// + /// 获取所有下拉框 枚举 bool 数据 + /// + /// + [AllowAnonymous] + public async Task>> GetBasicDataAllSelect(GetBasicDataAllSelectInDto inDto) + { + var searchList = await _dicRepository.Where(t => t.ParentId != null && t.IsEnable ).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var result = searchList.GroupBy(t => t.ParentCode).ToDictionary(g => g.Key, g => g.OrderBy(t => t.ShowOrder).ToList()); + + if (inDto.TrialReadingCriterionId == null) + { + return result; + } + else + { + List selectCode = await _trialCriterionDictionaryCodeRepository.Where(x => x.TrialCriterionId == inDto.TrialReadingCriterionId).Select(x => x.Code).ToListAsync(); + + + var criterionCode = await _dicRepository.Where(x => x.ConfigDictionary.Code == "Reading_eCRF_Criterion").Select(x => x.Code).ToListAsync(); + + + foreach (var item in criterionCode) + { + if (result.ContainsKey(item)) + { + result[item] = new List(); + } + } + + + + + var criterionDictionList = await _readingCriterionDictionaryRepository.Where(x => x.CriterionId == inDto.TrialReadingCriterionId).Select(x => new BasicDicSelect() + { + ChildGroup = x.Dictionary.ChildGroup, + Code = x.Dictionary.Code, + Description=x.Dictionary.Description, + DataTypeEnum = x.Dictionary.DataTypeEnum, + ParentChildCodeEnum = x.Dictionary.Parent.ChildCodeEnum, + ShowOrder = x.Dictionary.ShowOrder, + ParentCode = x.ParentCode, + Id = x.DictionaryId, + ParentId = x.Dictionary.ParentId, + Value = x.Dictionary.Value, + ValueCN = x.Dictionary.ValueCN + + }).ToListAsync(); + + criterionDictionList = criterionDictionList.Where(x => selectCode.Contains(x.ParentCode)).ToList(); + + var criterionDic = criterionDictionList.GroupBy(x => x.ParentCode).ToDictionary(g => g.Key, g => g.OrderBy(t => t.ShowOrder).ToList()); + + foreach (var item in criterionDic) + { + result[item.Key] = item.Value; + } + + return result; + } + + + } + + #region 稽查相关 + + /// + /// 获取是和否 + /// + /// + /// + public async Task GetBoolValueState(bool value) + { + return await _dicRepository.Where(t => t.Parent.Code == "YesOrNo" && t.Code == value.ToString()).Select(x => x.ValueCN).FirstOrDefaultAsync() ?? string.Empty; + } + + + /// + /// 获取审核状态 + /// + /// + /// + /// + /// + public async Task GetAuditState(Guid trial, T childCode) + { + var QCProcessEnum = _trialRepository.Where(x => x.Id == trial).Select(x => x.QCProcessEnum).FirstOrDefault(); + switch (QCProcessEnum) + { + + + case TrialQCProcess.SingleAudit: + return await _dicRepository.Where(t => t.Parent.Code == "AuditStatePE" && t.Code == Convert.ToInt32(childCode).ToString()).Select(x => x.ValueCN).FirstOrDefaultAsync() ?? string.Empty; + + case TrialQCProcess.DoubleAudit: + return await _dicRepository.Where(t => t.Parent.Code == "AuditStateRC" && t.Code == Convert.ToInt32(childCode).ToString()).Select(x => x.ValueCN).FirstOrDefaultAsync() ?? string.Empty; + default: + return string.Empty; + + } + } + + public async Task GetBasicDataTranslateItem(string parentCode, T childCode) + { + return await _dicRepository.Where(t => t.Parent.Code == parentCode && t.Code == Convert.ToInt32(childCode).ToString()).Select(x => x.ValueCN).FirstOrDefaultAsync() ?? string.Empty; + } + + + #endregion + + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Common/EmailNoticeConfigService.cs b/IRaCIS.Core.Application/Service/Common/EmailNoticeConfigService.cs new file mode 100644 index 0000000..27adfc3 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/EmailNoticeConfigService.cs @@ -0,0 +1,65 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-02-15 13:11:20 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// 系统邮件配置表 + /// + [ApiExplorerSettings(GroupName = "Common")] + public class EmailNoticeConfigService : BaseService, IEmailNoticeConfigService + { + private readonly IRepository _emailNoticeConfigrepository; + + public EmailNoticeConfigService(IRepository repository) + { + _emailNoticeConfigrepository = repository; + } + + [HttpPost] + public async Task> GetEmailNoticeConfigList(EmailNoticeConfigQuery queryEmailNoticeConfig) + { + var emailNoticeConfigQueryable = _emailNoticeConfigrepository + .WhereIf(queryEmailNoticeConfig.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == queryEmailNoticeConfig.BusinessScenarioEnum) + .WhereIf(queryEmailNoticeConfig.IsReturnRequired != null, t => t.IsReturnRequired == queryEmailNoticeConfig.IsReturnRequired) + .WhereIf(queryEmailNoticeConfig.IsUrgent != null, t => t.IsUrgent == queryEmailNoticeConfig.IsUrgent) + .WhereIf(queryEmailNoticeConfig.IsEnable != null, t => t.IsEnable == queryEmailNoticeConfig.IsEnable) + .ProjectTo(_mapper.ConfigurationProvider); + + return await emailNoticeConfigQueryable.ToPagedListAsync(queryEmailNoticeConfig.PageIndex, queryEmailNoticeConfig.PageSize, queryEmailNoticeConfig.SortField, queryEmailNoticeConfig.Asc); + } + + + public async Task AddOrUpdateEmailNoticeConfig(EmailNoticeConfigAddOrEdit addOrEditEmailNoticeConfig) + { + + var entity = await _emailNoticeConfigrepository.InsertOrUpdateAsync(addOrEditEmailNoticeConfig, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + + [HttpDelete("{emailNoticeConfigId:guid}")] + public async Task DeleteEmailNoticeConfig(Guid emailNoticeConfigId) + { + var success = await _emailNoticeConfigrepository.BatchDeleteNoTrackingAsync(t => t.Id == emailNoticeConfigId); + + return ResponseOutput.Result(success); + } + + + + public async Task> GetEmailScenarioEnumSelect() + { + return await Task.FromResult(EnumToSelectExtension.ToSelect()); + } + } +} diff --git a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs new file mode 100644 index 0000000..540da3e --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs @@ -0,0 +1,1196 @@ +using DocumentFormat.OpenXml.Office2010.ExcelAc; +using DocumentFormat.OpenXml.Spreadsheet; +using IRaCIS.Application.Contracts; +using IRaCIS.Application.Interfaces; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore.Common; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using MiniExcelLibs; +using MiniExcelLibs.OpenXml; +using NPOI.HPSF; +using NPOI.HSSF.UserModel; +using NPOI.XSSF.UserModel; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.Common +{ + + [ApiExplorerSettings(GroupName = "Common")] + public class ExcelExportService : BaseService + { + + #region 导表查询 + + + /// + /// 影像上传列表 只导出已上传状态的检查批次记录 + /// + /// + /// + /// + /// + /// + /// + [HttpPost] + [AllowAnonymous] + public async Task CRCVisitList_Export(CRCVisitSearchDTO visitSearchDTO, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _subjectVisitRepository, + [FromServices] IRepository _trialRepository + ) + { + + var svExpression = QCCommon.GetSubjectVisitFilter(visitSearchDTO.VisitPlanArray); + + var list = await _subjectVisitRepository.Where(x => x.TrialId == visitSearchDTO.TrialId) + .Where(t => t.Subject.FinalSubjectVisitId != null ? t.VisitNum <= t.Subject.FinalSubjectVisit.VisitNum : true) + .WhereIf(visitSearchDTO.SiteId != null, t => t.SiteId == visitSearchDTO.SiteId) + .WhereIf(visitSearchDTO.SubjectId != null, t => t.Subject.Id == visitSearchDTO.SubjectId) + .WhereIf(!string.IsNullOrEmpty(visitSearchDTO.SubjectInfo), t => t.Subject.Code.Contains(visitSearchDTO.SubjectInfo)) + + .WhereIf(visitSearchDTO.VisitPlanArray != null && visitSearchDTO.VisitPlanArray?.Length > 0, svExpression) + //.WhereIf(!string.IsNullOrEmpty(visitSearchDTO.VisitPlanInfo), visitSearchDTO.VisitPlanInfo.Contains('.') ? t => t.InPlan == false : t => t.VisitNum == decimal.Parse(visitSearchDTO.VisitPlanInfo)) + .WhereIf(visitSearchDTO.AuditStateArray != null && visitSearchDTO.AuditStateArray?.Length > 0, t => visitSearchDTO.AuditStateArray!.Contains(t.AuditState)) + .WhereIf(visitSearchDTO.SubmitState != null, t => t.SubmitState == visitSearchDTO.SubmitState) + .WhereIf(visitSearchDTO.ChallengeState != null, t => t.ChallengeState == visitSearchDTO.ChallengeState) + .WhereIf(visitSearchDTO.IsUrgent != null, t => t.IsUrgent == visitSearchDTO.IsUrgent) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.BlindName).ToList(); + + var exportInfo = (await _trialRepository.Where(t => t.Id == visitSearchDTO.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = list; + + + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialCRCUploadImageList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(CRCVisitExportDTO)); + + } + + + + + /// + /// 质疑列表 + /// + /// + /// + /// + /// + /// + [HttpPost] + [AllowAnonymous] + public async Task GetQCChallengeList_Export(ChallengeQuery challengeQuery, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository + ) + + { + + var svExpression = QCCommon.GetQCChallengeFilter(challengeQuery.VisitPlanArray); + + var list = await _repository.Where(x => x.TrialId == challengeQuery.TrialId) + //.WhereIf(challengeQuery.ChallengeState != null, t => t.SubjectVisit.ChallengeState == challengeQuery.ChallengeState) + .WhereIf(challengeQuery.ReuploadEnum != null, t => t.ReuploadEnum == challengeQuery.ReuploadEnum) + .WhereIf(challengeQuery.IsClosed != null, t => t.IsClosed == challengeQuery.IsClosed) + .WhereIf(challengeQuery.SiteId != null, t => t.SubjectVisit.SiteId == challengeQuery.SiteId) + .WhereIf(challengeQuery.SubjectId != null, t => t.SubjectVisit.SubjectId == challengeQuery.SubjectId) + .WhereIf(challengeQuery.CreateUserId != null, t => t.CreateUserId == challengeQuery.CreateUserId) + .WhereIf(!string.IsNullOrEmpty(challengeQuery.SubjectCode), t => t.SubjectVisit.Subject.Code.Contains(challengeQuery.SubjectCode)) + .WhereIf(challengeQuery.VisitPlanArray != null && challengeQuery.VisitPlanArray?.Length > 0, svExpression) + //.WhereIf(!string.IsNullOrEmpty(challengeQuery.VisitPlanInfo), challengeQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.InPlan == false : t => t.SubjectVisit.VisitNum == decimal.Parse(challengeQuery.VisitPlanInfo)) + .WhereIf(challengeQuery.IsUrgent != null, t => t.SubjectVisit.IsUrgent == challengeQuery.IsUrgent) + .WhereIf(challengeQuery.IsOverTime != null && challengeQuery.IsOverTime == true, t => t.IsClosed ? t.ClosedTime > t.DeadlineTime : DateTime.Now > t.DeadlineTime) + .WhereIf(challengeQuery.IsOverTime != null && challengeQuery.IsOverTime == false, t => t.IsClosed ? t.ClosedTime < t.DeadlineTime : DateTime.Now < t.DeadlineTime) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.ChallengeCode).ToList(); + + var exportInfo = (await _trialRepository.Where(t => t.Id == challengeQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = list; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialQCImageChanllengeList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(QCChanllengeExportDto)); + } + + + /// + /// 患者信息导出表 + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetSubjectList_Export(SubjectQueryParam param, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + + var list = await _repository.Where(u => u.TrialId == param.TrialId) + .WhereIf(!string.IsNullOrWhiteSpace(param.Code), t => t.Code.Contains(param.Code)) + .WhereIf(!string.IsNullOrWhiteSpace(param.Name), t => t.ShortName.Contains(param.Name)) + .WhereIf(!string.IsNullOrWhiteSpace(param.Sex), t => t.Sex.Contains(param.Sex)) + .WhereIf(param.Status != null, t => t.Status == param.Status) + .WhereIf(param.SiteId != null, t => t.SiteId == param.SiteId) + // IC 只负责他管理site的患者 + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.Code).ToList(); + + var exportInfo = (await _trialRepository.Where(t => t.Id == param.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = list; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSubjectList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository , _hostEnvironment, _dictionaryService, typeof(SubjectExportDTO)); + + } + + + + ///// + ///// 患者 阅片期 进度表 导出 + ///// + ///// + ///// + ///// + ///// + //[HttpPost] + //public async Task GetSubjectReadingPeriod_Export(GetReadModuleDto dto, + // [FromServices] IRepository _commonDocumentRepository, + // [FromServices] IDictionaryService _dictionaryService, + // [FromServices] IRepository _trialRepository) + //{ + + + // var list = await _repository.Where(u => u.TrialId == dto.TrialId) + // .WhereIf(dto.SubjectId != null, x => x.Id == dto.SubjectId) + // .WhereIf(dto.TrialSiteCode != null && dto.TrialSiteCode != string.Empty, x => x.TrialSite.TrialSiteCode == dto.TrialSiteCode) + // .WhereIf(dto.SubjectCode != null && dto.SubjectCode != string.Empty, x => x.Code == dto.SubjectCode) + // //.WhereIf(dto.ReadingStatus != null, x => x.ReadingStatus == dto.ReadingStatus) + + // .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + // list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.Code).ToList(); + + // var exportInfo = (await _trialRepository.Where(t => t.Id == dto.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + // exportInfo.List = list; + + // return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSubjectList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(SubjectExportDTO)); + //} + + + + /// + /// Subject 进展表 --new + /// + /// + /// + /// + /// + /// + + [HttpPost] + public async Task GetSubjectProgress_Export(SubjectProgressQuery dto, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + + + var query = from subject in _repository.Where(u => u.TrialId == dto.TrialId) + join trialReadingCriterion in _repository.Where(u => u.TrialId == dto.TrialId && u.IsConfirm && u.Id == dto.TrialReadingCriterionId) on subject.TrialId equals trialReadingCriterion.TrialId + + select new SubjectProgressDto() + { + TrialSiteCode = subject.TrialSite.TrialSiteCode, + SubjectCode = subject.Code, + SubjectStatus = subject.Status, + + + VisitList = subject.SubjectVisitList.Where(t => t.VisitExecuted != VisitExecutedEnum.Unavailable).Select(t => new SubjectProgressDto.MouduleProgress() + { + TaskName = t.VisitName, + VisitTaskNum = t.VisitNum, + Inplan = t.InPlan, + IsFinalVisit = t.IsFinalVisit, + IsLostVisit = t.IsLostVisit, + ReadingStatus = + t.VisitTaskList.Where(t => t.TaskState == TaskState.Effect && t.TrialReadingCriterionId==dto.TrialReadingCriterionId && t.TaskAllocationState == TaskAllocationState.Allocated && t.IsAnalysisCreate == false && t.ReadingCategory == ReadingCategory.Visit && t.ReadingTaskState == ReadingTaskState.HaveSigned).Count() == (int)trialReadingCriterion.ReadingType ? + ReadingStatusEnum.ReadCompleted : t.VisitTaskList.Where(t => t.TaskState == TaskState.Effect && t.TrialReadingCriterionId == dto.TrialReadingCriterionId && t.IsAnalysisCreate == false && t.ReadingCategory == ReadingCategory.Visit && t.TaskAllocationState == TaskAllocationState.Allocated).Count() == (int)trialReadingCriterion.ReadingType ? ReadingStatusEnum.ImageReading : + t.ReadingStatus + }).ToList(), + + ModuleList = subject.ReadModuleList.Where(t=>t.TrialReadingCriterionId==dto.TrialReadingCriterionId).Select(t => new SubjectProgressDto.MouduleProgress() + { + TaskName = t.ModuleName, + VisitTaskNum = (t.ReadingSetType == ReadingSetType.ImageReading ? ReadingCommon.TaskNumDic[ReadingCategory.Global] : ReadingCommon.TaskNumDic[ReadingCategory.Oncology]) + t.SubjectVisit.VisitNum, + ReadingSetType = t.ReadingSetType, + ReadingStatus = t.ModuleTaskList.Where(t => t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false && (t.ReadingCategory == ReadingCategory.Oncology || t.ReadingCategory == ReadingCategory.Global) && t.ReadingTaskState == ReadingTaskState.HaveSigned).Count() == (int)trialReadingCriterion.ReadingType ? + ReadingStatusEnum.ReadCompleted : t.ModuleTaskList.Where(t => t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false && (t.ReadingCategory == ReadingCategory.Oncology || t.ReadingCategory == ReadingCategory.Global) && t.TaskAllocationState == TaskAllocationState.Allocated).Count() == (int)trialReadingCriterion.ReadingType ? + ReadingStatusEnum.ImageReading : t.ReadingStatus + }).ToList() + + + } + ; + + + var list = query.ToList(); + //.ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + + var exportInfo = (await _trialRepository.Where(t => t.Id == dto.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.CriterionName = await _repository.Where(u => u.TrialId == dto.TrialId && u.IsConfirm && u.Id == dto.TrialReadingCriterionId).Select(t=>t.CriterionName).FirstOrDefaultAsync(); + + exportInfo.List = list; + + var (memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(StaticData.Export.TrialSubjectProgressList_Export, exportInfo, /*"", */_commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(SubjectProgressDto)); + + + // 使用NPOI 进行二次处理 + + var wb = new XSSFWorkbook(memoryStream); + + var sheet = wb.GetSheetAt(0); + + foreach (var subject in list) + { + var index = list.IndexOf(subject); + var row = sheet.GetRow(index + 4); + + + foreach (var item in subject.TotalList) + { + //从第五列开始动态写 + var visitIndex = subject.TotalList.IndexOf(item) + 4; + + var cell = row.CreateCell(visitIndex); + + if (item.IsLostVisit == true) + { + cell.CellStyle = sheet.GetRow(1).GetCell(7).CellStyle; + } + else + { + switch (item.ReadingStatus) + { + case ReadingStatusEnum.ImageNotSubmit: + + cell.CellStyle = sheet.GetRow(1).GetCell(1).CellStyle; + + break; + case ReadingStatusEnum.ImageQuality: + cell.CellStyle = sheet.GetRow(1).GetCell(2).CellStyle; + break; + case ReadingStatusEnum.ConsistencyCheck: + cell.CellStyle = sheet.GetRow(1).GetCell(3).CellStyle; + break; + case ReadingStatusEnum.TaskAllocate: + cell.CellStyle = sheet.GetRow(1).GetCell(4).CellStyle; + break; + case ReadingStatusEnum.ImageReading: + cell.CellStyle = sheet.GetRow(1).GetCell(5).CellStyle; + break; + case ReadingStatusEnum.ReadCompleted: + cell.CellStyle = sheet.GetRow(1).GetCell(6).CellStyle; + break; + + default: + break; + } + } + + + + cell.SetCellValue(item.TaskName); + } + } + + var memoryStream2 = new MemoryStream(); + wb.Write(memoryStream2); + memoryStream2.Seek(0, SeekOrigin.Begin); + + return new FileStreamResult(memoryStream2, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + { + FileDownloadName = $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}_{fileName.Substring(startIndex: 0, fileName.LastIndexOf('.'))}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx" + }; + + } + + + /// + /// 影像上传监控表 + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetStudyUploadMonitor_Export(StudyQuery studyQuery, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + + var svExpression = QCCommon.GetStudyMonitorSubjectVisitFilter(studyQuery.VisitPlanArray); + var StudyMonitorQuery = _repository.Where(t => t.TrialId == studyQuery.TrialId) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + .WhereIf(studyQuery.VisitPlanArray != null && studyQuery.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo)) + .WhereIf(studyQuery.SubjectId != null, t => t.SubjectId == studyQuery.SubjectId) + .WhereIf(studyQuery.SubjectVisitId != null, t => t.SubjectId == studyQuery.SubjectVisitId) + .WhereIf(studyQuery.SiteId != null, t => t.SiteId == studyQuery.SiteId) + .Select(t => new UnionStudyMonitorExportDto() + { + TrialSiteCode = t.TrialSite.TrialSiteCode, + TrialSiteAliasName = t.TrialSite.TrialSiteAliasName, + + SubjectCode = t.Subject.Code, + + + VisitName = t.SubjectVisit.VisitName, + + StudyCode = t.StudyCode, + + IsDicom = t.IsDicom, + + Uploader = t.Uploader.UserName, + + IP = t.IP, + + UploadStartTime = t.UploadStartTime, + + UploadFinishedTime = t.UploadFinishedTime, + + ArchiveFinishedTime=t.ArchiveFinishedTime, + + + UploadTime = t.CreateTime, + + FileCount = t.FileCount, + FileSize = t.FileSize, + + IsDicomReUpload = t.IsDicomReUpload, + + IsSuccess = t.IsSuccess, + Note = t.Note, + }); + + var list = await StudyMonitorQuery.ToListAsync(); + + var exportInfo = (await _trialRepository.Where(t => t.Id == studyQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = list; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialStudyUploadMonitor_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(UnionStudyMonitorExportDto)); + + + } + + + + /// + /// 阅片期信息表 + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetReadingPeriodList_Export(ReadPeriodQuery param, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + + var list = await _repository.Where(u => u.TrialId == param.TrialId) + + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ToList(); + + var exportInfo = (await _trialRepository.Where(t => t.Id == param.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = list; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSubjectReadingPeriodList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(ReadPeriodExportDto)); + + } + + /// + /// 一致性核查 检查信息表 + /// + /// + /// + /// + /// + /// + + [HttpPost] + public async Task GetDicomAndNoneDicomStudyList_Export(StudyQuery studyQuery, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + + var svExpression = QCCommon.GetDicomStudySubjectVisitFilter(studyQuery.VisitPlanArray); + + var dicomStudyQuery = _repository.Where(t => t.TrialId == studyQuery.TrialId) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + .WhereIf(studyQuery.VisitPlanArray != null && studyQuery.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo)) + .Where(x => x.SubjectVisit.AuditState == AuditStateEnum.QCPassed) + .Select(t => new UnionStudyExportDTO() + { + VisitName = t.SubjectVisit.VisitName, + VisitNum = t.SubjectVisit.VisitNum, + IsDicom = true, + SubjectCode = t.Subject.Code, + Modality = t.Modalities, + StudyCode = t.StudyCode, + StudyTime = t.StudyTime, + + TrialSiteAliasName = t.TrialSite.TrialSiteAliasName, + + TrialSiteCode = t.TrialSite.TrialSiteCode, + + CheckState = t.SubjectVisit.CheckState, + //Uploader = t.Uploader.UserName, + //UploadTime = t.CreateTime + + }); + + var svExpression2 = QCCommon.GetNoneDicomStudySubjectVisitFilter(studyQuery.VisitPlanArray); + + + var nodeDicomStudyQuery = _repository.Where(t => t.TrialId == studyQuery.TrialId) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + .WhereIf(studyQuery.VisitPlanArray != null && studyQuery.VisitPlanArray?.Length > 0, svExpression2) + .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo)) + .Where(x => x.SubjectVisit.AuditState == AuditStateEnum.QCPassed) + .Select(t => new UnionStudyExportDTO() + { + + VisitName = t.SubjectVisit.VisitName, + VisitNum = t.SubjectVisit.VisitNum, + IsDicom = false, + SubjectCode = t.Subject.Code, + Modality = t.Modality, + StudyCode = t.StudyCode, + StudyTime = t.ImageDate, + + TrialSiteAliasName = t.TrialSite.TrialSiteAliasName, + + TrialSiteCode = t.TrialSite.TrialSiteCode, + + CheckState = t.SubjectVisit.CheckState, + + //Uploader = t.CreateUser.UserName, + //UploadTime = t.CreateTime + + }); + + + + var list = await dicomStudyQuery.Union(nodeDicomStudyQuery) + .ToListAsync(); + + list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitNum).ToList(); + + var exportInfo = (await _trialRepository.Where(t => t.Id == studyQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = list; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialStudyList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(UnionStudyExportDTO)); + } + + + /// + /// 一致性核查记录表 + /// + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetConsistencyVerificationList_Export(CheckQuery checkQuery, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository, + [FromServices] IRepository _subjectVisitRepository) + { + + var svExpression = QCCommon.GetSubjectVisitFilter(checkQuery.VisitPlanArray); + + var list = await _subjectVisitRepository.Where(x => x.TrialId == checkQuery.TrialId) + .Where(x => x.AuditState == AuditStateEnum.QCPassed) //一致性核查中的,或者还没一致性核查的 + .WhereIf(checkQuery.CheckState != null, t => t.CheckState == checkQuery.CheckState) + .WhereIf(checkQuery.SiteId != null, t => t.SiteId == checkQuery.SiteId) + .WhereIf(!string.IsNullOrEmpty(checkQuery.SubjectInfo), t => t.Subject.Code.Contains(checkQuery.SubjectInfo)) + .WhereIf(checkQuery.VisitPlanArray != null && checkQuery.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id))//IC 过滤负责的site + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitNum).ToList(); + + var exportInfo = (await _trialRepository.Where(t => t.Id == checkQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = list; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSubjectVisitCheckList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(PMKCheckEXportDTO)); + } + + + + + /// + /// PM阅片跟踪 + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetReadingTaskList_Export(VisitTaskQuery queryVisitTask, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + var list = await _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.IsAnalysisCreate == false) + //.Where(t => t.IsAnalysisCreate == false && t.DoctorUserId != null) + + .WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + .WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId) + .WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) + .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + + + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => t.Subject.Code.Contains(queryVisitTask.SubjectCode) || t.BlindSubjectCode.Contains(queryVisitTask.SubjectCode)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + //var defalutSortArray = new string[] { nameof(VisitTask.IsUrgent) + " desc", nameof(VisitTask.SubjectId), nameof(VisitTask.VisitTaskNum) }; + + list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList(); + + + var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = list; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialReadingTaskList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(ReadingTaskExportDto)); + } + + + + + /// + /// PM 重阅追踪 + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetReReadingTaskList_Export(VisitTaskQuery queryVisitTask, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + + + var list = await _repository.Where(t => t.OriginalReReadingTask.TrialId == queryVisitTask.TrialId /*&& t.OriginalReReadingTask.IsAnalysisCreate == false*/) + .WhereIf(queryVisitTask.RootReReadingTaskId != null, t => t.RootReReadingTaskId == queryVisitTask.RootReReadingTaskId || t.OriginalReReadingTaskId == queryVisitTask.RootReReadingTaskId) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskCode), t => t.OriginalReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!) || t.RootReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!)) + .WhereIf(queryVisitTask.SiteId != null, t => t.OriginalReReadingTask.Subject.SiteId == queryVisitTask.SiteId) + .WhereIf(queryVisitTask.TaskState != null, t => t.OriginalReReadingTask.TaskState == queryVisitTask.TaskState) + .WhereIf(queryVisitTask.ReReadingApplyState != null, t => t.OriginalReReadingTask.ReReadingApplyState == queryVisitTask.ReReadingApplyState) + .WhereIf(queryVisitTask.SubjectId != null, t => t.OriginalReReadingTask.SubjectId == queryVisitTask.SubjectId) + .WhereIf(queryVisitTask.IsUrgent != null, t => t.OriginalReReadingTask.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.OriginalReReadingTask.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.OriginalReReadingTask.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.OriginalReReadingTask.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.OriginalReReadingTask.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.OriginalReReadingTask.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate) || (t.OriginalReReadingTask.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.OriginalReReadingTask.TaskName.Contains(queryVisitTask.TaskName) || t.OriginalReReadingTask.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => t.OriginalReReadingTask.Subject.Code.Contains(queryVisitTask.SubjectCode) || t.OriginalReReadingTask.BlindSubjectCode.Contains(queryVisitTask.SubjectCode)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + //var defalutSortArray = new string[] { nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.IsUrgent) + " desc", nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.SubjectId), nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.VisitTaskNum) }; + + list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList(); + + var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = list; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialReReadingTaskList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(ReReadingTaskExportDto)); + } + + + + /// + /// PM 医学审核(挑选任务生成后的列表) + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetMedicalReviewTaskList_Export(TaskMedicalReviewQuery inQuery, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + + var list = await _repository.Where(t => t.VisitTask.TrialId == inQuery.TrialId) + .WhereIf(inQuery.SiteId != null, t => t.VisitTask.Subject.SiteId == inQuery.SiteId) + .WhereIf(inQuery.SubjectId != null, t => t.VisitTask.SubjectId == inQuery.SubjectId) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.VisitTask.Subject.Code.Contains(inQuery.SubjectCode)) + .WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.VisitTask.TaskName.Contains(inQuery.TaskName) || t.VisitTask.TaskBlindName.Contains(inQuery.TaskName)) + .WhereIf(inQuery.IsUrgent != null, t => t.VisitTask.IsUrgent == inQuery.IsUrgent) + .WhereIf(inQuery.DoctorUserId != null, t => t.VisitTask.DoctorUserId == inQuery.DoctorUserId) + .WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => (t.VisitTask.BlindTrialSiteCode.Contains(inQuery.TrialSiteCode) && t.VisitTask.IsAnalysisCreate) || (t.VisitTask.Subject.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode) && t.VisitTask.IsAnalysisCreate == false)) + .WhereIf(inQuery.ReadingCategory != null, t => t.VisitTask.ReadingCategory == inQuery.ReadingCategory) + .WhereIf(inQuery.ReadingTaskState != null, t => t.VisitTask.ReadingTaskState == inQuery.ReadingTaskState) + + .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.VisitTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList(); + + + var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = list; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialMedicalReviewList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TaskMedicalReviewExportDto)); + } + + + + /// + /// 自身一致性分析(仅做了resist1.1) + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetSelfAnalysisTaskList_Export(VisitTaskQuery queryVisitTask, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + + var criterion = await _repository.Where(t => t.Id == queryVisitTask.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName }).FirstOrDefaultAsync(); + + if (criterion.CriterionType != CriterionType.RECIST1Pointt1) + { + throw new Exception("当前标准导出还未支持"); + } + //产生一致性分析的Subject + + var subjectQuerybal = _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.TaskState == TaskState.Effect && t.IsSelfAnalysis == true).Select(t => t.SubjectId).Distinct(); + + var allList = await _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.TaskState == TaskState.Effect && subjectQuerybal.Contains(t.SubjectId) && t.ReadingCategory == ReadingCategory.Visit) + .Where(t => (t.IsSelfAnalysis == true || t.IsSelfAnalysis == null)&& t.VisitTaskNum>0) //一致性分析的结果 + 正常任务的结果 + 仅仅检查批次的结果 +去除基线 + + //.WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) + //.WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + //.WhereIf(queryVisitTask.IsSelfAnalysis != null, t => t.IsSelfAnalysis == queryVisitTask.IsSelfAnalysis) + + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + .WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId) + + .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.ArmEnum != null, t => t.ArmEnum == queryVisitTask.ArmEnum) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => t.Subject.Code.Contains(queryVisitTask.SubjectCode)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + + var list = allList.Where(t => t.IsSelfAnalysis == null).OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList(); + + //To do 根据任务Id 找对评估结果,这里需要考虑标准 以及对应的翻译 + + //基线和检查批次的评估结果翻译枚举 分别对应着 + + foreach (var item in list) + { + //找到一致性分析的结果 + var selfAnalysisTask = allList.Where(t => t.IsSelfAnalysis == true && t.SubjectCode == item.SubjectCode && t.VisitTaskNum == item.VisitTaskNum && t.TaskName == t.TaskName).FirstOrDefault(); + + + //因为基线的评估结果是 是否存在疾病 而 其他检查批次的结果是 整体肿瘤评估结果 是用不同的枚举翻译的 所以这里手动翻译 不把翻译逻辑耦合到通用的翻译代码里面 在此特殊处理 + + item.AgainEvaluateResult = selfAnalysisTask?.EvaluateResult ?? String.Empty; + + //将自身一致性分析的字段 赋值到检查批次任务这个字段 + item.IsAnalysisDiffToOriginalData = selfAnalysisTask?.IsAnalysisDiffToOriginalData; + + } + + + var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.CriterionName = await _repository.Where(u => u.TrialId == queryVisitTask.TrialId && u.IsConfirm && u.Id == queryVisitTask.TrialReadingCriterionId).Select(t => t.CriterionName).FirstOrDefaultAsync(); + exportInfo.List = list; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSelfAnalysisList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(SelftAnalysisExport)); + } + + + /// + /// 组件一致性分析(仅做了resist1.1) + /// + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetGroupAnalysisTaskList_Export(VisitTaskQuery queryVisitTask, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + var criterion = await _repository.Where(t => t.Id == queryVisitTask.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName }).FirstOrDefaultAsync(); + + if (criterion.CriterionType != CriterionType.RECIST1Pointt1) + { + throw new Exception("当前标准导出还未支持"); + } + + //产生组间一致性分析的Subject + + var subjectQuerybal = _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.TaskState == TaskState.Effect && t.IsSelfAnalysis == false).Select(t => t.SubjectId).Distinct(); + + var allList = await _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.TaskState == TaskState.Effect && subjectQuerybal.Contains(t.SubjectId) && t.ReadingCategory == ReadingCategory.Visit) + .Where(t => (t.IsSelfAnalysis == false || t.IsSelfAnalysis == null) && t.VisitTaskNum > 0) //一致性分析的结果 + 正常任务的结果 +仅仅检查批次的结果 + + //.WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) + //.WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + //.WhereIf(queryVisitTask.IsSelfAnalysis != null, t => t.IsSelfAnalysis == queryVisitTask.IsSelfAnalysis) + + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + .WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId) + + .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.ArmEnum != null, t => t.ArmEnum == queryVisitTask.ArmEnum) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => t.Subject.Code.Contains(queryVisitTask.SubjectCode)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + + var list = allList.Where(t => t.IsSelfAnalysis == null).OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList(); + + + var newList = new List(); + + // 每个subject检查批次进行分组 + + foreach (var group in list.GroupBy(t => new { t.SubjectCode, t.VisitTaskNum, t.TaskName, t.ArmEnum }) + .OrderBy(g => g.Key.SubjectCode) + .ThenBy(g => g.Key.VisitTaskNum) + //.ThenBy(g => g.Key.ArmEnum) + ) + { + var subjectVisitGroupList = group.ToList(); + + + //找到当前检查批次组间一致性分析的任务结果 + + var groupTaskList = allList.Where(t => t.IsSelfAnalysis == false && t.SubjectCode == group.Key.SubjectCode && t.VisitTaskNum == group.Key.VisitTaskNum && t.TaskName == group.Key.TaskName).ToList(); + + foreach (var subjectVisitTaskArm in subjectVisitGroupList.OrderBy(t => t.ArmEnum)) + { + + foreach (var item in groupTaskList) + { + var cloneObj = subjectVisitTaskArm.Clone(); + + + cloneObj.AgainUserName = item.UserName; + cloneObj.AgainEvaluateResult = item.EvaluateResult; + cloneObj.AgainArmEnum = item.ArmEnum; + + cloneObj.IsGroupAnalysisDiffToOriginalData = cloneObj.ArmEnum == Arm.DoubleReadingArm1 ? item.IsGroupDiffArm1 : item.IsGroupDiffArm2; + + newList.Add(cloneObj); + } + } + + } + + + + + var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + exportInfo.CriterionName = await _repository.Where(u => u.TrialId == queryVisitTask.TrialId && u.IsConfirm && u.Id == queryVisitTask.TrialReadingCriterionId).Select(t => t.CriterionName).FirstOrDefaultAsync(); + exportInfo.List = newList; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialGroupAnalysisList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(GroupAnalysisExport)); + } + + + public class ExportDocumentDes + { + public string Code { get; set; } + + public string FileName { get; set; } + + public ExportCatogory ExportCatogory { get; set; } + } + + + public enum ExportCatogory + { + // 整体肿瘤评估 + OverallTumorEvaluation = 1, + + //肿瘤疗效评估 + EvaluationOfTumorEfficacy = 2, + + //评估病灶明细 + DetailedOfEvaluatedLesion = 3, + + + } + + /// + /// 获取阅片标准可以导出的列表 + /// + /// + /// + public async Task> GetTrialReadingCriterionCanExportDocumentList(Guid trialReadingCriterionId) + { + + var list = new List(); + var criterion = await _repository.Where(t => t.Id == trialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName }).FirstOrDefaultAsync(); + + if (criterion.CriterionType == CriterionType.RECIST1Pointt1) + { + //list.Add(new ExportDocumentDes() { Code = StaticData.Export.OverallTumorEvaluation_Export, ExportCatogory = ExportCatogory.OverallTumorEvaluation }); + //list.Add(new ExportDocumentDes() { Code = StaticData.Export.RECIST1Point1EvaluationOfTumorEfficacy_Export, ExportCatogory = ExportCatogory.EvaluationOfTumorEfficacy }); + list.Add(new ExportDocumentDes() { Code = StaticData.Export.RECIST1Point1DetailedOfEvaluatedLesion_Export, ExportCatogory = ExportCatogory.DetailedOfEvaluatedLesion }); + } + + if(criterion.CriterionType == CriterionType.PCWG3) + { + list.Add(new ExportDocumentDes() { Code = StaticData.Export.OverallTumorEvaluation_Export, ExportCatogory = ExportCatogory.OverallTumorEvaluation }); + list.Add(new ExportDocumentDes() { Code = StaticData.Export.PCWG3Point1DetailedOfEvaluatedLesion_Export, ExportCatogory = ExportCatogory.DetailedOfEvaluatedLesion }); + } + + var result = _repository.Where(t => list.Select(c => c.Code).Contains(t.Code)).Select(c => new ExportDocumentDes() { Code = c.Code, FileName = c.Name }).ToList(); + + foreach (var item in list) + { + item.FileName = result.Where(t => t.Code == item.Code).FirstOrDefault()?.FileName; + } + + return list; + + } + + + + /// + /// 整体肿瘤评估 (目前仅仅 RECIST1.1 多个标准一个接口 Excel 列是一样的 ) + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetOverallTumorEvaluationList_Export(VisitTaskQuery queryVisitTask, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + + + + + //每次查询必须是单标准的 + var criterion = await _repository.Where(t => t.Id == queryVisitTask.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName }).FirstNotNullAsync(); + + if (criterion.CriterionType != CriterionType.RECIST1Pointt1 && criterion.CriterionType != CriterionType.PCWG3) + { + throw new Exception("当前标准导出还未支持"); + } + + var list = await _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false && t.ReadingTaskState == ReadingTaskState.HaveSigned) + + //.WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) + //.WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + //.WhereIf(queryVisitTask.IsSelfAnalysis != null, t => t.IsSelfAnalysis == queryVisitTask.IsSelfAnalysis) + + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + .WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId) + + .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.ArmEnum != null, t => t.ArmEnum == queryVisitTask.ArmEnum) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => t.Subject.Code.Contains(queryVisitTask.SubjectCode)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider, new { criterionType = criterion.CriterionType }).ToListAsync(); + + list= list.OrderBy(t=>t.SubjectCode).ThenBy(t=>t.VisitTaskNum).ToList(); + + var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + exportInfo.CriterionName = criterion.CriterionName; + + exportInfo.List = list; + + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.OverallTumorEvaluation_Export, exportInfo, $"{exportInfo.TrialCode}_{exportInfo.CriterionName}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(OverallTumorEvaluationExport), criterion.CriterionType); + } + + /// + /// 肿瘤疗效评估表 ( 目前仅仅 RECIST1.1) + /// + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetEvaluationOfTumorEfficacy_Export(VisitTaskQuery queryVisitTask, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + + //每次查询必须是单标准的 + var criterion = await _repository.Where(t => t.Id == queryVisitTask.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName} ).FirstOrDefaultAsync(); + + var query = _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false && t.ReadingTaskState == ReadingTaskState.HaveSigned) + + //.WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) + //.WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + //.WhereIf(queryVisitTask.IsSelfAnalysis != null, t => t.IsSelfAnalysis == queryVisitTask.IsSelfAnalysis) + + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + .WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId) + + .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.ArmEnum != null, t => t.ArmEnum == queryVisitTask.ArmEnum) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => t.Subject.Code.Contains(queryVisitTask.SubjectCode)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)); + + var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + exportInfo.CriterionName = criterion.CriterionName; + + if (criterion.CriterionType == CriterionType.RECIST1Pointt1) + { + var list = await query.ProjectTo(_mapper.ConfigurationProvider, new { criterionType = criterion.CriterionType }).ToListAsync(); + + exportInfo.List = list; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.RECIST1Point1EvaluationOfTumorEfficacy_Export, exportInfo, $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(RECIST1Point1EvaluationOfTumorEfficacyExport), criterion.CriterionType); + } + + else + { + throw new Exception("当前标准导出还未支持"); + } + + + } + + + /// + /// 评估病灶明细表 (目前仅仅 RECIST1.1 RECIST1.1 PGW3 表都是不同的) + /// + /// + /// + /// + /// + /// + [HttpPost] + public async Task GetDetailedOfEvaluatedLesion_Export(VisitTaskQuery queryVisitTask, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository) + { + + //每次查询必须是单标准的 + var criterion = await _repository.Where(t => t.Id == queryVisitTask.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName }).FirstOrDefaultAsync(); + + var query = _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false && t.ReadingTaskState == ReadingTaskState.HaveSigned) + + //.WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) + //.WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + //.WhereIf(queryVisitTask.IsSelfAnalysis != null, t => t.IsSelfAnalysis == queryVisitTask.IsSelfAnalysis) + + .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + .WhereIf(queryVisitTask.SiteId != null, t => t.Subject.SiteId == queryVisitTask.SiteId) + + .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) + .WhereIf(queryVisitTask.DoctorUserId != null, t => t.DoctorUserId == queryVisitTask.DoctorUserId) + .WhereIf(queryVisitTask.ReadingCategory != null, t => t.ReadingCategory == queryVisitTask.ReadingCategory) + .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.ReadingTaskState == queryVisitTask.ReadingTaskState) + .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.TaskAllocationState == queryVisitTask.TaskAllocationState) + .WhereIf(queryVisitTask.ArmEnum != null, t => t.ArmEnum == queryVisitTask.ArmEnum) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) || (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => t.Subject.Code.Contains(queryVisitTask.SubjectCode)) + .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate) + .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)); + + var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + exportInfo.CriterionName = criterion.CriterionName; + + if (criterion.CriterionType == CriterionType.RECIST1Pointt1) + { + var list = await query.ProjectTo(_mapper.ConfigurationProvider, new { criterionType = criterion.CriterionType, isEn_Us = _userInfo.IsEn_Us }).ToListAsync(); + + + var exportList = list.SelectMany(c => + { + + return c.LesionList.Select(u => + { + var clone = c.Clone(); + clone.LessionCode = u.LessionCode; + clone.LessionType = u.LessionType; + + clone.IsLymph = u.IsLymph; + + clone.LessionLocation = u.LessionLocation; + clone.LessionOrgan = u.LessionOrgan; + clone.BodyPartDescription = u.BodyPartDescription; + + //clone.MeasurementResult = u.MeasurementResult; + clone.LongDiameter = u.LongDiameter; + clone.ShortDiameter = u.ShortDiameter; + clone.LessionState = u.LessionState; + + return clone; + }); + }).ToList(); + + exportInfo.List = exportList; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.RECIST1Point1DetailedOfEvaluatedLesion_Export, exportInfo, $"{exportInfo.TrialCode}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(RECIST1Point1DetailedOfEvaluatedLesionExport), criterion.CriterionType); + + + } + else if (criterion.CriterionType == CriterionType.PCWG3) + { + var list = await query.ProjectTo(_mapper.ConfigurationProvider, new { criterionType = criterion.CriterionType, isEn_Us = _userInfo.IsEn_Us }).ToListAsync(); + + + var exportList = list.SelectMany(c => + { + + return c.LesionList.Select(u => + { + var clone = c.Clone(); + clone.LessionCode = u.LessionCode; + clone.LessionType = u.LessionType; + + clone.LessionOrgan = u.LessionOrgan; + clone.LessionLocation = u.LessionLocation; + clone.BodyPartDescription = u.BodyPartDescription; + + clone.LessionState = u.LessionState; + + return clone; + }); + }).ToList(); + + exportInfo.List = exportList; + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.PCWG3Point1DetailedOfEvaluatedLesion_Export, exportInfo, $"{exportInfo.TrialCode}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(PCWG3DetailedOfEvaluatedLesionExport), criterion.CriterionType); + + } + + else + { + throw new Exception("当前标准导出还未支持"); + } + + + } + + + + + #endregion + } +} diff --git a/IRaCIS.Core.Application/Service/Common/FileService.cs b/IRaCIS.Core.Application/Service/Common/FileService.cs new file mode 100644 index 0000000..ecbce02 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/FileService.cs @@ -0,0 +1,212 @@ +using IRaCIS.Application.Interfaces; +using System.Text; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Application.Helper; +using Microsoft.AspNetCore.Hosting; + +namespace IRaCIS.Application.Services +{ + public class FileService : IFileService + { + + private readonly IDoctorService _doctorService; + private readonly IAttachmentService _attachmentService; + private readonly IWebHostEnvironment _hostEnvironment; + private string defaultUploadFilePath = string.Empty; + private readonly ILogger _logger; + public FileService(IDoctorService doctorService, IAttachmentService attachmentService, + IWebHostEnvironment hostEnvironment, ILogger logger) + { + _doctorService = doctorService; + _attachmentService = attachmentService; + _hostEnvironment = hostEnvironment; + + defaultUploadFilePath = FileStoreHelper.GetIRaCISRootPath(_hostEnvironment); + _logger = logger; + } + + /// + /// 打包医生官方简历 + /// + /// + /// + /// + public async Task CreateOfficialResumeZip(int language, Guid[] doctorIds) + { + + //准备下载文件的临时路径 + var guidStr = Guid.NewGuid().ToString(); + + string uploadFolderPath = Path.Combine(FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment), "UploadFile"); + + + + var tempSavePath = Path.Combine(uploadFolderPath, "temp", guidStr); //待压缩的文件夹,将需要下载的文件拷贝到此文件夹 + if (!Directory.Exists(tempSavePath)) + { + Directory.CreateDirectory(tempSavePath); + } + + //找到服务器简历路径 循环拷贝简历到临时路径 + foreach (var doctorId in doctorIds) + { + var doctor = await _doctorService.GetBasicInfo(doctorId); + var doctorName = doctor.FirstName + "_" + doctor.LastName; + + //找官方简历存在服务器的相对路径 + var sourceCvPath = await _attachmentService.GetDoctorOfficialCV(language, doctorId); + if (!string.IsNullOrWhiteSpace(sourceCvPath)) + { + //服务器简历文件实际路径 + //var sourceCvFullPath = HostingEnvironment.MapPath(sourceCvPath); + var sourceCvPathTemp = sourceCvPath.Substring(1, sourceCvPath.Length - 1);//.Replace('/','\\'); + string sourceCvFullPath = Path.Combine(defaultUploadFilePath, sourceCvPathTemp); + + var arr = sourceCvPath.Split('.'); + string extensionName = arr[arr.Length - 1]; //得到扩展名 + + //需要拷贝到的路径 + var doctorPath = Path.Combine(tempSavePath, doctor.ReviewerCode.ToString() + "_" + doctorName + "." + extensionName); + + if (File.Exists(sourceCvFullPath)) + { + File.Copy(sourceCvFullPath, doctorPath, true); + } + } + } + + //创建ZIP + DateTime now = DateTime.Now; + StringBuilder sb = new StringBuilder(); + sb.Append(now.Year).Append(now.Month.ToString().PadLeft(2, '0')).Append(now.Day.ToString().PadLeft(2, '0')) + .Append(now.Hour.ToString().PadLeft(2, '0')).Append(now.Minute.ToString().PadLeft(2, '0')) + .Append(now.Second.ToString().PadLeft(2, '0')).Append(now.Millisecond.ToString().PadLeft(3, '0')); + + string targetZipPath = Path.Combine(uploadFolderPath, "CV_" + sb.ToString() + ".zip"); + ZipHelper.CreateZip(tempSavePath, targetZipPath); + + //返回Zip路径 + return Path.Combine("/IRaCISData/UploadFile/", "CV_" + sb.ToString() + ".zip"); + } + + /// + /// 打包医生的所有附件 + /// + /// + /// + public async Task CreateDoctorsAllAttachmentZip(Guid[] doctorIds) + { + //准备下载文件的临时路径 + var guidStr = Guid.NewGuid().ToString(); + //string uploadFolderPath = HostingEnvironment.MapPath("/UploadFile/"); + string uploadFolderPath = Path.Combine(FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment), "UploadFile"); + var tempSavePath = Path.Combine(uploadFolderPath, "temp", guidStr); //待压缩的文件夹,将需要下载的文件拷贝到此文件夹 + if (!Directory.Exists(tempSavePath)) + { + Directory.CreateDirectory(tempSavePath); + } + + + foreach (var doctorId in doctorIds) + { + //获取医生基本信息 + var doctor = await _doctorService.GetBasicInfo(doctorId); + var doctorName = doctor.FirstName + "_" + doctor.LastName; + var doctorCode = doctor.ReviewerCode; + + var doctorDestPath = Path.Combine(tempSavePath, doctorCode + "_" + doctorName); + if (!Directory.Exists(doctorDestPath)) + { + Directory.CreateDirectory(doctorDestPath); + } + + //服务器上传后的源路径 + string doctorFileSourcePath = Path.Combine(uploadFolderPath, doctorId.ToString()); + + if (Directory.Exists(doctorFileSourcePath)) + { + CopyDirectory(doctorFileSourcePath, doctorDestPath); + } + } + string target = Guid.NewGuid().ToString(); + string targetPath = Path.Combine(uploadFolderPath, target + ".zip"); + ZipHelper.CreateZip(tempSavePath, targetPath); + return Path.Combine("/IRaCISData/UploadFile/", target + ".zip"); + } + + + public async Task CreateZipPackageByAttachment(Guid doctorId, Guid[] attachmentIds) + { + var doctor = await _doctorService.GetBasicInfo(doctorId); + var doctorName = doctor.FirstName + "_" + doctor.LastName; + + Guid temp = Guid.NewGuid(); + //string root = HostingEnvironment.MapPath("/UploadFile/"); //文件根目录 + string root = Path.Combine(defaultUploadFilePath, "UploadFile"); + + var tempPath = Path.Combine(root, "temp", temp.ToString(), doctor.ReviewerCode + doctorName); //待压缩的文件夹,将需要下载的文件拷贝到此文件夹 + var packagePath = Path.Combine(root, "temp", temp.ToString()); //打包目录 + if (!Directory.Exists(tempPath)) + { + Directory.CreateDirectory(tempPath); + } + var attachemnts = (await _attachmentService.GetAttachments(doctorId)).Where(a => attachmentIds.Contains(a.Id)); + + foreach (var item in attachemnts) + { + var arr = item.Path.Trim().Split('/'); + var myPath = string.Empty; + var myFile = string.Empty; + //需要改进 + if (arr.Length > 0) + { + myFile = arr[arr.Length - 1]; + foreach (var arrItem in arr) + { + if (arrItem != string.Empty && !"UploadFile".Equals(arrItem)) + { + myPath += (arrItem + "/"); + } + } + myPath = myPath.TrimEnd('/'); + } + var sourcePath = Path.Combine(root, myPath); + + if (!string.IsNullOrWhiteSpace(sourcePath) && File.Exists(sourcePath)) + { + File.Copy(sourcePath, Path.Combine(tempPath, myFile), true); + } + } + string target = Guid.NewGuid().ToString(); + string targetPath = Path.Combine(root, target + ".zip"); + ZipHelper.CreateZip(packagePath, targetPath); + + return Path.Combine("/IRaCISData/UploadFile/", target + ".zip"); + } + + private static void CopyDirectory(string srcPath, string destPath) + { + + DirectoryInfo dir = new DirectoryInfo(srcPath); + FileSystemInfo[] fileInfoArray = dir.GetFileSystemInfos(); //获取目录下(不包含子目录)的文件和子目录 + foreach (FileSystemInfo fileInfo in fileInfoArray) + { + if (fileInfo is DirectoryInfo) //判断是否文件夹 + { + if (!Directory.Exists(destPath + "\\" + fileInfo.Name)) + { + Directory.CreateDirectory(destPath + "\\" + fileInfo.Name); //目标目录下不存在此文件夹即创建子文件夹 + } + CopyDirectory(fileInfo.FullName, destPath + "\\" + fileInfo.Name); //递归调用复制子文件夹 + } + else + { + File.Copy(fileInfo.FullName, destPath + "\\" + fileInfo.Name, true); //不是文件夹即复制文件,true表示可以覆盖同名文件 + } + } + + } + } +} diff --git a/IRaCIS.Core.Application/Service/Common/Interface/ICommonDocumentService.cs b/IRaCIS.Core.Application/Service/Common/Interface/ICommonDocumentService.cs new file mode 100644 index 0000000..ef38e62 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/Interface/ICommonDocumentService.cs @@ -0,0 +1,24 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-31 13:18:53 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// ICommonDocumentService + /// + public interface ICommonDocumentService + { + + + Task> GetCommonDocumentList(CommonDocumentQuery queryCommonDocument); + + Task AddOrUpdateCommonDocument(CommonDocumentAddOrEdit addOrEditCommonDocument); + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Common/Interface/IDictionaryService.cs b/IRaCIS.Core.Application/Service/Common/Interface/IDictionaryService.cs new file mode 100644 index 0000000..a30dfd5 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/Interface/IDictionaryService.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using EasyCaching.Core.Interceptor; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IDictionaryService + { + /// + /// 获取是和否 + /// + /// + /// + Task GetBoolValueState(bool value); + + /// + /// 获取审核状态 + /// + /// + /// + /// + /// + Task GetAuditState(Guid trial, T childCode); + + /// + /// 获取枚举翻译 + /// + /// + /// + /// + /// + Task GetBasicDataTranslateItem(string parentCode, T childCode); + + Task>> GetBasicDataSelect(string[] searchArray); + + } +} diff --git a/IRaCIS.Core.Application/Service/Common/Interface/IEmailNoticeConfigService.cs b/IRaCIS.Core.Application/Service/Common/Interface/IEmailNoticeConfigService.cs new file mode 100644 index 0000000..eca5e5f --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/Interface/IEmailNoticeConfigService.cs @@ -0,0 +1,15 @@ +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-02-15 13:11:20 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IEmailNoticeConfigService + { + Task AddOrUpdateEmailNoticeConfig(EmailNoticeConfigAddOrEdit addOrEditEmailNoticeConfig); + Task DeleteEmailNoticeConfig(Guid emailNoticeConfigId); + Task> GetEmailNoticeConfigList(EmailNoticeConfigQuery queryEmailNoticeConfig); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Common/Interface/IFileService.cs b/IRaCIS.Core.Application/Service/Common/Interface/IFileService.cs new file mode 100644 index 0000000..10c6c07 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/Interface/IFileService.cs @@ -0,0 +1,17 @@ +using System; + +namespace IRaCIS.Application.Interfaces +{ + public interface IFileService + { + + //IResponseOutput DownloadOfficialResume(Guid[] doctorIds); + + + Task CreateOfficialResumeZip(int language, Guid[] doctorIds); + + Task CreateDoctorsAllAttachmentZip(Guid[] doctorIds); + + Task CreateZipPackageByAttachment(Guid doctorId, Guid[] attachmentIds); + } +} diff --git a/IRaCIS.Core.Application/Service/Common/Interface/IFrontAuditConfigService.cs b/IRaCIS.Core.Application/Service/Common/Interface/IFrontAuditConfigService.cs new file mode 100644 index 0000000..b8c0cab --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/Interface/IFrontAuditConfigService.cs @@ -0,0 +1,25 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-28 16:46:20 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// IFrontAuditConfigService + /// + public interface IFrontAuditConfigService + { + + + Task> GetFrontAuditConfigList(FrontAuditConfigQuery queryFrontAuditConfig); + + Task AddOrUpdateFrontAuditConfig(FrontAuditConfigAddOrEdit addOrEditFrontAuditConfig); + + Task DeleteFrontAuditConfig(Guid frontAuditConfigId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/Common/Interface/ILogService.cs b/IRaCIS.Core.Application/Service/Common/Interface/ILogService.cs new file mode 100644 index 0000000..706b3a0 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/Interface/ILogService.cs @@ -0,0 +1,21 @@ +using System; +using IRaCIS.Application.Contracts; +using System.Collections.Generic; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface ILogService + { + IResponseOutput SaveLog2Db(SystemLogDTO viewModel); + PageOutput GetLogList(QueryLogQueryDTO param); + + PageOutput GetAuditList(AuditQueryDTO param); + + List GetOptUserList(Guid trialId); + + List GetSubjectList(Guid trialId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/Common/Interface/IMessageService.cs b/IRaCIS.Core.Application/Service/Common/Interface/IMessageService.cs new file mode 100644 index 0000000..ddaf0c2 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/Interface/IMessageService.cs @@ -0,0 +1,15 @@ +using System; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IMessageService + { + int GetUnReadMessageCount(Guid doctorId); + IResponseOutput DeleteSysMessage(Guid messageId); + IResponseOutput MarkedAsRead(Guid messageId); + + PageOutput GetMessageList(Guid doctorId, int pageSize, int pageIndex); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Common/Interface/ISystemBasicDataService.cs b/IRaCIS.Core.Application/Service/Common/Interface/ISystemBasicDataService.cs new file mode 100644 index 0000000..c0b8844 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/Interface/ISystemBasicDataService.cs @@ -0,0 +1,25 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-02-15 15:47:41 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + + +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// ISystemBasicDataService + /// + public interface ISystemBasicDataService + { + + + Task> GetSystemBasicDataList(SystemBasicDataQuery querySystemBasicData); + + Task AddOrUpdateSystemBasicData(SystemBasicDataAddOrEdit addOrEditSystemBasicData); + + Task DeleteSystemBasicData(Guid systemBasicDataId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/Common/MailService.cs b/IRaCIS.Core.Application/Service/Common/MailService.cs new file mode 100644 index 0000000..a0ae80c --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/MailService.cs @@ -0,0 +1,704 @@ +using IRaCIS.Core.Domain.Share; +using MimeKit; +using IRaCIS.Core.Application.Helper; +using MailKit; +using Microsoft.AspNetCore.Hosting; +using IRaCIS.Core.Application.Auth; +using AutoMapper; +using IRaCIS.Application.Contracts; +using Nito.AsyncEx; +using Microsoft.Extensions.Options; + +namespace IRaCIS.Application.Services +{ + public interface IMailVerificationService + { + + Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode); + + Task SendEmailVerification(string emailAddress, int verificationCode); + + Task SiteSurveyRejectEmail(MimeMessage messageToSend); + + Task SendMailEditEmail(Guid userId, string userName, string emailAddress, int verificationCode); + + Task AnolymousSendEmailForResetAccount(string emailAddress, int verificationCode); + + Task AddUserSendEmailAsync(Guid userId, string baseUrl, string routeUrl); + + Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456"); + + Task SiteSurveyUserJoinEmail(Guid trialId, Guid userId, string baseUrl, string rootUrl); + + Task ExternalUserJoinEmail(Guid trialId, Guid userId, string baseUrl, string rootUrl); + + Task DoctorJoinTrialEmail(Guid trialId, Guid doctorId, string baseUrl, string rootUrl); + + + } + + public class MailVerificationService : IMailVerificationService + { + private readonly IRepository _verificationCodeRepository; + + private readonly IRepository _systemBasicDatarepository; + + private readonly IWebHostEnvironment _hostEnvironment; + private readonly IRepository _userRepository; + + private readonly ITokenService _tokenService; + + private readonly IMapper _mapper; + + private readonly IRepository _trialRepository; + private readonly IRepository _userTypeRepository; + + private readonly IRepository _doctorTypeRepository; + + private readonly AsyncLock _mutex = new AsyncLock(); + + private readonly SystemEmailSendConfig _systemEmailConfig; + + + + public MailVerificationService(IRepository verificationCodeRepository, + IRepository systemBasicDatarepository, + IWebHostEnvironment hostEnvironment, + IRepository userRepository, + ITokenService tokenService, + IRepository trialRepository, + IRepository userTypeRepository, + IRepository doctorTypeRepository, + IMapper mapper, IOptionsMonitor systemEmailConfig) + { + _systemEmailConfig = systemEmailConfig.CurrentValue; + _verificationCodeRepository = verificationCodeRepository; + _systemBasicDatarepository = systemBasicDatarepository; + + _hostEnvironment = hostEnvironment; + + _mapper = mapper; + + _tokenService = tokenService; + _userRepository = userRepository; + _trialRepository = trialRepository; + + _userTypeRepository = userTypeRepository; + _doctorTypeRepository = doctorTypeRepository; + + } + + //重置邮箱 + public async Task SendMailEditEmail(Guid userId, string userName, string emailAddress, int verificationCode) + { + + + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + //收件地址 + messageToSend.To.Add(new MailboxAddress(userName, emailAddress)); + //主题 + messageToSend.Subject = "[来自行藏] 关于重置邮箱的提醒"; + + var builder = new BodyBuilder(); + + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + "UserOptCommon.html"; + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + + builder.HtmlBody = string.Format(templateInfo, + $" 尊敬的 {userName} , ", + "您正在进行邮箱重置操作", + verificationCode + ); + } + + + messageToSend.Body = builder.ToMessageBody(); + + + + EventHandler sucessHandle = (sender, args) => + { + // args.Response + var code = verificationCode.ToString(); + _ = _verificationCodeRepository.AddAsync(new VerificationCode() + { + CodeType = 0, + HasSend = true, + Code = code, + UserId = userId, + ExpirationTime = DateTime.Now.AddMinutes(3) + }).Result; + _ = _verificationCodeRepository.SaveChangesAsync().Result; + + }; + + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); + + + } + + + + //不登录 通过邮箱重置密码 + public async Task AnolymousSendEmailForResetAccount(string emailAddress, int verificationCode) + { + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + //收件地址 + messageToSend.To.Add(new MailboxAddress(String.Empty, emailAddress)); + //主题 + messageToSend.Subject = "[来自行藏] 关于重置密码的提醒"; + + + + var builder = new BodyBuilder(); + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + "UserOptCommon.html"; + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + + builder.HtmlBody = string.Format(templateInfo, + "", + "您正在进行邮箱重置密码操作", + verificationCode + ); + } + + + + messageToSend.Body = builder.ToMessageBody(); + + + EventHandler sucessHandle = (sender, args) => + { + var code = verificationCode.ToString().Trim(); + _ = _verificationCodeRepository.AddAsync(new VerificationCode() + { + CodeType = Core.Domain.Share.VerifyType.Email, + HasSend = true, + Code = code, + UserId = Guid.Empty,//此时不知道用户 + EmailOrPhone = emailAddress, + ExpirationTime = DateTime.Now.AddMinutes(3) + }).Result; + _ = _verificationCodeRepository.SaveChangesAsync().Result; + }; + + + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); + } + + /// + /// 发送验证码 + /// + /// + /// + /// + public async Task SendEmailVerification(string emailAddress, int verificationCode) + { + + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + //收件地址 + messageToSend.To.Add(new MailboxAddress(String.Empty, emailAddress)); + //主题 + messageToSend.Subject = $"[来自行藏]的提醒"; + + + + + var builder = new BodyBuilder(); + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + "UserOptCommon.html"; + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + + builder.HtmlBody = string.Format(templateInfo, + "", + "您正在参与展影医疗IRC项目", + verificationCode + ); + } + + messageToSend.Body = builder.ToMessageBody(); + + + + EventHandler sucessHandle = (sender, args) => + { + // args.Response + var code = verificationCode.ToString(); + _ = _verificationCodeRepository.AddAsync(new VerificationCode() + { + CodeType = VerifyType.Email, + HasSend = true, + Code = code, + UserId = Guid.Empty,//此时不知道用户 + EmailOrPhone = emailAddress, + ExpirationTime = DateTime.Now.AddMinutes(3) + }).Result; + _ = _verificationCodeRepository.SaveChangesAsync().Result; + }; + + + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); + + + } + + + //中心调研 登陆 + public async Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode) + { + + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + //收件地址 + messageToSend.To.Add(new MailboxAddress(String.Empty, emailAddress)); + //主题 + messageToSend.Subject = $"[来自行藏] [{researchProgramNo}] 关于中心调研的提醒"; + + + + + var builder = new BodyBuilder(); + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + "UserOptCommon.html"; + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + + builder.HtmlBody = string.Format(templateInfo, + "", + "您正在参与展影医疗IRC项目中心调研工作", + verificationCode + ); + } + + messageToSend.Body = builder.ToMessageBody(); + + + + EventHandler sucessHandle = (sender, args) => + { + // args.Response + var code = verificationCode.ToString(); + _ = _verificationCodeRepository.AddAsync(new VerificationCode() + { + CodeType = VerifyType.Email, + HasSend = true, + Code = code, + UserId = Guid.Empty,//此时不知道用户 + EmailOrPhone = emailAddress, + ExpirationTime = DateTime.Now.AddMinutes(3) + }).Result; + _ = _verificationCodeRepository.SaveChangesAsync().Result; + }; + + + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig,sucessHandle); + + + } + + public async Task SiteSurveyRejectEmail(MimeMessage messageToSend) + { + + //发件地址 + messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + + + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); + + } + + //添加用户发送邮件 + public async Task AddUserSendEmailAsync(Guid userId, string baseUrl, string routeUrl) + { + + var sysUserInfo = (await _userRepository.Where(t => t.Id == userId).Include(t => t.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException(); + + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + //收件地址 + messageToSend.To.Add(new MailboxAddress(sysUserInfo.FullName, sysUserInfo.EMail)); + //主题 + messageToSend.Subject = "[来自行藏] 关于创建账户的提醒"; + + + + var builder = new BodyBuilder(); + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + "AdminAddUser.html"; + + var token = _tokenService.GetToken(IRaCISClaims.Create(_mapper.Map(sysUserInfo))); + + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new User() { EmailToken = token }); + + routeUrl = routeUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + sysUserInfo.UserTypeRole.UserTypeShortName + "&access_token=" + token; + + var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); + + var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + + builder.HtmlBody = string.Format(templateInfo, + sysUserInfo.FullName, + sysUserInfo.UserName, + sysUserInfo.UserTypeRole.UserTypeShortName, + redirectUrl + ); + } + + + + messageToSend.Body = builder.ToMessageBody(); + + + await SendEmailHelper.SendEmailAsync(messageToSend,_systemEmailConfig); + } + + //管理员重置密码发送邮件 + public async Task AdminResetPwdSendEmailAsync(Guid userId, string pwdNotMd5 = "123456") + { + var sysUserInfo = (await _userRepository.Where(t => t.Id == userId).Include(t => t.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException(); + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + //收件地址 + messageToSend.To.Add(new MailboxAddress(sysUserInfo.FullName, sysUserInfo.EMail)); + //主题 + messageToSend.Subject = "[来自行藏] 关于重置账户密码的提醒"; + + + + var builder = new BodyBuilder(); + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + "AdminResetUser.html"; + + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + + builder.HtmlBody = string.Format(templateInfo, + sysUserInfo.FullName, + sysUserInfo.UserName, + sysUserInfo.UserTypeRole.UserTypeShortName, + pwdNotMd5 + ); + } + + + + messageToSend.Body = builder.ToMessageBody(); + + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); + } + + + //Site调研 用户加入项目 + public async Task SiteSurveyUserJoinEmail(Guid trialId, Guid userId, string baseUrl, string rootUrl) + { + var sysUserInfo = (await _userRepository.Where(t => t.Id == userId).Include(t => t.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException(); + + var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + //收件地址 + messageToSend.To.Add(new MailboxAddress(sysUserInfo.FullName, sysUserInfo.EMail)); + //主题 + messageToSend.Subject = $"[来自行藏] [{trialInfo.ResearchProgramNo}]邀请信"; + + + + var builder = new BodyBuilder(); + + + var token = _tokenService.GetToken(IRaCISClaims.Create(_mapper.Map(sysUserInfo))); + + if (sysUserInfo.IsFirstAdd) + { + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new User() { EmailToken = token }); + } + + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + (sysUserInfo.IsFirstAdd ? "TrialUserFirstJoin.html" : "TrialUserExistJoin.html"); + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + sysUserInfo.UserTypeRole.UserTypeShortName + "&access_token=" + token; + + var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); + + var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; + + builder.HtmlBody = string.Format(templateInfo, + sysUserInfo.FullName, + trialInfo.ExperimentName, + trialInfo.ResearchProgramNo, + trialInfo.TrialCode, + sysUserInfo.UserName, + sysUserInfo.UserTypeRole.UserTypeShortName, + sysUserInfo.IsFirstAdd ? redirectUrl : baseUrl + ); + } + + messageToSend.Body = builder.ToMessageBody(); + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig,null); + + + } + + //外部用户 加入项目 + public async Task ExternalUserJoinEmail(Guid trialId, Guid userId, string baseUrl, string rootUrl) + { + var trialInfo = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId)).IfNullThrowException(); + + var sysUserInfo = (await _userRepository.Where(t => t.Id == userId).Include(t => t.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException(); + + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + //收件地址 + messageToSend.To.Add(new MailboxAddress(String.Empty, sysUserInfo.EMail)); + //主题 + messageToSend.Subject = $"[来自行藏] [{trialInfo.ResearchProgramNo}]邀请信"; + + + var builder = new BodyBuilder(); + + + + var token = _tokenService.GetToken(IRaCISClaims.Create(_mapper.Map(sysUserInfo))); + if (sysUserInfo.IsFirstAdd) + { + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new User() { EmailToken = token }); + } + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + (sysUserInfo.IsFirstAdd ? "TrialUserFirstJoin.html" : "TrialUserExistJoin.html"); + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); + + var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + sysUserInfo.UserTypeRole.UserTypeShortName + "&access_token=" + token; + + var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; + + builder.HtmlBody = string.Format(templateInfo, + sysUserInfo.FullName, + trialInfo.ExperimentName, + trialInfo.ResearchProgramNo, + trialInfo.TrialCode, + sysUserInfo.UserName, + sysUserInfo.UserTypeRole.UserTypeShortName, + sysUserInfo.IsFirstAdd ? redirectUrl : baseUrl + ); + } + + + + messageToSend.Body = builder.ToMessageBody(); + + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null); + + } + + + //医生生成账号加入 或者已存在账号加入到项目中 + public async Task DoctorJoinTrialEmail(Guid trialId, Guid doctorId, string baseUrl, string rootUrl) + { + var doctor = await _doctorTypeRepository.FindAsync(doctorId); + User sysUserInfo = null; + + var userType = await _userTypeRepository.FirstAsync(t => t.UserTypeEnum == UserTypeEnum.IndependentReviewer); + + using (await _mutex.LockAsync()) + { + var isDoctorHaveAccount = await _userRepository.AnyAsync(t => t.DoctorId == doctorId); + + + if (!isDoctorHaveAccount) + { + + var saveItem = new User() { FirstName = doctor.FirstName, LastName = doctor.LastName, EMail = doctor.EMail }; + + var trialType = await _trialRepository.Where(t => t.Id == trialId).Select(t => t.TrialType).FirstOrDefaultAsync(); + + if (trialType == TrialType.NoneOfficial) + { + saveItem.IsTestUser = true; + } + + + saveItem.Code = _userRepository.Select(t => t.Code).DefaultIfEmpty().Max() + 1; + + saveItem.UserCode = AppSettings.GetCodeStr(saveItem.Code, nameof(User)); + + saveItem.UserName = saveItem.UserCode; + + saveItem.UserTypeEnum = UserTypeEnum.IndependentReviewer; + + saveItem.DoctorId = doctorId; + saveItem.UserTypeId = userType.Id; + + var savedUser = await _userRepository.AddAsync(saveItem); + + //下面获取Token 需要这部分信息 + sysUserInfo = savedUser.Clone(); + + sysUserInfo.UserTypeRole = userType; + + await _userRepository.SaveChangesAsync(); + + } + else + { + sysUserInfo = (await _userRepository.Where(t => t.DoctorId == doctorId).Include(t => t.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException(); + } + } + + + var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + //收件地址 + messageToSend.To.Add(new MailboxAddress(doctor.FullName, doctor.EMail)); + //主题 + messageToSend.Subject = $"[来自行藏] [{trialInfo.ResearchProgramNo}]邀请信"; + + + var builder = new BodyBuilder(); + + + var basicInfo = IRaCISClaims.Create(_mapper.Map(sysUserInfo)); + + ////第一次添加的时候 注意赋值 + //basicInfo.PermissionStr = userType.PermissionStr; + //basicInfo.UserTypeShortName = userType.UserTypeShortName; + + var token = _tokenService.GetToken(basicInfo); + + if (sysUserInfo.IsFirstAdd) + { + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new User() { EmailToken = token }); + } + + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + (sysUserInfo.IsFirstAdd ? "TrialDoctorFirstJoin.html" : "TrialDoctorExistJoin.html"); + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + userType.UserTypeShortName + "&access_token=" + token; + + var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); + + var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; + + builder.HtmlBody = string.Format(templateInfo, + sysUserInfo.FullName, + trialInfo.ExperimentName, + trialInfo.ResearchProgramNo, + trialInfo.TrialCode, + sysUserInfo.UserName, + userType.UserTypeShortName, + sysUserInfo.IsFirstAdd ? redirectUrl : baseUrl + ); + } + + messageToSend.Body = builder.ToMessageBody(); + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null); + + return sysUserInfo.Id; + } + + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Common/SystemBasicDataService.cs b/IRaCIS.Core.Application/Service/Common/SystemBasicDataService.cs new file mode 100644 index 0000000..b45df75 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/SystemBasicDataService.cs @@ -0,0 +1,108 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-02-15 15:57:21 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.Contracts; +using Microsoft.AspNetCore.Mvc; +namespace IRaCIS.Core.Application.Services +{ + /// + /// SystemBasicDataService + /// + [ApiExplorerSettings(GroupName = "Common")] + public class SystemBasicDataService : BaseService, ISystemBasicDataService + { + private readonly IRepository _systemBasicDataRepository; + + public SystemBasicDataService(IRepository systemBasicDataRepository) { + this._systemBasicDataRepository = systemBasicDataRepository; + } + + + /// + /// 模板列表 + /// + /// + /// + [HttpPost] + public async Task> GetSystemBasicDataList(SystemBasicDataQuery querySystemBasicData) + { + var systemBasicDataQueryable = _systemBasicDataRepository.Where(t => t.ParentId == null) + .ProjectTo(_mapper.ConfigurationProvider); + + return await systemBasicDataQueryable.ToPagedListAsync(querySystemBasicData.PageIndex, querySystemBasicData.PageSize, String.IsNullOrEmpty(querySystemBasicData.SortField) ? "Code" : querySystemBasicData.SortField, querySystemBasicData.Asc); + } + + + [HttpGet("{code}")] + public async Task GetSystemBasicData(string code) + { + return (await _repository.Where(t => t.Code == code).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + } + + + /// + /// 模板关联的场景 + /// + /// + /// + [HttpGet("{parentId:guid}")] + public async Task> GetChildList(Guid parentId) + { + return await _systemBasicDataRepository.Where(t => t.ParentId == parentId&&t.IsEnable).OrderBy(t => t.Code).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + + public async Task AddOrUpdateSystemBasicData(SystemBasicDataAddOrEdit addOrEditSystemBasicData) + { + + if (addOrEditSystemBasicData.Id == null) + { + var entity = _mapper.Map(addOrEditSystemBasicData); + await _systemBasicDataRepository.AddAsync(entity, true); + return ResponseOutput.Ok(entity.Id.ToString()); + } + else + { + + var entity =await _systemBasicDataRepository.Where(t => t.Id == addOrEditSystemBasicData.Id,true).FirstOrDefaultAsync(); + _mapper.Map(addOrEditSystemBasicData, entity); + await _systemBasicDataRepository.SaveChangesAsync(); + + //var entity = _mapper.Map(addOrEditSystemBasicData); + //await _systemBasicDataRepository.UpdateFromDTOAsync(entity); + //await _systemBasicDataRepository.SaveChangesAsync(); + } + return ResponseOutput.Ok(true); + } + + + + + [HttpDelete("{systemBasicDataId:guid}")] + public async Task DeleteSystemBasicData(Guid systemBasicDataId) + { + var success = await _systemBasicDataRepository.DeleteFromQueryAsync(x=>x.Id== systemBasicDataId); + await _systemBasicDataRepository.SaveChangesAsync(); + return ResponseOutput.Result(true); + } + + + /// + /// 传递父亲Code 数组 返回多个下拉框数据 + /// + /// + /// + [HttpPost] + public async Task>> GetBasicDataSelect(string[] searchArray) + { + + var searchList = await _systemBasicDataRepository.Where(t => searchArray.Contains(t.Parent.Code) && t.ParentId != null).ProjectTo(_mapper.ConfigurationProvider,new { _userInfo.IsEn_Us}).ToListAsync(); + + return searchList.GroupBy(t => t.ParentCode).ToDictionary(g => g.Key, g => g.ToList()); + + } + } +} diff --git a/IRaCIS.Core.Application/Service/Common/_MapConfig.cs b/IRaCIS.Core.Application/Service/Common/_MapConfig.cs new file mode 100644 index 0000000..eeb64e0 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/_MapConfig.cs @@ -0,0 +1,64 @@ +using AutoMapper; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Application.Service +{ + public class CommonConfig : Profile + { + public CommonConfig() + { + CreateMap() + .ForMember(o => o.MessageTime, t => t.MapFrom(u => u.MessageTime.ToString())); + + CreateMap(); + + CreateMap(); + + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + CreateMap(); + + + CreateMap(); + + var isEn_Us = false; + CreateMap() + .ForMember(o => o.ParentCode, t => t.MapFrom(u => u.Parent.Code)) + .ForMember(o => o.Value, t => t.MapFrom(u => isEn_Us? u.Value:u.ValueCN)); + + CreateMap().ReverseMap(); + + + + CreateMap() + .ForMember(o => o.ConfigType, t => t.MapFrom(u => u.ConfigDictionary.Code)); + + CreateMap().ReverseMap(); + + CreateMap() .ForMember(o => o.ChildList, t => t.Ignore()); + CreateMap(); + + CreateMap() + .ForMember(o => o.ParentChildCodeEnum, t => t.MapFrom(u => u.Parent.ChildCodeEnum)) + .ForMember(o => o.Value, t => t.MapFrom(u => u.MappedValue)) + .ForMember(o => o.ParentCode, t => t.MapFrom(u => u.Parent.Code)); + + CreateMap() + .ForMember(o => o.ParentChildCodeEnum, t => t.MapFrom(u => u.Parent.ChildCodeEnum)) + .ForMember(o => o.ParentCode, t => t.MapFrom(u => u.Parent.Code)); + + var token = ""; + CreateMap() + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path + "?access_token=" + token)); + + CreateMap().ReverseMap(); + + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/Doctor/AttachmentService.cs b/IRaCIS.Core.Application/Service/Doctor/AttachmentService.cs new file mode 100644 index 0000000..0d1f651 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/AttachmentService.cs @@ -0,0 +1,254 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Application.Services +{ + /// + /// 医生文档关联关系维护 + /// + [ApiExplorerSettings(GroupName = "Reviewer")] + public class AttachmentService : BaseService, IAttachmentService + { + private readonly IRepository _attachmentrepository; + + public AttachmentService(IRepository attachmentrepository) + { + this._attachmentrepository = attachmentrepository; + } + + /// + /// 删除附件 + /// + /// + /// + + public async Task DeleteAttachment([FromBody]AttachementCommand param) + { + //var attachment = _doctorAttachmentApp.GetDetailById(id); + //string file = HostingEnvironment.MapPath(attachment.Path); + + //if (File.Exists(file)) + //{ + // File.Delete(file); + //} + //var temp = HostingEnvironment.MapPath(param.Path); + //if (File.Exists(temp)) + //{ + // File.Delete(temp); + //} + + var success =await _attachmentrepository.BatchDeleteNoTrackingAsync(a => a.Id == param.Id); + return ResponseOutput.Result(success); + } + + + /// + /// 根据医生Id 和 附件类型,获取记录 + /// + /// 医生Id + /// 附件类型 + /// + [HttpGet("{doctorId:guid}/{type}")] + public async Task> GetAttachmentByType(Guid doctorId, string type) + { + var attachmentList = await _attachmentrepository.Where(a => a.DoctorId == doctorId && a.Type.Equals(type)).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + attachmentList.ForEach(t => t.FullPath = t.Path + "?access_token=" + _userInfo.UserToken); + + return attachmentList; + } + + /// + /// 获取单个医生的多种证书附件 + /// + /// 医生Id + /// 类型数组 + /// + [HttpPost("{doctorId:guid}")] + public async Task> GetAttachmentByTypes(Guid doctorId, string[] types) + { + + var attachmentList =await _attachmentrepository.Where(a => a.DoctorId == doctorId && types.Contains(a.Type)).OrderBy(s => s.Type).ThenBy(m => m.CreateTime).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + attachmentList.ForEach(t => t.FullPath = t.Path + "?access_token=" + _userInfo.UserToken); + + return attachmentList; + } + + /// + /// 根据医生Id获取医生附件 + /// + /// 医生Id + /// + [HttpGet("{doctorId:guid}")] + public async Task> GetAttachments(Guid doctorId) + { + var attachmentList =await _attachmentrepository.Where(a => a.DoctorId == doctorId).OrderBy(s => s.Type).ThenBy(m => m.CreateTime).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + attachmentList.ForEach(t => t.FullPath = t.Path + "?access_token=" + _userInfo.UserToken); + + return attachmentList; + } + + [NonDynamicMethod] + public async Task GetDetailById(Guid attachmentId) + { + var attachment = await _attachmentrepository.FirstOrDefaultAsync(a => a.Id == attachmentId).IfNullThrowException(); + var temp= _mapper.Map(attachment); + temp.FullPath = temp.Path + "?access_token=" + _userInfo.UserToken; + return temp; + } + + /// + /// 保存多个附件 + /// + /// + /// + public async Task> SaveAttachments(IEnumerable attachmentList) + { + var attachments = _mapper.Map>(attachmentList).ToList(); + + //1 是中文 2是英文 中英文第一份简历默认设置为官方 + + var zhCount = attachments.Count(t => t.Language == 1); + var usCount = attachments.Count(t => t.Language == 2); + + if (zhCount == 1) + { + var k = attachments.First(t => t.Language == 1); + k.IsOfficial = true; + } + if (usCount == 1) + { + var k = attachments.First(t => t.Language == 2); + k.IsOfficial = true; + } + + + //处理重传 + var reUpload = attachmentList.FirstOrDefault(t => t.ReUpload == true); + if (reUpload != null) + { + //因为界面现实的列表用了 接口返回的列表,所以要把返回的模型对应的字段也要更改 + var attach = attachments.First(t => t.Id == reUpload.Id); + attach.CreateTime = DateTime.Now; + + + //重传的时候,发现 相同语言的官方简历数量为2 那么将重传的简历设置为非官方 + if (attachments.Count(t => t.Language == reUpload.Language && t.IsOfficial) == 2) + { + await _attachmentrepository.BatchUpdateNoTrackingAsync(t => t.Id == reUpload.Id, u => new Attachment() + { + Path = reUpload.Path, + CreateTime = DateTime.Now, + Language = reUpload.Language, + IsOfficial = false + }); + attach.IsOfficial = false; + } + else //相同语言的重传 + { + await _attachmentrepository.BatchUpdateNoTrackingAsync(t => t.Id == reUpload.Id, u => new Attachment() + { + Path = reUpload.Path, + CreateTime = DateTime.Now, + Language = reUpload.Language + }); + + } + + } + + + var newAttachment = attachments.Where(t => t.Id == Guid.Empty); + + await _repository.AddRangeAsync(newAttachment); + await _repository.SaveChangesAsync(); + + //_doctorAttachmentRepository.AddRange(newAttachment); + //_doctorAttachmentRepository.SaveChanges(); + + var list = _mapper.Map>(attachments).ToList(); + + list.ForEach(t => t.FullPath = t.Path + "?access_token=" + _userInfo.UserToken); + + return list; + } + + public async Task> AddAttachment(AttachmentDTO attachment) + { + + var newAttachment = _mapper.Map(attachment); + //如果这个医生不存在 这个语言的官方简历 就设置为官方简历 + if (! await _attachmentrepository.AnyAsync(t => t.Type == "Resume" && t.DoctorId == attachment.DoctorId && t.Language == attachment.Language && t.IsOfficial)) + { + newAttachment.IsOfficial = true; + + attachment.IsOfficial = true; + } + + await _repository.AddAsync(newAttachment); + var success = await _repository.SaveChangesAsync(); + return ResponseOutput.Result(success, attachment); + } + + + [NonDynamicMethod] + public async Task GetDoctorOfficialCV(int language, Guid doctorId) + { + var result = await _attachmentrepository.FirstOrDefaultAsync(a => a.DoctorId == doctorId && + a.IsOfficial && a.Type.Equals("Resume") && a.Language == language); + if (result != null) + { + return result.Path; + } + return string.Empty; + } + + + /// + /// 将简历设置为官方简历 + /// + /// + /// + /// + /// + + [HttpPost("{doctorId:guid}/{attachmentId:guid}/{language}")] + public async Task SetOfficial(Guid doctorId, Guid attachmentId, int language) + { + var resumeList = await _attachmentrepository.Where(t => t.DoctorId == doctorId && t.Type == "Resume" && t.Language == language).ToListAsync(); + foreach (var item in resumeList) + { + if (item.Id == attachmentId) item.IsOfficial = true; + else item.IsOfficial = false; + await _repository.UpdateAsync(item); + } + + return ResponseOutput.Result(await _repository.SaveChangesAsync()); + } + + /// + /// 设置简历的语言类型 + /// + /// + /// + /// 0-未设置,1-中文,2-英文 + /// + + [HttpPost("{doctorId:guid}/{attachmentId:guid}/{language}")] + public async Task SetLanguage(Guid doctorId, Guid attachmentId, int language) + { + bool result =await _attachmentrepository.BatchUpdateNoTrackingAsync(t => t.Id == attachmentId, a => new Attachment + { + Language = language, + IsOfficial = false + }); + return ResponseOutput.Result(result); + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Doctor/DTO/AttachmentModel.cs b/IRaCIS.Core.Application/Service/Doctor/DTO/AttachmentModel.cs new file mode 100644 index 0000000..1eb34fc --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/DTO/AttachmentModel.cs @@ -0,0 +1,64 @@ +namespace IRaCIS.Application.Contracts +{ + public class AttachmentDTO + { + public Guid Id { get; set; } + public Guid DoctorId { get; set; } + public bool IsOfficial { get; set; } + public string Type { get; set; } = string.Empty; + public string Path { get; set; } = string.Empty; + public string FullPath { get; set; } = string.Empty; + public string FileName { get; set; } = string.Empty; + public DateTime? CreateTime { get; set; } + public int Language { get; set; } + + public bool ReUpload { get; set; } = false; + } + + public class ReviewerAckDTO + { + public Guid Id { get; set; } + public Guid DoctorId { get; set; } + public string Type { get; set; } = string.Empty; + public string Path { get; set; } = string.Empty; + public string FullPath => Path; + public string FileName { get; set; } = string.Empty; + } + + + + + + public class TrialSOWPathDTO + { + public Guid TrialId { get; set; } + + public string SowName { get; set; } = string.Empty; + public string SowPath { get; set; } = string.Empty; + } + + public class DeleteSowPathDTO + { + public Guid TrialId { get; set; } + public string Path { get; set; } = string.Empty; + + } + + public class UploadAgreementAttachmentDTO + { + public Guid Id { get; set; } + + public Guid DoctorId { get; set; } + public string Type { get; set; } = string.Empty; + public string Path { get; set; } = string.Empty; + public string FullPath => Path; + + public string FileName { get; set; } = string.Empty; + } + + public class AttachementCommand + { + public Guid Id { get; set; } + public string Path { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Application/Service/Doctor/DTO/DoctorAccountRegisterModel.cs b/IRaCIS.Core.Application/Service/Doctor/DTO/DoctorAccountRegisterModel.cs new file mode 100644 index 0000000..456a61b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/DTO/DoctorAccountRegisterModel.cs @@ -0,0 +1,12 @@ +using System; + +namespace IRaCIS.Application.Contracts +{ + public class DoctorAccountRegisterModel : DoctorAccountLoginDTO + { + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string EMail { get; set; } = string.Empty; + public DateTime RegisterTime { get; set; } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Doctor/DTO/DoctorModel.cs b/IRaCIS.Core.Application/Service/Doctor/DTO/DoctorModel.cs new file mode 100644 index 0000000..8904477 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/DTO/DoctorModel.cs @@ -0,0 +1,838 @@ +using System; +using System.Collections.Generic; + +using IRaCIS.Core.Infrastructure.Extention; +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; +using System.Linq; + +namespace IRaCIS.Application.Contracts +{ + + #region 医生管理列表 + + public class DoctorDTO + { + + [JsonIgnore] + public List DictionaryList { get; set; } = new List(); + public List SubspecialityIds => DictionaryList.Where(t => t.ParentCode == StaticData.Subspeciality).OrderBy(t => t.ShowOrder).Select(t => t.Id).ToList(); + + + //第二专业 + public List SubspecialityList => DictionaryList.Where(t => t.ParentCode == StaticData.Subspeciality).OrderBy(t => t.ShowOrder).Select(t => t.Value).ToList(); + + public List SubspecialityCNList => DictionaryList.Where(t => t.ParentCode == StaticData.Subspeciality).OrderBy(t => t.ShowOrder).Select(t => t.ValueCN).ToList(); + + + + //临床实践中使用的模式 + public List ReadingTypeList => DictionaryList.Where(t => t.ParentCode == StaticData.ReadingType).OrderBy(t => t.ShowOrder).Select(t => t.Value).ToList(); + public List ReadingTypeCNList => DictionaryList.Where(t => t.ParentCode == StaticData.ReadingType).OrderBy(t => t.ShowOrder).Select(t => t.ValueCN).ToList(); + + public List ReadingTypeIds => DictionaryList.Where(t => t.ParentCode == StaticData.ReadingType).OrderBy(t => t.ShowOrder).Select(t => t.Id).ToList(); + + + public string AccountUserName { get; set; } + + + + public string ReadingTypeOther { get; set; } = String.Empty; + public string ReadingTypeOtherCN { get; set; } = String.Empty; + + public Guid Id { get; set; } + public DateTime CreateTime { get; set; } + public string ReviewerCode { get; set; } = String.Empty;//GUID + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string ChineseName { get; set; } = string.Empty; + public List TitleIdList { get; set; } = new List(); + public List TitleList { get; set; } = new List(); + public List TitleCNList { get; set; } = new List(); + public string Phone { get; set; } = string.Empty; + public string Introduction { get; set; } = string.Empty; + public string EMail { get; set; } = string.Empty; + + public ReviewerInformationConfirmStatus ReviewStatus { get; set; } = ReviewerInformationConfirmStatus.ConfirmRefuse; + public string WeChat { get; set; } = string.Empty; + + //部门 + public string Department { get; set; } = string.Empty; + public string DepartmentCN { get; set; } = string.Empty; + public Guid? DepartmentId { get; set; } + public string DepartmentOther { get; set; } = String.Empty; + public string DepartmentOtherCN { get; set; } = String.Empty; + + //增加的 + public Guid? SpecialityId { get; set; } = Guid.Empty; + public string SpecialityOther { get; set; } = string.Empty; + public string SpecialityOtherCN { get; set; } = string.Empty; + public string Speciality { get; set; } = string.Empty; + public string SpecialityCN { get; set; } = string.Empty; + + + //职称 + public string Rank { get; set; } = string.Empty; + public string RankCN { get; set; } = string.Empty; + public Guid? RankId { get; set; } + public string RankOther { get; set; } = String.Empty; + public string RankOtherCN { get; set; } = String.Empty; + + //职位 + public string Position { get; set; } = string.Empty; + public string PositionCN { get; set; } = string.Empty; + public Guid? PositionId { get; set; } + public string PositionOther { get; set; } = String.Empty; + public string PositionOtherCN { get; set; } = String.Empty; + + + + + public string SubspecialityOther { get; set; } = String.Empty; + public string SubspecialityOtherCN { get; set; } = String.Empty; + + public int GCP { get; set; } + public Guid? GCPId { get; set; } + public string ResumePath { get; set; } = string.Empty; + public bool HasResume + { + get; set; + } + public bool Reconfirmed { get; set; } + public int CooperateStatus { get; set; } + public int ResumeStatus { get; set; } + public bool AcceptingNewTrial { get; set; } = false; + public bool ActivelyReading { get; set; } = false; + + //医院 + + public Guid? HospitalId { get; set; } + public string HospitalOther { get; set; } = String.Empty; + public string HospitalName { get; set; } = string.Empty; + public string City { get; set; } = String.Empty; + public string Country { get; set; } = String.Empty; + + public string HospitalNameCN { get; set; } = string.Empty; + public string CityCN { get; set; } = String.Empty; + public string CountryCN { get; set; } = String.Empty; + + + public int? Reading { get; set; } + public int? Approved { get; set; } + public int? Submitted { get; set; } + + public int? Finished { get; set; } + + public bool IsVirtual { get; set; } + } + + /// + /// Reviewer 列表查询参数 + /// + + public class DoctorSearchDTO : PageInput + { + public string Name { get; set; } = string.Empty; + public List ReadingTypeIdList { get; set; } = new List(); + public List SubspecialityIdList { get; set; } = new List(); + + public List EvaluationCriteriaIdList { get; set; } = new List(); + + public List TitleIdList { get; set; } = new List(); + public Guid? DepartmentId { get; set; } + public Guid? SpecialityId { get; set; } + public Guid? PositionId { get; set; } + public Guid? RankId { get; set; } + public Guid? HospitalId { get; set; } + + //合作状态 + public ContractorStatusEnum? ContractorStatus { get; set; } + + // 简历审核状态 + public ResumeStatusEnum? InformationConfirmed { get; set; } + public int? EnrollStatus { get; set; } //入组状态 + + public bool? AcceptingNewTrial { get; set; }//是否接受新的项目 + public bool? ActivelyReading { get; set; }// 是否接受新的读片任务 + public int? Nation { get; set; }// 0-中国医生,2-美国医生,3-全部 + } + + /// + /// 入组 Selection 列表查询参数 + /// + public class ReviewerSelectionQueryDTO : DoctorSearchDTO + { + public Guid TrialId { get; set; } + } + + public class ReviewerSubmissionQueryDTO : PageInput + { + public Guid TrialId { get; set; } = Guid.Empty; + public int IntoGroupSearchState { get; set; } + } + + public class ReviewerConfirmationQueryDTO : PageInput + { + public Guid TrialId { get; set; } = Guid.Empty; + } + + public class SelectionReviewerDTO : DoctorDTO + { + public int DoctorTrialState { get; set; } + public string OptUserName { get; set; } = string.Empty; + public DateTime? OptTime { get; set; } + public string? OptTimeStr => OptTime?.ToString("yyyy-MM-dd HH:mm:ss"); + } + + public class DoctorOptDTO + { + public Guid Id { get; set; } + public string Code { get; set; } = String.Empty;//GUID + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string ChineseName { get; set; } = string.Empty; + + public string City { get; set; } = string.Empty; + public string HospitalName { get; set; } = string.Empty; + public string Country { get; set; } = string.Empty; + } + + + public class ConfirmationReviewerDTO : DoctorOptDTO + { + public int DoctorTrialState { get; set; } + public string OptUserName { get; set; } = string.Empty; + public DateTime? OptTime { get; set; } + public string? OptTimeStr => OptTime?.ToString("yyyy-MM-dd HH:mm:ss"); + + //SPM 需要看到 + public DateTime? SubmmitTime { get; set; } + public string SubmmitUserName { get; set; } = string.Empty; + public string BlindName { get; set; } + public string BlindNameCN { get; set; } = string.Empty; + + public Guid? SpecialityId { get; set; } = Guid.Empty; + + public string SpecialityCN { get; set; } = string.Empty; + public string Speciality { get; set; } = string.Empty; + public string SpecialityOther { get; set; } = string.Empty; + public string SpecialityOtherCN { get; set; } = string.Empty; + + [JsonIgnore] + public List DictionaryList { get; set; } = new List(); + public List SubspecialityIds => DictionaryList.Where(t => t.ParentCode == StaticData.Subspeciality).OrderBy(t => t.ShowOrder).Select(t => t.Id).ToList(); + + public Guid? HospitalId { get; set; } + } + + + + + public class DoctorStateModelDTO + { + public Guid DoctorId { get; set; } + public int IntoGroupState { get; set; } + public string OptUserName { get; set; } = String.Empty; + public DateTime? OptTime { get; set; } + + } + + #endregion + + public class DoctorDetailDTO + { + public DoctorBasicInfoDTO BasicInfoView { get; set; } + public EmploymentDTO EmploymentView { get; set; } + public SpecialtyDTO SpecialtyView { get; set; } + public IEnumerable EducationList { get; set; } + public IEnumerable PostgraduateList { get; set; } + public ResearchPublicationDTO ResearchPublicationView { get; set; } + public TrialExperienceModel TrialExperienceView { get; set; } + public ResumeConfirmDTO AuditView { get; set; } + public IEnumerable AttachmentList { get; set; } + + public List SowList { get; set; } + public List AckSowList { get; set; } + + public DoctorEnrollInfoDTO IntoGroupInfo { get; set; } + + public bool InHoliday { get; set; } + public DoctorDetailDTO() + { + BasicInfoView = new DoctorBasicInfoDTO(); + EmploymentView = new EmploymentDTO(); + SpecialtyView = new SpecialtyDTO(); + EducationList = new List(); + PostgraduateList = new List(); + ResearchPublicationView = new ResearchPublicationDTO(); + TrialExperienceView = new TrialExperienceModel(); + AuditView = new ResumeConfirmDTO(); + AttachmentList = new List(); + IntoGroupInfo = new DoctorEnrollInfoDTO(); + SowList = new List(); + AckSowList = new List(); + } + } + + public class DoctorEnrollInfoDTO + { + public Guid? DoctorId { get; set; } + public int? Submitted { get; set; } + public int? Approved { get; set; } + public int? Reading { get; set; } + } + + + + #region 基本信息模型 + + public class DoctorBasicInfo + { + public Guid? Id { get; set; } + public string ReviewerCode { get; set; } = string.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + public string ChineseName { get; set; } = String.Empty; + public int Sex { get; set; } + public string Phone { get; set; } = String.Empty; + public string Introduction { get; set; } = String.Empty; + public string EMail { get; set; } = String.Empty; + public string WeChat { get; set; } = String.Empty; + public int Nation { get; set; } + + + } + + public class DoctorBasicInfoCommand : DoctorBasicInfo + { + + //职称 + public List TitleIds { get; set; } = new List(); + } + + public class TempObj + { + public int ShowOrder { get; set; } + public Guid TitleId { get; set; } + public string TitleCN { get; set; } = string.Empty; + public string Title { get; set; } = string.Empty; + } + public class DicView + { + public int ShowOrder { get; set; } + public Guid Id { get; set; } + public string ValueCN { get; set; } = string.Empty; + public string ParentCode { get; set; } = string.Empty; + + + public string Value { get; set; } = string.Empty; + } + + + public class DoctorBasicInfoDTO : DoctorBasicInfo + { + public string BlindName { get; set; } + + public string BlindNameCN { get; set; } = string.Empty; + + public List DoctorDicViewDtos = new List(); + + //职称 + public List TitleIds => DoctorDicViewDtos.Select(t => t.Id).ToList(); + + public List TitleList=> DoctorDicViewDtos.Select(t => t.Value).ToList(); + + public List TitleCNList=> DoctorDicViewDtos.Select(t => t.ValueCN).ToList(); + + #region ef select + //[JsonIgnore] + //public List TempObjList { get; set; } + ////职称 + //public List TitleIds + //{ + // get + // { + // if (TempObjList.Count > 0) + // { + // return TempObjList.Select(t => t.TitleId).ToList(); + // } + // else + // { + // return new List(); + // } + // } + + //} + //public List TitleList + //{ + // get + // { + // if (TempObjList.Count > 0) + // { + // return TempObjList.Select(t => t.Title).ToList(); + // } + // else + // { + // return new List(); + // } + // } + //} + //public List TitleCNList + //{ + // get + // { + // if (TempObjList.Count > 0) + // { + // return TempObjList.Select(t => t.TitleCN).ToList(); + // } + // else + // { + // return new List(); + // } + // } + //} + + #endregion + } + + public class SowDTO + { + public string FileName { get; set; } = string.Empty; + public string TrialCode { get; set; } = string.Empty; + public string FilePath { get; set; } = string.Empty; + public string FullPath { get { return FilePath; } } + public DateTime CreateTime { get; set; } + } + + + #endregion + + + #region 工作信息模型 + + + //public class DoctorHospitalView + //{ + // public string HospitalName { get; set; } + // public string UniversityAffiliated { get; set; } + // public string Country { get; set; } + // public string Province { get; set; } + // public string City { get; set; } + + // public string HospitalNameCN { get; set; } + + // public string UniversityAffiliatedCN { get; set; } + // public string CountryCN { get; set; } + + // public string ProvinceCN { get; set; } + + // public string CityCN { get; set; } + //} + + public class EmploymentDTO : EmploymentInfo + { + //public DoctorHospitalView Hospital { get; set; } + + public string Department { get; set; } = String.Empty; + + public string Rank { get; set; } = String.Empty; + + public string Position { get; set; } = String.Empty; + + #region 医院信息 + public string HospitalName { get; set; } = String.Empty; + + public string UniversityAffiliated { get; set; } = String.Empty; + public string Country { get; set; } = String.Empty; + + public string Province { get; set; } = String.Empty; + + public string City { get; set; } = String.Empty; + + #endregion + + public string DepartmentCN { get; set; } = String.Empty; + + public string RankCN { get; set; } = String.Empty; + + public string PositionCN { get; set; } = String.Empty; + + + public string HospitalNameCN { get; set; } = String.Empty; + + public string UniversityAffiliatedCN { get; set; } = String.Empty; + public string CountryCN { get; set; } = String.Empty; + + public string ProvinceCN { get; set; } = String.Empty; + + public string CityCN { get; set; } = String.Empty; + } + + public class EmploymentCommand : EmploymentInfo + { + + } + + public class EmploymentInfo + { + public Guid Id { get; set; } + //部门 + public Guid? DepartmentId { get; set; } = Guid.Empty; + public string DepartmentOther { get; set; } = string.Empty; + public string DepartmentOtherCN { get; set; } = string.Empty; + //职称 + public Guid? RankId { get; set; } = Guid.Empty; + public string RankOther { get; set; } = string.Empty; + public string RankOtherCN { get; set; } = string.Empty; + //职位 主席 副主席 + public Guid? PositionId { get; set; } = Guid.Empty; + public string PositionOther { get; set; } = string.Empty; + public string PositionOtherCN { get; set; } = string.Empty; + + public Guid? HospitalId { get; set; } = Guid.Empty; + + public Guid? PhysicianId { get; set; } + public string Physician { get; set; } = string.Empty; + public string PhysicianCN { get; set; } = string.Empty; + + } + + #endregion + + #region Specialty模型 + public class SpecialtyDTO : SpecialtyCommand + { + [JsonIgnore] + public List DictionaryList { get; set; } = new List(); + + public string Speciality { get; set; } = string.Empty; + + + //临床实践中使用的模式 + public new List ReadingTypeIds + { + get + { + if (DictionaryList.Count > 0) + { + return DictionaryList.Where(t => t.ParentCode == StaticData.ReadingType).Select(t => t.Id).ToList(); + } + else + { + return new List(); + } + } + + } + + public new List SubspecialityIds + { + get + { + if (DictionaryList.Count > 0) + { + return DictionaryList.Where(t => t.ParentCode == StaticData.Subspeciality).Select(t => t.Id).ToList(); + } + else + { + return new List(); + } + } + + } + + + public List ReadingTypeList + { + get + { + if (DictionaryList.Count > 0) + { + return DictionaryList.Where(t => t.ParentCode == StaticData.ReadingType).Select(t => t.Value).ToList(); + } + else + { + return new List(); + } + } + + } + public List SubspecialityList + { + get + { + if (DictionaryList.Count > 0) + { + return DictionaryList.Where(t => t.ParentCode == StaticData.Subspeciality).Select(t => t.Value).ToList(); + } + else + { + return new List(); + } + } + + } + + public List ReadingTypeCNList + { + get + { + if (DictionaryList.Count > 0) + { + return DictionaryList.Where(t => t.ParentCode == StaticData.ReadingType).Select(t => t.ValueCN).ToList(); + } + else + { + return new List(); + } + } + + } + public List SubspecialityCNList + { + get + { + if (DictionaryList.Count > 0) + { + return DictionaryList.Where(t => t.ParentCode == StaticData.Subspeciality).Select(t => t.ValueCN).ToList(); + } + else + { + return new List(); + } + } + + } + } + + + + public class SpecialtyCommand + { + public List ReadingTypeIds { get; set; } = new List(); + + public List SubspecialityIds { get; set; } = new List(); + + public Guid Id { get; set; } + public string OtherSkills { get; set; } = string.Empty; + + public string ReadingTypeOther { get; set; } = string.Empty; + public string ReadingTypeOtherCN { get; set; } = string.Empty; + //亚专科 + + public string SubspecialityOther { get; set; } = string.Empty; + public string SubspecialityOtherCN { get; set; } = string.Empty; + + public Guid? SpecialityId { get; set; } = Guid.Empty; + public string SpecialityCN { get; set; } = string.Empty; + public string SpecialityOther { get; set; } = string.Empty; + public string SpecialityOtherCN { get; set; } = string.Empty; + + + } + + + #endregion + + #region 医生账户 + public class DoctorAccountLoginDTO + { + public string Phone { get; set; } = String.Empty; + public string Password { get; set; } = String.Empty; + } + + public class DoctorAccountDTO + { + public Guid Id { get; set; } + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + public string Phone { get; set; } = String.Empty; + public string PhotoPath { get; set; } = String.Empty; + } + + public class DoctorAccountUpdatePasswordCommand + { + public string Phone { get; set; } = String.Empty; + public string OldPassword { get; set; } = String.Empty; + public string NewPassword { get; set; } = String.Empty; + } + #endregion + + #region 审核模型 + + public class ResumeConfirmCommand + { + //int userId, int doctorId, int status, string memo + //public Guid FromUserId { get; set; } + public Guid Id { get; set; } + public ResumeStatusEnum ResumeStatus { get; set; } + public ReviewerInformationConfirmStatus ReviewStatus { get; set; } + public bool AcceptingNewTrial { get; set; } = false; + public bool ActivelyReading { get; set; } = false; + public string AdminComment { get; set; } = String.Empty; + public string MessageContent { get; set; } = String.Empty; + public ContractorStatusEnum CooperateStatus { get; set; } + + public bool IsVirtual { get; set; } + + public string BlindName { get; set; } = String.Empty; + + public string BlindNameCN { get; set; } = string.Empty; + public string BlindPublications { get; set; } + + } + public class DeleteDoctorCriterionFile + { + public Guid Id { get; set; } + + } + + public class AddDoctorCriterionFileDto + { + public Guid? Id { get; set; } + + public Guid? TrialId { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + public string CriterionName { get; set; } + + public bool IsEnable { get; set; } = true; + + /// + /// 文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件路径 + /// + public string FilePath { get; set; } + + /// + /// 标准类型 + /// + public CriterionType CriterionType { get; set; } + + /// + /// 医生Id + /// + public Guid DoctorId { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + + + /// + /// 文件类型 + /// + public CriterionFileType FileType { get; set; } + } + + + public class GetDoctorCriterionFileOutDto + { + public Guid Id { get; set; } + + /// + /// 文件名称 + /// + public string FileName { get; set; } + + public bool IsEnable { get; set; } + + public string CriterionName { get; set; } + + /// + /// 文件路径 + /// + public string FilePath { get; set; } + + /// + /// 标准类型 + /// + public CriterionType CriterionType { get; set; } + + /// + /// 医生Id + /// + public Guid DoctorId { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 文件类型 + /// + public CriterionFileType FileType { get; set; } + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + + + } + + public class GetDoctorCriterionFileInDto + { + public Guid DoctorId { get; set; } + + /// + /// 文件类型 + /// + public CriterionFileType? FileType { get; set; } + + + /// + /// 标准类型 + /// + public CriterionType? CriterionType { get; set; } + } + + public class ResumeConfirmDTO + { + public Guid Id { get; set; } + public int CooperateStatus { get; set; } + public int ResumeStatus { get; set; } + public int ReviewStatus { get; set; } //复审状态 + public bool AcceptingNewTrial { get; set; } + public bool ActivelyReading { get; set; } + public string AdminComment { get; set; } = String.Empty; + public bool InHoliday { get; set; } + + public bool IsVirtual { get; set; } + + public string BlindName { get; set; } = String.Empty; + public string BlindNameCN { get; set; } = string.Empty; + public string BlindPublications { get; set; } + } + + #endregion + + + public class TrialPaymentPriceQueryDTO : PageInput + { + public string KeyWord { get; set; } = String.Empty; + + public Guid? CroId { get; set; } + + } + + public class DoctorPaymentInfoQueryDTO : PageInput + { + public string SearchName { get; set; } = String.Empty; + public Guid? HospitalId { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/Doctor/DTO/EducationModel.cs b/IRaCIS.Core.Application/Service/Doctor/DTO/EducationModel.cs new file mode 100644 index 0000000..f9fdbde --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/DTO/EducationModel.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; + +namespace IRaCIS.Application.Contracts +{ + public class EducationCommand + { + public Guid? Id { get; set; } + public Guid DoctorId { get; set; } + public DateTime BeginDate { get; set; } + public DateTime EndDate { get; set; } + public string Degree { get; set; } = String.Empty; + public string Major { get; set; } = String.Empty; + public string Organization { get; set; } = String.Empty; + public string Country { get; set; } = String.Empty; + public string Province { get; set; } = String.Empty; + public string City { get; set; } = String.Empty; + + public string DegreeCN { get; set; } = String.Empty; + public string MajorCN { get; set; } = String.Empty; + public string OrganizationCN { get; set; } = String.Empty; + public string CountryCN { get; set; } = String.Empty; + public string ProvinceCN { get; set; } = String.Empty; + public string CityCN { get; set; } = String.Empty; + + } + + public class EducationInfoViewModel : EducationCommand + { + public DateTime? CreateTime { get; set; } + public string BeginDateStr => BeginDate.ToString("yyyy-MM"); + public string EndDateStr => EndDate.ToString("yyyy-MM"); + + } + public class PostgraduateCommand + { + public Guid? Id { get; set; } + public Guid DoctorId { get; set; } + + public DateTime BeginDate { get; set; } + + public DateTime EndDate { get; set; } + + public string Training { get; set; } = String.Empty; + + public string Major { get; set; } = String.Empty; + + public string Hospital { get; set; } = String.Empty; + + public string School { get; set; } = String.Empty; + + public string Country { get; set; } = String.Empty; + + public string Province { get; set; } = String.Empty; + + public string City { get; set; } = String.Empty; + + public string TrainingCN { get; set; } = String.Empty; + + public string MajorCN { get; set; } = String.Empty; + + public string HospitalCN { get; set; } = String.Empty; + + public string SchoolCN { get; set; } = String.Empty; + + public string CountryCN { get; set; } = String.Empty; + + public string ProvinceCN { get; set; } = String.Empty; + + public string CityCN { get; set; } = String.Empty; + + } + + + public class PostgraduateViewModel: PostgraduateCommand + { + public DateTime? CreateTime { get; set; } + public string BeginDateStr => BeginDate.ToString("yyyy-MM"); + + public string EndDateStr => EndDate.ToString("yyyy-MM"); + } + public class DoctorEducationExperienceDTO + { + public IEnumerable EducationList=new List(); + + public IEnumerable PostgraduateList = new List(); + } + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Doctor/DTO/HolidayModel.cs b/IRaCIS.Core.Application/Service/Doctor/DTO/HolidayModel.cs new file mode 100644 index 0000000..8635d43 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/DTO/HolidayModel.cs @@ -0,0 +1,13 @@ +using System; + +namespace IRaCIS.Application.Contracts +{ + public class VacationCommand + { + public Guid? Id { get; set; } + public Guid DoctorId { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public int Status { get; set; } = 1; + } +} diff --git a/IRaCIS.Core.Application/Service/Doctor/DTO/ResearchPublicationModel.cs b/IRaCIS.Core.Application/Service/Doctor/DTO/ResearchPublicationModel.cs new file mode 100644 index 0000000..d1f38df --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/DTO/ResearchPublicationModel.cs @@ -0,0 +1,24 @@ +using System; + +namespace IRaCIS.Application.Contracts +{ + public class ResearchPublicationDTO + { + public Guid? Id { get; set; } + public Guid DoctorId { get; set; } + public string Research { get; set; } = String.Empty; + public string Grants { get; set; } = String.Empty; + public string Publications { get; set; } = String.Empty; + public string AwardsHonors { get; set; } = String.Empty; + + public string ResearchCN { get; set; } = String.Empty; + public string GrantsCN { get; set; } = String.Empty; + public string PublicationsCN { get; set; } = String.Empty; + public string AwardsHonorsCN { get; set; } = String.Empty; + + + public string BlindPublications { get; set; } + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Doctor/DTO/TrialExperienceModel.cs b/IRaCIS.Core.Application/Service/Doctor/DTO/TrialExperienceModel.cs new file mode 100644 index 0000000..4ed924c --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/DTO/TrialExperienceModel.cs @@ -0,0 +1,87 @@ +namespace IRaCIS.Application.Contracts +{ + public class TrialExperienceCommand + { + public Guid? Id { get; set; } + + public Guid DoctorId { get; set; } + + public Guid? PhaseId { get; set; } + + public DateTime? StartTime { get; set; } + + public DateTime? EndTime { get; set; } + + public string EvaluationContent { get; set; } = String.Empty; + + public int VisitReadingCount { get; set; } + + //public string Term { get; set; } + + //public string EvaluationCriteria { get; set; } + + public List EvaluationCriteriaIdList { get; set; } = new List(); + + + } + + public class TrialExperienceListDTO: TrialExperienceCommand + { + public string Phase { get; set; } = String.Empty; + + public List EvaluationCriteriaList { get; set; } = new List(); + + } + + //public class EvaluationCriteriaDTO + //{ + // public Guid EvaluationCriteriaId { get; set; } + // public string EvaluationCriteria { get; set; } + //} + + + public class TrialExperienceModel : GcpAndOtherExperienceDTO + { + public List ClinicalTrialExperienceList = new List(); + + public string ExpiryDateStr { get; set; } = string.Empty; + public string GCPFullPath { get; set; } = String.Empty; + + } + + + public class GcpAndOtherExperienceDTO + { + public Guid Id { get; set; } + + public int GCP { get; set; } + + public Guid? GCPId { get; set; } + + public string OtherClinicalExperience { get; set; }=String.Empty; + public string OtherClinicalExperienceCN { get; set; } = String.Empty; + + public string Type { get; set; } = string.Empty; + + public string Path { get; set; } = string.Empty; + + public string FileName { get; set; } = string.Empty; + + } + + public class GCPExperienceCommand + { + public Guid Id { get; set; } + public int GCP { get; set; } + + public Guid? GCPId { get; set; } + } + + public class ClinicalExperienceCommand + { + public Guid DoctorId { get; set; } + + public string OtherClinicalExperience { get; set; } = String.Empty; + public string OtherClinicalExperienceCN { get; set; } = String.Empty; + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Doctor/DoctorListService.cs b/IRaCIS.Core.Application/Service/Doctor/DoctorListService.cs new file mode 100644 index 0000000..25fb244 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/DoctorListService.cs @@ -0,0 +1,274 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using System.Linq.Dynamic.Core; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Reviewer")] + public class DoctorListService : BaseService, IDoctorListQueryService + { + private readonly IRepository _doctorRepository; + + public DoctorListService(IRepository doctorRepository) + { + _doctorRepository = doctorRepository; + } + + /// + /// Reviewer列表分页查询 + /// + [HttpPost] + public async Task> GetDoctorSearchList(DoctorSearchDTO doctorSearch) + { + + // 项目经验 多选 + var evaluationCriteriaCount = doctorSearch.EvaluationCriteriaIdList.Count(); + + // 搜索条件 ReadingType 、Subspeciality、Title 多选 + var count = doctorSearch.ReadingTypeIdList.Count + doctorSearch.TitleIdList.Count + doctorSearch.SubspecialityIdList.Count; + + var guidList = doctorSearch.ReadingTypeIdList.Concat(doctorSearch.SubspecialityIdList).Concat(doctorSearch.TitleIdList); + + + var query = _doctorRepository.AsQueryable() + .WhereIf(doctorSearch.DepartmentId != null, t => t.DepartmentId == doctorSearch.DepartmentId) + .WhereIf(doctorSearch.SpecialityId != null, t => t.SpecialityId == doctorSearch.SpecialityId) + .WhereIf(doctorSearch.HospitalId != null, t => t.HospitalId == doctorSearch.HospitalId) + .WhereIf(doctorSearch.PositionId != null, t => t.PositionId == doctorSearch.PositionId) + .WhereIf(doctorSearch.RankId != null, t => t.RankId == doctorSearch.RankId) + .WhereIf(doctorSearch.ContractorStatus != null, t => t.CooperateStatus == doctorSearch.ContractorStatus) + .WhereIf(doctorSearch.InformationConfirmed != null, t => t.ResumeStatus == doctorSearch.InformationConfirmed) + .WhereIf(doctorSearch.AcceptingNewTrial != null, t => t.AcceptingNewTrial == doctorSearch.AcceptingNewTrial) + .WhereIf(!string.IsNullOrWhiteSpace(doctorSearch.Name), t => t.ChineseName.Contains(doctorSearch.Name) || (t.LastName + t.FirstName).Contains(doctorSearch.Name)) + .WhereIf(doctorSearch.Nation != null, t => t.Nation == doctorSearch.Nation) + .WhereIf(evaluationCriteriaCount > 0, t => t.TrialExperienceCriteriaList.Count(t => doctorSearch.EvaluationCriteriaIdList.Contains(t.EvaluationCriteriaId)) == evaluationCriteriaCount) + ////用户类型 看到简历的范围这里需要确认 + //.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ReviewerCoordinator, t => t.UserList.Any(u => u.UserId == _userInfo.Id)) + .WhereIf(count > 0, t => t.DoctorDicRelationList.Count(u => guidList.Contains(u.DictionaryId)) == count) + .WhereIf(doctorSearch.EnrollStatus != null && doctorSearch.EnrollStatus == (int)ReviewerEnrollStatus.Yes, t => t.EnrollList.Any(u => u.EnrollStatus == EnrollStatus.DoctorReading)) + + .ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToPagedListAsync(doctorSearch.PageIndex, doctorSearch.PageSize, doctorSearch.SortField == string.Empty ? "CreateTime" : doctorSearch.SortField, doctorSearch.Asc); + + + } + + + #region 入组查询相关 + /// + /// 获取可筛选筛选及已经筛选的医生列表 + /// + [HttpPost] + public async Task> GetSelectionReviewerList( + ReviewerSelectionQueryDTO selectionQuery) + { + //项目配置需要的医生过滤 2表示混合 + var trialConfig = await _repository.Where(s => s.Id == selectionQuery.TrialId).Select(t=>new { t.AttendedReviewerType ,t.TrialType} ).FirstOrDefaultAsync().IfNullThrowException(); + + var nation = trialConfig.AttendedReviewerType; + // 临床项目经验 多选 + var evaluationCriteriaCount = selectionQuery.EvaluationCriteriaIdList.Count(); + + // 搜索条件 ReadingType 、Subspeciality、Title 多选 + var count = selectionQuery.ReadingTypeIdList.Count + selectionQuery.TitleIdList.Count + selectionQuery.SubspecialityIdList.Count; + + var guidList = selectionQuery.ReadingTypeIdList.Concat(selectionQuery.SubspecialityIdList).Concat(selectionQuery.TitleIdList); + + var query = _doctorRepository.WhereIf(nation != AttendedReviewerType.USAndCN, t => t.Nation ==(int) nation) + .WhereIf(selectionQuery.DepartmentId != null, t => t.DepartmentId == selectionQuery.DepartmentId) + .WhereIf(selectionQuery.SpecialityId != null, t => t.SpecialityId == selectionQuery.SpecialityId) + .WhereIf(trialConfig.TrialType == TrialType.NoneOfficial, t => t.IsVirtual ==true) + .WhereIf(trialConfig.TrialType != TrialType.NoneOfficial, t => t.IsVirtual == false) + .WhereIf(selectionQuery.HospitalId != null, t => t.HospitalId == selectionQuery.HospitalId) + .WhereIf(selectionQuery.PositionId != null, t => t.PositionId == selectionQuery.PositionId) + .WhereIf(selectionQuery.RankId != null, t => t.RankId == selectionQuery.RankId) + .WhereIf(selectionQuery.ContractorStatus != null, t => t.CooperateStatus == selectionQuery.ContractorStatus ) + .WhereIf(selectionQuery.InformationConfirmed != null, t => t.ResumeStatus == selectionQuery.InformationConfirmed) + .WhereIf(selectionQuery.AcceptingNewTrial != null, t => t.AcceptingNewTrial == selectionQuery.AcceptingNewTrial) + .WhereIf(!string.IsNullOrWhiteSpace(selectionQuery.Name), t => t.ChineseName.Contains(selectionQuery.Name) || (t.LastName + t.FirstName).Contains(selectionQuery.Name)) + .WhereIf(evaluationCriteriaCount > 0, t => t.TrialExperienceCriteriaList.Count(t => selectionQuery.EvaluationCriteriaIdList.Contains(t.EvaluationCriteriaId)) == evaluationCriteriaCount) + //用户类型 看到简历的范围这里需要确认 + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ReviewerCoordinator, t => t.UserList.Any(u => u.UserId == _userInfo.Id)) + .WhereIf(count > 0, t => t.DoctorDicRelationList.Count(u => guidList.Contains(u.DictionaryId)) == count) + .WhereIf(selectionQuery.EnrollStatus != null && selectionQuery.EnrollStatus == (int)ReviewerEnrollStatus.Yes, t => t.EnrollList.Any(u => u.EnrollStatus == EnrollStatus.DoctorReading)) + + .ProjectTo(_mapper.ConfigurationProvider); + + var result = await query.ToPagedListAsync(selectionQuery.PageIndex, selectionQuery.PageSize, selectionQuery.SortField == string.Empty ? "ReviewerCode" : selectionQuery.SortField, selectionQuery.Asc); + + //是否已申请 申请时间 申请人 + var doctorStateList = await _repository.Where(x => x.TrialId == selectionQuery.TrialId && x.EnrollStatus == EnrollStatus.HasApplyDownloadResume) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + result.CurrentPageData.ToList().ForEach(doctor => + { + //简历申请列表 --处理已经申请的 + var doctorState = doctorStateList.FirstOrDefault(t => t.DoctorId == doctor.Id && t.IntoGroupState == (int)EnrollStatus.HasApplyDownloadResume); + if (doctorState != null) + { + doctor.DoctorTrialState = (int)EnrollStatus.HasApplyDownloadResume; + doctor.OptTime = doctorState.OptTime; + doctor.OptUserName = doctorState.OptUserName; + } + }); + + return result; + + + } + + /// + /// 获取提交CRO或者CRO审核的Reviewer列表 + /// + /// + /// 根据状态获取医生列表,入组 相关接口 (提交CRO-1) CRO确认-4 + /// + [HttpPost] + public async Task> GetSubmissionOrApprovalReviewerList( + ReviewerSubmissionQueryDTO param) + { + + var doctorQuery = _repository.Where(x => x.TrialId == param.TrialId) + //提交CRO 以及下载简历列表 + .WhereIf(param.IntoGroupSearchState == 1, t => t.EnrollStatus >= EnrollStatus.HasApplyDownloadResume) + //CRO确认列表 状态为 已提交CRO + .WhereIf(param.IntoGroupSearchState == 4, t => t.EnrollStatus >= EnrollStatus.HasCommittedToCRO) + .ProjectTo(_mapper.ConfigurationProvider); + + var doctorPageList = await doctorQuery.ToPagedListAsync(param.PageIndex, param.PageSize, param.SortField == "" ? "Code" : param.SortField, param.Asc); + + + var enrollStateList = await _repository.Where(x => x.TrialId == param.TrialId) + //提交CRO 以及下载简历列表 + .WhereIf(param.IntoGroupSearchState == 1, t => t.EnrollStatus == EnrollStatus.HasCommittedToCRO) + //CRO确认列表 状态为 已提交CRO + .WhereIf(param.IntoGroupSearchState == 4, t => t.EnrollStatus == EnrollStatus.InviteIntoGroup) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + + + doctorPageList.CurrentPageData.ToList().ForEach(u => + { + var opt = enrollStateList.FirstOrDefault(t => t.DoctorId == u.Id); + if (opt != null) + { + u.DoctorTrialState = param.IntoGroupSearchState == 1 ? (int)EnrollStatus.HasCommittedToCRO : (int)EnrollStatus.InviteIntoGroup; + u.OptTime = opt.OptTime; + u.OptUserName = opt.OptUserName; + } + }); + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM) + { + //SPM 要看到提交的时间 提交人 + var enrollCommitList = await _repository.Where(x => x.TrialId == param.TrialId) + //提交CRO 以及下载简历列表 + .WhereIf(param.IntoGroupSearchState == 4, t => t.EnrollStatus == EnrollStatus.HasCommittedToCRO) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + doctorPageList.CurrentPageData.ToList().ForEach(u => + { + var opt = enrollCommitList.FirstOrDefault(t => t.DoctorId == u.Id); + if (opt != null) + { + + u.SubmmitTime = opt.OptTime; + u.SubmmitUserName = opt.OptUserName; + } + }); + + } + + return doctorPageList; + + + + } + + + //public async Task> GetSPMSubmissionOrApprovalReviewerList( + // ReviewerSubmissionQueryDTO param) + //{ + // var doctorQuery = _repository.Where(x => x.TrialId == param.TrialId) + // //提交CRO 以及下载简历列表 + // .WhereIf(param.IntoGroupSearchState == 1, t => t.EnrollStatus >= (int)EnrollStatus.HasApplyDownloadResume) + // //CRO确认列表 状态为 已提交CRO + // .WhereIf(param.IntoGroupSearchState == 4, t => t.EnrollStatus >= (int)EnrollStatus.HasCommittedToCRO) + // .ProjectTo(_mapper.ConfigurationProvider); + + // var doctorPageList = await doctorQuery.ToPagedListAsync(param.PageIndex, param.PageSize, param.SortField == "" ? "Code" : param.SortField, param.Asc); + + + // var enrollStateList = await _repository.Where(x => x.TrialId == param.TrialId) + // //提交CRO 以及下载简历列表 + // .WhereIf(param.IntoGroupSearchState == 1, t => t.EnrollStatus == (int)EnrollStatus.HasCommittedToCRO) + // //CRO确认列表 状态为 已提交CRO + // .WhereIf(param.IntoGroupSearchState == 4, t => t.EnrollStatus == (int)EnrollStatus.InviteIntoGroup) + // .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + // doctorPageList.CurrentPageData.ToList().ForEach(u => + // { + // var opt = enrollStateList.FirstOrDefault(t => t.DoctorId == u.Id); + // if (opt != null) + // { + // u.DoctorTrialState = param.IntoGroupSearchState == 1 ? (int)EnrollStatus.HasCommittedToCRO : (int)EnrollStatus.InviteIntoGroup; + // u.OptTime = opt.OptTime; + // u.OptUserName = opt.OptUserName; + // } + // }); + + // return doctorPageList; + //} + + /// + /// 获取项目下医生入组状态列表[Confirmation] + /// + [HttpPost] + public async Task> GetConfirmationReviewerList( + ReviewerConfirmationQueryDTO param) + { + + + var doctorQuery = _repository.Where(x => x.TrialId == param.TrialId && x.EnrollStatus >= EnrollStatus.InviteIntoGroup) + .ProjectTo(_mapper.ConfigurationProvider); + + var doctorPageList = await doctorQuery.ToPagedListAsync(param.PageIndex, param.PageSize, param.SortField == "" ? "Code" : param.SortField, param.Asc); + + var enrollStateList = await _repository.Where(x => x.TrialId == param.TrialId && x.EnrollStatus > EnrollStatus.InviteIntoGroup) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + + doctorPageList.CurrentPageData.ToList().ForEach(u => + { + u.DoctorTrialState = (int)EnrollStatus.InviteIntoGroup; + var opt = enrollStateList.FirstOrDefault(t => t.DoctorId == u.Id); + if (opt != null) + { + u.DoctorTrialState = opt.IntoGroupState; + u.OptTime = opt.OptTime; + u.OptUserName = opt.OptUserName; + } + }); + + return doctorPageList; + + } + + + + #endregion + + + + + } +} + diff --git a/IRaCIS.Core.Application/Service/Doctor/DoctorService.cs b/IRaCIS.Core.Application/Service/Doctor/DoctorService.cs new file mode 100644 index 0000000..1244af6 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/DoctorService.cs @@ -0,0 +1,621 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using Microsoft.AspNetCore.Mvc; +using System.Linq.Expressions; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Reviewer")] + public class DoctorService : BaseService, IDoctorService + { + private readonly IRepository _doctorRepository; + private readonly IRepository _messageRepository; + private readonly IRepository _enrollRepository; + private readonly IRepository _doctorDictionaryRepository; + private readonly IRepository _attachmentRepository; + private readonly IRepository _doctorCriterionFileRepository; + private readonly IRepository _userDoctorRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _trialExtRepository; + private readonly IRepository _vacationRepository; + + + + public DoctorService(IRepository doctorInfoRepository, + IRepository dictionaryRepository, + IRepository sysMessageRepository, IRepository intoGroupRepository, + IRepository doctorDictionaryRepository, + IRepository attachmentRepository, + IRepository doctorCriterionFileRepository, + IRepository userDoctorRepository, + IRepository trialRepository, + IRepository trialExtRepository, IRepository vacationRepository) + { + _doctorRepository = doctorInfoRepository; + _messageRepository = sysMessageRepository; + _enrollRepository = intoGroupRepository; + _doctorDictionaryRepository = doctorDictionaryRepository; + _attachmentRepository = attachmentRepository; + this._doctorCriterionFileRepository = doctorCriterionFileRepository; + _userDoctorRepository = userDoctorRepository; + _trialRepository = trialRepository; + _trialExtRepository = trialExtRepository; + _vacationRepository = vacationRepository; + } + + + + + #region 医生基本信息--查询、新增、更新 + + /// + /// 添加/更新 医生基本信息 BasicInfo + /// + + [HttpPost] + public async Task> AddOrUpdateDoctorBasicInfo(DoctorBasicInfoCommand basicInfoModel) + { + Expression> verifyExp = t => t.Phone == basicInfoModel.Phone || t.EMail == basicInfoModel.EMail; + + var verifyPair = new KeyValuePair>, string>(verifyExp, "current phone or email number already existed"); + + if (basicInfoModel.Id == Guid.Empty || basicInfoModel.Id == null) + { + + var doctor = _mapper.Map(basicInfoModel); + + //验证用户手机号 + if (await _doctorRepository.AnyAsync(t => t.Phone == doctor.Phone)) + { + return ResponseOutput.NotOk("The current phone number already existed!", new DoctorBasicInfoCommand()); + } + + if (await _doctorRepository.AnyAsync(t => t.EMail == doctor.EMail)) + { + return ResponseOutput.NotOk("The current email already existed!", new DoctorBasicInfoCommand()); + } + + doctor.Code = (await _doctorRepository.MaxAsync(t => t.Code)) + 1; + + doctor.ReviewerCode = AppSettings.GetCodeStr(doctor.Code, nameof(Doctor)); + + doctor.Password = MD5Helper.Md5(doctor.Phone); + + //插入中间表 + basicInfoModel.TitleIds.ForEach(titleId => doctor.DoctorDicRelationList.Add(new DoctorDictionary() { DoctorId = doctor.Id, KeyName = StaticData.Title, DictionaryId = titleId })); + + + await _doctorRepository.AddAsync(doctor); + //_doctorRepository.Add(doctor); + + await _repository.AddAsync(new UserDoctor() { DoctorId = doctor.Id, UserId = _userInfo.Id }); + //_userDoctorRepository.Add(new UserDoctor() { DoctorId = doctor.Id, UserId = _userInfo.Id }); + + var success = await _repository.SaveChangesAsync(); + return ResponseOutput.Result(success, _mapper.Map(doctor)); + + } + else + { + var updateModel = basicInfoModel; + + var phone = updateModel.Phone.Trim(); + if ((await _doctorRepository.FirstOrDefaultAsync(t => t.Phone == phone && t.Id != updateModel.Id)) != null) + { + return ResponseOutput.NotOk("The current phone number already existed!", new DoctorBasicInfoCommand()); + } + var email = updateModel.EMail.Trim(); + if (await _doctorRepository.AnyAsync(t => t.EMail == email && t.Id != updateModel.Id)) + { + return ResponseOutput.NotOk("The current email already existed!", new DoctorBasicInfoCommand()); + } + + var doctor = await _doctorRepository.FirstOrDefaultAsync(t => t.Id == updateModel.Id).IfNullThrowException(); + + //删除中间表 Title对应的记录 + await _repository.BatchDeleteAsync(t => t.DoctorId == updateModel.Id && t.KeyName == StaticData.Title); + + + var adddata = new List(); + //重新插入新的 Title记录 + updateModel.TitleIds.ForEach(titleId => adddata.Add(new DoctorDictionary() { DoctorId = updateModel.Id.Value, KeyName = StaticData.Title, DictionaryId = titleId })); + + await _repository.AddRangeAsync(adddata); + + _mapper.Map(basicInfoModel, doctor); + + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Result(success, basicInfoModel); + + } + + } + + + /// + ///详情、编辑-获取 医生基本信息 BasicInfo + /// + /// ReviewerID + /// + + [HttpGet("{doctorId:guid}")] + public async Task GetBasicInfo(Guid doctorId) + { + + #region 用导航属性直接查询 + + //SELECT[t].[Id], [t].[Code], [t].[ChineseName], [t].[EMail], [t].[FirstName], [t].[Introduction], [t].[LastName], [t].[Phone], [t].[Sex], [t].[WeChat], [t].[Nation], [t0].[Title], [t0].[TitleCN], [t0].[TitleId], [t0].[ShowOrder], [t0].[Id], [t0].[Id0] + //FROM( + // SELECT TOP(1)[d].[Id], [d].[Code], [d].[ChineseName], [d].[EMail], [d].[FirstName], [d].[Introduction], [d].[LastName], [d].[Phone], [d].[Sex], [d].[WeChat], [d].[Nation] + // FROM[Doctor] AS[d] WITH(NOLOCK) + // WHERE[d].[Id] = @__doctorId_0 + //) AS[t] + //LEFT JOIN( + // SELECT[d1].[Value] AS[Title], [d1].[ValueCN] AS[TitleCN], [d0].[DictionaryId] AS[TitleId], [d1].[ShowOrder], [d0].[Id], [d1].[Id] AS[Id0], [d0].[DoctorId] + // FROM [DoctorDictionary] AS [d0] WITH (NOLOCK) + // INNER JOIN[Dictionary] AS [d1] WITH (NOLOCK) ON [d0].[DictionaryId] = [d1].[Id] + // WHERE[d0].[KeyName] = N'Title' + //) AS[t0] ON[t].[Id] = [t0].[DoctorId] + //ORDER BY[t].[Id], [t0].[ShowOrder], [t0].[Id] + + //var doctorQueryable = _doctorRepository + // .Find(t => t.Id == doctorId) + // .Select(doctor => new DoctorBasicInfoDTO() + // { + // Id = doctor.Id, + // Code = doctor.Code, + // ChineseName = doctor.ChineseName, + // EMail = doctor.EMail, + // FirstName = doctor.FirstName, + // Introduction = doctor.Introduction, + // LastName = doctor.LastName, + // Phone = doctor.Phone, + // Sex = doctor.Sex, + // WeChat = doctor.WeChat, + // Nation = doctor.Nation, + + // //不要分三个属性查询,会做三次左连接,这样 只会一个左连接 + // TempObjList = doctor.DoctorDicList.Where(t => t.KeyName == StaticData.Title) + // .Select(t => new TempObj { Title = t.Dictionary.Value, TitleCN = t.Dictionary.ValueCN, TitleId = t.DictionaryId, ShowOrder = t.Dictionary.ShowOrder }).OrderBy(k => k.ShowOrder).ToList(), + // }); + + //var doctorBasicInfo = doctorQueryable.FirstOrDefault(); + + + + + #endregion + + var doctorBasicInfo = (await _doctorRepository.Where(t => t.Id == doctorId) + .ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + return doctorBasicInfo; + + + } + + #endregion + + #region Employment信息--查询和更新 + /// + /// 详情、编辑-获取医生工作信息 Employment + /// + [HttpGet("{doctorId:Guid}")] + public async Task GetEmploymentInfo(Guid doctorId) + { + + #region init EF select 废弃 + //var dic = GetDictionary(); + + //var employmentQueryable = from doctorItem in _doctorRepository + // .Where(t => t.Id == doctorId) + // join hospitalItem in _hospitalRepository.AsQueryable() on doctorItem.HospitalId equals hospitalItem.Id into g + // from hospital in g.DefaultIfEmpty() + // select new EmploymentDTO() + // { + // Id = doctorItem.Id, + // //部门 + // DepartmentId = doctorItem.DepartmentId, + // DepartmentOther = doctorItem.DepartmentOther, + // DepartmentOtherCN = doctorItem.DepartmentOtherCN, + + // //医院 + // HospitalId = doctorItem.HospitalId, + + // PositionId = doctorItem.PositionId, + // PositionOther = doctorItem.PositionOther, + // PositionOtherCN = doctorItem.PositionOtherCN, + + // RankId = doctorItem.RankId, + // RankOther = doctorItem.RankOther, + // RankOtherCN = doctorItem.RankOtherCN, + + // City = hospital.City, + // Country = hospital.Country, + // UniversityAffiliated = hospital.UniversityAffiliated, + // HospitalName = hospital.HospitalName, + // Province = hospital.Province, + + // CityCN = hospital.CityCN, + // CountryCN = hospital.CountryCN, + // UniversityAffiliatedCN = hospital.UniversityAffiliatedCN, + // HospitalNameCN = hospital.HospitalNameCN, + // ProvinceCN = hospital.ProvinceCN + // }; + + //var employmentInfo = employmentQueryable.FirstOrDefault(); + + //if (employmentInfo != null) + //{ + // //医院信息设置 + // if (employmentInfo.HospitalId == Guid.Empty) + // { + // employmentInfo.City = string.Empty; + // employmentInfo.Country = string.Empty; + // employmentInfo.UniversityAffiliated = string.Empty; + // employmentInfo.HospitalName = string.Empty; + // employmentInfo.Province = string.Empty; + // } + // employmentInfo.Department = employmentInfo.DepartmentId == Guid.Empty ? employmentInfo.DepartmentOther : dic.FirstOrDefault(o => o.Id == employmentInfo.DepartmentId)?.Value ?? ""; + + // employmentInfo.Rank = employmentInfo.RankId == Guid.Empty ? employmentInfo.RankOther : dic.FirstOrDefault(o => o.Id == employmentInfo.RankId)?.Value ?? ""; + + // employmentInfo.Position = employmentInfo.PositionId == Guid.Empty ? employmentInfo.PositionOther : dic.FirstOrDefault(o => o.Id == employmentInfo.PositionId)?.Value ?? ""; + + + // employmentInfo.DepartmentCN = employmentInfo.DepartmentId == Guid.Empty ? employmentInfo.DepartmentOther : dic.FirstOrDefault(o => o.Id == employmentInfo.DepartmentId)?.ValueCN ?? ""; + + // employmentInfo.RankCN = employmentInfo.RankId == Guid.Empty ? employmentInfo.RankOther : dic.FirstOrDefault(o => o.Id == employmentInfo.RankId)?.ValueCN ?? ""; + + // employmentInfo.PositionCN = employmentInfo.PositionId == Guid.Empty ? employmentInfo.PositionOther : dic.FirstOrDefault(o => o.Id == employmentInfo.PositionId)?.ValueCN ?? ""; + //} + #endregion + + var query = _doctorRepository.Where(t => t.Id == doctorId) + .ProjectTo(_mapper.ConfigurationProvider); + + var employmentInfo = (await query.FirstOrDefaultAsync()).IfNullThrowException(); + + return employmentInfo; + } + + [HttpPost] + + public async Task UpdateEmploymentInfo(EmploymentCommand doctorWorkInfoModel) + { + #region 废弃 + //var success = _doctorRepository.Update(d => d.Id == doctorWorkInfoModel.Id, u => new Doctor() + //{ + // DepartmentId = doctorWorkInfoModel.DepartmentId, + // DepartmentOther = doctorWorkInfoModel.DepartmentOther, + // DepartmentOtherCN = doctorWorkInfoModel.DepartmentOtherCN, + + // SpecialityId = doctorWorkInfoModel.DepartmentId, + // SpecialityOther = doctorWorkInfoModel.DepartmentOther, + // SpecialityOtherCN = doctorWorkInfoModel.DepartmentOtherCN, + + // RankId = doctorWorkInfoModel.RankId, + // RankOther = doctorWorkInfoModel.RankOther, + // RankOtherCN = doctorWorkInfoModel.RankOtherCN, + + // PositionId = doctorWorkInfoModel.PositionId, + // PositionOther = doctorWorkInfoModel.PositionOther, + // PositionOtherCN = doctorWorkInfoModel.PositionOtherCN, + + // HospitalId = doctorWorkInfoModel.HospitalId, + // UpdateTime = DateTime.Now + //}); + + //var doctor = _doctorRepository.FirstOrDefault(d => d.Id == doctorWorkInfoModel.Id); + //_mapper.Map(doctorWorkInfoModel, doctor); + //var success = _doctorRepository.SaveChanges(); + #endregion + + + var entity = await _repository.InsertOrUpdateAsync(doctorWorkInfoModel, true); + + + return ResponseOutput.Ok(); + } + + #endregion + + + #region 医生技能信息 查询和 更新 + [HttpGet, Route("{doctorId:Guid}")] + public async Task GetSpecialtyInfo(Guid doctorId) + { + + #region 利用导航属性直接查询出来 生成的sql ok 废弃 + + //var specialtyQueryable = _doctorRepository + // .Where(t => t.Id == doctorId).Include(u => u.DoctorDicRelationList) + // .Select(specialty => new SpecialtyDTO() + // { + // Id = specialty.Id, + // ReadingTypeOther = specialty.ReadingTypeOther, + // ReadingTypeOtherCN = specialty.ReadingTypeOtherCN, + + // SubspecialityOther = specialty.SubspecialityOther, + // SubspecialityOtherCN = specialty.SubspecialityOtherCN, + + + // DictionaryList = specialty.DoctorDicRelationList.Where(t => t.KeyName == StaticData.ReadingType || t.KeyName == StaticData.Subspeciality) + // .Select(t => new SpecialtyDTO.DoctorDictionaryView() { DictionaryId = t.DictionaryId, Value = t.Dictionary.Value, ValueCN = t.Dictionary.ValueCN, ShowOrder = t.Dictionary.ShowOrder, KeyName = t.Dictionary.KeyName }) + // .OrderBy(t => t.ShowOrder).ToList(), + + // SpecialityId = specialty.SpecialityId, + // Speciality = specialty.Speciality.Value, + // SpecialityCN = specialty.Speciality.ValueCN, + + // SpecialityOther = specialty.SpecialityOther, + // SpecialityOtherCN = specialty.SpecialityOtherCN + // }); + + //var specialtyInfo = specialtyQueryable.FirstOrDefault(); + + //return specialtyInfo; + + #endregion + + + var test = await (_doctorRepository.Where(t => t.Id == doctorId) + .ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + return test; + } + + + [HttpPost] + public async Task UpdateSpecialtyInfo(SpecialtyCommand specialtyUpdateModel) + { + var doctor = await _doctorRepository.FirstOrDefaultAsync(t => t.Id == specialtyUpdateModel.Id); + + if (doctor == null) return Null404NotFound(doctor); + + + + ////删除中间表 + //_doctorDictionaryRepository.Delete(t => + // t.DoctorId == specialtyUpdateModel.Id && t.KeyName == StaticData.Subspeciality); + //_doctorDictionaryRepository.Delete(t => + // t.DoctorId == specialtyUpdateModel.Id && t.KeyName == StaticData.ReadingType); + + await _repository.BatchDeleteAsync(t => + t.DoctorId == specialtyUpdateModel.Id && (t.KeyName == StaticData.Subspeciality || t.KeyName == StaticData.ReadingType)); + + + + + + //重新插入新的 + var adddata = new List(); + specialtyUpdateModel.ReadingTypeIds.ForEach(readingTypeId => adddata.Add( + new DoctorDictionary() + { + DoctorId = specialtyUpdateModel.Id, + KeyName = StaticData.ReadingType, + DictionaryId = readingTypeId + })); + specialtyUpdateModel.SubspecialityIds.ForEach(subspecialityId => adddata.Add( + new DoctorDictionary() + { + DoctorId = specialtyUpdateModel.Id, + KeyName = StaticData.Subspeciality, + DictionaryId = subspecialityId + })); + + await _repository.AddRangeAsync(adddata); + + _mapper.Map(specialtyUpdateModel, doctor); + + var success = await _repository.SaveChangesAsync(); + return ResponseOutput.Result(success); + + + } + #endregion + + #region 简历审核 + + [HttpPost] + public async Task UpdateAuditResume(ResumeConfirmCommand auditResumeParam) + { + var userId = _userInfo.Id; + //判断 合作协议、正式简历 是否有。如果没有,显示提示信息,并且不能保存 + var attachmentList = await _attachmentRepository.Where(u => u.DoctorId == auditResumeParam.Id) + .Select(u => u.Type).ToListAsync(); + if (auditResumeParam.ResumeStatus == ResumeStatusEnum.Pass && ((!attachmentList.Contains("Resume")) || (!attachmentList.Contains("Consultant Agreement")))) + { + return ResponseOutput.NotOk("Resume & Consultant Agreement must be upload "); + } + var success = await _doctorRepository.BatchUpdateNoTrackingAsync(o => o.Id == auditResumeParam.Id, u => new Doctor() + { + CooperateStatus = auditResumeParam.CooperateStatus, + ResumeStatus = auditResumeParam.ResumeStatus, + AdminComment = auditResumeParam.AdminComment, + ReviewStatus = auditResumeParam.ReviewStatus, + AcceptingNewTrial = auditResumeParam.AcceptingNewTrial, + ActivelyReading = auditResumeParam.ActivelyReading, + IsVirtual = auditResumeParam.IsVirtual, + BlindName = auditResumeParam.BlindName, + BlindNameCN = auditResumeParam.BlindNameCN, + BlindPublications = auditResumeParam.BlindPublications, + AuditTime = DateTime.Now, + AuditUserId = userId + }); + + if (success) + { + if (!string.IsNullOrWhiteSpace(auditResumeParam.MessageContent)) + { + var message = new Message + { + FromUserId = userId, + ToDoctorId = auditResumeParam.Id, + Title = "Resume review results", + Content = auditResumeParam.MessageContent, + HasRead = false, + MessageTime = DateTime.Now + }; + await _repository.AddAsync(message); + success = await _repository.SaveChangesAsync(); + } + } + + return ResponseOutput.Result(success); + } + + /// + /// 添加修改医生标准文件 + /// + /// + /// + [HttpPost] + public async Task AddDoctorCriterionFile(AddDoctorCriterionFileDto inDto) + { + + if (await _doctorCriterionFileRepository.AnyAsync(x => inDto.IsEnable && x.DoctorId == inDto.DoctorId && x.FileType == inDto.FileType && x.TrialId==inDto.TrialId &&x.TrialReadingCriterionId==inDto.TrialReadingCriterionId && x.IsEnable && x.CriterionType == inDto.CriterionType && x.Id != inDto.Id)) + { + throw new BusinessValidationFailedException("当前标准已添加过此类型文件"); + } + + + var entity = await _doctorCriterionFileRepository.InsertOrUpdateAsync(inDto); + + if (inDto.TrialId != null) + { + entity.Remark = await _trialRepository.Where(t => t.Id == inDto.TrialId).Select(t => t.TrialCode).FirstOrDefaultAsync(); + } + + + await _doctorCriterionFileRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + /// + /// 删除医生标准文件 + /// + /// + /// + [HttpPost] + public async Task DeleteDoctorCriterionFile(DeleteDoctorCriterionFile inDto) + { + var result = await _doctorCriterionFileRepository.DeleteFromQueryAsync(inDto.Id, true); + return ResponseOutput.Ok(result.Id.ToString()); + } + + /// + /// 获取医生标准文件 + /// + /// + /// + [HttpPost] + public async Task> GetDoctorCriterionFile(GetDoctorCriterionFileInDto inDto) + { + var result = await _doctorCriterionFileRepository.Where(x => x.DoctorId == inDto.DoctorId) + .WhereIf(inDto.CriterionType != null, x => x.CriterionType == inDto.CriterionType) + .WhereIf(inDto.FileType != null, x => x.FileType == inDto.FileType) + .ProjectTo(_mapper.ConfigurationProvider) + .ToListAsync(); + return result; + } + + + [HttpGet("{doctorId:guid}")] + public async Task GetAuditState(Guid doctorId) + { + var doctor = (await _doctorRepository + .ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(t => t.Id == doctorId)).IfNullThrowException(); + + doctor.InHoliday = (await _repository.CountAsync(x => x.DoctorId == doctorId && x.EndDate <= DateTime.Now && x.StartDate <= DateTime.Now)) > 0; + + return doctor; + } + + + /// + /// 获取医生入组信息 正在提交的数量 已同意入组项目个数 正在读的 + /// + [HttpPost, Route("{doctorId:guid}")] + public DoctorEnrollInfoDTO GetDoctorIntoGroupInfo(Guid doctorId) + { + var doctorQueryable = + from doctor in _doctorRepository.Where(t => t.Id == doctorId) + join intoGroupItem in _enrollRepository.AsQueryable() on doctor.Id equals intoGroupItem.DoctorId + into t + from intoGroupItem in t.DefaultIfEmpty() + group intoGroupItem by intoGroupItem.DoctorId + into g + select new DoctorEnrollInfoDTO + { + DoctorId = g.Key, + + //Submitted = g.Sum(t => + // t.EnrollStatus == (int)EnrollStatus.HasCommittedToCRO ? 1 : 0), + //Approved = g.Sum(t => + // t.EnrollStatus == (int)EnrollStatus.InviteIntoGroup ? 1 : 0), + //Reading = g.Sum(t => + // t.EnrollStatus == (int)EnrollStatus.DoctorReading ? 1 : 0) + + Submitted = g.Count(t => + t.EnrollStatus == EnrollStatus.HasCommittedToCRO), + Approved = g.Count(t => + t.EnrollStatus == EnrollStatus.InviteIntoGroup), + Reading = g.Count(t => + t.EnrollStatus == EnrollStatus.DoctorReading) + }; + + return doctorQueryable.FirstOrDefault().IfNullThrowException(); + } + + + /// + /// Get Statement of Work list.[New] + /// + [HttpGet("{doctorId:guid}")] + public List GetDoctorSowList(Guid doctorId) + { + var query = from enroll in _enrollRepository.Where(u => u.DoctorId == doctorId && u.EnrollStatus >= EnrollStatus.ConfirmIntoGroup) + join trialExt in _trialExtRepository.AsQueryable() on enroll.TrialId equals trialExt.TrialId + join trial in _trialRepository.AsQueryable() on enroll.TrialId equals trial.Id + select new SowDTO + { + FileName = trialExt.SowName, + FilePath = trialExt.SowPath, + TrialCode = trial.TrialCode, + CreateTime = trialExt.CreateTime + }; + return query.ToList().Where(u => !string.IsNullOrWhiteSpace(u.FileName)).ToList(); + } + + /// + /// Get Ack Statement of Work[New] + /// + [HttpGet("{doctorId:guid}")] + public List GetDoctorAckSowList(Guid doctorId) + { + var query = from enroll in _enrollRepository.Where(u => u.DoctorId == doctorId) + join attachment in _attachmentRepository.Where(u => u.DoctorId == doctorId) + on enroll.AttachmentId equals attachment.Id + join trial in _trialRepository.AsQueryable() on enroll.TrialId equals trial.Id + select new SowDTO + { + FileName = attachment.FileName, + FilePath = attachment.Path, + TrialCode = trial.TrialCode, + CreateTime = attachment.CreateTime + }; + return query.ToList(); + } + + #endregion + + } +} diff --git a/IRaCIS.Core.Application/Service/Doctor/EducationService.cs b/IRaCIS.Core.Application/Service/Doctor/EducationService.cs new file mode 100644 index 0000000..05273e9 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/EducationService.cs @@ -0,0 +1,140 @@ + +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Reviewer")] + public class EducationService : BaseService, IEducationService + { + private readonly IRepository _postgraduateRepository; + private readonly IRepository _educationRepository; + + public EducationService(IRepository doctorNormalEducationRepository, + IRepository doctorContinueLearningRepository) + { + _educationRepository = doctorNormalEducationRepository; + _postgraduateRepository = doctorContinueLearningRepository; + } + + /// + /// 根据医生Id获取医生教育经历和继续学习经历列表 + /// + [HttpGet("{doctorId:Guid}")] + public async Task GetEducation(Guid doctorId) + { + var educationList = await _educationRepository.Where(o => o.DoctorId == doctorId) + .OrderBy(t => t.CreateTime).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var postgraduateList = await _postgraduateRepository.Where(o => o.DoctorId == doctorId) + .OrderBy(t => t.CreateTime).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + return new DoctorEducationExperienceDTO() + { + EducationList = educationList, + PostgraduateList = postgraduateList + }; + + } + /// + /// 新增医生教育经历 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateEducationInfo(EducationCommand educationInfoViewModel) + { + if (educationInfoViewModel.Id == Guid.Empty || educationInfoViewModel.Id == null) + { + var doctorEducationInfo = _mapper.Map(educationInfoViewModel); + switch (educationInfoViewModel.Degree) + { + case StaticData.ReviewerDegree.Bachelor: + doctorEducationInfo.ShowOrder = 1; + break; + case StaticData.ReviewerDegree.Master: + doctorEducationInfo.ShowOrder = 2; + break; + case StaticData.ReviewerDegree.Doctorate: + doctorEducationInfo.ShowOrder = 3; + break; + } + await _educationRepository.AddAsync(doctorEducationInfo); + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Result(success, doctorEducationInfo.Id.ToString()); + } + + else + { + var needUpdate = await _educationRepository.FirstOrDefaultAsync(t => t.Id == educationInfoViewModel.Id); + + if (needUpdate == null) return Null404NotFound(needUpdate); + + _mapper.Map(educationInfoViewModel, needUpdate); + + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(success); + + } + //_educationRepository.Update(needUpdate); + + + } + + [HttpDelete, Route("{doctorId:guid}")] + public async Task DeleteEducationInfo(Guid id) + { + var success = await _educationRepository.BatchDeleteNoTrackingAsync(o => o.Id == id); + + return ResponseOutput.Result(success); + } + /// 添加/更新医生继续学习经历 + + [HttpPost] + public async Task AddOrUpdatePostgraduateInfo(PostgraduateCommand postgraduateViewModel) + { + #region 封装前 + + //if (postgraduateViewModel.Id == Guid.Empty || postgraduateViewModel.Id == null) + //{ + // var doctorContinueLearning = _mapper.Map(postgraduateViewModel); + // _postgraduateRepository.Add(doctorContinueLearning); + // var success = _postgraduateRepository.SaveChanges(); + + // return ResponseOutput.Result(success, doctorContinueLearning.Id.ToString()); + //} + //else + //{ + // _postgraduateRepository.Update(_mapper.Map(postgraduateViewModel)); + // var success = _postgraduateRepository.SaveChanges(); + + // return ResponseOutput.Result(success); + + //} + #endregion + + var entity = await _repository.InsertOrUpdateAsync(postgraduateViewModel, true); + return ResponseOutput.Ok(entity.Id); + } + /// + /// 删除医生继续学习经历 + /// + /// 医生Id + /// + + [HttpDelete("{doctorId:guid}")] + public async Task DeletePostgraduateInfo(Guid doctorId) + { + var success = await _repository.BatchDeleteAsync(o => o.Id == doctorId); + return ResponseOutput.Result(success); + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Doctor/Interface/IAttachmentService.cs b/IRaCIS.Core.Application/Service/Doctor/Interface/IAttachmentService.cs new file mode 100644 index 0000000..f93c478 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/Interface/IAttachmentService.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; + +using IRaCIS.Application.Contracts; + +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IAttachmentService + { + Task> SaveAttachments(IEnumerable attachmentList); + + + Task> AddAttachment(AttachmentDTO attachment); + Task DeleteAttachment(AttachementCommand param); + Task GetDetailById(Guid attachmentId); + Task> GetAttachmentByType(Guid doctorId, string type); + Task> GetAttachmentByTypes(Guid doctorId, string[] types); + Task> GetAttachments(Guid doctorId); + Task GetDoctorOfficialCV(int language, Guid doctorId); + + Task SetOfficial(Guid doctorId, Guid attachmentId, int language); + + Task SetLanguage(Guid doctorId, Guid attachmentId, int language); + } +} diff --git a/IRaCIS.Core.Application/Service/Doctor/Interface/IDoctorAccountService.cs b/IRaCIS.Core.Application/Service/Doctor/Interface/IDoctorAccountService.cs new file mode 100644 index 0000000..44d2149 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/Interface/IDoctorAccountService.cs @@ -0,0 +1,14 @@ +using System; + +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IDoctorAccountService + { + IResponseOutput Register(DoctorAccountRegisterModel doctorAccount); + DoctorAccountDTO Login(DoctorAccountLoginDTO doctorAccount); + IResponseOutput UpdatePassword(DoctorAccountUpdatePasswordCommand doctorAccount); + } +} diff --git a/IRaCIS.Core.Application/Service/Doctor/Interface/IDoctorListQueryService.cs b/IRaCIS.Core.Application/Service/Doctor/Interface/IDoctorListQueryService.cs new file mode 100644 index 0000000..8c8faf9 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/Interface/IDoctorListQueryService.cs @@ -0,0 +1,32 @@ +using IRaCIS.Application.Contracts; + +namespace IRaCIS.Application.Interfaces +{ + public interface IDoctorListQueryService + { + /// + /// 医生多条件查询 + /// + Task> GetDoctorSearchList(DoctorSearchDTO param); + + /// + /// 筛选医生列表 + /// + /// + /// + // + Task> GetSelectionReviewerList( + ReviewerSelectionQueryDTO doctorSearchModel); + + /// + /// //入组 相关接口 (提交CRO-1) CRO确认-4 + /// + Task> GetSubmissionOrApprovalReviewerList( + ReviewerSubmissionQueryDTO doctorIntoGroupSearchModel); + + + //医生确认状态列表 + Task> GetConfirmationReviewerList( + ReviewerConfirmationQueryDTO trialIdPageModel); + } +} diff --git a/IRaCIS.Core.Application/Service/Doctor/Interface/IDoctorService.cs b/IRaCIS.Core.Application/Service/Doctor/Interface/IDoctorService.cs new file mode 100644 index 0000000..cffc252 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/Interface/IDoctorService.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IDoctorService + { + #region 医生 基本信息 + + /// + /// 基本信息详情展示、编辑使用 + /// + /// + /// + Task GetBasicInfo(Guid doctorId); + + /// + /// 添加医生基本信息 + /// + /// + /// + Task> AddOrUpdateDoctorBasicInfo(DoctorBasicInfoCommand addBasicInfoParam); + + #endregion + + #region 医生 工作信息 + + /// + /// 获取医生 工作信息 + /// + /// + /// + Task GetEmploymentInfo(Guid doctorId); + /// + /// 更新医生 工作信息 + /// + /// + /// + Task UpdateEmploymentInfo(EmploymentCommand updateDoctorWorkInfoViewModel); + + #endregion + + /// + /// 获取医生技能信息 + /// + Task GetSpecialtyInfo(Guid doctorId); + + /// + /// 更新医生技能信息 + /// + Task UpdateSpecialtyInfo(SpecialtyCommand specialtyUpdateModel); + + /// + /// 获取医生 审核状态 + /// + Task GetAuditState(Guid doctorId); + + /// + /// 审核简历 和合作关系 + /// + Task UpdateAuditResume(ResumeConfirmCommand auditResumeParam); + + /// 医生详情 入组信息 + DoctorEnrollInfoDTO GetDoctorIntoGroupInfo(Guid doctorId); + + /// 获取医生参与项目的Sow协议 + List GetDoctorSowList(Guid doctorId); + + /// 获取医生入组的 ack Sow + List GetDoctorAckSowList(Guid doctorId); + } +} diff --git a/IRaCIS.Core.Application/Service/Doctor/Interface/IEducationService.cs b/IRaCIS.Core.Application/Service/Doctor/Interface/IEducationService.cs new file mode 100644 index 0000000..958fdae --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/Interface/IEducationService.cs @@ -0,0 +1,26 @@ +using System; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IEducationService + { + + Task GetEducation(Guid doctorId); + + #region 教育经历 + Task AddOrUpdateEducationInfo(EducationCommand doctorEducationInfoViewModel); + + Task DeleteEducationInfo(Guid doctorId); + + #endregion + + #region 继续教育经历 + Task AddOrUpdatePostgraduateInfo(PostgraduateCommand doctorContinueLearningViewModel); + Task DeletePostgraduateInfo(Guid doctorId); + + #endregion + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Doctor/Interface/IResearchPublicationService.cs b/IRaCIS.Core.Application/Service/Doctor/Interface/IResearchPublicationService.cs new file mode 100644 index 0000000..ba983de --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/Interface/IResearchPublicationService.cs @@ -0,0 +1,13 @@ +using System; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IResearchPublicationService + { + Task GetResearchPublication(Guid doctorId); + + Task AddOrUpdateResearchPublication(ResearchPublicationDTO param); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Doctor/Interface/ITrialExperienceService.cs b/IRaCIS.Core.Application/Service/Doctor/Interface/ITrialExperienceService.cs new file mode 100644 index 0000000..bb803c3 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/Interface/ITrialExperienceService.cs @@ -0,0 +1,15 @@ +using System; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface ITrialExperienceService + { + Task GetTrialExperience(Guid doctorId); + Task AddOrUpdateTrialExperience(TrialExperienceCommand model); + Task DeleteTrialExperience(Guid id); + Task UpdateGcpExperience(GCPExperienceCommand model); + Task UpdateOtherExperience(ClinicalExperienceCommand updateOtherClinicalExperience); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Doctor/Interface/IVacationService.cs b/IRaCIS.Core.Application/Service/Doctor/Interface/IVacationService.cs new file mode 100644 index 0000000..834864a --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/Interface/IVacationService.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +using IRaCIS.Application.Contracts; + +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IVacationService + { + Task AddOrUpdateVacation(VacationCommand vacationViewModel); + Task DeleteVacation(Guid id); + Task> GetVacationList(Guid doctorId, int pageIndex, int pageSize); + + /// 判断当前时间是否在休假 + Task OnVacation(Guid reviewerId); + } +} diff --git a/IRaCIS.Core.Application/Service/Doctor/ResearchPublicationService.cs b/IRaCIS.Core.Application/Service/Doctor/ResearchPublicationService.cs new file mode 100644 index 0000000..67925dc --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/ResearchPublicationService.cs @@ -0,0 +1,41 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Reviewer")] + public class ResearchPublicationService : BaseService, IResearchPublicationService + { + private readonly IRepository researchPublicationRepository; + + public ResearchPublicationService(IRepository _researchPublicationRepository) + { + researchPublicationRepository = _researchPublicationRepository; + } + + /// + /// 查询-医生科学研究信息 + /// + /// 医生Id + /// + [HttpGet("{doctorId:guid}")] + public async Task GetResearchPublication(Guid doctorId) + { + var doctorScientificResearchInfo = await researchPublicationRepository.Where(o => o.DoctorId == doctorId) + .ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + + return doctorScientificResearchInfo; + } + + + [HttpPost] + public async Task AddOrUpdateResearchPublication(ResearchPublicationDTO param) + { + + var entity = await _repository.InsertOrUpdateAsync(param, true); + + return ResponseOutput.Ok(entity.Id); + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Doctor/TrialExperienceService.cs b/IRaCIS.Core.Application/Service/Doctor/TrialExperienceService.cs new file mode 100644 index 0000000..16a7a2e --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/TrialExperienceService.cs @@ -0,0 +1,181 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Reviewer")] + public class TrialExperienceService : BaseService, ITrialExperienceService + { + private readonly IRepository _trialExperienceRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _attachmentRepository; + private readonly IRepository _trialExperienceCriteriaRepository; + + + + public TrialExperienceService(IRepository trialExperienceRepository, IRepository doctorRepository, IRepository attachmentRepository, + IRepository trialExperienceCriteriaRepository) + { + _trialExperienceRepository = trialExperienceRepository; + _doctorRepository = doctorRepository; + _attachmentRepository = attachmentRepository; + _trialExperienceCriteriaRepository = trialExperienceCriteriaRepository; + + } + + + /// + /// 根据医生Id,获取临床试验经历 界面所有数据 + /// + [HttpGet("{doctorId:guid}")] + public async Task GetTrialExperience(Guid doctorId) + { + var trialExperience = new TrialExperienceModel(); + + var doctor = await _doctorRepository.Where(o => o.Id == doctorId) + .ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + + trialExperience.ClinicalTrialExperienceList = await GetTrialExperienceList(doctorId); + + if (doctor != null) + { + trialExperience.GCP = doctor.GCP; + trialExperience.Id = doctor.Id; + trialExperience.OtherClinicalExperience = doctor.OtherClinicalExperience ?? ""; + trialExperience.OtherClinicalExperienceCN = doctor.OtherClinicalExperienceCN ?? ""; + var attachment = await _attachmentRepository.FirstOrDefaultAsync(t => t.Id == doctor.GCPId); + if (attachment != null) + { + trialExperience.ExpiryDateStr = attachment.ExpiryDate == null ? "" : attachment.ExpiryDate.Value.ToString("yyyy-MM-dd HH:mm"); + + trialExperience.Path = attachment.Path; + trialExperience.GCPFullPath = attachment.Path + "?access_token=" + _userInfo.UserToken; + trialExperience.Type = attachment.Type; + trialExperience.FileName = attachment.FileName; + trialExperience.GCPId = attachment.Id; + } + } + + return trialExperience; + } + + private async Task> GetTrialExperienceList(Guid doctorId) + { + var doctorClinicalTrialExperienceList = await _trialExperienceRepository.Where(o => o.DoctorId == doctorId).OrderBy(t => t.CreateTime) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + return doctorClinicalTrialExperienceList; + + } + /// 添加或更新医生临床经验列表项 + + [HttpPost] + public async Task AddOrUpdateTrialExperience(TrialExperienceCommand trialExperienceViewModel) + { + if (trialExperienceViewModel.Id == Guid.Empty || trialExperienceViewModel.Id == null) + { + var trialExperience = + _mapper.Map(trialExperienceViewModel); + + trialExperience = await _repository.AddAsync(trialExperience); + + List criteriaList = new List(); + trialExperienceViewModel.EvaluationCriteriaIdList.ForEach(t => criteriaList.Add(new TrialExperienceCriteria() + { + DoctorId = trialExperienceViewModel.DoctorId, + //EvaluationCriteria = t.EvaluationCriteria, + EvaluationCriteriaId = t, + TrialExperienceId = trialExperience.Id + })); + + await _repository.AddRangeAsync(criteriaList); + + + var success = await _repository.SaveChangesAsync(); + return ResponseOutput.Result(success, trialExperience.Id); + } + else + { + var needUpdate = await _trialExperienceRepository.FirstOrDefaultAsync(t => t.Id == trialExperienceViewModel.Id); + + if (needUpdate == null) return Null404NotFound(needUpdate); + + _mapper.Map(trialExperienceViewModel, needUpdate); + await _repository.UpdateAsync(needUpdate); + + await _repository.BatchDeleteAsync(t => t.TrialExperienceId == needUpdate.Id); + + List criteriaList = new List(); + + trialExperienceViewModel.EvaluationCriteriaIdList.ForEach(t => criteriaList.Add(new TrialExperienceCriteria() + { + DoctorId = trialExperienceViewModel.DoctorId, + EvaluationCriteriaId = t, + TrialExperienceId = needUpdate.Id + })); + + await _repository.AddRangeAsync(criteriaList); + + + var success = await _repository.SaveChangesAsync(); + return ResponseOutput.Result(success, trialExperienceViewModel.Id); + } + } + + /// + /// 删除临床经验 + /// + + [HttpDelete, Route("{doctorId:guid}")] + public async Task DeleteTrialExperience(Guid doctorId) + { + var success = await _repository.BatchDeleteAsync(o => o.Id == doctorId); + return ResponseOutput.Result(success); + } + /// + /// 更新-GCP和其他临床经验 + /// + /// + /// + + [HttpPost] + public async Task UpdateGcpExperience(GCPExperienceCommand updateGCPExperienceParam) + { + //_attachmentRepository.Delete(t => t.DoctorId == updateGCPExperienceParam.Id && t.Type == StaticData.GCP); + + var successs = await _repository.BatchUpdateAsync(o => o.Id == updateGCPExperienceParam.Id, u => new Doctor() + { + GCP = updateGCPExperienceParam.GCP, + GCPId = updateGCPExperienceParam.GCP==0&&updateGCPExperienceParam.GCPId==null?Guid.Empty: updateGCPExperienceParam.GCPId!.Value + }); + + if (updateGCPExperienceParam.GCP == 0 && updateGCPExperienceParam.GCPId != null) + { + await _repository.BatchDeleteAsync(a => a.Id == updateGCPExperienceParam.GCPId); + } + + + + return ResponseOutput.Result(successs, updateGCPExperienceParam.GCPId.ToString()); + + } + + /// + /// 更新其他技能经验 + /// + + [HttpPost] + public async Task UpdateOtherExperience(ClinicalExperienceCommand updateOtherClinicalExperience) + { + var success = await _repository.BatchUpdateAsync(o => o.Id == updateOtherClinicalExperience.DoctorId, u => new Doctor() + { + OtherClinicalExperience = updateOtherClinicalExperience.OtherClinicalExperience ?? string.Empty, + OtherClinicalExperienceCN = updateOtherClinicalExperience.OtherClinicalExperienceCN ?? string.Empty + }); + + return ResponseOutput.Result(success); + } + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Doctor/VacationService.cs b/IRaCIS.Core.Application/Service/Doctor/VacationService.cs new file mode 100644 index 0000000..54f7b4b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/VacationService.cs @@ -0,0 +1,88 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Application.Services +{ + [ ApiExplorerSettings(GroupName = "Reviewer")] + public class VacationService : BaseService, IVacationService + { + private readonly IRepository _vacationRepository; + + public VacationService(IRepository vacationRepository) + { + _vacationRepository = vacationRepository; + } + /// + /// 添加休假时间段 + /// + /// Status不传 + /// + + [HttpPost] + public async Task AddOrUpdateVacation(VacationCommand param) + { + if (param.Id == Guid.Empty|| param.Id ==null) + { + var result = await _vacationRepository.AddAsync(_mapper.Map(param)); + + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Result(success, result.Id); + + } + else + { + var success = await _vacationRepository.BatchUpdateNoTrackingAsync(u => u.Id == param.Id, + h => new Vacation + { + StartDate = param.StartDate, + EndDate = param.EndDate + }); + + return ResponseOutput.Result(success); + + } + + } + /// + /// 删除休假时间段 + /// + /// 记录Id + /// + + [HttpDelete("{holidayId:guid}")] + public async Task DeleteVacation(Guid holidayId) + { + var success = await _vacationRepository.BatchDeleteNoTrackingAsync(u => u.Id == holidayId); + + return ResponseOutput.Result(success); + + } + + /// + /// 获取休假时间段列表 + /// + /// + [HttpGet("{doctorId:guid}/{pageIndex:int}/{pageSize:int}")] + public async Task> GetVacationList(Guid doctorId, int pageIndex, int pageSize) + { + var query = _vacationRepository.Where(u => u.DoctorId == doctorId) + .ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToPagedListAsync(pageIndex, pageSize, "StartDate"); + + } + + [NonDynamicMethod] + public async Task OnVacation(Guid doctorId) + { + var count = await _vacationRepository.CountAsync(u => u.DoctorId == doctorId && u.EndDate >= DateTime.Now && u.StartDate <= DateTime.Now); + + return ResponseOutput.Result(count > 0); + } + + } +} diff --git a/IRaCIS.Core.Application/Service/Doctor/_MapConfig.cs b/IRaCIS.Core.Application/Service/Doctor/_MapConfig.cs new file mode 100644 index 0000000..5492872 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Doctor/_MapConfig.cs @@ -0,0 +1,159 @@ +using AutoMapper; +using AutoMapper.EquivalencyExpression; +using IRaCIS.Application.Contracts; +using IRaCIS.Application.Contracts.Pay; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Service +{ + public class DoctorConfig : Profile + { + public DoctorConfig() + { + #region reviewer + + //基本信息 工作信息 添加时转换使用 + CreateMap().EqualityComparison((odto, o) => odto.Id == o.Id); + + //学习经历 添加时转换使用 + CreateMap().EqualityComparison((odto, o) => odto.Id == o.Id); + CreateMap().EqualityComparison((odto, o) => odto.Id == o.Id); + CreateMap().EqualityComparison((odto, o) => odto.Id == o.Id); + CreateMap().EqualityComparison((odto, o) => odto.Id == o.Id); + + //医生账户 + CreateMap(); + CreateMap(); + + CreateMap().EqualityComparison((odto, o) => odto.Id == o.Id); + + + CreateMap().EqualityComparison((odto, o) => odto.Id == o.Id); + + CreateMap().EqualityComparison((odto, o) => odto.Id == o.Id); + + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + + CreateMap(); + + CreateMap(); + CreateMap(); + + CreateMap(); + + #endregion + CreateMap(); + + CreateMap(); + + CreateMap().ReverseMap(); + + //医生列表、项目显示列表模型转换 + CreateMap(); + + CreateMap() + .ForMember(d => d.UserTypeShortName, u => u.MapFrom(t => t.UserTypeRole.UserTypeShortName)) + .ForMember(d => d.Code, u => u.MapFrom(t => t.UserCode)) + .ForMember(d => d.PermissionStr, u => u.MapFrom(t => t.UserTypeRole.PermissionStr)) + .ForMember(d => d.RealName, u => u.MapFrom(user => string.IsNullOrEmpty(user.FirstName) ? user.LastName : user.LastName + " / " + user.FirstName)); + + CreateMap() + .ForMember(d => d.Phase, u => u.MapFrom(t => t.Phase.Value)) + .ForMember(d => d.EvaluationCriteriaList, u => u.MapFrom(t => t.ExperienceCriteriaList.Select(t => t.EvaluationCriteria.Value))) + .ForMember(d => d.EvaluationCriteriaIdList, u => u.MapFrom(t => t.ExperienceCriteriaList.Select(t => t.EvaluationCriteriaId))); + + CreateMap() + .ForMember(d => d.Code, u => u.MapFrom(t => t.ReviewerCode)) + .ForMember(d => d.RealName, u => u.MapFrom(t => t.ChineseName)) + .ForMember(d => d.IsReviewer, u => u.MapFrom(t => true)) + .ForMember(d => d.UserName, u => u.MapFrom(doctor => doctor.LastName + " / " + doctor.FirstName)); + + #region 医生基本信息 + CreateMap(); + CreateMap().IncludeMembers(t => t.Hospital).Include() + .ForMember(d => d.AccountUserName, u => u.MapFrom(s => s.EnrollList.Where(t=>t.DoctorUserId!=null).Select(c=>c.DoctorUser.UserName).FirstOrDefault())) + .ForMember(d => d.Department, u => u.MapFrom(s => s.Department.Value)) + .ForMember(d => d.DepartmentCN, u => u.MapFrom(s => s.Department.ValueCN)) + .ForMember(d => d.Position, u => u.MapFrom(s => s.Position.Value)) + .ForMember(d => d.PositionCN, u => u.MapFrom(s => s.Position.ValueCN)) + .ForMember(d => d.Rank, u => u.MapFrom(s => s.Rank.Value)) + .ForMember(d => d.RankCN, u => u.MapFrom(s => s.Rank.ValueCN)) + .ForMember(d => d.Speciality, u => u.MapFrom(s => s.Speciality.Value)) + .ForMember(d => d.SpecialityCN, u => u.MapFrom(s => s.Speciality.ValueCN)) + .ForMember(d => d.HasResume, u => u.MapFrom(s => s.AttachmentList.Any(u => u.Type == "Resume" && u.IsOfficial))) + .ForMember(d => d.Submitted, u => u.MapFrom(s => s.EnrollList.Count(t => t.EnrollStatus == EnrollStatus.HasCommittedToCRO))) + .ForMember(d => d.Approved, u => u.MapFrom(s => s.EnrollList.Count(t => t.EnrollStatus == EnrollStatus.InviteIntoGroup))) + .ForMember(d => d.Reading, u => u.MapFrom(s => s.EnrollList.Count(t => t.EnrollStatus == EnrollStatus.DoctorReading))) + .ForMember(d => d.Finished, u => u.MapFrom(s => s.EnrollList.Count(t => t.EnrollStatus == EnrollStatus.Finished))) + .ForMember(d => d.Reconfirmed, u => u.MapFrom(s => s.ReviewStatus == ReviewerInformationConfirmStatus.ConfirmPass)) + .ForMember(o => o.DictionaryList, t => t.MapFrom(u => u.DoctorDicRelationList.Where(t => t.KeyName == StaticData.ReadingType || t.KeyName == StaticData.Subspeciality).Select(t => t.Dictionary).OrderBy(t => t.ShowOrder))); + CreateMap(); + + CreateMap(); + + + //这样会左连接三次 + // CreateMap() + //.ForMember(d => d.TitleCNList, u => u.MapFrom(s => s.DoctorDicRelationList.Where(t => t.KeyName == StaticData.Title) + //.Select(t => new { TitleCN = t.Dictionary.ValueCN, ShowOrder = t.Dictionary.ShowOrder }).OrderBy(k => k.ShowOrder).Select(t => t.TitleCN))) + // .ForMember(d => d.TitleList, u => u.MapFrom(s => s.DoctorDicRelationList.Where(t => t.KeyName == StaticData.Title) + //.Select(t => new { Title = t.Dictionary.Value, ShowOrder = t.Dictionary.ShowOrder }).OrderBy(k => k.ShowOrder).Select(t => t.Title))) + // .ForMember(d => d.TitleIds, u => u.MapFrom(s => s.DoctorDicRelationList.Where(t => t.KeyName == StaticData.Title) + //.Select(t => new { TitleId = t.Dictionary.Id, ShowOrder = t.Dictionary.ShowOrder }).OrderBy(k => k.ShowOrder).Select(t => t.TitleId))); + + //这样只会查询一次 + CreateMap() + .ForMember(o => o.DoctorDicViewDtos, t => t.MapFrom(u => u.DoctorDicRelationList.Where(t => t.KeyName == StaticData.Title).Select(t => t.Dictionary).OrderBy(t => t.ShowOrder))); + CreateMap() + .ForMember(t=>t.ParentCode,u=>u.MapFrom(c=>c.Parent.Code)); + //CreateMap(); + + CreateMap() + .ForMember(o => o.Speciality, t => t.MapFrom(u => u.Speciality.Value)) + .ForMember(o => o.DictionaryList, t => t.MapFrom(u => u.DoctorDicRelationList.Where(t => t.KeyName == StaticData.ReadingType || t.KeyName == StaticData.Subspeciality).Select(t => t.Dictionary).OrderBy(t => t.ShowOrder))); + CreateMap(); + + //医生职业信息 + CreateMap().IncludeMembers(t => t.Hospital) + .ForMember(d => d.Department, u => u.MapFrom(s => s.Department.Value)) + .ForMember(d => d.DepartmentCN, u => u.MapFrom(s => s.Department.ValueCN)) + .ForMember(d => d.Position, u => u.MapFrom(s => s.Position.Value)) + .ForMember(d => d.PositionCN, u => u.MapFrom(s => s.Position.ValueCN)) + .ForMember(d => d.Rank, u => u.MapFrom(s => s.Rank.Value)) + .ForMember(d => d.RankCN, u => u.MapFrom(s => s.Rank.ValueCN)); + CreateMap(); + + CreateMap() + .ForMember(d => d.IntoGroupState, u => u.MapFrom(s => s.EnrollStatus)) + .ForMember(d => d.OptTime, u => u.MapFrom(s => s.CreateTime)) + .ForMember(d => d.OptUserName, u => u.MapFrom(s => s.CreateUser.UserName)); + + CreateMap().IncludeMembers(t => t.Doctor, t => t.Doctor.Hospital) + .ForMember(o => o.DictionaryList, t => t.MapFrom(u => u.Doctor.DoctorDicRelationList.Where(t => t.KeyName == StaticData.ReadingType || t.KeyName == StaticData.Subspeciality).Select(t => t.Dictionary).OrderBy(t => t.ShowOrder))) + .ForMember(d => d.Speciality, u => u.MapFrom(s => s.Doctor.Speciality.Value)) + .ForMember(d => d.SpecialityCN, u => u.MapFrom(s => s.Doctor.Speciality.ValueCN)) + .ForMember(d => d.Id, u => u.MapFrom(s => s.Doctor.Id)) + .ForMember(d => d.Code, u => u.MapFrom(s => s.DoctorUser.UserName)) + ; + CreateMap(); + CreateMap(); + + + #endregion + + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/Document/DTO/SystemDocumentViewModel.cs b/IRaCIS.Core.Application/Service/Document/DTO/SystemDocumentViewModel.cs new file mode 100644 index 0000000..78f9d47 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Document/DTO/SystemDocumentViewModel.cs @@ -0,0 +1,165 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-05 09:17:10 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Core.Application.Contracts +{ + /// SystemDocumentView 列表视图模型 + public class SystemDocumentView : SystemDocumentAddOrEdit + { + public string FullFilePath { get; set; } = string.Empty; + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + + public string FileType { get; set; } = string.Empty; + + public List NeedConfirmedUserTypeList { get; set; }=new List(); + } + + public class UnionDocumentView : SystemDocumentAddOrEdit + { + public string FullFilePath { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + + public bool IsSystemDoc { get; set; } + + public string FileType { get; set; } = string.Empty; + + } + + + public class UnionDocumentWithConfirmInfoView: UnionDocumentView + { + public DateTime? ConfirmTime { get; set; } + + public Guid? ConfirmUserId { get; set; } + + public bool IsConfirmed => ConfirmTime is not null; + + public string UserName { get; set; } = string.Empty; + + public string RealName { get; set; } = string.Empty; + + + public Guid UserTypeId { get; set; } + public string UserTypeShortName { get; set; } = string.Empty; + } + + + public class TrialUserDto + { + public Guid UserId { get; set; } + public string UserName { get; set; } = string.Empty; + + public string RealName { get; set; } = string.Empty; + } + + public class DocumentUnionWithUserStatView: UnionDocumentView + { + public int? DocumentUserCount { get; set; } + public int? DocumentConfirmedUserCount { get; set; } + } + + public class TrialUserUnionDocumentView + { + public Guid UserId { get; set; } + + public string UserTypeShortName { get; set; } = string.Empty; + public string UserName { get; set; } = string.Empty; + public string RealName { get; set; } = string.Empty; + + + public int? SystemDocumentCount { get; set; } + public int? TrialDocumentCount { get; set; } + public int? TrialDocumentConfirmedCount { get; set; } + public int? SystemDocumentConfirmedCount { get; set; } + + //public List DocumentList { get; set; } + } + + + + + + + ///SystemDocumentQuery 列表查询参数模型 + public class SystemDocumentQuery : PageInput + { + + + public Guid? SystemDocumentId { get; set; } + + public Guid? FileTypeId { get; set; } + + public string Name { get; set; } = string.Empty; + + + } + + public class TrialUserDocUnionQuery: PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public Guid? FileTypeId { get; set; } + + + public string Name { get; set; } = string.Empty; + + public bool? IsSign { get; set; } + + } + + public class UserConfirmCommand + { + + [NotDefault] + public Guid DocumentId { get; set; } + + + public bool isSystemDoc { get; set; } + + public string SignText { get; set; } = string.Empty; + } + + public class DocumentTrialUnionQuery : TrialUserDocUnionQuery + { + + public Guid? UserTypeId { get; set; } + public Guid? UserId { get; set; } + } + + /// SystemDocumentAddOrEdit 列表查询参数模型 + public class SystemDocumentAddOrEdit + { + public Guid? Id { get; set; } + public Guid FileTypeId { get; set; } + public string Name { get; set; } = string.Empty; + public string Path { get; set; } = string.Empty; + + public bool IsDeleted { get; set; } + + public int SignViewMinimumMinutes { get; set; } + + } + + public class AddOrEditSystemDocument : SystemDocumentAddOrEdit + { + + public List NeedConfirmedUserTypeIdList { get; set; }=new List(); + } +} + + + + + diff --git a/IRaCIS.Core.Application/Service/Document/DTO/TrialDocumentUserConfirmViewModel.cs b/IRaCIS.Core.Application/Service/Document/DTO/TrialDocumentUserConfirmViewModel.cs new file mode 100644 index 0000000..8bd7c33 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Document/DTO/TrialDocumentUserConfirmViewModel.cs @@ -0,0 +1,47 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-05 09:17:10 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.Contracts +{ + /// TrialDocumentUserConfirmView 列表视图模型 + public class TrialDocumentUserConfirmView + { + public Guid TrialId { get; set; } + + public Guid? TrialDocumentId { get; set; } + + public DateTime? ConfirmTime { get; set; } + + public Guid? ConfirmUserId { get; set; } + + public string UserName { get; set; } = string.Empty; + + public string RealName { get; set; } = string.Empty; + + + + } + + + + + + + public class NeedConfirmedUserTypeView + { + public Guid NeedConfirmUserTypeId { get; set; } + + public string UserTypeShortName { get; set; } = string.Empty; + } + + + + + +} + + diff --git a/IRaCIS.Core.Application/Service/Document/DTO/TrialDocumentViewModel.cs b/IRaCIS.Core.Application/Service/Document/DTO/TrialDocumentViewModel.cs new file mode 100644 index 0000000..f481f3d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Document/DTO/TrialDocumentViewModel.cs @@ -0,0 +1,75 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-05 09:17:10 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Infrastructure.Extention; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Core.Application.Contracts +{ + /// TrialDocumentView 列表视图模型 + public class TrialDocumentView : TrialDocumentAddOrEdit + { + public string FullFilePath { get; set; } = String.Empty; + + public bool IsSomeUserSigned{get;set;} + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + public string FileType { get; set; } = string.Empty; + + public List NeedConfirmedUserTypeList { get; set; } = new List(); + + + } + + + + ///TrialDocumentQuery 列表查询参数模型 + public class TrialDocumentQuery : PageInput + { + + public Guid? FileTypeId { get; set; } + + public string Name { get; set; } = String.Empty; + + [NotDefault] + public Guid TrialId { get; set; } + + } + + /// TrialDocumentAddOrEdit 列表查询参数模型 + public class TrialDocumentAddOrEdit + { + public Guid? Id { get; set; } + public Guid TrialId { get; set; } + + public Guid FileTypeId { get; set; } + public string Name { get; set; } = String.Empty; + public string Path { get; set; } = String.Empty; + + [MaxLength(500)] + public string Description { get; set; } = String.Empty; + public bool IsDeleted { get; set; } + + public int SignViewMinimumMinutes { get; set; } + + } + + public class AddOrEditTrialDocument: TrialDocumentAddOrEdit + { + + public List NeedConfirmedUserTypeIdList { get; set; } = new List(); + } + + + + +} + + diff --git a/IRaCIS.Core.Application/Service/Document/DTO/TrialEmailNoticeConfigViewModel.cs b/IRaCIS.Core.Application/Service/Document/DTO/TrialEmailNoticeConfigViewModel.cs new file mode 100644 index 0000000..8ecdc76 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Document/DTO/TrialEmailNoticeConfigViewModel.cs @@ -0,0 +1,170 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-10-20 11:52:31 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Application.ViewModel +{ + /// TrialEmailNoticeConfigView 列表视图模型 + public class TrialEmailNoticeConfigView : TrialEmailNoticeConfigAddOrEdit + { + + public Guid UpdateUserId { get; set; } + + public DateTime UpdateTime { get; set; } + + public DateTime CreateTime { get; set; } + + public Guid CreateUserId { get; set; } + + public string TrialCriterionName { get; set; } + + + public List TrialEmailNoticeUserList { get; set; } + + + public new List ToUserTypeList => TrialEmailNoticeUserList.Where(t => t.EmailUserType == EmailUserType.To).Select(t => t.UserType).ToList(); + public new List CopyUserTypeList => TrialEmailNoticeUserList.Where(t => t.EmailUserType == EmailUserType.Copy).Select(t => t.UserType).ToList(); + + + } + + + public class EmailUserInfoDto + { + + public Guid TrialEmailNoticeConfigId { get; set; } + + + public EmailUserType EmailUserType { get; set; } + + + + public UserTypeEnum UserType { get; set; } + } + + + public class GetTrialEmailSetOutDto + { + public Guid TrialId { get; set; } + + /// + /// 发件箱账号 + /// + public string EmailFromEmail { get; set; } = string.Empty; + + /// + /// 发件人 + /// + public string EmailFromName { get; set; } = string.Empty; + + /// + /// 密码/授权码 + /// + public string EmailAuthorizationCode { get; set; } = string.Empty; + + /// + /// SMTP服务器 + /// + public string EmailSMTPServerAddress { get; set; } = string.Empty; + + /// + /// SMTP端口 + /// + public int? EmailSMTPServerPort { get; set; } + + /// + /// 是否配置过邮箱 + /// + public bool IsConfigureEmail { get; set; } = false; + } + + public class SetTrialEmailInDto : GetTrialEmailSetOutDto + { + + } + public class GetTrialEmailSetInDto + { + public Guid TrialId { get; set; } + } + + ///TrialEmailNoticeConfigQuery 列表查询参数模型 + public class TrialEmailNoticeConfigQuery + { + [NotDefault] + public Guid TrialId { get; set; } + + public CommonDocumentBusinessScenario? BusinessScenarioEnum { get; set; } + + //public CriterionType? CriterionTypeEnum { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + public bool IsDistinguishCriteria { get; set; } + + } + + + public class GenerateEmailCommand + { + public Guid SubjectId { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + + public CommonDocumentBusinessScenario BusinessScenarioEnum { get; set; } + } + + /// TrialEmailNoticeConfigAddOrEdit 列表查询参数模型 + public class TrialEmailNoticeConfigAddOrEdit + { + public Guid? Id { get; set; } + + public CommonDocumentBusinessScenario BusinessScenarioEnum { get; set; } + + public string Code { get; set; } = string.Empty; + + public Guid? TrialReadingCriterionId { get; set; } + + public Guid TrialId { get; set; } + + public string AuthorizationCode { get; set; } = string.Empty; + + public string SMTPServerAddress { get; set; } = string.Empty; + + public int SMTPServerPort { get; set; } + + public CriterionType CriterionTypeEnum { get; set; } + + public string FromName { get; set; } = string.Empty; + + public string FromEmail { get; set; } = string.Empty; + + + public List? ToUserTypeList { get; set; } + public List CopyUserTypeList { get; set; } + + + + public bool IsUrgent { get; set; } + + public bool IsAutoSend { get; set; } + + + public bool IsReturnRequired { get; set; } + + public string FilePath { get; set; } = string.Empty; + public string FileName { get; set; } = string.Empty; + + + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Document/Interface/ISystemDocumentService.cs b/IRaCIS.Core.Application/Service/Document/Interface/ISystemDocumentService.cs new file mode 100644 index 0000000..dfbf08e --- /dev/null +++ b/IRaCIS.Core.Application/Service/Document/Interface/ISystemDocumentService.cs @@ -0,0 +1,31 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-05 09:17:00 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Infrastructure.Extention; +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// ISystemDocumentService + /// + public interface ISystemDocumentService + { + + //PageOutput GetSystemDocumentList(SystemDocumentQuery querySystemDocument); + + + //IResponseOutput AddOrUpdateSystemDocument(AddOrEditSystemDocument addOrEditSystemDocument); + + //IResponseOutput DeleteSystemDocument(Guid systemDocumentId); + + Task> GetSystemDocumentListAsync(SystemDocumentQuery querySystemDocument); + + Task AddOrUpdateSystemDocumentAsync(AddOrEditSystemDocument addOrEditSystemDocument); + + Task DeleteSystemDocumentAsync(Guid systemDocumentId); + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Document/Interface/ITrialDocumentService.cs b/IRaCIS.Core.Application/Service/Document/Interface/ITrialDocumentService.cs new file mode 100644 index 0000000..e9e90f7 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Document/Interface/ITrialDocumentService.cs @@ -0,0 +1,31 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-05 09:17:03 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface ITrialDocumentService + { + Task UserAbandonDoc(Guid documentId, bool isSystemDoc); + Task AddOrUpdateTrialDocument(AddOrEditTrialDocument addOrEditTrialDocument); + Task DeleteTrialDocument(Guid trialDocumentId, Guid trialId); + Task<(PageOutput, object)> GetDocumentConfirmList(DocumentTrialUnionQuery querySystemDocument); + Task> GetTrialDocumentList(TrialDocumentQuery queryTrialDocument); + + Task<(PageOutput, object)> GetUserDocumentList(TrialUserDocUnionQuery querySystemDocument); + Task SetFirstViewDocumentTime(Guid documentId, bool isSystemDoc); + Task UserConfirm(UserConfirmCommand userConfirmCommand); + Task> GetTrialUserSelect(Guid trialId); + + + PageOutput GetTrialSystemDocumentList(DocumentTrialUnionQuery querySystemDocument); + List GetTrialUserDocumentList(Guid trialId); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Document/Interface/ITrialEmailNoticeConfigService.cs b/IRaCIS.Core.Application/Service/Document/Interface/ITrialEmailNoticeConfigService.cs new file mode 100644 index 0000000..b30ba64 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Document/Interface/ITrialEmailNoticeConfigService.cs @@ -0,0 +1,36 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-10-20 11:52:48 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Domain.Share.Common; + +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// ITrialEmailNoticeConfigService + /// + public interface ITrialEmailNoticeConfigService + { + + + Task> GetTrialEmailNoticeConfigList(TrialEmailNoticeConfigQuery inQuery); + + Task AddOrUpdateTrialEmailNoticeConfig(TrialEmailNoticeConfigAddOrEdit addOrEditTrialEmailNoticeConfig); + + Task DeleteTrialEmailNoticeConfig(Guid trialEmailNoticeConfigId); + + Task BaseBusinessScenarioSendEmailAsync(Guid visitTaskId, bool? isMedicalReviewAndSuggestApplyReReading = null, EmailStoreSendMode emailStoreMode = EmailStoreSendMode.StoreLocalSend, string sendFileRelativePath=""); + + } + + public class EmailStoreSendDto + { + public EmailStoreSendMode EmailStoreSendMode { get; set; } = EmailStoreSendMode.StoreLocalSend; + + public string SendFileRelativePath { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/Document/SystemDocumentService.cs b/IRaCIS.Core.Application/Service/Document/SystemDocumentService.cs new file mode 100644 index 0000000..2b00d70 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Document/SystemDocumentService.cs @@ -0,0 +1,171 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-05 09:17:03 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Contracts; +using User = IRaCIS.Core.Domain.Models.User; + +namespace IRaCIS.Core.Application.Services +{ + /// + /// SystemDocumentService + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class SystemDocumentService : BaseService, ISystemDocumentService + { + + private readonly IRepository _systemDocumentRepository; + private readonly IRepository _systemDocNeedConfirmedUserTypeRepository; + + public SystemDocumentService( IRepository systemDocumentRepository, + IRepository systemDocNeedConfirmedUserTypeRepository + ) + { + _systemDocumentRepository = systemDocumentRepository; + this._systemDocNeedConfirmedUserTypeRepository = systemDocNeedConfirmedUserTypeRepository; + } + + + + /// + /// 管理端列表 + /// + /// + /// + [HttpPost] + public async Task> GetSystemDocumentListAsync(SystemDocumentQuery querySystemDocument) + { + var systemDocumentQueryable = _systemDocumentRepository.AsQueryable(true) + .WhereIf(!string.IsNullOrEmpty(querySystemDocument.Name), t => t.Name.Contains(querySystemDocument.Name)) + .WhereIf(querySystemDocument.FileTypeId != null, t => t.FileTypeId == querySystemDocument.FileTypeId) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken, userId = _userInfo.Id }); + + return await systemDocumentQueryable.ToPagedListAsync(querySystemDocument.PageIndex, querySystemDocument.PageSize, querySystemDocument.SortField, querySystemDocument.Asc); + } + + public async Task AddOrUpdateSystemDocumentAsync(AddOrEditSystemDocument addOrEditSystemDocument) + { + if (addOrEditSystemDocument.Id == null) + { + var entity = _mapper.Map(addOrEditSystemDocument); + + + if (await _systemDocumentRepository.AnyAsync(t => t.FileTypeId == addOrEditSystemDocument.FileTypeId && t.Name == addOrEditSystemDocument.Name,true)) + { + return ResponseOutput.NotOk("系统中已存在同类型的同名文件。"); + } + + await _systemDocumentRepository.AddAsync(entity, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + else + { + var document = (await _systemDocumentRepository.Where(t => t.Id == addOrEditSystemDocument.Id, true, true).Include(t => t.NeedConfirmedUserTypeList).FirstOrDefaultAsync()).IfNullThrowException(); + + + if (await _systemDocumentRepository.AnyAsync(t => t.FileTypeId == addOrEditSystemDocument.FileTypeId && t.Name == addOrEditSystemDocument.Name && t.Id != addOrEditSystemDocument.Id, true)) + { + return ResponseOutput.NotOk("系统中已存在同类型的同名文件。"); + } + + + _mapper.Map(addOrEditSystemDocument, document); + + #region 之前区分路径文件夹 现在不区分废弃 + + //if (document.FileTypeId != addOrEditSystemDocument.FileTypeId) + //{ + // var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).IfNullThrowException().FullName; + // var beforeFilePath = Path.Combine(rootPath, document.Path); + + // document.Path = document.Path.Replace(document.FileTypeId.ToString(), addOrEditSystemDocument.FileTypeId.ToString()); + + // var nowPath = Path.Combine(rootPath, document.Path); + + // if (File.Exists(beforeFilePath)) + // { + // File.Move(beforeFilePath, nowPath, true); + // File.Delete(beforeFilePath); + // } + + //} + + #endregion + + + var success = await _systemDocumentRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(document.Id.ToString()); + } + + + + } + + + [HttpDelete("{systemDocumentId:guid}")] + public async Task DeleteSystemDocumentAsync(Guid systemDocumentId) + { + + if (await _repository.Where(t => t.Id == systemDocumentId).AnyAsync(u => u.SystemDocConfirmedUserList.Any())) + { + return ResponseOutput.NotOk("已有用户阅读该文档,并签名,不允许删除。"); + } + + var success = await _systemDocumentRepository.DeleteFromQueryAsync(t => t.Id == systemDocumentId,true,true); + + return ResponseOutput.Result(true); + } + + + /// + /// 获取需要签署的系统文档列表 + /// + /// + [HttpPost] + public async Task> getWaitSignSysDocList(SystemDocumentQuery querySystemDocument) + { + var query = from sysDoc in _systemDocumentRepository.Where(t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + .WhereIf(!string.IsNullOrEmpty(querySystemDocument.Name), t => t.Name.Contains(querySystemDocument.Name)) + .WhereIf(querySystemDocument.FileTypeId != null, t => t.FileTypeId == querySystemDocument.FileTypeId) + join confirm in _repository.GetQueryable() on new { ConfirmUserId = _userInfo.Id, SystemDocumentId = sysDoc.Id } equals new { confirm.ConfirmUserId, confirm.SystemDocumentId } into cc + from confirm in cc.DefaultIfEmpty() + + join user in _repository.GetQueryable() on _userInfo.Id equals user.Id + + select new UnionDocumentWithConfirmInfoView() + { + IsSystemDoc = true, + + Id = sysDoc.Id, + CreateTime = sysDoc.CreateTime, + IsDeleted = sysDoc.IsDeleted, + SignViewMinimumMinutes = sysDoc.SignViewMinimumMinutes, + Name = sysDoc.Name, + Path = sysDoc.Path, + FileType = sysDoc.FileType.MappedValue, + UpdateTime = sysDoc.UpdateTime, + + FullFilePath = sysDoc.Path , + + ConfirmUserId = confirm.ConfirmUserId, + ConfirmTime = confirm.ConfirmTime, + RealName = user.LastName + " / " + user.FirstName, + UserName = user.UserName, + UserTypeId = user.UserTypeId, + UserTypeShortName = user.UserTypeRole.UserTypeShortName + }; + + return await query.Where(t=>t.ConfirmTime==null).ToPagedListAsync(querySystemDocument.PageIndex, querySystemDocument.PageSize, querySystemDocument.SortField, querySystemDocument.Asc); + + + + } + + } +} diff --git a/IRaCIS.Core.Application/Service/Document/TrialDocumentService.cs b/IRaCIS.Core.Application/Service/Document/TrialDocumentService.cs new file mode 100644 index 0000000..e4e100a --- /dev/null +++ b/IRaCIS.Core.Application/Service/Document/TrialDocumentService.cs @@ -0,0 +1,724 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-05 09:17:03 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using Microsoft.AspNetCore.Mvc; + +using IRaCIS.Core.Application.Contracts; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Auth; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Core.Application.Services +{ + /// + /// TrialDocumentService + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialDocumentService : BaseService, ITrialDocumentService + { + + + private readonly IRepository _trialDocumentRepository; + private readonly IRepository _trialDocUserTypeConfirmedUserRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _systemDocConfirmedUserRepository; + private readonly IRepository _systemDocumentRepository; + + public TrialDocumentService(IRepository trialDocumentRepository, + IRepository trialDocUserTypeConfirmedUserRepository, + IRepository trialRepository, + IRepository systemDocConfirmedUserRepository + , IRepository systemDocumentRepository) + { + _trialDocumentRepository = trialDocumentRepository; + this._trialDocUserTypeConfirmedUserRepository = trialDocUserTypeConfirmedUserRepository; + this._trialRepository = trialRepository; + this._systemDocConfirmedUserRepository = systemDocConfirmedUserRepository; + _systemDocumentRepository = systemDocumentRepository; + } + + /// + /// Setting 界面的 项目所有文档列表 + /// + /// + /// + [HttpPost] + public async Task> GetTrialDocumentList(TrialDocumentQuery queryTrialDocument) + { + + var trialDocumentQueryable = _trialDocumentRepository.AsQueryable(true).Where(t => t.TrialId == queryTrialDocument.TrialId) + .WhereIf(!string.IsNullOrEmpty(queryTrialDocument.Name), t => t.Name.Contains(queryTrialDocument.Name)) + .WhereIf(queryTrialDocument.FileTypeId != null, t => t.FileTypeId == queryTrialDocument.FileTypeId) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken }); + + return await trialDocumentQueryable.ToPagedListAsync(queryTrialDocument.PageIndex, queryTrialDocument.PageSize, queryTrialDocument.SortField, queryTrialDocument.Asc); + } + + + + /// + /// 具体用户看到的 系统文件列表 + 项目类型文档 + /// + /// + /// + [HttpPost] + public async Task<(PageOutput,object)> GetUserDocumentList(TrialUserDocUnionQuery querySystemDocument) + { + #region https://github.com/dotnet/efcore/issues/16243 操作不行 + ////系统文档查询 + //var systemDocumentQueryable = _systemDocumentRepository + // .WhereIf(!_userInfo.IsAdmin, t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + // .WhereIf(!_userInfo.IsAdmin, t => t.IsAbandon == false || (t.IsAbandon == true && t.SystemDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id))) + //.ProjectTo(_mapper.ConfigurationProvider, new { userId = _userInfo.Id, token = _userInfo.UserToken }); + + ////项目文档查询 + //var trialDocQueryable = _trialDocumentRepository.Where(t => t.TrialId == querySystemDocument.TrialId) + // .WhereIf(!_userInfo.IsAdmin, t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + // .WhereIf(!_userInfo.IsAdmin, t => t.IsAbandon == false || (t.IsAbandon == true && t.TrialDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id))) + // .ProjectTo(_mapper.ConfigurationProvider, new { userId = _userInfo.Id, token = _userInfo.UserToken }); + + //var unionQuery = systemDocumentQueryable.Union(trialDocQueryable); + // .WhereIf(!string.IsNullOrEmpty(querySystemDocument.Name), t => t.Name.Contains(querySystemDocument.Name)) + // .WhereIf(!string.IsNullOrEmpty(querySystemDocument.Type), t => t.Type.Contains(querySystemDocument.Type)); + #endregion + + #region 仅仅文档信息 + + ////系统文档查询 + //var systemDocumentQueryable = _systemDocumentRepository + // .WhereIf(!_userInfo.IsAdmin, t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + // .WhereIf(!_userInfo.IsAdmin, t => t.IsAbandon == false || (t.IsAbandon == true && t.SystemDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id))) + // .Select(t => new UnionDocumentView() + // { + // Id = t.Id, + // IsSystemDoc = true, + // CreateTime = t.CreateTime, + // FullFilePath = t.Path + "?access_token=" + _userInfo.UserToken, + // IsAbandon = t.IsAbandon, + // Name = t.Name, + // Path = t.Path, + // Type = t.Type, + // UpdateTime = t.UpdateTime, + // SignViewMinimumMinutes = t.SignViewMinimumMinutes, + + // }); + + ////项目文档查询 + //var trialDocQueryable = _trialDocumentRepository.Where(t => t.TrialId == querySystemDocument.TrialId) + // .WhereIf(!_userInfo.IsAdmin, t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + // .WhereIf(!_userInfo.IsAdmin, t => t.IsAbandon == false || (t.IsAbandon == true && t.TrialDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id))) + // .Select(t => new UnionDocumentView() + // { + // Id = t.Id, + // IsSystemDoc = false, + // CreateTime = t.CreateTime, + // FullFilePath = t.Path + "?access_token=" + _userInfo.UserToken, + // IsAbandon = t.IsAbandon, + // Name = t.Name, + // Path = t.Path, + // Type = t.Type, + // UpdateTime = t.UpdateTime, + // SignViewMinimumMinutes = t.SignViewMinimumMinutes, + + // }); + + #endregion + + var trialInfo = await (_repository.Where(t => t.Id == querySystemDocument.TrialId).Select(t => new { t.TrialFinishedTime,t.TrialStatusStr } ).FirstOrDefaultAsync()); + + //系统文档查询 + var systemDocumentQueryable = from needConfirmedUserType in _repository.Where(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId) + //.Where(u => u.UserTypeRole.UserList.SelectMany(cc => cc.UserTrials.Where(t => t.TrialId == querySystemDocument.TrialId)).Any(e => e.Trial.TrialFinishedTime < u.SystemDocument.CreateTime)) + .WhereIf(trialInfo.TrialFinishedTime != null, u => u.SystemDocument.CreateTime < trialInfo.TrialFinishedTime) + .WhereIf(!_userInfo.IsAdmin, t => t.SystemDocument.IsDeleted == false || (t.SystemDocument.IsDeleted == true && t.SystemDocument.SystemDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id))) + + join trialUser in _repository.Where(t => t.TrialId == querySystemDocument.TrialId && t.UserId == _userInfo.Id) + on needConfirmedUserType.NeedConfirmUserTypeId equals trialUser.User.UserTypeId + join confirm in _repository.GetQueryable() on new { ConfirmUserId = trialUser.UserId, SystemDocumentId = needConfirmedUserType.SystemDocumentId } equals new { confirm.ConfirmUserId, confirm.SystemDocumentId } into cc + from confirm in cc.DefaultIfEmpty() + select new UnionDocumentWithConfirmInfoView() + { + IsSystemDoc = true, + + Id = needConfirmedUserType.SystemDocument.Id, + CreateTime = needConfirmedUserType.SystemDocument.CreateTime, + IsDeleted = needConfirmedUserType.SystemDocument.IsDeleted, + SignViewMinimumMinutes = needConfirmedUserType.SystemDocument.SignViewMinimumMinutes, + Name = needConfirmedUserType.SystemDocument.Name, + Path = needConfirmedUserType.SystemDocument.Path, + FileTypeId = needConfirmedUserType.SystemDocument.FileTypeId, + FileType = needConfirmedUserType.SystemDocument.FileType.MappedValue, + UpdateTime = needConfirmedUserType.SystemDocument.UpdateTime, + + FullFilePath = needConfirmedUserType.SystemDocument.Path , + + ConfirmUserId = confirm.ConfirmUserId, + ConfirmTime = confirm.ConfirmTime, + RealName = trialUser.User.FullName, + UserName = trialUser.User.UserName, + UserTypeId = trialUser.User.UserTypeId, + UserTypeShortName = trialUser.User.UserTypeRole.UserTypeShortName + }; + + //项目文档查询 + var trialDocQueryable = from trialDoc in _trialDocumentRepository.AsQueryable(true).Where(t => t.TrialId == querySystemDocument.TrialId) + .WhereIf(!_userInfo.IsAdmin, t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + .WhereIf(!_userInfo.IsAdmin, t => t.IsDeleted == false || (t.IsDeleted == true && t.TrialDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id))) + + join trialUser in _repository.Where(t => t.TrialId == querySystemDocument.TrialId && t.UserId == _userInfo.Id) on trialDoc.TrialId equals trialUser.TrialId + join confirm in _repository.Where(t => t.TrialDocument.TrialId == querySystemDocument.TrialId) on + new { trialUser.UserId, TrialDocumentId = trialDoc.Id } equals new { UserId = confirm.ConfirmUserId, confirm.TrialDocumentId } into cc + from confirm in cc.DefaultIfEmpty() + select new UnionDocumentWithConfirmInfoView() + { + Id = trialDoc.Id, + IsSystemDoc = false, + CreateTime = trialDoc.CreateTime, + FullFilePath = trialDoc.Path , + IsDeleted = trialDoc.IsDeleted, + Name = trialDoc.Name, + Path = trialDoc.Path, + FileTypeId = trialDoc.FileTypeId, + FileType = trialDoc.FileType.MappedValue, + UpdateTime = trialDoc.UpdateTime, + SignViewMinimumMinutes = trialDoc.SignViewMinimumMinutes, + + ConfirmUserId = confirm.ConfirmUserId, + ConfirmTime = confirm.ConfirmTime, + RealName = trialUser.User.FullName, + UserName = trialUser.User.UserName, + UserTypeId = trialUser.User.UserTypeId, + UserTypeShortName = trialUser.User.UserTypeRole.UserTypeShortName + + }; + + + var unionQuery = systemDocumentQueryable.Union(trialDocQueryable) + .WhereIf(!string.IsNullOrEmpty(querySystemDocument.Name), t => t.Name.Contains(querySystemDocument.Name)) + .WhereIf(querySystemDocument.FileTypeId != null, t => t.FileTypeId == querySystemDocument.FileTypeId) + .WhereIf(querySystemDocument.IsSign == true, t => t.ConfirmTime != null) + .WhereIf(querySystemDocument.IsSign == false, t => t.ConfirmTime == null); + + var result = await unionQuery.ToPagedListAsync(querySystemDocument.PageIndex, querySystemDocument.PageSize, querySystemDocument.SortField, querySystemDocument.Asc); + + var needSignTrialDocCount = await _trialDocumentRepository.AsQueryable(true).Where(t => t.TrialId== querySystemDocument.TrialId && t.Trial.TrialStatusStr != StaticData.TrialState.TrialStopped) + .Where(t => t.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)) + .Where(t => t.IsDeleted == false && !t.TrialDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id && t.ConfirmTime!=null) && t.NeedConfirmedUserTypeList.Any(u => u.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + .CountAsync(); + + + var needSignSystemDocCount = await _systemDocumentRepository + .Where(t => t.IsDeleted == false && !t.SystemDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id && t.ConfirmTime != null) && t.NeedConfirmedUserTypeList.Any(u => u.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + .CountAsync(); + + var trialTaskConfig = _trialRepository.Where(t => t.Id == querySystemDocument.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + + + return (result, new { NeedSignCount = needSignTrialDocCount + needSignSystemDocCount, NeedSignTrialDocCount = needSignTrialDocCount, NeedSignSystemDocCount = needSignSystemDocCount, TrialStatusStr= trialInfo.TrialStatusStr,TrialConfig= trialTaskConfig }); + } + + + + + /// + /// 获取确认列表情况 项目文档+系统文档+具体的人 + /// + /// + /// + [HttpPost] + public async Task<(PageOutput,object)> GetDocumentConfirmList(DocumentTrialUnionQuery querySystemDocument) + { + + + #region linq join 方式 + //var trialDocQuery = from trialDocumentNeedConfirmedUserType in _trialDocumentNeedConfirmedUserTypeRepository.Where(t => t.TrialDocument.TrialId == querySystemDocument.TrialId) + // join trialUser in _trialUserRepository.Where(t => t.TrialId == querySystemDocument.TrialId) + // .WhereIf(querySystemDocument.UserId != null, t => t.UserId == querySystemDocument.UserId) + // on trialDocumentNeedConfirmedUserType.NeedConfirmUserTypeId equals trialUser.User.UserTypeId + + // join confirm in _trialDocuserConfrimedRepository.AsQueryable() on trialUser.UserId equals confirm.ConfirmUserId into cc + // from confirm in cc.DefaultIfEmpty() + // select new UnionDocumentConfirmListView() + // { + // Id = trialDocumentNeedConfirmedUserType.TrialDocument.Id, + // CreateTime = trialDocumentNeedConfirmedUserType.TrialDocument.CreateTime, + // IsAbandon = trialDocumentNeedConfirmedUserType.TrialDocument.IsAbandon, + // SignViewMinimumMinutes = trialDocumentNeedConfirmedUserType.TrialDocument.SignViewMinimumMinutes, + // Name = trialDocumentNeedConfirmedUserType.TrialDocument.Name, + // Path = trialDocumentNeedConfirmedUserType.TrialDocument.Path, + // Type = trialDocumentNeedConfirmedUserType.TrialDocument.Type, + // UpdateTime = trialDocumentNeedConfirmedUserType.TrialDocument.UpdateTime, + + // UserConfirmInfo = /*confirm == null ? null : */new UnionDocumentUserConfirmView() + // { + + // ConfirmUserId = confirm.ConfirmUserId, + // ConfirmTime = confirm.ConfirmTime, + // RealName = trialUser.User.LastName + " / " + trialUser.User.LastName, + // UserName = trialUser.User.UserName, + // }, + + // FullFilePath = trialDocumentNeedConfirmedUserType.TrialDocument.Path + "?access_token=" + _userInfo.UserToken + // }; + + #endregion + + var trialInfo = (await _repository.Where(t => t.Id == querySystemDocument.TrialId).Select(t => new { t.TrialFinishedTime, t.TrialStatusStr }).FirstOrDefaultAsync()); + + var trialDocQuery = from trialDocumentNeedConfirmedUserType in _repository.Where(t => t.TrialDocument.TrialId == querySystemDocument.TrialId) + join trialUser in _repository.Where(t => t.TrialId == querySystemDocument.TrialId) + .WhereIf(querySystemDocument.UserId != null, t => t.UserId == querySystemDocument.UserId) + .WhereIf(querySystemDocument.UserTypeId != null, t => t.User.UserTypeId == querySystemDocument.UserTypeId) + on trialDocumentNeedConfirmedUserType.NeedConfirmUserTypeId equals trialUser.User.UserTypeId + + join confirm in _repository.Where(t => t.TrialDocument.TrialId == querySystemDocument.TrialId) on + new { trialUser.UserId, TrialDocumentId = trialDocumentNeedConfirmedUserType.TrialDocumentId } equals new { UserId = confirm.ConfirmUserId, confirm.TrialDocumentId } into cc + from confirm in cc.DefaultIfEmpty() + select new UnionDocumentWithConfirmInfoView() + { + IsSystemDoc = false, + + Id = trialDocumentNeedConfirmedUserType.TrialDocument.Id, + CreateTime = trialDocumentNeedConfirmedUserType.TrialDocument.CreateTime, + IsDeleted = trialDocumentNeedConfirmedUserType.TrialDocument.IsDeleted, + SignViewMinimumMinutes = trialDocumentNeedConfirmedUserType.TrialDocument.SignViewMinimumMinutes, + Name = trialDocumentNeedConfirmedUserType.TrialDocument.Name, + Path = trialDocumentNeedConfirmedUserType.TrialDocument.Path, + FileTypeId = trialDocumentNeedConfirmedUserType.TrialDocument.FileTypeId, + FileType = trialDocumentNeedConfirmedUserType.TrialDocument.FileType.MappedValue, + UpdateTime = trialDocumentNeedConfirmedUserType.TrialDocument.UpdateTime, + + + + ConfirmUserId = confirm.ConfirmUserId, + ConfirmTime = confirm.ConfirmTime, + RealName = trialUser.User.FullName, + UserName = trialUser.User.UserName, + UserTypeId = trialUser.User.UserTypeId, + UserTypeShortName = trialUser.User.UserTypeRole.UserTypeShortName, + + FullFilePath = trialDocumentNeedConfirmedUserType.TrialDocument.Path + }; + + + + var systemDocQuery = from needConfirmEdUserType in _repository.WhereIf(trialInfo.TrialFinishedTime != null, u => u.SystemDocument.CreateTime < trialInfo.TrialFinishedTime) + + join trialUser in _repository.Where(t => t.TrialId == querySystemDocument.TrialId) + .WhereIf(querySystemDocument.UserId != null, t => t.UserId == querySystemDocument.UserId) + on needConfirmEdUserType.NeedConfirmUserTypeId equals trialUser.User.UserTypeId + join confirm in _repository.GetQueryable() on new { ConfirmUserId = trialUser.UserId, SystemDocumentId = needConfirmEdUserType.SystemDocumentId } equals new { confirm.ConfirmUserId, confirm.SystemDocumentId } into cc + from confirm in cc.DefaultIfEmpty() + select new UnionDocumentWithConfirmInfoView() + { + IsSystemDoc = true, + + Id = needConfirmEdUserType.SystemDocument.Id, + CreateTime = needConfirmEdUserType.SystemDocument.CreateTime, + IsDeleted = needConfirmEdUserType.SystemDocument.IsDeleted, + SignViewMinimumMinutes = needConfirmEdUserType.SystemDocument.SignViewMinimumMinutes, + Name = needConfirmEdUserType.SystemDocument.Name, + Path = needConfirmEdUserType.SystemDocument.Path, + FileType = needConfirmEdUserType.SystemDocument.FileType.MappedValue, + FileTypeId = needConfirmEdUserType.SystemDocument.FileTypeId, + UpdateTime = needConfirmEdUserType.SystemDocument.UpdateTime, + + + ConfirmUserId = confirm.ConfirmUserId, + ConfirmTime = confirm.ConfirmTime, + RealName = trialUser.User.FullName, + UserName = trialUser.User.UserName, + UserTypeId = trialUser.User.UserTypeId, + UserTypeShortName = trialUser.User.UserTypeRole.UserTypeShortName, + + FullFilePath = needConfirmEdUserType.SystemDocument.Path + }; + + var unionQuery = trialDocQuery.Union(systemDocQuery) + .WhereIf(!string.IsNullOrEmpty(querySystemDocument.Name), t => t.Name.Contains(querySystemDocument.Name)) + .WhereIf(querySystemDocument.FileTypeId != null, t => t.FileTypeId == querySystemDocument.FileTypeId) + .WhereIf(querySystemDocument.UserTypeId != null, t => t.UserTypeId == querySystemDocument.UserTypeId); + + var result = await unionQuery.ToPagedListAsync(querySystemDocument.PageIndex, querySystemDocument.PageSize, querySystemDocument.SortField, querySystemDocument.Asc); + + var needSignTrialDocCount = await _trialDocumentRepository.AsQueryable(true) + .Where(t => t.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)) + .Where(t => t.IsDeleted == false && !t.TrialDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id) && t.NeedConfirmedUserTypeList.Any(u => u.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + .CountAsync(); + + + var needSignSystemDocCount = await _systemDocumentRepository + .Where(t => t.IsDeleted == false && !t.SystemDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id) && t.NeedConfirmedUserTypeList.Any(u => u.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + .CountAsync(); + + + return (result, new { NeedSignCount = needSignTrialDocCount + needSignSystemDocCount, NeedSignTrialDocCount = needSignTrialDocCount, NeedSignSystemDocCount = needSignSystemDocCount,TrialStatusStr = trialInfo.TrialStatusStr }); + } + + + /// + /// 项目下面的参与用户下拉 + /// + /// + /// + [HttpGet("{trialId:guid}")] + public async Task> GetTrialUserSelect(Guid trialId) + { + return await _repository.Where(t => t.TrialId == trialId) + .Select(t => new TrialUserDto() { UserId = t.UserId, RealName = t.User.FullName, UserName = t.User.UserName }) + .ToListAsync(); + } + + + /// + /// 项目+系统的文档类型 下拉 + /// + /// + /// + [HttpGet("{trialId:guid}")] + public async Task GetTrialDocAndSystemDocType(Guid trialId) + { + var result = await _trialDocumentRepository.Where(t => t.TrialId == trialId).Select(t => new { FileType = t.FileType.Value, t.FileTypeId }) + .Union(_systemDocumentRepository.Select(t => new { FileType = t.FileType.Value, t.FileTypeId })) + .ToListAsync(); + + return ResponseOutput.Ok(result); + } + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM)] + public async Task AddOrUpdateTrialDocument(AddOrEditTrialDocument addOrEditTrialDocument) + { + if (addOrEditTrialDocument.Id == null) + { + var entity = _mapper.Map(addOrEditTrialDocument); + + + if (await _trialDocumentRepository.AnyAsync(t => t.FileTypeId == addOrEditTrialDocument.FileTypeId && t.Name == addOrEditTrialDocument.Name && t.TrialId == addOrEditTrialDocument.TrialId, true)) + { + return ResponseOutput.NotOk("该项目中已经存在同类型的同名文件。"); + } + + //entity.Id = NewId.NextGuid(); + await _trialDocumentRepository.AddAsync(entity, true); + return ResponseOutput.Ok(entity.Id.ToString()); + } + else + { + if (await _trialDocumentRepository.AnyAsync(t => t.FileTypeId == addOrEditTrialDocument.FileTypeId && t.Name == addOrEditTrialDocument.Name && t.Id != addOrEditTrialDocument.Id && t.TrialId == addOrEditTrialDocument.TrialId, true)) + { + return ResponseOutput.NotOk("该项目中已经存在同类型的同名文件。"); + } + + var document = (await _trialDocumentRepository.Where(t => t.Id == addOrEditTrialDocument.Id, true).Include(t => t.NeedConfirmedUserTypeList).FirstOrDefaultAsync()).IfNullThrowException(); + + + + _mapper.Map(addOrEditTrialDocument, document); + + #region 不区分路径了 + + //if (document.FileTypeId != addOrEditTrialDocument.FileTypeId) + //{ + + + // var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).IfNullThrowException().FullName; + // var beforeFilePath = Path.Combine(rootPath, document.Path); + + // document.Path = document.Path.Replace(document.FileTypeId.ToString(), addOrEditTrialDocument.FileTypeId.ToString()); + + // var nowPath = Path.Combine(rootPath, document.Path); + + // if (File.Exists(beforeFilePath)) + // { + // File.Move(beforeFilePath, nowPath, true); + // File.Delete(beforeFilePath); + // } + + //} + + #endregion + + + var success = await _trialDocumentRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(document.Id.ToString()); + } + } + + + /// + /// 已签名的文档 不允许删除 + /// + /// + /// + /// + [HttpDelete("{trialId:guid}/{trialDocumentId:guid}")] + //[Authorize(Policy = IRaCISPolicy.PM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + public async Task DeleteTrialDocument(Guid trialDocumentId, Guid trialId) + { + if (await _trialDocumentRepository.AsQueryable(true).Where(t => t.Id == trialDocumentId).AnyAsync(t => t.TrialDocConfirmedUserList.Any())) + { + return ResponseOutput.NotOk("已有用户阅读该文档,并签名,不允许删除。"); + } + + var success = await _trialDocumentRepository.BatchDeleteNoTrackingAsync(t => t.Id == trialDocumentId); + + return ResponseOutput.Result(success); + } + + + /// + /// 浏览文档说明时调用,记录第一次看的时间 + /// + /// + /// + /// + [HttpPut("{trialId:guid}/{documentId:guid}/{isSystemDoc:bool}")] + [UnitOfWork] + public async Task SetFirstViewDocumentTime(Guid documentId, bool isSystemDoc) + { + + var success = false; + if (isSystemDoc) + { + if(!await _systemDocConfirmedUserRepository.AnyAsync(t=>t.SystemDocumentId==documentId && t.ConfirmUserId == _userInfo.Id)) + { + await _repository.AddAsync(new SystemDocConfirmedUser() { SystemDocumentId = documentId, ConfirmUserId = _userInfo.Id, SignFirstViewTime = DateTime.Now }); + + } + + } + else + { + + if (!await _trialDocUserTypeConfirmedUserRepository.AnyAsync(t => t.TrialDocumentId == documentId && t.ConfirmUserId == _userInfo.Id)) + { + + await _repository.AddAsync(new TrialDocConfirmedUser() { TrialDocumentId = documentId, ConfirmUserId = _userInfo.Id, SignFirstViewTime = DateTime.Now }); + + } + + + } + + success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(success); + } + + [HttpPut("{documentId:guid}")] + public async Task SetSystemDocFirstViewTime(Guid documentId) + { + if (!await _systemDocConfirmedUserRepository.AnyAsync(t => t.SystemDocumentId == documentId && t.ConfirmUserId == _userInfo.Id)) + { + await _repository.AddAsync(new SystemDocConfirmedUser() { SystemDocumentId = documentId, ConfirmUserId = _userInfo.Id, SignFirstViewTime = DateTime.Now }); + + } + + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(success); + } + + [HttpPut("{trialId:guid}/{documentId:guid}")] + public async Task SetTrialDocFirstViewTime(Guid documentId) + { + if (!await _trialDocUserTypeConfirmedUserRepository.AnyAsync(t => t.TrialDocumentId == documentId && t.ConfirmUserId == _userInfo.Id)) + { + + await _repository.AddAsync(new TrialDocConfirmedUser() { TrialDocumentId = documentId, ConfirmUserId = _userInfo.Id, SignFirstViewTime = DateTime.Now }); + + } + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(success); + } + + /// + /// 用户 签名某个文档 可能是系统的,也可能是项目的 + /// + /// + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + public async Task UserConfirm(UserConfirmCommand userConfirmCommand) + { + + if (userConfirmCommand.isSystemDoc) + { + + var sysDocConfirm = await _systemDocConfirmedUserRepository.FirstOrDefaultAsync(t => t.SystemDocumentId == userConfirmCommand.DocumentId && t.ConfirmUserId == _userInfo.Id,true); + + if(sysDocConfirm.ConfirmTime != null) + { + return ResponseOutput.NotOk("该文件已经签名"); + } + + if (sysDocConfirm.IsDeleted) + { + return ResponseOutput.NotOk("文件已废除,签署失败!"); + } + + + sysDocConfirm.ConfirmTime = DateTime.Now; + sysDocConfirm.SignText = userConfirmCommand.SignText; + + + + await _systemDocConfirmedUserRepository.SaveChangesAsync(); + + + } + else + { + + var trialDocConfirm = await _trialDocUserTypeConfirmedUserRepository.FirstOrDefaultAsync(t => t.TrialDocumentId == userConfirmCommand.DocumentId && t.ConfirmUserId == _userInfo.Id, true); + + if (trialDocConfirm.ConfirmTime != null) + { + return ResponseOutput.NotOk("该文件已经签名"); + } + + if (trialDocConfirm.IsDeleted) + { + return ResponseOutput.NotOk("文件已废除,签署失败!"); + } + + trialDocConfirm.ConfirmTime = DateTime.Now; + trialDocConfirm.SignText = userConfirmCommand.SignText; + + await _trialDocUserTypeConfirmedUserRepository.SaveChangesAsync(); + + } + + + return ResponseOutput.Ok(); + } + + + /// + /// 用户 废除某个文档 + /// + /// + /// + /// + [HttpPut("{documentId:guid}/{isSystemDoc:bool}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + public async Task UserAbandonDoc(Guid documentId, bool isSystemDoc) + { + if (isSystemDoc) + { + await _systemDocumentRepository.UpdatePartialFromQueryAsync(documentId, u => new SystemDocument() { IsDeleted = true }); + await _systemDocConfirmedUserRepository.UpdatePartialFromQueryAsync(x => x.SystemDocumentId == documentId, x => new SystemDocConfirmedUser() + { + IsDeleted = true + }); + } + else + { + await _trialDocumentRepository.UpdatePartialFromQueryAsync(documentId, u => new TrialDocument() { IsDeleted = true }); + await _trialDocUserTypeConfirmedUserRepository.UpdatePartialFromQueryAsync(x => x.TrialDocumentId == documentId, x => new TrialDocConfirmedUser() + { + IsDeleted = true + }); + } + await _systemDocumentRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } + + + #region 废弃 + + + /// + /// 从项目下参与者的维度 先看人员列表(展示统计数字) 点击数字 再看人员具体签署的 系统文档+项目文档(共用上面与人相关的具体文档列表) + /// + /// + /// + [HttpGet("{trialId:guid}")] + [Obsolete] + public List GetTrialUserDocumentList(Guid trialId) + { + var query = _repository.Where(t => t.TrialId == trialId) + .Select(t => new TrialUserUnionDocumentView() + { + UserId = t.UserId, + UserName = t.User.UserName, + RealName = t.User.FullName, + UserTypeShortName = t.User.UserTypeRole.UserTypeShortName, + TrialDocumentCount = t.Trial.TrialDocumentList.Count(u => u.NeedConfirmedUserTypeList.Any(k => k.NeedConfirmUserTypeId == t.User.UserTypeId)), + TrialDocumentConfirmedCount = t.Trial.TrialDocumentList.SelectMany(u => u.TrialDocConfirmedUserList).Count(k => k.ConfirmUserId == t.UserId), + SystemDocumentConfirmedCount = t.User.SystemDocConfirmedList.Count(), + //这样写不行 + //SystemDocumentCount = _systemDocumentRepository.Where(s => s.NeedConfirmedUserTypeList.Any(kk => kk.NeedConfirmUserTypeId == t.User.UserTypeId)) + // .WhereIf(!_userInfo.IsAdmin, s => s.IsAbandon == false || (s.IsAbandon == true && s.SystemDocConfirmedUserList.Any(uu => uu.ConfirmUserId == t.UserId))).Count() + SystemDocumentCount = t.User.UserTypeRole.SystemDocNeedConfirmedUserTypeList.Where(cc => cc.NeedConfirmUserTypeId == t.User.UserTypeId).Select(y => y.SystemDocument).Count() + }); + + return query.ToList(); + } + + + /// + /// 从 文档的维度 先看到文档列表(系统文档+项目文档 以及需要确认的人数 和已经确认人数) 点击数字查看某文档下面人确认情况 + /// + /// + /// + [HttpPost] + [Obsolete] + public PageOutput GetTrialSystemDocumentList(DocumentTrialUnionQuery querySystemDocument) + { + var systemDocumentQueryable = _repository + .WhereIf(!_userInfo.IsAdmin, t => t.IsDeleted == false) + .Select(t => new DocumentUnionWithUserStatView() + { + Id = t.Id, + IsSystemDoc = true, + CreateTime = t.CreateTime, + FullFilePath = t.Path , + IsDeleted = t.IsDeleted, + Name = t.Name, + Path = t.Path, + FileType = t.FileType.Value, + UpdateTime = t.UpdateTime, + SignViewMinimumMinutes = t.SignViewMinimumMinutes, + DocumentConfirmedUserCount = t.SystemDocConfirmedUserList.Count(), + + //DocumentUserCount= _trialUserRepository.Where(tu=>tu.TrialId== querySystemDocument.TrialId).Count(u=>t.NeedConfirmedUserTypeList.Any(cc=>cc.NeedConfirmUserTypeId== u.User.UserTypeId )) + DocumentUserCount = t.NeedConfirmedUserTypeList.SelectMany(u => u.UserTypeRole.UserList.SelectMany(b => b.UserTrials.Where(r => r.TrialId == querySystemDocument.TrialId))).Count() + }); + + var trialDocQueryable = _trialDocumentRepository.Where(t => t.TrialId == querySystemDocument.TrialId).Select(t => new DocumentUnionWithUserStatView() + { + Id = t.Id, + IsSystemDoc = false, + CreateTime = t.CreateTime, + FullFilePath = t.Path , + IsDeleted = t.IsDeleted, + Name = t.Name, + Path = t.Path, + FileType = t.FileType.Value, + UpdateTime = t.UpdateTime, + SignViewMinimumMinutes = t.SignViewMinimumMinutes, + + DocumentConfirmedUserCount = t.TrialDocConfirmedUserList.Count(), + DocumentUserCount = t.Trial.TrialUserList.Count(cc => t.NeedConfirmedUserTypeList.Any(k => k.NeedConfirmUserTypeId == cc.User.UserTypeId)) + + }); + + var unionQuery = systemDocumentQueryable.Union(trialDocQueryable) + .WhereIf(!string.IsNullOrEmpty(querySystemDocument.Name), t => t.Name.Contains(querySystemDocument.Name)) + .WhereIf(querySystemDocument.FileTypeId != null, t => t.FileTypeId == querySystemDocument.FileTypeId); + + return unionQuery.ToPagedList(querySystemDocument.PageIndex, querySystemDocument.PageSize, querySystemDocument.SortField, querySystemDocument.Asc); + } + #endregion + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs b/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs new file mode 100644 index 0000000..6870fea --- /dev/null +++ b/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs @@ -0,0 +1,1463 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-10-20 11:52:17 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Application.Helper; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore.Common; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Filter; +using MiniSoftware; +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Domain.Share.Common; +using System.IO; +using System.Linq; +using Spire.Doc; + +namespace IRaCIS.Core.Application.Service +{ + /// + /// TrialEmailNoticeConfigService + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialEmailNoticeConfigService : BaseService, ITrialEmailNoticeConfigService + { + + private readonly IRepository _trialEmailNoticeConfigRepository; + + private readonly IRepository _trialRepository; + + private readonly IRepository _taskMedicalReviewRepository; + + public IRepository _visitTaskRepository { get; } + public IRepository _trialUserRepository { get; } + + public IRepository _subjectRepository { get; } + + public IRepository _subjectVisitRepository { get; } + + public TrialEmailNoticeConfigService(IRepository trialEmailNoticeConfigRepository, IRepository visitTaskRepository, + IRepository trialRepository, + IRepository trialUserRepository, IRepository taskMedicalReviewRepository, IRepository subjectRepository, IRepository subjectVisitRepository) + { + _trialEmailNoticeConfigRepository = trialEmailNoticeConfigRepository; + _visitTaskRepository = visitTaskRepository; + this._trialRepository = trialRepository; + _trialUserRepository = trialUserRepository; + _taskMedicalReviewRepository = taskMedicalReviewRepository; + _subjectRepository = subjectRepository; + + _subjectVisitRepository = subjectVisitRepository; + } + + /// + /// 获取项目邮箱 + /// + /// + /// + [HttpPost] + public async Task GetTrialEmail(GetTrialEmailSetInDto inDto) + { + return await _trialRepository.Where(x => x.Id == inDto.TrialId).Select(x => new GetTrialEmailSetOutDto() + { + TrialId = inDto.TrialId, + EmailAuthorizationCode = x.EmailAuthorizationCode, + EmailSMTPServerAddress = x.EmailSMTPServerAddress, + EmailFromEmail = x.EmailFromEmail, + EmailFromName = x.EmailFromName, + IsConfigureEmail = x.IsConfigureEmail, + EmailSMTPServerPort = x.EmailSMTPServerPort + + }).FirstNotNullAsync(); + } + + /// + /// 设置项目邮箱 + /// + /// + /// + public async Task SetTrialEmail(SetTrialEmailInDto inDto) + { + + await TestEmailConfigAsync(new TrialEmailNoticeConfigAddOrEdit() + { + + AuthorizationCode = inDto.EmailAuthorizationCode, + FromEmail = inDto.EmailFromEmail, + FromName = inDto.EmailFromName, + SMTPServerAddress = inDto.EmailSMTPServerAddress, + SMTPServerPort = inDto.EmailSMTPServerPort.Value, + TrialId = inDto.TrialId, + }); + + await _trialRepository.UpdatePartialFromQueryAsync(inDto.TrialId, x => new Trial() + { + + EmailFromEmail = inDto.EmailFromEmail, + EmailFromName = inDto.EmailFromName, + EmailAuthorizationCode = inDto.EmailAuthorizationCode, + EmailSMTPServerAddress = inDto.EmailSMTPServerAddress, + EmailSMTPServerPort = inDto.EmailSMTPServerPort, + IsConfigureEmail = true, + }); + + + + await _trialEmailNoticeConfigRepository.BatchUpdateNoTrackingAsync(x => x.TrialId == inDto.TrialId, x => new TrialEmailNoticeConfig() + { + AuthorizationCode = inDto.EmailAuthorizationCode, + FromEmail = inDto.EmailFromEmail, + FromName = inDto.EmailFromName, + SMTPServerAddress = inDto.EmailSMTPServerAddress, + SMTPServerPort = inDto.EmailSMTPServerPort.Value, + }); + + await _trialRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } + + /// + /// 同步系统配置的文档到想项目中 + /// + /// + /// + /// + private async Task SyncSystemEmainCofigDocListAsync(Guid trialId) + { + + //判断流程配置是否确认 确认了一定确认了标准 可以进行同步 + + if (_repository.Where(t => t.Id == trialId).Any(t => t.IsTrialProcessConfirmed == true)) + { + + //只要有系统标准的文档 说明同步过了 + var trialDocCount = _trialEmailNoticeConfigRepository.Where(t =>/* t.CriterionTypeEnum == criterionTypeEnum &&*/ t.TrialId == trialId && t.TrialReadingCriterionId != null).Count(); + + if (trialDocCount == 0) + { + //找到确认的标准 + var list = await _repository.Where(t => t.TrialId == trialId && t.IsConfirm).Select(t => new { t.CriterionType, TrialReadingCriterionId = t.Id }).ToListAsync(); + + var confirmedCriterionTypeList = list.Select(t => (CriterionType?)t.CriterionType).ToList(); + + var docmentList = _repository.Where(t => confirmedCriterionTypeList.Contains(t.CriterionTypeEnum)).Select(t => new { t.Path, t.Name, t.Code, t.BusinessScenarioEnum, t.CriterionTypeEnum }).ToList(); + + + foreach (var item in docmentList) + { + await _trialEmailNoticeConfigRepository.AddAsync(new TrialEmailNoticeConfig() + { + TrialId = trialId, + TrialReadingCriterionId = list.Where(t => t.CriterionType == item.CriterionTypeEnum).FirstOrDefault()?.TrialReadingCriterionId, + FileName = item.Name, + FilePath = item.Path, + BusinessScenarioEnum = item.BusinessScenarioEnum, + Code = item.Code + }); + } + + } + + + } + + + + await _trialEmailNoticeConfigRepository.SaveChangesAsync(); + } + + + + private async Task DealMedicalReviewTasKGenerateAndIsSendAsync(Guid trialId, bool? isHandSend, string pdAnswer, List taskIdList, List minUserIdList) + { + + var isNeedSend = true; + + //手动发送的时候,也有可能答案是是 此时是 这里不发送,发送已经生成的文件 + if (pdAnswer == "是" && isHandSend == null) + { + isNeedSend = true; + + } + else + { + //正常阅片为否的 + if (isHandSend == null) + { + isNeedSend = false; + + //生成任务 + foreach (var taskId in taskIdList) + { + await _taskMedicalReviewRepository.AddAsync(new TaskMedicalReview() + { + TrialId = trialId, + VisitTaskId = taskId, + MedicalManagerUserId = minUserIdList.FirstOrDefault(), + AllocateTime = DateTime.Now + , + IsAutoGenerate = true, + PDRelationTaskIdListStr = string.Join('|', taskIdList.Distinct()) + }, true); + + } + } + else if (isHandSend == true) + { + //手动发送 + isNeedSend = false; + } + else + { + // 医学审核确认未否了 才发 + isNeedSend = true; + } + } + + + + + return isNeedSend; + + } + + /// + /// 测试邮件 带附件 填充word --前端不需 + /// + /// + /// 为空 代表 正常任务自动发送,为true 代表医学审核手动发送 为false 代表医学审核自动发送 + /// + /// + /// + /// + public async Task BaseBusinessScenarioSendEmailAsync(Guid visitTaskId, bool? isHandSend, EmailStoreSendMode emailStoreMode, string sendFileRelativePath) + { + + CommonDocumentBusinessScenario? businessScenarioEnum = null; + + #region 任务关联的项目配置 标准信息及配置,subject 信息 + var taskInfo = await _visitTaskRepository.Where(t => t.Id == visitTaskId).Select(t => new + { + + t.Trial.ResearchProgramNo, + t.Subject.TrialSite.TrialSiteCode, + SubjectCode = t.Subject.Code, + t.Trial.Sponsor.SponsorName, + t.Trial.IsEnrollementQualificationConfirm, + t.Trial.IsPDProgressView, + + + VisitEarliestScanDate = t.SourceSubjectVisit.EarliestScanDate, + VisitName = (string?)t.SourceSubjectVisit.VisitName, + IsFinalVisit = (bool?)t.SourceSubjectVisit.IsFinalVisit, + PDState = (PDStateEnum?)t.SourceSubjectVisit.PDState, + IsEnrollmentConfirm = (bool?)t.SourceSubjectVisit.IsEnrollmentConfirm, + IsBaseline = (bool?)t.SourceSubjectVisit.IsBaseLine, + + + ModuleEarliestScanDate = t.ReadModule.SubjectVisit.EarliestScanDate, + ModuleVisitName = (string?)t.ReadModule.SubjectVisit.VisitName, + MoudulePDState = (PDStateEnum?)t.ReadModule.SubjectVisit.PDState, + + t.SourceSubjectVisitId, + t.SouceReadModuleId, + t.SubjectId, + t.Subject.SiteId, + + t.DoctorUserId, + t.ReadingTaskState, + t.ReadingCategory, + t.SignTime, + //仲裁规则 + t.TrialReadingCriterion.ArbitrationRule, + //单双中 + t.TrialReadingCriterion.ReadingType, + + t.TrialReadingCriterion.CriterionType, + //有序与否 + t.TrialReadingCriterion.IsReadingTaskViewInOrder, + + t.TrialId, + + + + t.IsAnalysisCreate, + t.TrialReadingCriterionId, + }).FirstNotNullAsync(); + + + if (taskInfo.IsAnalysisCreate) + { + return string.Empty; + } + + #endregion + + + #region 任务 -邮件场景区分 + + if (taskInfo.ReadingTaskState == ReadingTaskState.HaveSigned) + { + //入组确认场景 + if (taskInfo.IsEnrollmentConfirm == true && taskInfo.IsEnrollementQualificationConfirm == true && taskInfo.IsBaseline == true) + { + businessScenarioEnum = CommonDocumentBusinessScenario.EnrollConfirmed; + + + } + //PD确认场景 + else if (taskInfo.IsPDProgressView && + (taskInfo.PDState == PDStateEnum.PDProgress && taskInfo.SourceSubjectVisitId != null) || + (taskInfo.SouceReadModuleId != null && taskInfo.MoudulePDState == PDStateEnum.PDProgress)) + { + businessScenarioEnum = CommonDocumentBusinessScenario.PDConfirmed; + } + else + { + return string.Empty; + } + + } + else + { + throw new BusinessValidationFailedException("进行邮件发送前,该任务必须已签名完成并已经触发完成相应的任务生成"); + } + + + + #endregion + + + #region 发收件人配置 确保无误 + + + var emailConfig = await _trialEmailNoticeConfigRepository.Where(t => t.TrialId == taskInfo.TrialId && t.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && t.BusinessScenarioEnum == businessScenarioEnum) + .Include(t => t.TrialEmailNoticeUserList).FirstOrDefaultAsync(); + + + if (emailConfig == null || emailConfig.IsAutoSend == false) + { + //throw new BusinessValidationFailedException("找不到该项目标准场景下邮件的配置"); + + return string.Empty; + } + + + var sendEmailConfig = new SMTPEmailConfig(); + + //收件人 如果是IC CRA 要按照中心发送 + var toUserTypeEnumList = emailConfig.TrialEmailNoticeUserList.Where(t => t.EmailUserType == EmailUserType.To).Select(c => c.UserType).ToList(); + + + + var toUserList = _repository.Where(t => t.TrialId == taskInfo.TrialId && toUserTypeEnumList.Contains(t.User.UserTypeEnum) && t.SiteId == taskInfo.SiteId).Select(t => new { t.User.EMail, t.User.FullName }).ToList(); + + var copyUserTypeEnumList = emailConfig.TrialEmailNoticeUserList.Where(t => t.EmailUserType == EmailUserType.Copy).Select(c => c.UserType).ToList(); + var copyUserList = _repository.Where(t => t.TrialId == taskInfo.TrialId && copyUserTypeEnumList.Contains(t.User.UserTypeEnum)).Select(t => new { t.User.EMail, t.User.FullName }).ToList(); + + + if (toUserList.Count() == 0) + { + throw new BusinessValidationFailedException("没有收件人,无法发送邮件"); + } + + + if (emailConfig.FromEmail.Contains("@") && !string.IsNullOrEmpty(emailConfig.FromEmail)) + { + + sendEmailConfig.FromEmailAddress = new MimeKit.MailboxAddress(emailConfig.FromName, emailConfig.FromEmail); + sendEmailConfig.AuthorizationCode = emailConfig.AuthorizationCode; + sendEmailConfig.UserName = emailConfig.FromEmail; + + sendEmailConfig.Host = emailConfig.SMTPServerAddress; + sendEmailConfig.Port = emailConfig.SMTPServerPort; + + + //测试 + //sendEmailConfig.ToMailAddressList.Add(new MimeKit.MailboxAddress("ddd", "872297557@qq.com")); + + } + else + { + throw new BusinessValidationFailedException("项目发件邮箱配置有误,请核实"); + } + + foreach (var item in toUserList) + { + + if (item.EMail.Contains("@") && !string.IsNullOrEmpty(item.EMail)) + { + + sendEmailConfig.ToMailAddressList.Add(new MimeKit.MailboxAddress(item.FullName, item.EMail)); + + } + } + foreach (var item in copyUserList) + { + + if (item.EMail.Contains("@") && !string.IsNullOrEmpty(item.EMail)) + { + + sendEmailConfig.CopyToMailAddressList.Add(new MimeKit.MailboxAddress(item.FullName, item.EMail)); + + } + } + #endregion + + #region 确保 邮件Html存在 + + //邮件附件 + var path = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, emailConfig.FilePath); + + if (!File.Exists(path)) + { + throw new BusinessValidationFailedException("找不到该项目标准场景下邮件模板"); + } + + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + "SubjectEnrollConfirmOrPDProgress.html"; + + #endregion + + + #region 不同场景 Tile 设置 + + if (businessScenarioEnum == CommonDocumentBusinessScenario.EnrollConfirmed) + { + sendEmailConfig.TopicDescription = $"【入组确认报告】关于{taskInfo.ResearchProgramNo}项目{taskInfo.SubjectCode}患者"; + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + + sendEmailConfig.HtmlBodyStr = string.Format(templateInfo, + $" 附件为入组确认报告,请查收 " + ); + } + } + else if (businessScenarioEnum == CommonDocumentBusinessScenario.PDConfirmed) + { + sendEmailConfig.TopicDescription = $"【疾病进展确认报告】关于{taskInfo.ResearchProgramNo}项目{taskInfo.SubjectCode}患者"; + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + + sendEmailConfig.HtmlBodyStr = string.Format(templateInfo, + $" 附件为疾病进展确认报告,请查收 " + ); + } + } + else + { + // + } + + #endregion + + + #region 不同标准 不同项目配置 发送邮件的时机 处理具体逻辑 + + var answer = "否"; + var isNeedSend = true; + var minUserIdList = _trialUserRepository.Where(t => t.User.UserTypeEnum == Domain.Share.UserTypeEnum.MIM && t.TrialId == taskInfo.TrialId).Select(t => t.UserId).ToList(); + + + + //入组确认 根据每个标准配置的是否自动发送,发送邮件与否 + if (businessScenarioEnum == CommonDocumentBusinessScenario.EnrollConfirmed) + { + if (await _repository.Where().AnyAsync(x => x.VisitTaskId == visitTaskId && x.Answer == TargetState.Exist.GetEnumInt() && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State && x.ReadingQuestionTrial.LesionType == LesionType.TargetLesion)) + { + answer = "是"; + } + + + //如果其他阅片人已经做了,说明发送了入组确认报告,第二个人做完就不发送了 + + //入组确认一直交给第一个人,如果第一个人重阅 还未做完,第二个人先做完了,此时不发 + + var existFirstEnrollTask = await _visitTaskRepository.Where(t => t.SourceSubjectVisitId == taskInfo.SourceSubjectVisitId && t.IsAnalysisCreate == false + && t.ReadingTaskState == ReadingTaskState.HaveSigned && t.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId).OrderBy(t => t.SignTime).FirstOrDefaultAsync(); + + //入组确认的医生已确定 + if ((existFirstEnrollTask != null) && (taskInfo.DoctorUserId != existFirstEnrollTask.DoctorUserId)) + { + isNeedSend = false; + } + else + { + isNeedSend = await DealMedicalReviewTasKGenerateAndIsSendAsync(taskInfo.TrialId, isHandSend, answer, new List() { visitTaskId }, minUserIdList); + + + if (answer == "是") + { + //把另外一个人的任务设置为不加急(如果项目加急是否 subject 加急是否) + var urgent = _repository.Where(t => t.Id == taskInfo.SourceSubjectVisitId).Select(t => new { IsSubjectUrgent = t.Subject.IsUrgent, t.Trial.IsUrgent }).FirstOrDefault(); + + if (urgent?.IsUrgent == false || urgent?.IsSubjectUrgent == false) + { + await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.SourceSubjectVisitId == taskInfo.SourceSubjectVisitId && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false && + t.Id != visitTaskId && t.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId, u => new VisitTask() { IsUrgent = false }); + } + } + + + } + + + + + } + else if (businessScenarioEnum == CommonDocumentBusinessScenario.PDConfirmed) + { + + + //有序 + + if (taskInfo.IsReadingTaskViewInOrder) + { + + + + //双重 + if (taskInfo.ReadingType == ReadingMethod.Double) + { + + + //仲裁在检查批次上 就没有全局阅片 没有阅片期 + if (taskInfo.ArbitrationRule == ArbitrationRule.Visit) + { + //找到 检查批次,裁判 所有有效任务(不可能有全局的) 检查批次和裁判任务的SourceSubjectVisitId 一样 + var taskList = await _visitTaskRepository.Where(t => t.SourceSubjectVisitId == taskInfo.SourceSubjectVisitId && t.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && + (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Judge)).ToListAsync(); + + + //这里要求 到这里已经如果有裁判 已经生成裁判了保存数据库 + //双人阅片,没有产生裁判 第二个人读完发 + if (taskList.Count == 2 && taskList.Count(t => t.ReadingTaskState == ReadingTaskState.HaveSigned && t.ReadingCategory == ReadingCategory.Visit) == 2) + { + + answer = await TranslatePdStateAsync(visitTaskId, taskInfo.ReadingCategory, taskInfo.CriterionType); + + + isNeedSend = await DealMedicalReviewTasKGenerateAndIsSendAsync(taskInfo.TrialId, isHandSend, answer, taskList.Select(t => t.Id).ToList(), minUserIdList); + + } + //双人 产生裁判,并且裁判完成 发 + else if (taskList.Count == 3 && taskList.Count(t => t.ReadingTaskState == ReadingTaskState.HaveSigned) == 3 && taskList.Where(t => t.ReadingCategory == ReadingCategory.Judge).Count() == 1) + { + var judgeResultId = taskList.Where(t => t.ReadingCategory == ReadingCategory.Judge).First().JudgeResultTaskId.Value; + answer = await TranslatePdStateAsync(judgeResultId, ReadingCategory.Visit, taskInfo.CriterionType); + + isNeedSend = await DealMedicalReviewTasKGenerateAndIsSendAsync(taskInfo.TrialId, isHandSend, answer, taskList.Where(t => t.ReadingCategory == ReadingCategory.Judge).Select(t => t.Id).ToList(), minUserIdList); + + } + else + { + isNeedSend = false; + + } + + + + + } + //仲裁在阅片期 + else if (taskInfo.ArbitrationRule == ArbitrationRule.Reading) + { + //是检查批次任务 不可能是裁判任务(检查批次上不会生成裁判),也不会是全局任务(全局任务 SourceSubjectVisitId=null ) + if (taskInfo.SourceSubjectVisitId != null) + { + + //检查批次类型的任务 根本就不需要发送邮件 + + isNeedSend = false; + + } + //是全局任务 或者全局的裁判任务 (如果是全局任务,那么此时裁判任务已经生成) + else if (taskInfo.SouceReadModuleId != null) + { + var taskList = await _visitTaskRepository.Where(t => t.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && t.SouceReadModuleId == taskInfo.SouceReadModuleId + && (t.ReadingCategory == ReadingCategory.Global || t.ReadingCategory == ReadingCategory.Judge)).ToListAsync(); + + //两个全局没有裁判 + if (taskList.Count == 2 && taskList.Count(t => t.ReadingTaskState == ReadingTaskState.HaveSigned && t.ReadingCategory == ReadingCategory.Global) == 2) + { + + answer = await TranslatePdStateAsync(visitTaskId, taskInfo.ReadingCategory, taskInfo.CriterionType); + + isNeedSend = await DealMedicalReviewTasKGenerateAndIsSendAsync(taskInfo.TrialId, isHandSend, answer, taskList.Select(t => t.Id).ToList(), minUserIdList); + } + //双人全局产生裁判 + else if (taskList.Count == 3 && taskList.Count(t => t.ReadingTaskState == ReadingTaskState.HaveSigned) == 3 && taskList.Where(t => t.ReadingCategory == ReadingCategory.Judge).Count() == 1 && taskList.Where(t => t.ReadingCategory == ReadingCategory.Global).Count() == 2) + { + + var judgeResultId = taskList.Where(t => t.ReadingCategory == ReadingCategory.Judge).First().JudgeResultTaskId.Value; + answer = await TranslatePdStateAsync(judgeResultId, ReadingCategory.Global, taskInfo.CriterionType); + + isNeedSend = await DealMedicalReviewTasKGenerateAndIsSendAsync(taskInfo.TrialId, isHandSend, answer, taskList.Where(t => t.ReadingCategory == ReadingCategory.Judge).Select(t => t.Id).ToList(), minUserIdList); + + } + else + { + isNeedSend = false; + + } + } + else + { + throw new BusinessValidationFailedException("发送PD 进展邮件中发现任务数据有问题!"); + } + + + + } + else + { + + throw new BusinessValidationFailedException("双重有序阅片 没有定义该仲裁规则处理逻辑,请联系业务和后台开发核查!"); + } + + + } + + //屏蔽单重阅片添加 + else + { + isNeedSend = false; + return string.Empty; + } + + #region 发邮件屏蔽单重的 + ////单重 + //else if (taskInfo.ReadingType == ReadingMethod.Single) + //{ + // //仲裁在检查批次上 或者在阅片期 + // if (taskInfo.ArbitrationRule != ArbitrationRule.None) + // { + + // throw new BusinessValidationFailedException("单重有序阅片配置有误(不应该有仲裁对象配置),请核查!"); + // } + + + // //要求PD 确认的检查批次 是截止检查批次 还是非截止检查批次(根据该检查批次有没有配置阅片期来判断) + + // if (taskInfo.ReadingCategory == ReadingCategory.Visit) + // { + // //存在阅片期 那么就是截止检查批次 + // if (await _repository.Where(t => t.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && t.SubjectVisitId == taskInfo.SourceSubjectVisitId && t.ReadingSetType == ReadingSetType.ImageReading).AnyAsync()) + // { + // isNeedSend = false; + // } + // else//非截止检查批次 在检查批次读完后,发送 + // { + // answer = await TranslatePdStateAsync(visitTaskId, ReadingCategory.Visit, taskInfo.CriterionType); + // } + // } + // //截止检查批次 在检查批次读完,并完成全局阅片后发送全局的结果 + // else if (taskInfo.ReadingCategory == ReadingCategory.Global) + // { + // answer = await TranslatePdStateAsync(visitTaskId, ReadingCategory.Global, taskInfo.CriterionType); + // } + // else + // { + // throw new BusinessValidationFailedException("单重有序阅片 该类型的任务不应进入此处逻辑,请联系后台开发核查!"); + // } + + // isNeedSend = await DealMedicalReviewTasKGenerateAndIsSendAsync(taskInfo.TrialId, isHandSend, answer, new List() { visitTaskId }, minUserIdList); + + + //} + //else + //{ + // throw new BusinessValidationFailedException("有序阅片配置有误(应为单重或者双重阅片),请核查!"); + //} + + #endregion + + + + + } + //屏蔽无序阅片添加 + else + { + isNeedSend = false; + return string.Empty; + } + + #region 发送邮件屏蔽无序的 + // //无序 + //else + //{ + // //单重 + + + // if (taskInfo.ReadingType == ReadingMethod.Single && taskInfo.ArbitrationRule == ArbitrationRule.None) + // { + // answer = await TranslatePdStateAsync(visitTaskId, taskInfo.ReadingCategory, taskInfo.CriterionType); + + // isNeedSend = await DealMedicalReviewTasKGenerateAndIsSendAsync(taskInfo.TrialId, isHandSend, answer, new List() { visitTaskId }, minUserIdList); + // } + // //双重 截止检查批次只在阅片期的时候存在 要求PD确认的检查批次 肯定是非截止检查批次 + // else if (taskInfo.ReadingType == ReadingMethod.Double && taskInfo.ArbitrationRule == ArbitrationRule.Visit) + // { + // //在两位阅片人读完检查批次后,如果有裁判者等裁判读完,如果无裁判则等第二个人的读完 + + // var taskList = await _visitTaskRepository.Where(t => t.SourceSubjectVisitId == taskInfo.SourceSubjectVisitId && t.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect + // && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Judge)).ToListAsync(); + + // //这里要求 到这里已经如果有裁判 已经生成裁判了保存数据库 + // if (taskList.Count == 2 && taskList.Count(t => t.ReadingTaskState == ReadingTaskState.HaveSigned && t.ReadingCategory == ReadingCategory.Visit) == 2) + // { + + // answer = await TranslatePdStateAsync(visitTaskId, taskInfo.ReadingCategory, taskInfo.CriterionType); + + // isNeedSend = await DealMedicalReviewTasKGenerateAndIsSendAsync(taskInfo.TrialId, isHandSend, answer, taskList.Select(t => t.Id).ToList(), minUserIdList); + // } + // else if (taskList.Count == 3 && taskList.Count(t => t.ReadingTaskState == ReadingTaskState.HaveSigned) == 3 && taskList.Where(t => t.ReadingCategory == ReadingCategory.Judge).Count() == 1) + // { + // var judgeResultId = taskList.Where(t => t.ReadingCategory == ReadingCategory.Judge).First().JudgeResultTaskId.Value; + // answer = await TranslatePdStateAsync(judgeResultId, ReadingCategory.Visit, taskInfo.CriterionType); + + // isNeedSend = await DealMedicalReviewTasKGenerateAndIsSendAsync(taskInfo.TrialId, isHandSend, answer, taskList.Where(t => t.ReadingCategory == ReadingCategory.Judge).Select(t => t.Id).ToList(), minUserIdList); + + + // } + // else + // { + // isNeedSend = false; + // } + + // } + // else + // { + // throw new BusinessValidationFailedException("无序阅片配置有误(应为单重无仲裁对象,双重针对检查批次仲裁),请核查!"); + // } + + //} + + #endregion + } + else + { + isNeedSend = false; + } + + #endregion + + + #region MiniWord 组织字典 发送 + + + if (emailStoreMode == EmailStoreSendMode.NotStoreLocalOnlySentEmail) + { + + var phyPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, sendFileRelativePath); + + + //先预先生成了邮件,发送预先生成的邮件 + sendEmailConfig.EmailAttachMentConfigList.Add(new EmailAttachMentConfig() + { + FileName = $"{taskInfo.SubjectCode}_{Path.GetFileNameWithoutExtension(emailConfig.FileName)}.pdf", + + FileStream = File.OpenRead(phyPath), + }); + + + await SendEmailHelper.SendEmailAsync(sendEmailConfig); + + return string.Empty; + } + + var value = new Dictionary() + { + ["SponsorName"] = taskInfo.SponsorName, + ["ResearchProgramNo"] = taskInfo.ResearchProgramNo, + ["TrialSiteCode"] = taskInfo.TrialSiteCode, + ["SubjectCode"] = taskInfo.SubjectCode, + ["VisitName"] = taskInfo.SourceSubjectVisitId != null ? taskInfo.VisitName : taskInfo.ModuleVisitName, + ["EarliestScanDate"] = taskInfo.SourceSubjectVisitId != null ? taskInfo.VisitEarliestScanDate?.ToString("yyyy-MM-dd") : taskInfo.ModuleEarliestScanDate?.ToString("yyyy-MM-dd"), + ["SignTime"] = taskInfo.SignTime?.ToString("yyyy-MM-dd"), + ["Result"] = answer + + }; + + var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetSubjectEnrollConfirmOrPDEmailPath(_hostEnvironment, Path.GetFileName(path), taskInfo.TrialId, taskInfo.SiteId, taskInfo.SubjectId,true); + + if (emailStoreMode == EmailStoreSendMode.StoreLocalSend || emailStoreMode == EmailStoreSendMode.OnlyStoreLocalNotSentEmail) + { + + MemoryStream wordMemoryStream = new MemoryStream(); + + Document document = new Document(); + + MiniSoftware.MiniWord.SaveAsByTemplate(wordMemoryStream, path, value); + + document.LoadFromStream(wordMemoryStream,FileFormat.Docx); + + document.SaveToFile(serverFilePath, FileFormat.PDF); + + + } + //手动生成发送的邮件内容,但是并不发送 + if (emailStoreMode == EmailStoreSendMode.OnlyStoreLocalNotSentEmail) + { + isNeedSend = false; + + return relativePath; + } + + + //正常的即时生成邮件 并发送邮件 + if (isNeedSend) + { + + + MemoryStream memoryStream = new MemoryStream(); + MemoryStream pdfMemoryStream = new MemoryStream(); + + + MiniSoftware.MiniWord.SaveAsByTemplate(memoryStream, path, value); + Document document = new Document(); + + document.LoadFromStream(memoryStream, FileFormat.Docx); + document.SaveToStream(pdfMemoryStream, FileFormat.PDF); + pdfMemoryStream.Seek(0, SeekOrigin.Begin); + + sendEmailConfig.EmailAttachMentConfigList.Add(new EmailAttachMentConfig() + { + FileName = $"{taskInfo.SubjectCode}_{Path.GetFileNameWithoutExtension(emailConfig.FileName)}.pdf", + + FileStream = pdfMemoryStream + }); + + + await SendEmailHelper.SendEmailAsync(sendEmailConfig); + + } + + + + return string.Empty; + + + + + + #endregion + + + } + + + + /// + /// 手动生成入组确认 或者PD 进展的邮件 如果能发送,会返回文件的路径,否则会给出提示 + /// + /// + + /// + [HttpPost] + public async Task ManualGenerateEmailFile(GenerateEmailCommand generateEmailCommand) + { + var subjectId = generateEmailCommand.SubjectId; + var businessScenarioEnum = generateEmailCommand.BusinessScenarioEnum; + var trialReadingCriterionId = generateEmailCommand.TrialReadingCriterionId; + + + var trialConfig = await _subjectRepository.Where(t => t.Id == subjectId).Select(t => new { t.Trial.IsEnrollementQualificationConfirm, t.Trial.IsPDProgressView }).FirstNotNullAsync(); + + //找到入组确认 或者Pd 进展 已生成任务的 检查批次 + var subjectVisitList = await _subjectVisitRepository.Where(t => t.SubjectId == subjectId & t.CheckState == CheckStateEnum.CVPassed && (t.IsEnrollmentConfirm == true || t.PDState == PDStateEnum.PDProgress)).ToListAsync(); + + if (businessScenarioEnum == CommonDocumentBusinessScenario.EnrollConfirmed) + { + + if (trialConfig.IsEnrollementQualificationConfirm == false) + { + return ResponseOutput.NotOk("项目未配置入组确认!"); + + } + var exisitBaseline = subjectVisitList.FirstOrDefault(t => t.IsEnrollmentConfirm); + if (exisitBaseline == null) + { + return ResponseOutput.NotOk("不存在配置了入组确认的并且生成任务的基线检查批次"); + } + else + { + //入组确认不用管项目的 有序 无序 单重 双重 阅片 + + //找到最早签名的 + var firstSignTask = await _visitTaskRepository.Where(t => t.SourceSubjectVisitId == exisitBaseline.Id /*&& t.TaskState == TaskState.Effect*/ && t.IsAnalysisCreate == false + && t.ReadingTaskState == ReadingTaskState.HaveSigned && t.TrialReadingCriterionId == trialReadingCriterionId).OrderBy(t => t.SignTime).FirstOrDefaultAsync(); + + + if (firstSignTask != null) + { + var task = await _visitTaskRepository.Where(t => t.SourceSubjectVisitId == exisitBaseline.Id && t.TaskState == TaskState.Effect && t.DoctorUserId == firstSignTask.DoctorUserId && t.IsAnalysisCreate == false + && t.ReadingTaskState == ReadingTaskState.HaveSigned && t.TrialReadingCriterionId == trialReadingCriterionId).OrderBy(t => t.SignTime).FirstOrDefaultAsync(); + + //如果存在做完的该任务 + + if (task == null) + { + return ResponseOutput.NotOk("做入组确认的阅片人基线任务没有阅片完!"); + } + else + { + var filePath = await BaseBusinessScenarioSendEmailAsync(task.Id, true, EmailStoreSendMode.OnlyStoreLocalNotSentEmail, string.Empty); + + return ResponseOutput.Ok(new { RelativePath = filePath, TaskName = task.TaskName, VisitTaskId = task.Id }); + + + } + } + else + { + return ResponseOutput.NotOk("当前未有阅片人读完基线任务!"); + } + + + + + + } + } + + else if (businessScenarioEnum == CommonDocumentBusinessScenario.PDConfirmed) + { + + if (trialConfig.IsPDProgressView == false) + { + return ResponseOutput.NotOk("项目未配置PD进展!"); + } + + //是否是截止检查批次 截止检查批次在全局发 否则就在当前检查批次发 + + var pdSubjectVisitIdList = subjectVisitList.Where(t => t.PDState == PDStateEnum.PDProgress).OrderBy(t => t.VisitNum).Select(t => (Guid?)t.Id).ToList(); + + if (pdSubjectVisitIdList.Count == 0) + { + return ResponseOutput.NotOk("不存在配置了PD进展的并且生成任务的检查批次"); + } + + + var currentLatestPdVisitId = pdSubjectVisitIdList.Last(); + //标准配置 + var trialReadingCriterionConfig = await _repository.Where(t => t.Id == trialReadingCriterionId).Select(t => new + { TrialReadingCriterionId = t.Id, t.ReadingType, t.IsReadingTaskViewInOrder, t.CriterionType, t.ArbitrationRule }).FirstNotNullAsync(); + + // 项目双重 + if (trialReadingCriterionConfig.ReadingType == ReadingMethod.Double && trialReadingCriterionConfig.IsReadingTaskViewInOrder) + { + //仲裁在检查批次上面 + if (trialReadingCriterionConfig.ArbitrationRule == ArbitrationRule.Visit) + { + //在两位阅片人读完检查批次后,如果有裁判者等裁判读完,如果无裁判则等第二个人的读完 + + var taskList = await _visitTaskRepository.Where(t => t.SourceSubjectVisitId == currentLatestPdVisitId && t.TrialReadingCriterionId == trialReadingCriterionId && t.IsAnalysisCreate == false + && t.TaskState == TaskState.Effect && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Judge)).ToListAsync(); + + var totalTaskCount = taskList.Count; + var finishedCount = taskList.Where(t => t.ReadingTaskState == ReadingTaskState.HaveSigned).Count(); + + + //发送随访的 + if (totalTaskCount == 2 && totalTaskCount == finishedCount) + { + var task = taskList.FirstOrDefault(t => t.ReadingCategory == ReadingCategory.Visit); + + var filePath = await BaseBusinessScenarioSendEmailAsync(task.Id, true, EmailStoreSendMode.OnlyStoreLocalNotSentEmail, string.Empty); + + return ResponseOutput.Ok(new { RelativePath = filePath, TaskName = task.TaskName, VisitTaskId = task.Id }); + } + //发送全局 + else if (totalTaskCount == 3 && totalTaskCount == finishedCount) + { + var task = taskList.FirstOrDefault(t => t.ReadingCategory == ReadingCategory.Judge); + + var filePath = await BaseBusinessScenarioSendEmailAsync(task.Id, true, EmailStoreSendMode.OnlyStoreLocalNotSentEmail, string.Empty); + + return ResponseOutput.Ok(new { RelativePath = filePath, TaskName = task.TaskName, VisitTaskId = task.Id }); + } + else + { + return ResponseOutput.NotOk("当前患者最新PD检查批次阅片任务完成状态不符合发送条件"); + } + + } + //仲裁在阅片期上 + else if (trialReadingCriterionConfig.ArbitrationRule == ArbitrationRule.Reading) + { + var existReadModule = await _repository.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.SubjectVisitId == currentLatestPdVisitId && t.ReadingSetType == ReadingSetType.ImageReading) + .FirstOrDefaultAsync(); + + if (existReadModule == null) + { + return ResponseOutput.NotOk("项目配置了阅片期仲裁,但是当前患者最新PD检查批次没有影像学阅片期"); + } + else + { + var taskList = await _visitTaskRepository.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && t.SouceReadModuleId == existReadModule.Id + && (t.ReadingCategory == ReadingCategory.Global || t.ReadingCategory == ReadingCategory.Judge)).ToListAsync(); + + var totalTaskCount = taskList.Count; + var finishedCount = taskList.Where(t => t.ReadingTaskState == ReadingTaskState.HaveSigned).Count(); + + + + //发送全局的 + if (totalTaskCount == 2 && totalTaskCount == finishedCount) + { + var task = taskList.FirstOrDefault(t => t.ReadingCategory == ReadingCategory.Global); + + var filePath = await BaseBusinessScenarioSendEmailAsync(task.Id, true, EmailStoreSendMode.OnlyStoreLocalNotSentEmail, string.Empty); + + return ResponseOutput.Ok(new { RelativePath = filePath, TaskName = task.TaskName, VisitTaskId = task.Id }); + } + //发送全局裁判的 + else if (totalTaskCount == 3 && totalTaskCount == finishedCount) + { + var task = taskList.FirstOrDefault(t => t.ReadingCategory == ReadingCategory.Judge); + + var filePath = await BaseBusinessScenarioSendEmailAsync(task.Id, true, EmailStoreSendMode.OnlyStoreLocalNotSentEmail, string.Empty); + + return ResponseOutput.Ok(new { RelativePath = filePath, TaskName = task.TaskName, VisitTaskId = task.Id }); + } + + + else + { + return ResponseOutput.NotOk("当前患者最新PD检查批次阅片期任务完成状态不符合发送条件"); + } + } + + + } + else + { + return ResponseOutput.NotOk("未定义该仲裁规则发送业务逻辑!"); + } + } + + #region 发送邮件屏蔽单重阅片情况 + //// 项目单重 判断最新的Pd 检查批次是否完成 是否有阅片期即可 + //else if (trialReadingCriterionConfig.ReadingType == ReadingMethod.Single) + //{ + + + + // var task = await _visitTaskRepository.Where(t => t.SourceSubjectVisitId == currentLatestPdVisitId && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false + // && t.ReadingTaskState == ReadingTaskState.HaveSigned && t.TrialReadingCriterionId == trialReadingCriterionId).FirstOrDefaultAsync(); + + + // if (task == null) + // { + // return ResponseOutput.NotOk("当前患者最新PD检查批次任务未阅片完成"); + // } + // else + // { + // //存在阅片期 那么就是截止检查批次 + + // var existReadModule = await _repository.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.SubjectVisitId == currentLatestPdVisitId && t.ReadingSetType == ReadingSetType.ImageReading) + // .FirstOrDefaultAsync(); + // if (existReadModule != null) + // { + + // var global = await _visitTaskRepository.Where(t => t.SouceReadModuleId == currentLatestPdVisitId && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false + // && t.ReadingTaskState == ReadingTaskState.HaveSigned && t.TrialReadingCriterionId == trialReadingCriterionId).FirstOrDefaultAsync(); + + + // if (global != null) + // { + // var filePath = await BaseBusinessScenarioSendEmailAsync(global.Id, true, EmailStoreSendMode.OnlyStoreLocalNotSentEmail, string.Empty); + + // return ResponseOutput.Ok(new { RelativePath = filePath, TaskName = task.TaskName, VisitTaskId = task.Id }); + // } + // else + // { + // return ResponseOutput.NotOk("当前患者阅片期任务未阅片完成"); + // } + + // } + // else//非截止检查批次 在检查批次读完后,发送 + // { + // var filePath = await BaseBusinessScenarioSendEmailAsync(task.Id, true, EmailStoreSendMode.OnlyStoreLocalNotSentEmail, string.Empty); + + // return ResponseOutput.Ok(new { RelativePath = filePath, TaskName = task.TaskName, VisitTaskId = task.Id }); + // } + // } + + + //} + #endregion + + else + { + return ResponseOutput.NotOk("当前项目配置,不满足双重有序阅片,不满足发送条件!"); + } + + } + + else + { + return ResponseOutput.NotOk("当前项目配置,未定义发送业务逻辑!"); + } + + + + + } + + /// + /// 手动发送邮件 + /// + /// + /// + /// + [HttpPut] + public async Task ManualSendEmail(Guid visitTaskId, string sendFileRelativePath) + { + var filePath = await BaseBusinessScenarioSendEmailAsync(visitTaskId, true, EmailStoreSendMode.NotStoreLocalOnlySentEmail, sendFileRelativePath); + + return ResponseOutput.Ok(); + + } + + /// + /// + /// + /// 任务Id + /// 任务类型 + /// 标准类型 + /// 是否是全局产生(区分裁判任务) + /// + /// + private async Task TranslatePdStateAsync(Guid visitTaskId, ReadingCategory readingCategory, CriterionType criterionType, bool? IsGlobalGenerate = null) + { + + var answer = string.Empty; + + ReadingTaskQuestionAnswer visitQuestionAnswer = null; + + ReadingTaskQuestionAnswer globalQuestionAnswer = null; + + + + switch (criterionType) + { + + + case CriterionType.RECIST1Pointt1: + + if (readingCategory == ReadingCategory.Visit) + { + + visitQuestionAnswer = await _repository.Where(t => t.VisitTaskId == visitTaskId && t.ReadingQuestionTrial.QuestionType == QuestionType.Tumor).FirstNotNullAsync(); + + + answer = visitQuestionAnswer.Answer; + } + else if (readingCategory == ReadingCategory.Global) + { + var questionAnsewer = await _repository.Where(t => t.GlobalTaskId == visitTaskId && t.TrialReadingQuestion.QuestionType == QuestionType.Tumor).OrderByDescending(c => c.VisitTask.VisitTaskNum).FirstNotNullAsync(); + + + answer = questionAnsewer.Answer; + if (string.IsNullOrEmpty(questionAnsewer.Answer)) + { + answer = await _repository.Where(t => questionAnsewer.TaskId == t.VisitTaskId && t.ReadingQuestionTrial.QuestionType == QuestionType.Tumor) + .Select(t => t.Answer).FirstOrDefaultAsync(); + } + + } + //else if (readingCategory == ReadingCategory.Judge) + //{ + // var judgeResultTaskId = await _visitTaskRepository.Where(t => t.Id == visitTaskId).Select(t => t.JudgeResultTaskId).FirstNotNullAsync(); + + // var questionAnsewer = await _repository.Where(t => t.VisitTaskId == judgeResultTaskId && t.ReadingQuestionTrial.QuestionType == QuestionType.Tumor).FirstNotNullAsync(); + + + // answer = questionAnsewer.Answer; + //} + else + { + throw new BusinessValidationFailedException("不应有 除检查批次、裁判、全局其他类型的任务进行发送邮件,请核查业务逻辑"); + } + + if (answer == OverallAssessment.PD.GetEnumInt()) + { + answer = "是"; + } + else + { + answer = "否"; + } + + break; + + case CriterionType.PCWG3: + if (readingCategory == ReadingCategory.Visit) + { + + visitQuestionAnswer = await _repository.Where(t => t.VisitTaskId == visitTaskId && t.ReadingQuestionTrial.QuestionType == QuestionType.SiteVisitForTumorEvaluation).FirstNotNullAsync(); + + + answer = visitQuestionAnswer.Answer; + } + else if (readingCategory == ReadingCategory.Global) + { + var questionAnsewer = await _repository.Where(t => t.TaskId == visitTaskId && t.TrialReadingQuestion.QuestionType == QuestionType.SiteVisitForTumorEvaluation).FirstNotNullAsync(); + + answer = questionAnsewer.Answer; + } + //else if (readingCategory == ReadingCategory.Judge) + //{ + // //var judgeResultTaskId = await _visitTaskRepository.Where(t => t.Id == visitTaskId).Select(t => t.JudgeResultTaskId).FirstNotNullAsync(); + + // var questionAnsewer = await _repository.Where(t => t.VisitTaskId == visitTaskId && t.ReadingQuestionTrial.QuestionType == QuestionType.SiteVisitForTumorEvaluation).FirstNotNullAsync(); + + // answer = questionAnsewer.Answer; + //} + else + { + throw new BusinessValidationFailedException("不应有 除检查批次、裁判、全局其他类型的任务进行发送邮件,请核查业务逻辑"); + } + + if (answer == VisitTumorEvaluation.PD.GetEnumInt()) + { + answer = "是"; + } + if (answer == VisitTumorEvaluation.ND.GetEnumInt()) + { + answer = "ND"; + } + if (answer == VisitTumorEvaluation.NE.GetEnumInt()) + { + answer = "NE"; + } + else + { + answer = "否"; + } + break; + case CriterionType.SelfDefine: + case CriterionType.mRECISTMesothelioma: + case CriterionType.RECIL: + case CriterionType.RECIST1Point0: + case CriterionType.WHO: + case CriterionType.PERCIST: + case CriterionType.Forrest: + case CriterionType.Lugano2014: + case CriterionType.iRECIST: + case CriterionType.RANO_BM: + case CriterionType.RANO: + case CriterionType.IWCLL2018: + case CriterionType.mRECISTHCC: + case CriterionType.Cheson2007: + case CriterionType.IMWG2016: + default: + + throw new BusinessValidationFailedException("该标准任务还未定义PD获取逻辑,联系业务和后台开发协商后补充"); + } + + + + return answer; + + + + + } + + + /// + /// 选择人员下拉 + /// + /// + /// + public async Task> GetTrialUserTypeSelectList(Guid trialId) + { + + var query = _trialUserRepository.Where(t => t.TrialId == trialId, false, true).IgnoreQueryFilters().Select(t => t.User.UserTypeRole).Distinct() + + .ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToListAsync(); + + } + + [HttpPost] + public async Task> GetTrialEmailNoticeConfigList(TrialEmailNoticeConfigQuery inQuery) + { + await SyncSystemEmainCofigDocListAsync(inQuery.TrialId); + + var trialEmailNoticeConfigQueryable = _trialEmailNoticeConfigRepository.Where(t => t.TrialId == inQuery.TrialId) + .WhereIf(inQuery.IsDistinguishCriteria == false, t => t.TrialReadingCriterionId == null) + .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) + .WhereIf(inQuery.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == inQuery.BusinessScenarioEnum) + .ProjectTo(_mapper.ConfigurationProvider); + + return await trialEmailNoticeConfigQueryable.ToListAsync(); + } + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddOrUpdateTrialEmailNoticeConfig(TrialEmailNoticeConfigAddOrEdit addOrEditTrialEmailNoticeConfig) + { + await TestEmailConfigAsync(addOrEditTrialEmailNoticeConfig); + + + if (addOrEditTrialEmailNoticeConfig.Id == null) + { + + var entity = _mapper.Map(addOrEditTrialEmailNoticeConfig); + + + foreach (var item in addOrEditTrialEmailNoticeConfig.ToUserTypeList) + { + entity.TrialEmailNoticeUserList.Add(new TrialEmailNoticeUser() { EmailUserType = EmailUserType.To, UserType = item }); + + } + + foreach (var item in addOrEditTrialEmailNoticeConfig.CopyUserTypeList) + { + entity.TrialEmailNoticeUserList.Add(new TrialEmailNoticeUser() { EmailUserType = EmailUserType.Copy, UserType = item }); + + } + + + await _trialEmailNoticeConfigRepository.AddAsync(entity, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + + + } + else + { + await _repository.BatchDeleteAsync(t => t.TrialEmailNoticeConfigId == addOrEditTrialEmailNoticeConfig.Id); + + + var entity = (await _trialEmailNoticeConfigRepository.Where(t => t.Id == addOrEditTrialEmailNoticeConfig.Id, true, true).Include(t => t.TrialEmailNoticeUserList).FirstOrDefaultAsync()).IfNullThrowException(); + + + List trialEmailNoticeUsers = new List(); + + + foreach (var item in addOrEditTrialEmailNoticeConfig.ToUserTypeList) + { + trialEmailNoticeUsers.Add(new TrialEmailNoticeUser() { EmailUserType = EmailUserType.To, UserType = item, TrialEmailNoticeConfigId = entity.Id }); + + } + + foreach (var item in addOrEditTrialEmailNoticeConfig.CopyUserTypeList) + { + trialEmailNoticeUsers.Add(new TrialEmailNoticeUser() { EmailUserType = EmailUserType.Copy, UserType = item, TrialEmailNoticeConfigId = entity.Id }); + + } + await _repository.AddRangeAsync(trialEmailNoticeUsers); + + await _trialEmailNoticeConfigRepository.UpdateFromDTOAsync(addOrEditTrialEmailNoticeConfig); + + + await _trialEmailNoticeConfigRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + } + + private async Task TestEmailConfigAsync(TrialEmailNoticeConfigAddOrEdit config) + { + var toUserList = await _repository.Where(t => t.TrialId == config.TrialId) + .WhereIf(config.ToUserTypeList != null, t => config.ToUserTypeList.Contains(t.User.UserTypeEnum)) + .Select(t => new { t.User.EMail, t.User.FullName }).ToListAsync(); + + + if (!config.FromEmail.Contains("@") || string.IsNullOrEmpty(config.FromEmail)) + { + throw new BusinessValidationFailedException("项目发件邮箱配置有误,请核实"); + } + + + if (toUserList.Count() == 0 || toUserList.Where(t => t.EMail.Contains("@")).Count() == 0) + { + throw new BusinessValidationFailedException("项目没有有效的收件人,无法发送邮件"); + } + + var sendEmailConfig = new SMTPEmailConfig(); + + sendEmailConfig.FromEmailAddress = new MimeKit.MailboxAddress(config.FromName, config.FromEmail); + sendEmailConfig.AuthorizationCode = config.AuthorizationCode; + sendEmailConfig.UserName = config.FromEmail; + + sendEmailConfig.Host = config.SMTPServerAddress; + sendEmailConfig.Port = config.SMTPServerPort; + + + sendEmailConfig.ToMailAddressList.Add(new MimeKit.MailboxAddress(config.FromName, config.FromEmail)); + + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + "EmailConfigTest.html"; + + + sendEmailConfig.TopicDescription = "项目邮件测试"; + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + + sendEmailConfig.HtmlBodyStr = string.Format(templateInfo, + $" 收到此邮件,代表邮件配置正确" + ); + } + + try + { + await SendEmailHelper.SendEmailAsync(sendEmailConfig); + + } + catch (Exception ex) + { + + throw new BusinessValidationFailedException("发件人配置错误,请核对服务器地址或者授权码是否填写有误" + ex.Message); + } + + + } + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [HttpDelete("{trialEmailNoticeConfigId:guid}")] + public async Task DeleteTrialEmailNoticeConfig(Guid trialEmailNoticeConfigId) + { + var success = await _trialEmailNoticeConfigRepository.DeleteFromQueryAsync(t => t.Id == trialEmailNoticeConfigId, true); + + return ResponseOutput.Ok(); + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Document/_MapConfig.cs b/IRaCIS.Core.Application/Service/Document/_MapConfig.cs new file mode 100644 index 0000000..61a8836 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Document/_MapConfig.cs @@ -0,0 +1,91 @@ +using AutoMapper; +using AutoMapper.EquivalencyExpression; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Application.Service +{ + public class DocumentConfig : Profile + { + public DocumentConfig() + { + + var userId = Guid.Empty; + var token = string.Empty; + CreateMap() + .ForMember(d => d.FileType, u => u.MapFrom(s => s.FileType.Value)) + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path)); + + CreateMap() + .ForMember(d => d.FileType, u => u.MapFrom(s => s.FileType.MappedValue)) + .ForMember(d => d.IsSomeUserSigned, u => u.MapFrom(s => s.TrialDocConfirmedUserList.Any())) + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path )); + + + CreateMap() + .ForMember(d => d.IsSystemDoc, u => u.MapFrom(s => true)) + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path )); + + CreateMap() + .ForMember(d => d.IsSystemDoc, u => u.MapFrom(s => false)) + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path )); + + CreateMap().ForMember(d => d.UserTypeShortName, t => t.MapFrom(c => c.UserTypeRole.UserTypeShortName)); + CreateMap().ForMember(d => d.UserTypeShortName, t => t.MapFrom(c => c.UserTypeRole.UserTypeShortName)); + + + //CreateMap() + // .ForMember(t => t.UserConfirmInfo, c => c.MapFrom(t => t.TrialDocConfirmedUserList.Where(u => u.ConfirmUserId == userId).FirstOrDefault())) + // .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path + "?access_token=" + token)); ; + + CreateMap() + .ForMember(d => d.UserName, c => c.MapFrom(t => t.User.UserName)) + .ForMember(d => d.RealName, c => c.MapFrom(t => t.User.FullName)); + + //CreateMap() + // .ForMember(d => d.UserName, c => c.MapFrom(t => t.User.UserName)) + // .ForMember(d => d.RealName, c => c.MapFrom(t => t.User.LastName + " / " + t.User.FirstName)); + + + CreateMap(); + + + + + CreateMap() + .ForMember(d => d.NeedConfirmedUserTypeList, c => c.MapFrom(t => t.NeedConfirmedUserTypeIdList)); + + CreateMap().EqualityComparison((odto, o) => odto == o.NeedConfirmUserTypeId) + .ForMember(d => d.NeedConfirmUserTypeId, c => c.MapFrom(t => t)) + .ForMember(d => d.TrialDocumentId, c => c.Ignore()); + + + + CreateMap().ForMember(d => d.NeedConfirmedUserTypeList, c => c.MapFrom(t => t.NeedConfirmedUserTypeIdList)); + CreateMap().EqualityComparison((odto, o) => odto == o.NeedConfirmUserTypeId) + .ForMember(d => d.NeedConfirmUserTypeId, c => c.MapFrom(t => t)) + .ForMember(d => d.SystemDocumentId, c => c.Ignore()); + + + CreateMap() + + .ForMember(d => d.TrialCriterionName, c => c.MapFrom(t => t.TrialReadingCriterion.CriterionName)) + .ForMember(d => d.TrialEmailNoticeUserList, c => c.MapFrom(t => t.TrialEmailNoticeUserList)); + + CreateMap(); + + CreateMap() + .ForMember(d => d.Phone, c => c.MapFrom(t => t.User.Phone)) + .ForMember(d => d.Email, c => c.MapFrom(t => t.User.EMail)) + .ForMember(d => d.RealName, c => c.MapFrom(t => t.User.FullName)) + .ForMember(d => d.UserName, c => c.MapFrom(t => t.User.UserName)); + + + CreateMap(); + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/Financial/CalculateService.cs b/IRaCIS.Core.Application/Service/Financial/CalculateService.cs new file mode 100644 index 0000000..d4c4c1c --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/CalculateService.cs @@ -0,0 +1,713 @@ +using AutoMapper; +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Application.Contracts.Pay; +using IRaCIS.Core.Domain.Share; +using System.Linq.Expressions; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure.Extention; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Application.Services +{ + public class CalculateService : ICalculateService + { + private readonly IRepository _paymentRepository; + private readonly IRepository _trialPaymentRepository; + private readonly IRepository _doctorPayInfoRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _doctorWorkloadRepository; + private readonly IRepository _rankPriceRepository; + private readonly IRepository _paymentDetailRepository; + private readonly IVolumeRewardService _volumeRewardPriceService; + private readonly IRepository _exchangeRateRepository; + private readonly IRepository _payAdjustmentRepository; + private readonly IRepository _enrollRepository; + private readonly IMapper _mapper; + + public CalculateService(IRepository paymentRepository, IRepository trialPaymentPriceRepository, + IRepository reviewerPayInfoRepository, + IRepository trialRepository, + IRepository doctorRepository, + IRepository workloadRepository, + IRepository rankPriceRepository, + IRepository paymentDetailRepository, + IVolumeRewardService volumeRewardService, + IRepository exchangeRateRepository, + IRepository EnrollRepository, + IRepository paymentAdjustmentRepository, IMapper mapper) + { + _paymentRepository = paymentRepository; + _trialPaymentRepository = trialPaymentPriceRepository; + _doctorPayInfoRepository = reviewerPayInfoRepository; + _trialRepository = trialRepository; + _doctorRepository = doctorRepository; + _doctorWorkloadRepository = workloadRepository; + _rankPriceRepository = rankPriceRepository; + _paymentDetailRepository = paymentDetailRepository; + _volumeRewardPriceService = volumeRewardService; + _exchangeRateRepository = exchangeRateRepository; + _payAdjustmentRepository = paymentAdjustmentRepository; + this._enrollRepository = EnrollRepository; + _mapper = mapper; + } + + /// + /// 获取某个月下的某些医生最终确认的工作量,用于计算月度费用 + /// + private async Task< List> GetFinalConfirmedWorkloadAndPayPriceList(CalculateDoctorAndMonthDTO calculateFeeParam) + { + Expression> workloadLambda = x => true; + + DateTime bTime = new DateTime(calculateFeeParam.CalculateMonth.Year, calculateFeeParam.CalculateMonth.Month, 1); + var eTime = bTime.AddMonths(1); + workloadLambda = workloadLambda.And(t => + t.WorkTime >= bTime && t.WorkTime < eTime); + + workloadLambda = workloadLambda.And(t => calculateFeeParam.NeedCalculateReviewers.Contains(t.DoctorId) && t.DataFrom == (int)WorkLoadFromStatus.FinalConfirm); + var workLoadQueryable = from doctor in _doctorRepository.AsQueryable() + join workLoad in _doctorWorkloadRepository.Where(workloadLambda) on + doctor.Id equals workLoad.DoctorId + join trial in _trialRepository.AsQueryable() on workLoad.TrialId equals trial.Id + join trialPay in _trialPaymentRepository.AsQueryable() on trial.Id equals trialPay.TrialId + into temp + from trialPay in temp.DefaultIfEmpty() + join doctorPayInfo in _doctorPayInfoRepository.AsQueryable() on doctor.Id equals doctorPayInfo.DoctorId + join rankPrice in _rankPriceRepository.AsQueryable() on doctorPayInfo.RankId equals rankPrice.Id + select new CalculatePaymentDTO() + { + Id = workLoad.Id, + DoctorId = workLoad.DoctorId, + WorkTime = workLoad.WorkTime, + DataFrom = workLoad.DataFrom, + TrialId = workLoad.TrialId, + TrialCode = trial.TrialCode, + + Timepoint = workLoad.Timepoint, + TimepointIn24H = workLoad.TimepointIn24H, + TimepointIn48H = workLoad.TimepointIn48H, + Global = workLoad.Global, + Adjudication = workLoad.Adjudication, + AdjudicationIn24H = workLoad.AdjudicationIn24H, + AdjudicationIn48H = workLoad.AdjudicationIn48H, + Training = workLoad.Training, + RefresherTraining = workLoad.RefresherTraining, + Downtime = workLoad.Downtime, + + TrialAdditional = trialPay.TrialAdditional, + PersonalAdditional = doctorPayInfo.Additional, + AdjustmentMultiple = trialPay.AdjustmentMultiple, + + TimepointPrice = rankPrice.Timepoint, + TimepointIn24HPrice = rankPrice.TimepointIn24H, + TimepointIn48HPrice = rankPrice.TimepointIn48H, + AdjudicationPrice = rankPrice.Adjudication, + AdjudicationIn24HPrice = rankPrice.AdjudicationIn24H, + AdjudicationIn48HPrice = rankPrice.AdjudicationIn48H, + DowntimePrice = rankPrice.Downtime, + GlobalPrice = rankPrice.Global, + TrainingPrice = rankPrice.Training, + RefresherTrainingPrice = rankPrice.RefresherTraining + }; + + return await workLoadQueryable.ToListAsync(); + } + + /// + /// 计算月度费用,并调用AddOrUpdateMonthlyPayment和AddOrUpdateMonthlyPaymentDetail方法, + /// 将费用计算的月度数据及详情保存 + /// + [NonDynamicMethod] + public async Task CalculateMonthlyPayment(CalculateDoctorAndMonthDTO param, string token) + { + var yearMonth = param.CalculateMonth.ToString("yyyy-MM"); + var rate = await _exchangeRateRepository.FirstOrDefaultAsync(u => u.YearMonth == yearMonth); + + decimal exchangeRate = rate?.Rate ?? 0; + + var workLoadAndPayPriceList = await GetFinalConfirmedWorkloadAndPayPriceList(param); + var volumeRewardPriceList = await _volumeRewardPriceService.GetVolumeRewardPriceList(); + + #region 奖励数据校验 + for (int i = 0; i < volumeRewardPriceList.Count; i++) + { + if (i == 0 && volumeRewardPriceList[i].Min != 0) + { + return ResponseOutput.NotOk("Volume reward data error."); + } + if (i > 0) + { + if (volumeRewardPriceList[i - 1].Max + 1 != volumeRewardPriceList[i].Min) + return ResponseOutput.NotOk("Volume reward data error."); + } + } + + + #endregion + + + List paymentList = new List(); + List reviewerPaymentUSDList = new List(); + + // 获取所有医生费用 一次从数据库里面全部取出来 + + var allDoctorList = workLoadAndPayPriceList.Where(x => param.NeedCalculateReviewers.Contains(x.DoctorId)).ToList(); + var allDoctorIds = allDoctorList.Select(x => x.DoctorId).Distinct().ToList(); + var listTrialId = allDoctorList.Select(x => x.TrialId).Distinct().ToList(); + + var trialDoctorlist= await (from enroll in _enrollRepository.Where(x=> listTrialId.Contains(x.TrialId)|| allDoctorIds.Contains(x.DoctorId)) + join price in _trialPaymentRepository.Where() on enroll.TrialId equals price.TrialId + select new DoctorPrice() + { + IsNewTrial = price.IsNewTrial, + AdjustmentMultiple = enroll.AdjustmentMultiple, + TrialId=enroll.TrialId, + DoctorId = enroll.DoctorId, + Training=enroll.Training, + Adjudication=enroll.Adjudication, + Adjudication24H=enroll.Adjudication24H, + Adjudication48H= enroll.Adjudication48H, + Downtime=enroll.Downtime, + Global=enroll.Global, + RefresherTraining=enroll.RefresherTraining, + Timepoint= enroll.Timepoint, + Timepoint24H=enroll.Timepoint24H, + Timepoint48H=enroll.Timepoint48H, + }).ToListAsync(); + + + foreach (var doctor in param.NeedCalculateReviewers) + { + if (await _paymentRepository.AnyAsync(u => u.DoctorId == doctor && u.YearMonth == yearMonth && u.IsLock)) + { + break; + } + List paymentDetailList = new List(); + decimal totalNormal = 0; + + //计算单个医生费用统,并且插入到统计表 + var doctorWorkloadAndPayPriceList = workLoadAndPayPriceList.Where(u => u.DoctorId == doctor).ToList(); + + //阅片数量 计算奖励费用 + int readCount = 0; + + int codeOrder = 0; + + //这里需要改 + + foreach (var item in doctorWorkloadAndPayPriceList) + { + var doctordata = trialDoctorlist.Where(x => x.IsNewTrial ?? false && x.Training == item.Training && x.DoctorId == item.DoctorId).FirstOrDefault(); + + if (doctordata != null) + { + item.Training = doctordata.Training??0; + item.Adjudication = doctordata.Adjudication??0; + item.AdjudicationIn24H = doctordata.Adjudication24H??0; + item.AdjudicationIn48H = doctordata.Adjudication48H??0; + item.Downtime = doctordata.Downtime??0; + item.Global = doctordata.Global??0; + item.RefresherTraining = doctordata.RefresherTraining??0; + item.Timepoint = doctordata.Timepoint??0; + item.TimepointIn24H = doctordata.Timepoint24H??0; + item.TimepointIn48H = doctordata.Timepoint48H??0; + item.PersonalAdditional = 0; + } + ++codeOrder; + readCount += (item.Timepoint + item.TimepointIn24H + item.TimepointIn48H + + item.Adjudication + item.AdjudicationIn24H + item.AdjudicationIn48H); + decimal trainingTotal = item.Training * item.TrainingPrice; + decimal refresherTrainingTotal = item.RefresherTraining * item.RefresherTrainingPrice; + decimal downtimeTotal = item.Downtime * item.DowntimePrice; + //规则定义 global 的价格是Tp和个人附加的一半 + decimal globalTotal = item.Global * (item.TimepointPrice / 2 + item.PersonalAdditional / 2); + + //项目如果没有添加附加数据 默认为0 + decimal timePointTotal = item.Timepoint * (item.TimepointPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional)); + + decimal timePointIn24HTotal = item.TimepointIn24H * (item.TimepointIn24HPrice * item.AdjustmentMultiple + item.PersonalAdditional); + decimal timePointIn48HTotal = item.TimepointIn48H * (item.TimepointIn48HPrice * item.AdjustmentMultiple + item.PersonalAdditional); + decimal adjudicationTotal = item.Adjudication * (item.AdjudicationPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional)); + decimal adjudicationIn24HTotal = item.AdjudicationIn24H * (item.AdjudicationIn24HPrice * item.AdjustmentMultiple + item.PersonalAdditional); + decimal adjudicationIn48HTotal = item.AdjudicationIn48H * (item.AdjudicationIn48HPrice * item.AdjustmentMultiple + item.PersonalAdditional); + + totalNormal += (trainingTotal + refresherTrainingTotal + downtimeTotal + globalTotal + timePointTotal + timePointIn24HTotal + + timePointIn48HTotal + adjudicationTotal + adjudicationIn24HTotal + adjudicationIn48HTotal); + + #region 统计明细信息 + + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = item.TrialCode, + PaymentType = "Training", + Count = item.Training, + BasePrice = item.TrainingPrice, + PersonalAdditional = 0, + TrialAdditional = 0, + PaymentId = Guid.Empty, + DoctorId = item.DoctorId, + TrialId = item.TrialId, + ShowTypeOrder = 1, + ShowCodeOrder = codeOrder, + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = item.Training * item.TrainingPrice, + PaymentCNY = item.Training * item.TrainingPrice * exchangeRate + }); + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = item.TrialCode, + PaymentType = "Refresher Training", + Count = item.RefresherTraining, + BasePrice = item.RefresherTrainingPrice, + PersonalAdditional = 0, + TrialAdditional = 0, + PaymentId = Guid.Empty, + DoctorId = item.DoctorId, + TrialId = item.TrialId, + ShowTypeOrder = 2, + ShowCodeOrder = codeOrder, + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = item.RefresherTraining * item.RefresherTrainingPrice, + PaymentCNY = item.RefresherTraining * item.RefresherTrainingPrice * exchangeRate + }); + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = item.TrialCode, + PaymentType = "Downtime", + Count = item.Downtime, + BasePrice = item.DowntimePrice, + PersonalAdditional = 0, + TrialAdditional = 0, + PaymentId = Guid.Empty, + DoctorId = item.DoctorId, + TrialId = item.TrialId, + ShowTypeOrder = 3, + ShowCodeOrder = codeOrder, + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = item.Downtime * item.DowntimePrice, + PaymentCNY = item.Downtime * item.DowntimePrice * exchangeRate + }); + + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = item.TrialCode, + PaymentType = "Timepoint Regular", + Count = item.Timepoint, + BasePrice = item.TimepointPrice, + PersonalAdditional = doctordata!=null?0: item.PersonalAdditional, + TrialAdditional = doctordata != null ? 0 : item.TimepointPrice * (item.AdjustmentMultiple - 1) + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional), + PaymentId = Guid.Empty, + DoctorId = item.DoctorId, + TrialId = item.TrialId, + ShowTypeOrder = 4, + ShowCodeOrder = codeOrder, + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = item.Timepoint * (item.TimepointPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional)), + PaymentCNY = item.Timepoint * (item.TimepointPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional)) * exchangeRate + }); + + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = item.TrialCode, + PaymentType = "Timepoint 48-Hour", + Count = item.TimepointIn48H, + BasePrice = item.TimepointIn48HPrice, + PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional, + TrialAdditional = doctordata != null ? 0 : item.TimepointIn48HPrice * (item.AdjustmentMultiple - 1) + 0,//48小时不加项目附加 + PaymentId = Guid.Empty, + DoctorId = item.DoctorId, + TrialId = item.TrialId, + ShowTypeOrder = 5, + ShowCodeOrder = codeOrder, + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = item.TimepointIn48H * (item.TimepointIn48HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional), + PaymentCNY = item.TimepointIn48H * (item.TimepointIn48HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional) * exchangeRate + }); + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = item.TrialCode, + PaymentType = "Timepoint 24-Hour", + Count = item.TimepointIn24H, + BasePrice = item.TimepointIn24HPrice, + PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional, + TrialAdditional = doctordata != null ? 0 : item.TimepointIn24HPrice * (item.AdjustmentMultiple - 1) + 0, + PaymentId = Guid.Empty, + DoctorId = item.DoctorId, + TrialId = item.TrialId, + ShowTypeOrder = 6, + ShowCodeOrder = codeOrder, + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = item.TimepointIn24H * (item.TimepointIn24HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional), + PaymentCNY = item.TimepointIn24H * (item.TimepointIn24HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional) * exchangeRate + }); + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = item.TrialCode, + PaymentType = "Adjudication Regular", + Count = item.Adjudication, + BasePrice = item.AdjudicationPrice, + PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional, + TrialAdditional = doctordata != null ? 0 : item.AdjudicationPrice * (item.AdjustmentMultiple - 1) + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional), + PaymentId = Guid.Empty, + DoctorId = item.DoctorId, + TrialId = item.TrialId, + ShowTypeOrder = 7, + ShowCodeOrder = codeOrder, + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = item.Adjudication * (item.AdjudicationPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional)), + PaymentCNY = item.Adjudication * (item.AdjudicationPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional)) * exchangeRate + }); + + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = item.TrialCode, + PaymentType = "Adjudication 48-Hour", + Count = item.AdjudicationIn48H, + BasePrice = item.AdjudicationIn48HPrice, + PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional, + TrialAdditional = doctordata != null ? 0 : item.AdjudicationIn48HPrice * (item.AdjustmentMultiple - 1) + 0, + PaymentId = Guid.Empty, + DoctorId = item.DoctorId, + TrialId = item.TrialId, + ShowTypeOrder = 8, + ShowCodeOrder = codeOrder, + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = item.AdjudicationIn48H * (item.AdjudicationIn48HPrice * item.AdjustmentMultiple + item.PersonalAdditional + 0), + PaymentCNY = item.AdjudicationIn48H * (item.AdjudicationIn48HPrice * item.AdjustmentMultiple + item.PersonalAdditional + 0) * exchangeRate + }); + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = item.TrialCode, + PaymentType = "Adjudication 24-Hour", + Count = item.AdjudicationIn24H, + BasePrice = item.AdjudicationIn24HPrice, + PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional, + TrialAdditional = doctordata != null ? 0 : item.AdjudicationIn24HPrice * (item.AdjustmentMultiple - 1) + 0, + PaymentId = Guid.Empty, + DoctorId = item.DoctorId, + TrialId = item.TrialId, + ShowTypeOrder = 9, + ShowCodeOrder = codeOrder, + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = item.AdjudicationIn24H * (item.AdjudicationIn24HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional), + PaymentCNY = item.AdjudicationIn24H * (item.AdjudicationIn24HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional) * exchangeRate + }); + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = item.TrialCode, + PaymentType = "Global", + Count = item.Global, + BasePrice = item.TimepointPrice / 2,//item.GlobalPrice, + PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional / 2, + TrialAdditional = 0, + PaymentId = Guid.Empty, + DoctorId = item.DoctorId, + TrialId = item.TrialId, + ShowTypeOrder = 10, + ShowCodeOrder = codeOrder, + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = item.Global * (item.TimepointPrice / 2 + item.PersonalAdditional / 2), + PaymentCNY = item.Global * (item.TimepointPrice / 2 + item.PersonalAdditional / 2) * exchangeRate + }); + #endregion + } + + int typeOrder = 0; + if (readCount > 0) + { + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = "Volume Reward", + PaymentType = "Total TP & AD", + Count = readCount, + BasePrice = 0, + PersonalAdditional = 0, + TrialAdditional = 0, + PaymentId = Guid.Empty, + DoctorId = doctor, + TrialId = Guid.Empty, + ShowTypeOrder = typeOrder, + ShowCodeOrder = (++codeOrder), + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = 0, + PaymentCNY = 0 + }); + + foreach (var awardItem in volumeRewardPriceList) + { + ++typeOrder; + if ((readCount - awardItem.Min + 1) < 0) + { + break; + } + if (awardItem.Min == 0) + { + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = "Volume Reward", + PaymentType = awardItem.Min + "-" + awardItem.Max, + Count = readCount >= awardItem.Max ? + (awardItem.Max - awardItem.Min) : (readCount - awardItem.Min), + BasePrice = awardItem.Price, + PersonalAdditional = 0, + TrialAdditional = 0, + PaymentId = Guid.Empty,//result.Data, + DoctorId = doctor, + TrialId = Guid.Empty, + ShowTypeOrder = typeOrder, + ShowCodeOrder = (++codeOrder), + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = (readCount >= awardItem.Max ? + (awardItem.Max - awardItem.Min) : (readCount - awardItem.Min)) * awardItem.Price, + PaymentCNY = (readCount >= awardItem.Max ? + (awardItem.Max - awardItem.Min) : (readCount - awardItem.Min)) * awardItem.Price * exchangeRate + }); + } + else + { + paymentDetailList.Add(new PaymentDetailCommand + { + TrialCode = "Volume Reward", + PaymentType = awardItem.Min + "-" + awardItem.Max, + Count = readCount >= awardItem.Max ? + (awardItem.Max - awardItem.Min + 1) : (readCount - awardItem.Min + 1), + BasePrice = awardItem.Price, + PersonalAdditional = 0, + TrialAdditional = 0, + PaymentId = Guid.Empty, + DoctorId = doctor, + TrialId = Guid.Empty, + ShowTypeOrder = typeOrder, + ShowCodeOrder = (++codeOrder), + ExchangeRate = exchangeRate, + YearMonth = yearMonth, + PaymentUSD = (readCount >= awardItem.Max ? + (awardItem.Max - awardItem.Min + 1) : (readCount - awardItem.Min + 1)) * awardItem.Price, + PaymentCNY = (readCount >= awardItem.Max ? + (awardItem.Max - awardItem.Min + 1) : (readCount - awardItem.Min + 1)) * awardItem.Price * exchangeRate + }); + } + } + + } + decimal award = 0; + volumeRewardPriceList = volumeRewardPriceList.OrderBy(u => u.Min).ToList(); + + var levelTemp = -1; //用来计算属于哪一个挡位 + foreach (var awarPriceitem in volumeRewardPriceList) + { + if (awarPriceitem.Min == 0) + { + if (readCount > awarPriceitem.Max) + { + ++levelTemp; + award += (awarPriceitem.Max - awarPriceitem.Min) * awarPriceitem.Price; + } + if (awarPriceitem.Min < readCount && readCount < awarPriceitem.Max) + { + ++levelTemp; + award += (readCount - awarPriceitem.Min) * awarPriceitem.Price; + break; ; + } + } + else + { + if (readCount > awarPriceitem.Max) + { + ++levelTemp; + award += (awarPriceitem.Max - awarPriceitem.Min + 1) * awarPriceitem.Price; + } + if (awarPriceitem.Min < readCount && readCount < awarPriceitem.Max) + { + ++levelTemp; + award += (readCount - awarPriceitem.Min + 1) * awarPriceitem.Price; + break; ; + } + } + } + decimal totalUSD = award + totalNormal;//总费用 + + var result = await AddOrUpdateMonthlyPayment(new PaymentCommand + { + DoctorId = doctor, + Year = param.CalculateMonth.Year, + Month = param.CalculateMonth.Month, + PaymentUSD = totalUSD, + CalculateUser = token, + CalculateTime = DateTime.Now, + ExchangeRate = exchangeRate, + PaymentCNY = exchangeRate * totalUSD, + }); + + + reviewerPaymentUSDList.Add(new ReviewerPaymentUSD { DoctorId = doctor, PaymentUSD = totalUSD, RecordId = result.Data }); + foreach (var detail in paymentDetailList) + { + //var data = trialDoctorlist.FirstOrDefault(x => x.DoctorId == detail.DoctorId && x.TrialId == detail.TrialId && x.IsNewTrial == true && (x.AdjustmentMultiple??0) != 0); + //if (data != null) + //{ + // detail.BasePrice = data.AdjustmentMultiple??0; + // detail.PersonalAdditional = 0; + // detail.TrialAdditional = 0; + //} + + detail.PaymentId = result.Data; + } + await AddOrUpdateMonthlyPaymentDetail(paymentDetailList, result.Data); + + await UpdatePaymentAdjustment(doctor, yearMonth); + } + return ResponseOutput.Ok(reviewerPaymentUSDList); + } + + + + + // 重新计算调整费用 + + private async Task UpdatePaymentAdjustment(Guid reviewerId, string yearMonth) + { + var adjustList = await _payAdjustmentRepository.Where(u => u.YearMonth == yearMonth && + !u.IsLock && u.ReviewerId == reviewerId).ToListAsync(); + + + var needUpdatePayment = adjustList.GroupBy(t => t.ReviewerId).Select(g => new + { + ReviewerId = g.Key, + AdjustCNY = g.Sum(t => t.AdjustmentCNY), + AdjustUSD = g.Sum(t => t.AdjustmentUSD) + }); + + foreach (var reviewer in needUpdatePayment) + { + await _paymentRepository.BatchUpdateNoTrackingAsync(u => u.YearMonth == yearMonth && + !u.IsLock && u.DoctorId == reviewer.ReviewerId, t => new Payment() + { + AdjustmentUSD = reviewer.AdjustUSD, + AdjustmentCNY = reviewer.AdjustCNY + + }); + } + } + + /// + /// 保存费用计算的月度数据 + /// + private async Task> AddOrUpdateMonthlyPayment(PaymentCommand addOrUpdateModel) + { + var success = false; + var paymentModel = await _paymentRepository.FirstOrDefaultAsync(t => + t.DoctorId == addOrUpdateModel.DoctorId && t.YearMonth == addOrUpdateModel.YearMonth); + + + + //var taxCNY = GetTax(addOrUpdateModel.PaymentCNY); + //var actuallyPaidCNY = addOrUpdateModel.PaymentCNY - taxCNY; + //var bankTransferCNY = addOrUpdateModel.PaymentCNY - taxCNY; + + if (paymentModel == null) + { + var payment = _mapper.Map(addOrUpdateModel); + + //payment.BankTransferCNY = bankTransferCNY; + //payment.TaxCNY= taxCNY; + //payment.BankTransferCNY = bankTransferCNY; + payment.YearMonthDate = DateTime.Parse(payment.YearMonth); + + payment =await _paymentRepository.AddAsync(payment); + success =await _paymentRepository.SaveChangesAsync(); + return ResponseOutput.Result(success, payment.Id); + } + else + { + // 如果是 当月计算的工作量费用 和 调整费用都为0,则删除该行记录 + if (addOrUpdateModel.PaymentUSD == 0 && paymentModel.AdjustmentUSD == 0) + { + success =await _paymentRepository.BatchDeleteNoTrackingAsync(u => u.Id == paymentModel.Id); + //_paymentDetailRepository.Delete(u=>u.PaymentId==paymentModel.Id); + } + else + { + success = await _paymentRepository.BatchUpdateNoTrackingAsync(t => t.Id == paymentModel.Id, u => new Payment() + { + PaymentUSD = addOrUpdateModel.PaymentUSD, + CalculateTime = addOrUpdateModel.CalculateTime, + CalculateUser = addOrUpdateModel.CalculateUser, + //TaxCNY = taxCNY, + //ActuallyPaidCNY = actuallyPaidCNY, + //BankTransferCNY = bankTransferCNY, + PaymentCNY = addOrUpdateModel.PaymentCNY, + ExchangeRate = addOrUpdateModel.ExchangeRate + }); + } + + return ResponseOutput.Result(success, paymentModel.Id); + } + + } + + + + + /// + /// 保存费用计算的月度详情 + /// + private async Task AddOrUpdateMonthlyPaymentDetail(List addOrUpdateList, Guid paymentId) + { + //var paymentDetailIds = addOrUpdateList.Select(t => t.PaymentId).ToList(); + await _paymentDetailRepository.BatchDeleteNoTrackingAsync(t => t.PaymentId == paymentId); + await _paymentDetailRepository.AddRangeAsync(_mapper.Map>(addOrUpdateList)); + return await _paymentDetailRepository.SaveChangesAsync(); + } + + + + /// + /// 获取待计算费用的Reviewer对应的月份列表 + /// + public async Task> GetNeedCalculateReviewerList(Guid reviewerId, string yearMonth) + { + Expression> calculateLambda = u => !u.IsLock; + if (reviewerId != Guid.Empty) + { + calculateLambda = calculateLambda.And(u => u.DoctorId == reviewerId); + } + if (!string.IsNullOrWhiteSpace(yearMonth)) + { + calculateLambda = calculateLambda.And(u => u.YearMonth == yearMonth); + } + return await _paymentRepository.Where(calculateLambda).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + /// + /// 查询Reviewer某个月的费用是否被锁定 + /// + public async Task IsLock(Guid reviewerId, string yearMonth) + { + return await _paymentRepository.AnyAsync(u => u.DoctorId == reviewerId && u.YearMonth == yearMonth && u.IsLock); + } + + //public bool ResetMonthlyPayment(Guid reviewerId, Guid trialId, string yearMonth) + //{ + // var payment = _paymentRepository.FindSingleOrDefault(u => u.DoctorId == reviewerId && u.YearMonth == yearMonth); + // payment.PaymentCNY = 0; + // payment.PaymentUSD = 0; + // _paymentRepository.Update(payment); + // _paymentDetailRepository.Delete(u=>u.DoctorId==reviewerId && u.TrialId==trial) + //} + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/DTO/AwardPriceModel.cs b/IRaCIS.Core.Application/Service/Financial/DTO/AwardPriceModel.cs new file mode 100644 index 0000000..2dfffd2 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/DTO/AwardPriceModel.cs @@ -0,0 +1,37 @@ +using System; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public class AwardPriceDTO: AwardPriceCalculateDTO + { + public Guid Id { get; set; } + } + + public class AwardPriceCalculateDTO + { + public decimal Price { get; set; } + public int Max { get; set; } + public int Min { get; set; } + } + + public class AwardPriceCommand + { + //public Guid Id { get; set; } + public decimal Price { get; set; } + public int Min { get; set; } + public int Max { get; set; } + public Guid OptUserId { get; set; } + } + + public class AwardPriceQueryDTO : PageInput + { + } + + public class ExchangeRateQueryDTO : PageInput + { + public DateTime? SearchMonth { get; set; } + } + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/DTO/CalculateModel.cs b/IRaCIS.Core.Application/Service/Financial/DTO/CalculateModel.cs new file mode 100644 index 0000000..4d82f74 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/DTO/CalculateModel.cs @@ -0,0 +1,44 @@ +using System; + +namespace IRaCIS.Application.Contracts +{ + public class CalculateNeededDTO + { + public Guid Id { get; set; } + public Guid DoctorId { get; set; } + public string YearMonth { get; set; } = String.Empty; + public bool IsLock { get; set; } + } + + public class DoctorPrice + { + public decimal? AdjustmentMultiple { get; set; } + + public Guid? DoctorId { get; set; } + + public Guid? TrialId { get; set; } + + public bool? IsNewTrial { get; set; } + + public int? Training { get; set; } + + public int? RefresherTraining { get; set; } + + public int? Timepoint { get; set; } + + public int? Timepoint48H { get; set; } + + public int? Timepoint24H { get; set; } + + public int? Adjudication { get; set; } + + public int? Adjudication48H { get; set; } + + public int? Adjudication24H { get; set; } + + public int? Global { get; set; } + + + public int? Downtime { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/DTO/ExchangeRateModel.cs b/IRaCIS.Core.Application/Service/Financial/DTO/ExchangeRateModel.cs new file mode 100644 index 0000000..a56b4c1 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/DTO/ExchangeRateModel.cs @@ -0,0 +1,13 @@ +using System; + +namespace IRaCIS.Application.Contracts +{ + public class ExchangeRateCommand + { + public Guid? Id { get; set; } + public string YearMonth { get; set; }=String.Empty; + public decimal Rate { get; set; } + + public DateTime UpdateTime { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/DTO/PaymentAdjustmentModel.cs b/IRaCIS.Core.Application/Service/Financial/DTO/PaymentAdjustmentModel.cs new file mode 100644 index 0000000..1a39923 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/DTO/PaymentAdjustmentModel.cs @@ -0,0 +1,54 @@ +using System; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts.Pay +{ + public class PaymentAdjustmentCommand + { + public Guid? Id { get; set; } + public Guid ReviewerId { get; set; } + public DateTime YearMonth { get; set; } + public decimal AdjustPaymentUSD { get; set; } + public decimal AdjustPaymentCNY { get; set; } + public string Note { get; set; } = string.Empty; + } + + public class PaymentAdjustmentDTO + { + public Guid Id { get; set; } + public Guid ReviewerId { get; set; } + public string YearMonth { get; set; }=String.Empty; + public DateTime YearMonthDate { get; set; } + public bool IsLock { get; set; } + public decimal AdjustPaymentUSD { get; set; } + public decimal AdjustPaymentCNY { get; set; } + public string Note { get; set; } = String.Empty; + } + + public class PaymentAdjustmentDetailDTO: PaymentAdjustmentDTO + { + public string ReviewerCode { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + public string FullName => LastName + " / " + FirstName; + public string ChineseName { get; set; } = String.Empty; + } + + public class PaymentAdjustmentQueryDTO:PageInput + { + public string TrialCode { get; set; } = string.Empty; + public string Reviewer { get; set; } = string.Empty; + public DateTime BeginMonth { get; set; } + public DateTime EndMonth { get; set; } + } + + public class DoctorSelectDTO + { + public Guid Id { get; set; } + public string Code { get; set; } = string.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + public string FullName => LastName + " / " + FirstName; + public string ChineseName { get; set; } = String.Empty; + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/DTO/PaymentDetailModel.cs b/IRaCIS.Core.Application/Service/Financial/DTO/PaymentDetailModel.cs new file mode 100644 index 0000000..ae8aaa0 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/DTO/PaymentDetailModel.cs @@ -0,0 +1,195 @@ +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts.Pay +{ + public class PaymentDetailDTO + { + public Guid Id { get; set; } + public Guid PaymentId { get; set; } + public string YearMonth { get; set; } = String.Empty; + public Guid DoctorId { get; set; } + public Guid TrialId { get; set; } + public string TrialCode { get; set; } = String.Empty; + public string PaymentType { get; set; } = String.Empty; + public int Count { get; set; } + public decimal BasePrice { get; set; } + public decimal PersonalAdditional { get; set; } + + public decimal? NewPersonalAdditional { get; set; } + public decimal TrialAdditional { get; set; } + public int ShowTypeOrder { get; set; } + public int ShowCodeOrder { get; set; } + + public decimal ExchangeRate { get; set; } + + public decimal PaymentUSD { get; set; } + public decimal PaymentCNY { get; set; } + + public bool? IsNewTrial { get; set; } + + public decimal TotalUnitPrice => BasePrice + PersonalAdditional + TrialAdditional; + + public AdjustmentDTO AdjustmentView { get; set; } = new AdjustmentDTO(); + + } + + public class AdjustmentDTO + { + public decimal AdjustPaymentUSD { get; set; } + + public decimal AdjustPaymentCNY { get; set; } + + public string AdjustType + { + get + { + if (AdjustPaymentUSD > 0) + { + return "+"; + } + else if (AdjustPaymentUSD < 0) + { + return "-"; + } + else { return string.Empty; } + } + } + public string Note { get; set; } = String.Empty; + } + + + public class PaymentDetailCommand : PaymentDetailDTO + { + + } + + public class PayDetailDTO + { + public IEnumerable DetailList { get; set; } = new List(); + public DoctorPayInfo DoctorInfo { get; set; } = new DoctorPayInfo(); + } + + public class LockPaymentDTO + { + public List ReviewerIdList { get; set; }=new List(); + public DateTime Month { get; set; } + + public bool IsLock { get; set; } = true; + } + + public class DoctorPayInfo + { + public Guid DoctorId { get; set; } + public string ChineseName { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + public string Phone { get; set; } = String.Empty; + public string PayTitle { get; set; } = String.Empty; + public string Code { get; set; } = String.Empty; + public string YearMonth { get; set; } = String.Empty; + } + + + + + + + + public class ReviewerPaymentUSD + { + public Guid RecordId { get; set; } + public Guid DoctorId { get; set; } + public decimal PaymentUSD { get; set; } + } + + public class PaymentQueryDTO : PageInput + { + public string Reviewer { get; set; } = String.Empty; + public DateTime BeginMonth { get; set; } + public DateTime EndMonth { get; set; } + public int? Nation { get; set; } + } + public class MonthlyPaymentDTO + { + public Guid ReviewerId { get; set; } + public string ReviewerCode { get; set; } = String.Empty; + public string ChineseName { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + + public string FullName { get; set; } = String.Empty; + + public decimal AdjustmentUSD { get; set; } + public decimal AdjustmentCNY { get; set; } + public decimal PaymentUSD { get; set; } + public decimal PaymentCNY { get; set; } + + public decimal TotalUSD { get; set; } + public decimal TotalCNY { get; set; } + + } + + public class VolumeStatisticsDTO + { + public Guid StatisticsId { get; set; } + public string Month { get; set; } = String.Empty; + public decimal VolumeReward { get; set; } + public decimal ExchangeRate { get; set; } + + public decimal AdjustmentUSD { get; set; } + public decimal AdjustmentCNY { get; set; } + public decimal PaymentUSD { get; set; } + public decimal PaymentCNY { get; set; } + public decimal TotalCNY => AdjustmentCNY + PaymentCNY; + public decimal TotalUSD => AdjustmentUSD + PaymentUSD; + + public List TrialPaymentList = new List(); + } + + public class TrialPaymentDTO + { + public Guid TrialId { get; set; } + public string TrialCode { get; set; } = String.Empty; + public decimal TrialPayment { get; set; } + + } + public class VolumeQueryDTO + { + public DateTime BeginMonth { get; set; } + public DateTime EndMonth { get; set; } + public Guid ReviewerId { get; set; } + } + + public class RevenuesDTO + { + public List MissingTrialCodes = new List(); + public Guid Id { get; set; } + public string TrialCode { get; set; } = String.Empty; + public Guid TrialId { get; set; } + public string Indication { get; set; } = String.Empty; + public Guid? CroId { get; set; } + public string Cro { get; set; } = string.Empty; + + public int Expedited { get; set; } + public string ChineseName { get; set; } = string.Empty; + + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string ReviewerCode { get; set; } = string.Empty; + + public decimal Training { get; set; } + public decimal Downtime { get; set; } + public decimal Timepoint { get; set; } + public decimal TimepointIn24H { get; set; } + public decimal TimepointIn48H { get; set; } + public decimal Adjudication { get; set; } + public decimal AdjudicationIn24H { get; set; } + public decimal AdjudicationIn48H { get; set; } + public decimal Global { get; set; } + public decimal Total { get; set; } + + public string YearMonth { get; set; } = String.Empty; + } + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/DTO/PaymentModel.cs b/IRaCIS.Core.Application/Service/Financial/DTO/PaymentModel.cs new file mode 100644 index 0000000..af36489 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/DTO/PaymentModel.cs @@ -0,0 +1,179 @@ +using System; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + public class PaymentDTO + { + public PageOutput CostList { get; set; }=new PageOutput(); + public decimal ExchangeRate { get; set; } + } + public class PaymentModel + { + public Guid Id { get; set; } + public string RankName { get; set; } = String.Empty; + public Guid DoctorId { get; set; } + public string YearMonth { get; set; } = String.Empty; + public DateTime YearMonthDate { get; set; } + public DateTime? CalculateTime { get; set; } + public string CalculateUser { get; set; } = String.Empty; + + //额外信息 + public string Code { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + + public string ChineseName { get; set; } = String.Empty; + public string Phone { get; set; } = String.Empty; + public string DoctorNameInBank { get; set; } = String.Empty; + public string IDCard { get; set; } = String.Empty; + public string BankCardNumber { get; set; } = String.Empty; + public string BankName { get; set; } = String.Empty; + + public bool IsLock { get; set; } = false; + + public decimal ExchangeRate { get; set; } + + public decimal PaymentCNY { get; set; } + + public decimal AdjustPaymentCNY { get; set; } + public decimal TotalPaymentCNY { get; set; } + public decimal PaymentUSD { get; set; } + + public decimal AdjustPaymentUSD { get; set; } + public decimal TotalPaymentUSD { get; set; } + } + + public class PaymentCommand + { + public Guid Id { get; set; } + public Guid DoctorId { get; set; } + public string YearMonth => new DateTime(Year, Month, 1).ToString("yyyy-MM"); + public int Year { get; set; } + public int Month { get; set; } + public decimal PaymentUSD { get; set; } + public DateTime CalculateTime { get; set; } + public decimal PaymentCNY { get; set; } + public decimal ExchangeRate { get; set; } + public string CalculateUser { get; set; } = String.Empty; + } + + public class MonthlyPaymentQueryDTO : PageInput + { + public DateTime StatisticsDate { get; set; } + public string KeyWord { get; set; } = String.Empty; + public int? Nation { get; set; } + } + + public class MonthlyPaymentDetailQuery + { + public Guid PaymentId { get; set; } + public Guid ReviewerId { get; set; } + + public DateTime YearMonth { get; set; } + } + + //public class LaborPaymentQuery + //{ + // public Guid PaymentId { get; set; } + // public Guid ReviewerId { get; set; } + + // public DateTime YearMonth { get; set; } + //} + + + public class TrialAnalysisDTO + { + public Guid TrialId { get; set; } + public string Indication { get; set; } = String.Empty; + + public string TrialCode { get; set; } = String.Empty; + public string Cro { get; set; } = string.Empty; + + public int Expedited { get; set; } + + public string Type { get; set; } = String.Empty; + + public decimal PaymentUSD { get; set; } + public decimal RevenusUSD { get; set; } + public decimal GrossProfit => RevenusUSD - PaymentUSD; + public decimal GrossProfitMargin + { + get { + if (RevenusUSD == 0) + return 0; + else + { + return GrossProfit / RevenusUSD; + } + } + } + + } + + public class LaborPayment + { + public string ChineseName { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + + public string ResidentId { get; set; } = String.Empty; + + public string Phone { get; set; } = String.Empty; + + public string AccountNumber { get; set; } = String.Empty; + + public string Bank { get; set; } = String.Empty; + public string YearMonth { get; set; } = String.Empty; + + public decimal PaymentCNY { get; set; } + public decimal TaxCNY { get; set; } + public decimal ActuallyPaidCNY { get; set; } + public decimal BankTransferCNY { get; set; } + } + public class ReviewerAnalysisDTO + { + public List MissingTrialCodes = new List(); + public string ChineseName { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + + public Guid ReviewerId { get; set; } + public string ReviewerCode { get; set; } = String.Empty; + + public decimal PaymentUSD { get; set; } + public decimal RevenusUSD { get; set; } + public decimal GrossProfit => RevenusUSD - PaymentUSD; + + public decimal GrossProfitMargin + { + get { + if (RevenusUSD == 0) + return 0; + else + { + return GrossProfit / RevenusUSD; + } + } + } + } + public class AnalysisQueryDTO + { + public string Reviewer { get; set; } = String.Empty; + public DateTime BeginDate { get; set; } + public DateTime EndDate { get; set; } + + public int? Nation { get; set; } + } + + public class TrialAnalysisQueryDTO + { + public Guid? CroId { get; set; } + public string TrialCode { get; set; } = String.Empty; + public DateTime BeginDate { get; set; } + public DateTime EndDate { get; set; } + + public AttendedReviewerType? AttendedReviewerType { get; set; } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/DTO/RankPriceModel.cs b/IRaCIS.Core.Application/Service/Financial/DTO/RankPriceModel.cs new file mode 100644 index 0000000..4e39bdc --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/DTO/RankPriceModel.cs @@ -0,0 +1,50 @@ +using System; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + public class RankPriceDTO + { + public Guid Id { get; set; } + public string RankName { get; set; } = string.Empty; + public decimal Timepoint { get; set; } + public decimal TimepointIn24H { get; set; } + public decimal TimepointIn48H { get; set; } + public decimal Adjudication { get; set; } + public decimal AdjudicationIn24H { get; set; } + public decimal AdjudicationIn48H { get; set; } + public decimal Global { get; set; } + public decimal Training { get; set; } + public decimal Downtime { get; set; } + + public decimal RefresherTraining { get; set; } + public int ShowOrder { get; set; } + } + + public class RankDic + { + public Guid Id { get; set; } + public string RankName { get; set; } = string.Empty; + } + + public class RankPriceCommand + { + public Guid? Id { get; set; } + public string RankName { get; set; } = string.Empty; + public decimal Timepoint { get; set; } + public decimal TimepointIn24H { get; set; } + public decimal TimepointIn48H { get; set; } + public decimal Adjudication { get; set; } + public decimal AdjudicationIn24H { get; set; } + public decimal AdjudicationIn48H { get; set; } + public decimal Global { get; set; } + public decimal Training { get; set; } + public decimal Downtime { get; set; } + public decimal RefresherTraining { get; set; } + + + } + public class RankPriceQueryDTO : PageInput + { + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/DTO/ReviewerPayInfoModel.cs b/IRaCIS.Core.Application/Service/Financial/DTO/ReviewerPayInfoModel.cs new file mode 100644 index 0000000..12e7de8 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/DTO/ReviewerPayInfoModel.cs @@ -0,0 +1,40 @@ +using System; + +namespace IRaCIS.Application.Contracts +{ + public class ReviewerPayInfoQueryDTO + { + public Guid DoctorId { get; set; } + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + public string ChineseName { get; set; } = String.Empty; + public string Code { get; set; } = String.Empty; + public string Phone { get; set; } = String.Empty; + public string DoctorNameInBank { get; set; } = String.Empty; + public string IDCard { get; set; } = String.Empty; + public string BankCardNumber { get; set; } = String.Empty; + public string BankName { get; set; } = String.Empty; + public Guid? RankId { get; set; } + public decimal? Additional { get; set; } + public DateTime? CreateTime { get; set; } + } + + public class DoctorPayInfoQueryListDTO : ReviewerPayInfoQueryDTO + { + public string Hospital { get; set; } = String.Empty; + public string RankName { get; set; } = String.Empty; + } + + public class ReviewerPayInfoCommand + { + //public Guid Id { get; set; } + public Guid DoctorId { get; set; } + public string DoctorNameInBank { get; set; } = String.Empty; + public string IDCard { get; set; } = String.Empty; + public string BankCardNumber { get; set; } = String.Empty; + public string BankName { get; set; } = String.Empty; + public Guid RankId { get; set; } + public decimal? Additional { get; set; } + } +} + diff --git a/IRaCIS.Core.Application/Service/Financial/DTO/TrialPaymentPriceModel.cs b/IRaCIS.Core.Application/Service/Financial/DTO/TrialPaymentPriceModel.cs new file mode 100644 index 0000000..f05cc3b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/DTO/TrialPaymentPriceModel.cs @@ -0,0 +1,46 @@ +using IRaCIS.Core.Domain.Share; +using System; + +namespace IRaCIS.Application.Contracts +{ + + public class DtoDoctorList + { + public Guid TrialId { get; set; } + + public string Name { get; set; } + } + + public class TrialPaymentPriceDTO + { + public Guid TrialId { get; set; } + public string TrialCode { get; set; } = String.Empty; + public string Indication { get; set; } = String.Empty; + public string Cro { get; set; } = String.Empty; + public int Expedited { get; set; } + + public bool? IsNewTrial { get; set; } + public decimal? TrialAdditional { get; set; } + public DateTime? CreateTime { get; set; } + public string SowName { get; set; } = String.Empty; + public string SowPath { get; set; } = String.Empty; + public string SowFullPath => SowPath; + public decimal AdjustmentMultiple { get; set; } = 1; + + public string DoctorsNames{ get; set; }=String.Empty; + + public string ReviewMode { get; set; } = String.Empty; + + + } + + public class TrialPaymentPriceCommand + { + public Guid TrialId { get; set; } + public decimal TrialAdditional { get; set; } + public decimal AdjustmentMultiple { get; set; } + + public bool? IsNewTrial { get; set; } + + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/DTO/TrialRevenuesPriceModel.cs b/IRaCIS.Core.Application/Service/Financial/DTO/TrialRevenuesPriceModel.cs new file mode 100644 index 0000000..afe5207 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/DTO/TrialRevenuesPriceModel.cs @@ -0,0 +1,37 @@ +using System; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + public class TrialRevenuesPriceDTO + { + + public Guid TrialId { get; set; } + public decimal Timepoint { get; set; } + public decimal TimepointIn24H { get; set; } + public decimal TimepointIn48H { get; set; } + public decimal Adjudication { get; set; } + public decimal AdjudicationIn24H { get; set; } + public decimal AdjudicationIn48H { get; set; } + public decimal RefresherTraining { get; set; } + + public decimal Global { get; set; } + public decimal Training { get; set; } + public decimal Downtime { get; set; } + } + + public class TrialRevenuesPriceDetialDTO : TrialRevenuesPriceDTO + { + public Guid Id { get; set; } + public string TrialCode { get; set; } = String.Empty; + public string Indication { get; set; } = string.Empty; + public int Expedited { get; set; } + public string ReviewMode { get; set; } = String.Empty; + public string Cro { get; set; } = String.Empty; + } + public class TrialRevenuesPriceQueryDTO : PageInput + { + public string KeyWord { get; set; } = String.Empty; + public Guid? CroId { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/DTO/TrialRevenuesPriceVerificationDTO.cs b/IRaCIS.Core.Application/Service/Financial/DTO/TrialRevenuesPriceVerificationDTO.cs new file mode 100644 index 0000000..4492ed0 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/DTO/TrialRevenuesPriceVerificationDTO.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; + +namespace IRaCIS.Core.Application.Contracts +{ + public class RevenusVerifyQueryDTO + { + public DateTime BeginDate { get; set; } = DateTime.Now; + public DateTime EndDate { get; set; } = DateTime.Now; + + } + + public class AnalysisVerifyQueryDTO + { + public DateTime BeginDate { get; set; } = DateTime.Now; + public DateTime EndDate { get; set; } = DateTime.Now; + } + + + public class AnalysisNeedLockDTO + { + public string YearMonth { get; set; } = string.Empty; + + public string ReviewerCode { get; set; } = string.Empty; + + public string ReviewerName { get; set; } = string.Empty; + + public string ReviewerNameCN { get; set; } = string.Empty; + } + + public class AnalysisVerifyResultDTO + { + public List MonthVerifyResult = new List(); + + public List RevenuesVerifyList = new List(); + } + + + + public class MonthlyResult + { + public string YearMonth { get; set; } = string.Empty; + public List ReviewerNameList = new List(); + public List ReviewerNameCNList = new List(); + public List ReviewerCodeList = new List(); + } + + + public class RevenusVerifyDTO + { + public string TrialCode { get; set; } = string.Empty; + + public bool Training { get; set; } = false; + + public bool Downtime { get; set; } = false; + + public bool Global { get; set; } = false; + + public bool Timepoint { get; set; } = false; + + public bool TimepointIn24H { get; set; } = false; + + public bool TimepointIn48H { get; set; } = false; + + public bool Adjudication { get; set; } = false; + + public bool AdjudicationIn24H { get; set; } = false; + + public bool AdjudicationIn48H { get; set; } = false; + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/ExchangeRateService.cs b/IRaCIS.Core.Application/Service/Financial/ExchangeRateService.cs new file mode 100644 index 0000000..6f11fcc --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/ExchangeRateService.cs @@ -0,0 +1,115 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Application.Services +{ + + [ApiExplorerSettings(GroupName = "Financial")] + public class ExchangeRateService : BaseService, IExchangeRateService + { + private readonly IRepository _exchangeRateRepository; + private readonly IRepository _paymentRepository; + + public ExchangeRateService(IRepository exchangeRateRepository, IRepository paymentRepository) + { + _exchangeRateRepository = exchangeRateRepository; + _paymentRepository = paymentRepository; + } + + [NonDynamicMethod] + public async Task AddOrUpdateExchangeRate(ExchangeRateCommand model) + { + if (model.Id == Guid.Empty || model.Id == null) + { + var existItem = await _exchangeRateRepository.FirstOrDefaultAsync(u => u.YearMonth == model.YearMonth); + if (existItem != null) + { + return ResponseOutput.NotOk("The exchange rate of the same month already existed."); + } + var rate = _mapper.Map(model); + rate = await _exchangeRateRepository.AddAsync(rate); + if (await _exchangeRateRepository.SaveChangesAsync()) + { + return ResponseOutput.Ok(rate.Id.ToString()); + } + else + { + return ResponseOutput.NotOk(); + } + + } + else + { + var success = await _exchangeRateRepository.BatchUpdateNoTrackingAsync(t => t.Id == model.Id, u => new ExchangeRate() + { + //YearMonth = model.YearMonth, + Rate = model.Rate, + UpdateTime = DateTime.Now + }); + return ResponseOutput.Result(success); + } + } + + /// + /// 根据记录Id,删除汇率记录 + /// + /// 汇率记录Id + + [HttpDelete("{id:guid}")] + public async Task DeleteExchangeRate(Guid id) + { + var monthInfo = await _exchangeRateRepository.FirstOrDefaultAsync(t => t.Id == id); + + if (await _paymentRepository.AnyAsync(t => t.YearMonth == monthInfo.YearMonth)) + { + return ResponseOutput.NotOk("The exchange rate has been used in monthly payment"); + } + + + var success = await _exchangeRateRepository.BatchDeleteNoTrackingAsync(t => t.Id == id); + + return ResponseOutput.Ok(success); + } + + + [NonDynamicMethod] + public async Task GetExchangeRateByMonth(string month) + { + //var rate = _exchangeRateRepository.FindSingleOrDefault(u => u.YearMonth.Equals(month)); + //if (rate == null) + //{ + // return 0; + //} + //return rate.Rate; + + var rate = await _exchangeRateRepository.FirstOrDefaultAsync(t => t.YearMonth == month); + if (rate == null) + { + return 0; + } + return rate.Rate; + } + + [HttpPost] + public async Task> GetExchangeRateList(ExchangeRateQueryDTO queryParam) + { + + var yearMonth = queryParam.SearchMonth?.ToString("yyyy-MM"); + + var exchangeRateQueryable = _exchangeRateRepository.AsQueryable() + .WhereIf(queryParam.SearchMonth != null, o => o.YearMonth == yearMonth) + .ProjectTo(_mapper.ConfigurationProvider); + + return await exchangeRateQueryable.ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize, "YearMonth", false); + + + + } + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/FinancialService.cs b/IRaCIS.Core.Application/Service/Financial/FinancialService.cs new file mode 100644 index 0000000..d47b6a4 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/FinancialService.cs @@ -0,0 +1,1310 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Application.Contracts.Pay; +using IRaCIS.Core.Domain.Share; +using System.Linq.Expressions; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; +using System.Linq.Dynamic.Core; + +namespace IRaCIS.Application.Services +{ + [ ApiExplorerSettings(GroupName = "Financial")] + public class FinancialService : BaseService, IPaymentService + { + private readonly IRepository _paymentRepository; + private readonly IRepository _doctorPayInfoRepository; + private readonly IRepository _TrialPaymentPriceRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _rankPriceRepository; + private readonly IRepository _paymentDetailRepository; + private readonly IRepository _croRepository; + private readonly IRepository _workloadRepository; + private readonly IRepository _trialRevenuePriceRepository; + private readonly IRepository _payAdjustmentRepository; + + private readonly IRepository _enrollRepository; + + private readonly IRepository _doctorWorkloadRepository; + + public FinancialService(IRepository costStatisticsRepository, + IRepository doctorPayInfoRepository, + IRepository trialRepository, + IRepository doctorRepository, + IRepository rankPriceRepository, + IRepository costStatisticsDetailRepository, + IRepository croCompanyRepository, + IRepository workloadRepository, + IRepository intoGroupRepository, + IRepository trialPaymentPriceRepository, + IRepository trialCostRepository, + IRepository costAdjustmentRepository, + IRepository doctoWorkloadRepository + ) + { + _TrialPaymentPriceRepository = trialPaymentPriceRepository; + _paymentRepository = costStatisticsRepository; + _doctorPayInfoRepository = doctorPayInfoRepository; + _trialRepository = trialRepository; + _doctorRepository = doctorRepository; + _rankPriceRepository = rankPriceRepository; + _paymentDetailRepository = costStatisticsDetailRepository; + _croRepository = croCompanyRepository; + _enrollRepository = intoGroupRepository; + _workloadRepository = workloadRepository; + _trialRevenuePriceRepository = trialCostRepository; + _payAdjustmentRepository = costAdjustmentRepository; + + _doctorWorkloadRepository = doctoWorkloadRepository; + } + + /// + /// Financials /MonthlyPayment 列表查询接口 + /// + [NonDynamicMethod] + public async Task> GetMonthlyPaymentList(MonthlyPaymentQueryDTO queryParam) + { + //var year = queryParam.StatisticsDate.Year; + //var month = queryParam.StatisticsDate.Month; + //var searchBeginDateTime = queryParam.StatisticsDate; + //Expression> workloadLambda = t => t.DataFrom == (int)WorkLoadFromStatus.FinalConfirm; + //workloadLambda = workloadLambda.And(t => + // t.WorkTime >= queryParam.StatisticsDate && t.WorkTime <= queryParam.StatisticsDate); + //var rate = _exchangeRateRepository.FindSingleOrDefault(u => u.YearMonth == yearMonth); + //var exchangeRate = rate == null ? 0 : rate.Rate; + + var yearMonth = queryParam.StatisticsDate.ToString("yyyy-MM"); + + Expression> doctorLambda = x => true; + if (!string.IsNullOrWhiteSpace(queryParam.KeyWord)) + { + var reviewer = queryParam.KeyWord.Trim(); + doctorLambda = doctorLambda.And(u => u.ChineseName.Contains(reviewer) + || u.FirstName.Contains(reviewer) + || u.LastName.Contains(reviewer) + || u.ReviewerCode.Contains(reviewer)); + } + + if (queryParam.Nation != null) + { + doctorLambda = doctorLambda.And(u => u.Nation == queryParam.Nation); + } + + var costStatisticsQueryable = + + from monthlyPayment in _paymentRepository.Where(t => t.YearMonth == yearMonth) + join payInfo in _doctorPayInfoRepository.AsQueryable() on monthlyPayment.DoctorId equals payInfo.DoctorId + into cc + from payInfo in cc.DefaultIfEmpty() + join doctor in _doctorRepository.Where(doctorLambda) on monthlyPayment.DoctorId equals doctor.Id + join rankPrice in _rankPriceRepository.AsQueryable() on payInfo.RankId equals rankPrice.Id into dd + from rankPriceItem in dd.DefaultIfEmpty() + select new PaymentModel() + { + Id = monthlyPayment.Id, + DoctorId = monthlyPayment.DoctorId, + YearMonth = monthlyPayment.YearMonth, + PaymentUSD = monthlyPayment.PaymentUSD, + CalculateTime = monthlyPayment.CalculateTime, + CalculateUser = monthlyPayment.CalculateUser, + IsLock = monthlyPayment.IsLock, + ExchangeRate = monthlyPayment.ExchangeRate, + PaymentCNY = monthlyPayment.PaymentCNY, + AdjustPaymentCNY = monthlyPayment.AdjustmentCNY, + AdjustPaymentUSD = monthlyPayment.AdjustmentUSD, + + TotalPaymentCNY = monthlyPayment.AdjustmentCNY + monthlyPayment.PaymentCNY, + TotalPaymentUSD = monthlyPayment.AdjustmentUSD + monthlyPayment.PaymentUSD, + + + Code = doctor.ReviewerCode, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + ChineseName = doctor.ChineseName, + Phone = doctor.Phone, + DoctorNameInBank = payInfo.DoctorNameInBank, + RankName = rankPriceItem.RankName, + IDCard = payInfo.IDCard, + BankCardNumber = payInfo.BankCardNumber, + BankName = payInfo.BankName + }; + + + + return await costStatisticsQueryable.ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize, string.IsNullOrWhiteSpace(queryParam.SortField) ? "Code" : queryParam.SortField, queryParam.Asc); + + //var propName = string.IsNullOrWhiteSpace(queryParam.SortField) ? "Code" : queryParam.SortField; + //costStatisticsQueryable = queryParam.Asc ? costStatisticsQueryable.OrderBy(propName).ThenBy(u => u.Code) + // : costStatisticsQueryable.OrderByDescending(propName).ThenBy(u => u.Code); + //var count = costStatisticsQueryable.Count(); + //costStatisticsQueryable = costStatisticsQueryable.Skip((queryParam.PageIndex - 1) * queryParam.PageSize).Take(queryParam.PageSize); + //var costStatisticsList = await costStatisticsQueryable.ToListAsync(); + //return new PageOutput(queryParam.PageIndex, queryParam.PageSize, count, costStatisticsList); + + + #region 增加调整费用总计字段之前 + //var doctorIds = costStatisticsList.Select(t => t.DoctorId).ToList(); + + //var adjList = _payAdjustmentRepository.GetAll() + // .Where(t => t.YearMonth == yearMonth && doctorIds.Contains(t.ReviewerId)) + // .GroupBy(t => t.ReviewerId).Select(g => new + // { + // ReviewerId = g.Key, + // AdjustPaymentCNY = g.Sum(t => t.AdjustPaymentCNY), + // AdjustPaymentUSD = g.Sum(t => t.AdjustPaymentUSD) + // }).ToList(); + + //costStatisticsList.ForEach(t => + //{ + // var tempAdj = adjList.FirstOrDefault(u => u.ReviewerId == t.DoctorId); + // if (tempAdj != null) + // { + // t.AdjustPaymentUSD = tempAdj.AdjustPaymentUSD; + // t.AdjustPaymentCNY = tempAdj.AdjustPaymentCNY; + + // } + //}); + + + #endregion + + } + + /// + /// Financials /MonthlyPaymentDetail 详情查询接口 + /// + [HttpPost("{paymentId:guid}/{doctorId:guid}/{yearMonth:datetime}")] + public async Task GetMonthlyPaymentDetailList(Guid paymentId, Guid reviewerId, DateTime yearMonth) + { + var returnModel = new PayDetailDTO(); + var yearMonthStr = yearMonth.ToString("yyyy-MM"); + + List detailList = new List(); + //有详细表的数据 先查出来 + if (paymentId != Guid.Empty) + { + + //from enroll in _enrollRepository.Where(t => t.TrialId == challengeQuery.TrialId) + //join dociorc in _doctorRepository.Where() on enroll.DoctorId equals dociorc.Id + //join price in _TrialPaymentPriceRepository.Where() on enroll.TrialId equals price.TrialId + + + //select new EnrollViewModel() + //{ + // ChineseName = dociorc.ChineseName, + // AdjustmentMultiple = enroll.AdjustmentMultiple, + // FirstName = dociorc.FirstName, + // LastName = dociorc.LastName, + // DoctorId = dociorc.Id, + // TrialId = challengeQuery.TrialId + + //}; + //detailList = _paymentDetailRepository.AsQueryable() + // .Where(t => t.PaymentId == paymentId) + // .OrderBy(t => t.ShowCodeOrder).ThenBy(t => t.ShowTypeOrder).ProjectTo(_mapper.ConfigurationProvider).ToList(); + + + detailList = await (from pay in _paymentDetailRepository.Where(t => t.PaymentId == paymentId) + join enroll in _enrollRepository.Where() on new { pay.DoctorId, pay.TrialId } equals new { enroll.DoctorId, enroll.TrialId } + join price in _TrialPaymentPriceRepository.Where() on pay.TrialId equals price.TrialId + orderby pay.ShowCodeOrder + orderby pay.ShowTypeOrder + select new PaymentDetailDTO() + { + Id=pay.Id, + ShowCodeOrder = pay.ShowCodeOrder, + ShowTypeOrder = pay.ShowTypeOrder, + PaymentUSD = pay.PaymentUSD, + BasePrice = pay.BasePrice, + PersonalAdditional = pay.PersonalAdditional, + TrialAdditional = pay.TrialAdditional, + TrialCode = pay.TrialCode, + PaymentCNY = pay.PaymentCNY, + DoctorId = pay.DoctorId, + ExchangeRate = pay.ExchangeRate, + IsNewTrial = price.IsNewTrial, + NewPersonalAdditional= enroll.AdjustmentMultiple, + + }).ToListAsync(); + + } + //费用调整 + //_payAdjustmentRepository.Where(t => t.YearMonth == yearMonthStr && t.ReviewerId == reviewerId) + var adjList = + await (from costAdjustment in _payAdjustmentRepository.Where(t => t.YearMonth == yearMonthStr&& t.ReviewerId == paymentId) + join enroll in _enrollRepository.Where() on new { costAdjustment.ReviewerId, costAdjustment.TrialId } equals new { ReviewerId= enroll.DoctorId, enroll.TrialId } + join price in _TrialPaymentPriceRepository.Where() on costAdjustment.TrialId equals price.TrialId + + + select new PaymentDetailDTO() + { + Id=costAdjustment.Id, + TrialCode = "Adjustment", + PaymentCNY = costAdjustment.AdjustmentCNY, + PaymentUSD = costAdjustment.AdjustmentUSD, + DoctorId = costAdjustment.ReviewerId, + ExchangeRate = costAdjustment.ExchangeRate, + AdjustmentView = new AdjustmentDTO() + { + AdjustPaymentUSD = costAdjustment.AdjustmentUSD, + AdjustPaymentCNY = costAdjustment.AdjustmentCNY, + Note = costAdjustment.Note + }, + IsNewTrial = price.IsNewTrial, + NewPersonalAdditional = enroll.AdjustmentMultiple, + + }).ToListAsync(); + + + + + detailList.AddRange(adjList); + + detailList.ForEach(x => + { + x.PersonalAdditional = x.IsNewTrial == true && x.NewPersonalAdditional != null ? 0 : x.PersonalAdditional; + x.TrialAdditional = x.IsNewTrial == true && x.NewPersonalAdditional != null ? 0 : x.TrialAdditional; + x.BasePrice = x.IsNewTrial == true && x.NewPersonalAdditional != null ? x.NewPersonalAdditional.Value : x.BasePrice; + + }); + + returnModel.DetailList = detailList; + + + + var doctorId = returnModel.DetailList.FirstOrDefault()?.DoctorId; + var query = from doctor in _doctorRepository.AsQueryable() + .Where(t => t.Id == doctorId) + join payInfo in _doctorPayInfoRepository.AsQueryable() on doctor.Id equals payInfo.DoctorId + join rank in _rankPriceRepository.AsQueryable() on payInfo.RankId equals rank.Id + select new DoctorPayInfo() + { + Code = doctor.ReviewerCode, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + Phone = doctor.Phone, + DoctorId = doctor.Id, + PayTitle = rank.RankName, + YearMonth = yearMonthStr + }; + + returnModel.DoctorInfo =(await query.FirstOrDefaultAsync()).IfNullThrowException(); + + return returnModel; + } + + /// + /// NEW 导出Excel压缩包 数据获取 + /// + [HttpPost] + public async Task> GetReviewersMonthlyPaymentDetail(List manyReviewers) + { + List result = new List(); + foreach (var t in manyReviewers) + { + result.Add(await GetMonthlyPaymentDetailList(t.PaymentId, t.ReviewerId, t.YearMonth)); + } + + + return result; + + } + + + /// + /// Financials / Payment History 列表查询接口(已经支付锁定的数据,包含调整的)[New] + /// + [HttpPost] + public async Task> GetPaymentHistoryList(PaymentQueryDTO queryParam) + { + Expression> doctorLambda = x => true; + if (!string.IsNullOrWhiteSpace(queryParam.Reviewer)) + { + var reviewer = queryParam.Reviewer.Trim(); + doctorLambda = doctorLambda.And(u => u.ChineseName.Contains(reviewer) + || u.FirstName.Contains(reviewer) + || u.LastName.Contains(reviewer) + || u.ReviewerCode.Contains(reviewer)); + } + if (queryParam.Nation != null) + { + doctorLambda = doctorLambda.And(u => u.Nation == queryParam.Nation); + } + + Expression> paymentLambda = x => x.IsLock && x.YearMonthDate >= queryParam.BeginMonth && x.YearMonthDate <= queryParam.EndMonth; + + #region 在 payment表加 调整总计前 + + //var paymentQueryable = _paymentRepository.Find(paymentLambda) + // .Select( + // t => new + // { + // ReviewerId = t.DoctorId, + // t.YearMonth, + // t.PaymentUSD, + // t.PaymentCNY, + // t.AdjustPaymentCNY, + // t.AdjustPaymentUSD + // }); + + //Expression> payAdjustmentLambda = x => x.IsLock && x.AdjustedYearMonth >= param.BeginMonth && x.AdjustedYearMonth <= param.EndMonth; + + //var adjQueryable = _payAdjustmentRepository.GetAll() + // .Where(payAdjustmentLambda) + // .GroupBy(t => new { t.ReviewerId, t.YearMonth }).Select(g => new + // { + // g.Key.ReviewerId, + // g.Key.YearMonth, + // PaymentUSD = 0, + // PaymentCNY = 0, + // AdjustPaymentCNY = g.Sum(t => t.AdjustPaymentCNY), + // AdjustPaymentUSD = g.Sum(t => t.AdjustPaymentUSD) + // }); + + //var query = from pay in (from paymentCost in paymentQueryable + // join adjCost in adjQueryable on new { paymentCost.YearMonth, paymentCost.ReviewerId } equals new { adjCost.YearMonth, adjCost.ReviewerId } into a + // from adjCost in a.DefaultIfEmpty() + // select new + // { + // paymentCost.ReviewerId, + // paymentCost.PaymentUSD, + // paymentCost.PaymentCNY, + // AdjustmentCNY = adjCost == null ? 0 : adjCost.AdjustPaymentCNY, + // AdjustmentUSD = adjCost == null ? 0 : adjCost.AdjustPaymentUSD + // } + // ).Union( + // from adjCost in adjQueryable + // join paymentCost in paymentQueryable on new { adjCost.YearMonth, adjCost.ReviewerId } equals new { paymentCost.YearMonth, paymentCost.ReviewerId } into a + // from paymentCost in a.DefaultIfEmpty() + // select new + // { + // ReviewerId = adjCost.ReviewerId, + // PaymentUSD = paymentCost == null ? 0 : paymentCost.PaymentUSD, + // PaymentCNY = paymentCost == null ? 0 : paymentCost.PaymentCNY, + // AdjustmentCNY = adjCost.AdjustPaymentCNY, + // AdjustmentUSD = adjCost.AdjustPaymentUSD + // } + // ) + + #endregion + + var query = from monthlyPay in _paymentRepository.Where(paymentLambda) + .GroupBy(t => t.DoctorId).Select(g => new + { + ReviewerId = g.Key, + PaymentUSD = g.Sum(u => u.PaymentUSD), + PaymentCNY = g.Sum(u => u.PaymentCNY), + AdjustmentCNY = g.Sum(u => u.AdjustmentCNY), + AdjustmentUSD = g.Sum(u => u.AdjustmentCNY) + }) + join doctor in _doctorRepository.Where(doctorLambda) on monthlyPay.ReviewerId equals doctor.Id + select new MonthlyPaymentDTO + { + ReviewerId = doctor.Id, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + FullName= doctor.FullName, + ReviewerCode = doctor.ReviewerCode, + PaymentUSD = monthlyPay.PaymentUSD, + PaymentCNY = monthlyPay.PaymentCNY, + AdjustmentCNY = monthlyPay.AdjustmentCNY, + AdjustmentUSD = monthlyPay.AdjustmentUSD, + TotalCNY = monthlyPay.AdjustmentCNY + monthlyPay.PaymentCNY, + TotalUSD = monthlyPay.AdjustmentUSD + monthlyPay.PaymentUSD + }; + + return await query.ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize, string.IsNullOrWhiteSpace(queryParam.SortField) ? "ReviewerCode" : queryParam.SortField, queryParam.Asc); + + //var propName = queryParam.SortField == string.Empty ? "ReviewerCode" : queryParam.SortField; + //query = queryParam.Asc ? query.OrderBy(propName).ThenBy(u => u.ReviewerCode) : query.OrderByDescending(propName).ThenBy(u => u.ReviewerCode); + //if (propName == "FirstName" || propName == "LastName") + //{ + // query = queryParam.Asc ? query.OrderBy(t => t.LastName).ThenBy(t => t.FirstName) + // : query.OrderByDescending(t => t.LastName).ThenBy(t => t.FirstName); + //} + //var count = await query.CountAsync(); + //query = query.Skip((queryParam.PageIndex - 1) * queryParam.PageSize).Take(queryParam.PageSize); + //var list = await query.ToListAsync(); + //return new PageOutput(queryParam.PageIndex, queryParam.PageSize, count, list); + + } + + /// + /// Financials / Payment History 详情接口[New] + /// + [HttpPost] + public async Task> GetPaymentHistoryDetailList(VolumeQueryDTO param) + { + var beginDate = new DateTime(param.BeginMonth.Year, param.BeginMonth.Month, 1); + var endDate = new DateTime(param.EndMonth.Year, param.EndMonth.Month, 1); + + Expression> monthlyPayLambda = x => x.IsLock && x.DoctorId == param.ReviewerId && x.YearMonthDate >= beginDate && x.YearMonthDate <= endDate; + + var query = + from monthlyPayment in _paymentRepository.Where(monthlyPayLambda) + + select new VolumeStatisticsDTO + { + StatisticsId = monthlyPayment.Id, + Month = monthlyPayment.YearMonth, + ExchangeRate = monthlyPayment.ExchangeRate, + PaymentUSD = monthlyPayment.PaymentUSD, + PaymentCNY = monthlyPayment.PaymentCNY, + AdjustmentCNY = monthlyPayment.AdjustmentCNY, + AdjustmentUSD = monthlyPayment.AdjustmentUSD, + VolumeReward = 0 + }; + + var payDetailList = await query.OrderBy(t => t.Month).ToListAsync(); + + List statisticsIdList = payDetailList.Select(t => t.StatisticsId).ToList(); + + var monthlyPayDetailList = await _paymentDetailRepository.Where(u => + statisticsIdList.Contains(u.PaymentId)).Select(t => new + { + PaymentId = t.PaymentId, + t.PaymentUSD, + t.TrialCode, + t.PaymentCNY, + t.YearMonth + }).ToListAsync(); + + payDetailList.ForEach(t => + { + t.VolumeReward = monthlyPayDetailList + .Where(u => u.PaymentId == t.StatisticsId && u.TrialCode == "Volume Reward") + .Sum(k => k.PaymentUSD); + + t.TrialPaymentList = monthlyPayDetailList + .Where(u => u.PaymentId == t.StatisticsId && u.TrialCode != "Volume Reward" && u.YearMonth == t.Month) + .GroupBy(u => u.TrialCode).Select(g => new TrialPaymentDTO + { + TrialPayment = g.Sum(k => k.PaymentUSD), + TrialCode = g.Key + }).ToList(); + }); + + + #region 改表之前 + + //var tempVolume = _costStatisticsDetailRepository.Find(u => + // statisticsIdList.Contains(u.PaymentId)).ToList(); + + //foreach (var item in payDetailList) + //{ + // var temp = tempVolume.Where(u => u.PaymentId == item.StatisticsId && u.TrialCode == "Volume Reward"); + // var Volume = 0.0; + // foreach (var t in temp) + // { + // Volume += (t.BasePrice * t.Count); + // } + // item.VolumeReward = Volume; + + // var trialCodes = tempVolume.Where(u => u.PaymentId == item.StatisticsId + // && u.TrialCode != "Volume Reward").GroupBy(u => u.TrialCode).Select(u => u.Key).Distinct(); + + // foreach (var trial in trialCodes) + // { + // var trialFee = 0.0; + // var aa = tempVolume.Where(u => u.PaymentId == item.StatisticsId && u.TrialCode == trial + // ).ToList(); + + // foreach (var a in aa) + // { + // trialFee += (a.Count * (a.BasePrice + a.PersonalAdditional + a.TrialAdditional)); + // } + // item.TrialFeeList.Add(new TrialFeeViewModel + // { + // TrialCode = trial, + // TrialFee = trialFee + // }); + // } + + //} + + #endregion + + return payDetailList; + } + + /// + /// Revenues列表接口,收入统计[New] 0是Detail 1是按照项目 2是按照人 3按照月份 + /// + [HttpPost] + public async Task> GetRevenuesStatistics(StatisticsQueryDTO queryParam) + { + var bDate = new DateTime(queryParam.BeginDate.Year, queryParam.BeginDate.Month, 1); + var eDate = new DateTime(queryParam.EndDate.Year, queryParam.EndDate.Month, 1); + Expression> workloadLambda = x => x.DataFrom == (int)WorkLoadFromStatus.FinalConfirm; + workloadLambda = workloadLambda.And(x => x.WorkTime >= bDate && x.WorkTime <= eDate); + + Expression> trialLambda = x => true; + if (Guid.Empty != queryParam.CroId && queryParam.CroId != null) + { + trialLambda = trialLambda.And(u => u.CROId == queryParam.CroId); + } + if (!string.IsNullOrWhiteSpace(queryParam.TrialCode)) + { + var trialCode = queryParam.TrialCode.Trim(); + trialLambda = trialLambda.And(u => u.TrialCode.Contains(trialCode)); + } + + if (queryParam.AttendedReviewerType != null) + { + trialLambda = trialLambda.And(u => u.AttendedReviewerType == queryParam.AttendedReviewerType); + } + + Expression> doctorLambda = x => true; + + if (!string.IsNullOrWhiteSpace(queryParam.Reviewer)) + { + var reviewer = queryParam.Reviewer.Trim(); + doctorLambda = doctorLambda.And(u => u.ChineseName.Contains(reviewer) + || u.FirstName.Contains(reviewer) + || u.LastName.Contains(reviewer) + || u.ReviewerCode.Contains(reviewer)); + } + if (queryParam.Nation != null) + { + doctorLambda = doctorLambda.And(u => u.Nation == queryParam.Nation); + } + + List incomeList = new List(); + IQueryable? query=null ; + var propName = string.Empty; + var count = 0; + + #region 按照详细维度 ReviewId TrialId + + if (queryParam.StatType == 0) + { + query = from workLoad in _workloadRepository.Where(workloadLambda) + join doctor in _doctorRepository.Where(doctorLambda) + on workLoad.DoctorId equals doctor.Id + join trial in _trialRepository.Where(trialLambda) + on workLoad.TrialId equals trial.Id + join cro in _croRepository.AsQueryable() on trial.CROId equals cro.Id into ttt + from croItem in ttt.DefaultIfEmpty() + join trialCost in _trialRevenuePriceRepository.AsQueryable() on workLoad.TrialId equals trialCost.TrialId into c + from trialCost in c.DefaultIfEmpty() + select new RevenuesDTO + { + + TrialId = workLoad.TrialId, + Cro = croItem.CROName, + ReviewerCode = doctor.ReviewerCode, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + + Indication = trial.Indication, + TrialCode = trial.TrialCode, + Expedited = trial.Expedited, + CroId = trial.CROId, + + Downtime = workLoad.Downtime * trialCost.Downtime, + Training = workLoad.Training * trialCost.Training, + Timepoint = workLoad.Timepoint * trialCost.Timepoint, + TimepointIn24H = workLoad.TimepointIn24H * trialCost.TimepointIn24H, + TimepointIn48H = workLoad.TimepointIn48H * trialCost.TimepointIn48H, + Global = workLoad.Global * trialCost.Global, + Adjudication = workLoad.Adjudication * trialCost.Adjudication, + AdjudicationIn24H = + workLoad.AdjudicationIn24H * trialCost.AdjudicationIn24H, + AdjudicationIn48H = workLoad.AdjudicationIn48H * trialCost.AdjudicationIn48H, + + YearMonth = workLoad.YearMonth, + + Total = (workLoad.Downtime * trialCost.Downtime + + workLoad.Training * trialCost.Training + + workLoad.Timepoint * trialCost.Timepoint + + workLoad.TimepointIn24H * trialCost.TimepointIn24H + + workLoad.TimepointIn48H * trialCost.TimepointIn48H + + workLoad.Global * trialCost.Global + + workLoad.Adjudication * trialCost.Adjudication + + workLoad.AdjudicationIn24H * trialCost.AdjudicationIn24H + + workLoad.AdjudicationIn48H * trialCost.AdjudicationIn48H + + workLoad.RefresherTraining * trialCost.RefresherTraining + ) + + }; + + //处理排序字段 + propName = queryParam.SortField == "" ? "ReviewerCode" : queryParam.SortField; + + } + //按照项目维度 + if (queryParam.StatType == 1) + { + + var workloadQuery = from workLoad in _workloadRepository.Where(workloadLambda) + + group workLoad by workLoad.TrialId + into gWorkLoad + select new + { + TrialId = gWorkLoad.Key, + Downtime = gWorkLoad.Sum(t => t.Downtime), + Training = gWorkLoad.Sum(t => t.Training), + Timepoint = gWorkLoad.Sum(t => t.Timepoint), + TimepointIn24H = gWorkLoad.Sum(t => t.TimepointIn24H), + TimepointIn48H = gWorkLoad.Sum(t => t.TimepointIn48H), + Global = gWorkLoad.Sum(t => t.Global), + Adjudication = gWorkLoad.Sum(t => t.Adjudication), + AdjudicationIn24H = gWorkLoad.Sum(t => t.AdjudicationIn24H), + AdjudicationIn48H = gWorkLoad.Sum(t => t.AdjudicationIn48H), + RefresherTraining = gWorkLoad.Sum(t => t.RefresherTraining), + }; + + query = from workLoad in workloadQuery + join trialCost in _trialRevenuePriceRepository.AsQueryable() on workLoad.TrialId equals trialCost.TrialId + into c + from trialCost in c.DefaultIfEmpty() + join trial in _trialRepository.Where(trialLambda) on workLoad.TrialId equals trial.Id + join cro in _croRepository.AsQueryable() on trial.CROId equals cro.Id into ttt + from croItem in ttt.DefaultIfEmpty() + select new RevenuesDTO + { + + TrialId = trial.Id, + Indication = trial.Indication, + TrialCode = trial.TrialCode, + Expedited = trial.Expedited, + CroId = trial.CROId, + Cro = croItem.CROName, + Training = workLoad.Training * trialCost.Training, + Timepoint = workLoad.Timepoint * trialCost.Timepoint, + TimepointIn24H = workLoad.TimepointIn24H * trialCost.TimepointIn24H, + TimepointIn48H = workLoad.TimepointIn48H * trialCost.TimepointIn48H, + Adjudication = workLoad.Adjudication * trialCost.Adjudication, + AdjudicationIn24H = workLoad.AdjudicationIn24H * trialCost.AdjudicationIn24H, + AdjudicationIn48H = workLoad.AdjudicationIn48H * trialCost.AdjudicationIn48H, + Global = workLoad.Global * trialCost.Global, + Downtime = workLoad.Downtime * trialCost.Downtime, + + Total = (workLoad.Downtime * trialCost.Downtime + + workLoad.Training * trialCost.Training + + workLoad.Timepoint * trialCost.Timepoint + + workLoad.TimepointIn24H * trialCost.TimepointIn24H + + workLoad.TimepointIn48H * trialCost.TimepointIn48H + + workLoad.Global * trialCost.Global + + workLoad.Adjudication * trialCost.Adjudication + + workLoad.AdjudicationIn24H * trialCost.AdjudicationIn24H + + workLoad.AdjudicationIn48H * trialCost.AdjudicationIn48H + + workLoad.RefresherTraining * trialCost.RefresherTraining + ) + }; + + //处理排序字段 + propName = queryParam.SortField == "" ? "TrialCode" : queryParam.SortField; + + } + + //按照人的维度 + if (queryParam.StatType == 2) + { + var workloadQuery = from workLoad in _workloadRepository.Where(workloadLambda) + join doctor in _doctorRepository.Where(doctorLambda) + on workLoad.DoctorId equals doctor.Id + join trial in _trialRepository.Where(trialLambda) on workLoad.TrialId equals trial.Id + join trialCost in _trialRevenuePriceRepository.AsQueryable() on workLoad.TrialId equals trialCost.TrialId into c + from trialCost in c.DefaultIfEmpty() + + select new + { + DoctorId = workLoad.DoctorId, + Downtime = workLoad.Downtime * trialCost.Downtime, + Training = workLoad.Training * trialCost.Training, + Timepoint = workLoad.Timepoint * trialCost.Timepoint, + TimepointIn24H = workLoad.TimepointIn24H * trialCost.TimepointIn24H, + TimepointIn48H = workLoad.TimepointIn48H * trialCost.TimepointIn48H, + Global = workLoad.Global * trialCost.Global, + Adjudication = workLoad.Adjudication * trialCost.Adjudication, + AdjudicationIn24H = + workLoad.AdjudicationIn24H * trialCost.AdjudicationIn24H, + AdjudicationIn48H = workLoad.AdjudicationIn48H * trialCost.AdjudicationIn48H, + RefresherTraining = workLoad.RefresherTraining * trialCost.RefresherTraining, + ReviewerCode = doctor.ReviewerCode, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + + }; + + + query = workloadQuery.GroupBy(t => new { t.DoctorId, t.ReviewerCode, t.ChineseName, t.FirstName, t.LastName }).Select(gWorkLoad => new RevenuesDTO + { + ReviewerCode = gWorkLoad.Key.ReviewerCode, + ChineseName = gWorkLoad.Key.ChineseName, + FirstName = gWorkLoad.Key.FirstName, + LastName = gWorkLoad.Key.LastName, + + Downtime = gWorkLoad.Sum(t => t.Downtime), + Training = gWorkLoad.Sum(t => t.Training), + Timepoint = gWorkLoad.Sum(t => t.Timepoint), + TimepointIn24H = gWorkLoad.Sum(t => t.TimepointIn24H), + TimepointIn48H = gWorkLoad.Sum(t => t.TimepointIn48H), + Global = gWorkLoad.Sum(t => t.Global), + Adjudication = gWorkLoad.Sum(t => t.Adjudication), + AdjudicationIn24H = gWorkLoad.Sum(t => t.AdjudicationIn24H), + AdjudicationIn48H = gWorkLoad.Sum(t => t.AdjudicationIn48H), + + Total = gWorkLoad.Sum(t => t.Downtime) + + gWorkLoad.Sum(t => t.Training) + + gWorkLoad.Sum(t => t.Timepoint) + + gWorkLoad.Sum(t => t.TimepointIn24H) + + gWorkLoad.Sum(t => t.TimepointIn48H) + + gWorkLoad.Sum(t => t.Global) + + gWorkLoad.Sum(t => t.Adjudication) + + gWorkLoad.Sum(t => t.AdjudicationIn24H) + + gWorkLoad.Sum(t => t.AdjudicationIn48H) + + gWorkLoad.Sum(t => t.RefresherTraining) + + }); + + propName = queryParam.SortField == string.Empty ? "ReviewerCode" : queryParam.SortField; + + + } + + //按照月份维度 + if (queryParam.StatType == 3) + { + var workloadQuery = from workLoad in _workloadRepository.Where(workloadLambda) + join trial in _trialRepository.Where(trialLambda) + on workLoad.TrialId equals trial.Id + join doctor in _doctorRepository.Where(doctorLambda) + on workLoad.DoctorId equals doctor.Id + join trialCost in _trialRevenuePriceRepository.AsQueryable() on workLoad.TrialId equals trialCost.TrialId into c + from trialCost in c.DefaultIfEmpty() + + select new + { + workLoad.YearMonth, + Downtime = workLoad.Downtime * trialCost.Downtime, + Training = workLoad.Training * trialCost.Training, + Timepoint = workLoad.Timepoint * trialCost.Timepoint, + TimepointIn24H = workLoad.TimepointIn24H * trialCost.TimepointIn24H, + TimepointIn48H = workLoad.TimepointIn48H * trialCost.TimepointIn48H, + Global = workLoad.Global * trialCost.Global, + Adjudication = workLoad.Adjudication * trialCost.Adjudication, + AdjudicationIn24H = + workLoad.AdjudicationIn24H * trialCost.AdjudicationIn24H, + AdjudicationIn48H = workLoad.AdjudicationIn48H * trialCost.AdjudicationIn48H, + RefresherTraining = workLoad.RefresherTraining * trialCost.RefresherTraining, + + }; + + query = workloadQuery.GroupBy(t => t.YearMonth).Select(gWorkLoad => new RevenuesDTO + { + + YearMonth = gWorkLoad.Key, + Downtime = gWorkLoad.Sum(t => t.Downtime), + Training = gWorkLoad.Sum(t => t.Training), + Timepoint = gWorkLoad.Sum(t => t.Timepoint), + TimepointIn24H = gWorkLoad.Sum(t => t.TimepointIn24H), + TimepointIn48H = gWorkLoad.Sum(t => t.TimepointIn48H), + Global = gWorkLoad.Sum(t => t.Global), + Adjudication = gWorkLoad.Sum(t => t.Adjudication), + AdjudicationIn24H = gWorkLoad.Sum(t => t.AdjudicationIn24H), + AdjudicationIn48H = gWorkLoad.Sum(t => t.AdjudicationIn48H), + Total = gWorkLoad.Sum(t => t.Downtime) + + gWorkLoad.Sum(t => t.Training) + + gWorkLoad.Sum(t => t.Timepoint) + + gWorkLoad.Sum(t => t.TimepointIn24H) + + gWorkLoad.Sum(t => t.TimepointIn48H) + + gWorkLoad.Sum(t => t.Global) + + gWorkLoad.Sum(t => t.Adjudication) + + gWorkLoad.Sum(t => t.AdjudicationIn24H) + + gWorkLoad.Sum(t => t.AdjudicationIn48H) + + gWorkLoad.Sum(t => t.RefresherTraining) + + }); + propName = queryParam.SortField == string.Empty ? "YearMonth" : queryParam.SortField; + } + + + #endregion + //if (propName == "FirstName" || propName == "LastName") + //{ + // query = param.Asc + // ? query.OrderBy(t => t.LastName).ThenBy(t => t.FirstName) + // : query.OrderByDescending(t => t.LastName).ThenBy(t => t.FirstName); + //} + + //非详细 只用单字段排序 + if (queryParam.StatType != 0) + { + query = queryParam.Asc ? query.OrderBy(propName) : query.OrderBy(propName + " desc"); + } + //详情 多字段排序 + if (queryParam.StatType == 0) + { + query = queryParam.Asc ? query.OrderBy(propName).ThenBy(t => t.TrialId).ThenBy(t => t.YearMonth).ThenBy(t => t.ReviewerCode) : + query.OrderBy(propName + " desc").ThenBy(t => t.TrialId).ThenBy(t => t.YearMonth).ThenBy(t => t.ReviewerCode) + ; + } + + + count = query!.Count(); + query = query!.Skip((queryParam.PageIndex - 1) * queryParam.PageSize).Take(queryParam.PageSize); + + incomeList = await query.ToListAsync(); + + #region 处理缺失项目价格 按照医生 或者月份维度 + + if (queryParam.StatType == 2 || queryParam.StatType == 3) + { + var hasIncomePriceTrialIds = await _trialRevenuePriceRepository.Select(t => t.TrialId).ToListAsync(); + + workloadLambda = workloadLambda.And(t => !hasIncomePriceTrialIds.Contains(t.TrialId)); + var doctorMissingTrialCodesQuery = (from workLoad in _workloadRepository.Where(workloadLambda) + join trial in _trialRepository.AsQueryable() on workLoad.TrialId equals trial.Id + select new + { + DoctorId = workLoad.DoctorId, + Month = workLoad.YearMonth, + TrialCode = trial.TrialCode + }).Distinct(); + + var doctorMissingTrialCodes = ((await doctorMissingTrialCodesQuery.ToListAsync()).GroupBy(t => t.DoctorId).Select(g => new + { + DoctorId = g.Key, + TrialCodes = g.Select(u => u.TrialCode).Distinct().ToList() + })).ToList(); + + var monthMissingTrialCode = ((await doctorMissingTrialCodesQuery.ToListAsync()).GroupBy(t => t.Month).Select(g => new + { + Month = g.Key, + TrialCodes = g.Select(u => u.TrialCode).Distinct().ToList() + })).ToList(); + + incomeList.ForEach(income => + { + var doctor = doctorMissingTrialCodes.FirstOrDefault(t => t.DoctorId == income.Id); + var month = monthMissingTrialCode.FirstOrDefault(t => t.Month == income.YearMonth); + + if (queryParam.StatType == 2) + { + income.MissingTrialCodes = doctor == null ? new List() : doctor.TrialCodes; + } + else + { + income.MissingTrialCodes = month == null ? new List() : month.TrialCodes; + } + }); + } + + if (queryParam.StatType == 0 || queryParam.StatType == 1) + { + incomeList.ForEach(income => + { + List temp = new List(); + + + + if (Math.Abs(income.Total) < 0.001m) + { + temp.Add(income.TrialCode); + income.MissingTrialCodes = temp; + } + + }); + } + #endregion + + return new PageOutput(queryParam.PageIndex, queryParam.PageSize, count, incomeList); + + } + + /// + /// 收入支出分析接口,按照项目维度分析统计 + /// + [HttpPost] + public async Task> GetTrialAnalysisList(TrialAnalysisQueryDTO param) + { + var bDate = new DateTime(param.BeginDate.Year, param.BeginDate.Month, 1); + var eDate = new DateTime(param.EndDate.Year, param.EndDate.Month, 1); + + Expression> workloadLambda = x => x.DataFrom == (int)WorkLoadFromStatus.FinalConfirm; + + Expression> trialLambda = x => true; + if (Guid.Empty != param.CroId && param.CroId != null) + { + trialLambda = trialLambda.And(u => u.CROId == param.CroId); + } + if (!string.IsNullOrWhiteSpace(param.TrialCode)) + { + var trialCode = param.TrialCode.Trim(); + trialLambda = trialLambda.And(u => u.TrialCode.Contains(trialCode)); + } + if (param.AttendedReviewerType != null) + { + trialLambda = trialLambda.And(u => u.AttendedReviewerType == param.AttendedReviewerType); + } + + var lockedPaymentIdAndYearMonth = _paymentRepository.Where(t => t.IsLock && t.YearMonthDate >= bDate && t.YearMonthDate <= eDate).Select(t => new { PaymentId = t.Id, t.YearMonth, t.DoctorId }); + + var costStatisticsIds = await lockedPaymentIdAndYearMonth.Select(t => t.PaymentId).ToListAsync(); + + var lockedDoctorIdAndYearMonthQueryable = lockedPaymentIdAndYearMonth.Select(t => new { t.YearMonth, t.DoctorId }); + + + ////工作量过滤查询 锁定得月份 + //workloadLambda = workloadLambda.And(x => lockedDoctorIdAndYearMonthQueryable.Contains()); + + var trialPayQuery = from costStatisticsDetail in _paymentDetailRepository.AsQueryable() + .Where(t => costStatisticsIds.Contains(t.PaymentId) && t.TrialCode != "Volume Reward") + group costStatisticsDetail by costStatisticsDetail.TrialId + into g + select new + { + TrialId = g.Key, + PaymentUSD = g.Sum(t => t.PaymentUSD) + }; + + + var workloadQuery = from workLoad in _workloadRepository.Where(workloadLambda) + join lockedDoctorAndMonth in lockedDoctorIdAndYearMonthQueryable on new { workLoad.DoctorId, YearMonth = workLoad.YearMonth } equals new { lockedDoctorAndMonth.DoctorId, lockedDoctorAndMonth.YearMonth } + group workLoad by workLoad.TrialId + + into gWorkLoad + select new + { + TrialId = gWorkLoad.Key, + Downtime = gWorkLoad.Sum(t => t.Downtime), + Training = gWorkLoad.Sum(t => t.Training), + Timepoint = gWorkLoad.Sum(t => t.Timepoint), + TimepointIn24H = gWorkLoad.Sum(t => t.TimepointIn24H), + TimepointIn48H = gWorkLoad.Sum(t => t.TimepointIn48H), + Global = gWorkLoad.Sum(t => t.Global), + Adjudication = gWorkLoad.Sum(t => t.Adjudication), + AdjudicationIn24H = gWorkLoad.Sum(t => t.AdjudicationIn24H), + AdjudicationIn48H = gWorkLoad.Sum(t => t.AdjudicationIn48H) + + }; + + var workloadIncomeQuery = from workLoad in workloadQuery + join trialCost in _trialRevenuePriceRepository.AsQueryable() on workLoad.TrialId equals trialCost.TrialId + into c + from trialCost in c.DefaultIfEmpty() + join trial in _trialRepository.Where(trialLambda) on workLoad.TrialId equals trial.Id into t + from trial in t.DefaultIfEmpty() + join cro in _croRepository.AsQueryable() on trial.CROId equals cro.Id into ttt + from croItem in ttt.DefaultIfEmpty() + select new TrialAnalysisDTO + { + TrialId = trial.Id, + Indication = trial.Indication, + TrialCode = trial.TrialCode, + Expedited = trial.Expedited, + Cro = croItem == null ? "" : croItem.CROName, + + Type = "Trial", + + RevenusUSD = (workLoad.Downtime * trialCost.Downtime) + + (workLoad.Training * trialCost.Training) + + (workLoad.Timepoint * trialCost.Timepoint) + + (workLoad.TimepointIn24H * trialCost.TimepointIn24H) + + (workLoad.TimepointIn48H * trialCost.TimepointIn48H) + + (workLoad.Adjudication * trialCost.Adjudication) + + (workLoad.AdjudicationIn24H * trialCost.AdjudicationIn24H) + + (workLoad.AdjudicationIn48H * trialCost.AdjudicationIn48H) + + (workLoad.Global * trialCost.Global), + + }; + + + var trialPayList = await trialPayQuery.OrderBy(t => t.PaymentUSD).ToListAsync(); + var workloadIncomeList = await workloadIncomeQuery.ToListAsync(); + var returnList = workloadIncomeList; + returnList.ForEach(t => + { + var pay = trialPayList.FirstOrDefault(u => u.TrialId == t.TrialId); + t.PaymentUSD = pay == null ? 0 : pay.PaymentUSD; + }); + + if ((Guid.Empty == param.CroId || null == param.CroId) && string.IsNullOrWhiteSpace(param.TrialCode)) + { + var volumeReward = _paymentDetailRepository.AsQueryable() + .Where(t => costStatisticsIds.Contains(t.PaymentId) && t.TrialCode == "Volume Reward") + .Sum(t => (decimal?)t.PaymentUSD) ?? 0; + + var adjustment = _payAdjustmentRepository.AsQueryable() + .Where(t => t.YearMonthDate >= param.BeginDate + && t.YearMonthDate <= param.EndDate && t.IsLock) + .Sum(u => (decimal?)u.AdjustmentUSD) ?? 0; + + returnList.Add(new TrialAnalysisDTO() + { + Type = "Volume Reward", + RevenusUSD = 0, + PaymentUSD = volumeReward + }); + returnList.Add(new TrialAnalysisDTO() + { + Type = "Adjustment", + RevenusUSD = 0, + PaymentUSD = adjustment + }); + } + return returnList; + } + + /// + /// 收入支出分析接口,按照医生维度分析统计 + /// + [HttpPost] + public async Task> GetReviewerAnalysisList(AnalysisQueryDTO param) + { + var beginDate = new DateTime(param.BeginDate.Year, param.BeginDate.Month, 1); + var endDate = new DateTime(param.EndDate.Year, param.EndDate.Month, 1); + + Expression> workloadLambda = x => x.DataFrom == (int)WorkLoadFromStatus.FinalConfirm; + + Expression> doctorLambda = x => true; + if (!string.IsNullOrWhiteSpace(param.Reviewer)) + { + var reviewer = param.Reviewer.Trim(); + doctorLambda = doctorLambda.And(u => u.ChineseName.Contains(reviewer) + || u.FirstName.Contains(reviewer) + || u.LastName.Contains(reviewer) + || u.ReviewerCode.Contains(reviewer)); + } + if (param.Nation != null) + { + doctorLambda = doctorLambda.And(u => u.Nation == param.Nation); + } + + var lockedPaymentIdAndYearMonth = _paymentRepository.Where(t => t.IsLock && t.YearMonthDate >= param.BeginDate && t.YearMonthDate <= param.EndDate).Select(t => new { PaymentId = t.Id, t.YearMonth, t.DoctorId }); + + var lockedDoctorIdAndYearMonthQueryable = lockedPaymentIdAndYearMonth.Select(t => new { t.YearMonth, t.DoctorId }); + + + + Expression> monthlyPayLambda = x => x.IsLock && x.YearMonthDate >= beginDate && x.YearMonthDate <= endDate; + var payQuery = + from monthlyPayment in _paymentRepository.Where(monthlyPayLambda) + .GroupBy(t => t.DoctorId).Select(g => new + { + ReviewerId = g.Key, + PaymentUSD = g.Sum(u => u.PaymentUSD), + PaymentCNY = g.Sum(u => u.PaymentCNY), + AdjustmentCNY = g.Sum(u => u.AdjustmentCNY), + AdjustmentUSD = g.Sum(u => u.AdjustmentUSD) + }) + join doctor in _doctorRepository.Where(doctorLambda) on monthlyPayment.ReviewerId equals doctor.Id + select new MonthlyPaymentDTO + { + ReviewerId = doctor.Id, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + ReviewerCode = doctor.ReviewerCode, + PaymentUSD = monthlyPayment.PaymentUSD, + PaymentCNY = monthlyPayment.PaymentCNY, + AdjustmentCNY = monthlyPayment.AdjustmentCNY, + AdjustmentUSD = monthlyPayment.AdjustmentUSD, + TotalUSD = monthlyPayment.AdjustmentUSD + monthlyPayment.PaymentUSD, + TotalCNY = monthlyPayment.AdjustmentCNY + monthlyPayment.PaymentCNY, + }; + + //获取工作量收入 workloadLambda 已经加入过滤 payment锁定月份 + + ////工作量过滤查询 锁定得月份 + + var workloadIncomeQuery = from workLoad in _workloadRepository.Where(workloadLambda) + join doctor in _doctorRepository.Where(doctorLambda) + on workLoad.DoctorId equals doctor.Id + join lockedDoctorAndMonth in lockedDoctorIdAndYearMonthQueryable on new { workLoad.DoctorId, workLoad.YearMonth } equals new { lockedDoctorAndMonth.DoctorId, lockedDoctorAndMonth.YearMonth } + join trialCost in _trialRevenuePriceRepository.AsQueryable() on workLoad.TrialId equals trialCost.TrialId into c + from trialCost in c.DefaultIfEmpty() + + select new + { + DoctorId = workLoad.DoctorId, + Downtime = workLoad.Downtime * trialCost.Downtime, + Training = workLoad.Training * trialCost.Training, + Timepoint = workLoad.Timepoint * trialCost.Timepoint, + TimepointIn24H = workLoad.TimepointIn24H * trialCost.TimepointIn24H, + TimepointIn48H = workLoad.TimepointIn48H * trialCost.TimepointIn48H, + Global = workLoad.Global * trialCost.Global, + Adjudication = workLoad.Adjudication * trialCost.Adjudication, + AdjudicationIn24H = workLoad.AdjudicationIn24H * trialCost.AdjudicationIn24H, + AdjudicationIn48H = workLoad.AdjudicationIn48H * trialCost.AdjudicationIn48H + + + }; + + //工作量收入按照人分组统计 + var workloadIncomeList = await workloadIncomeQuery.GroupBy(t => t.DoctorId).Select(gWorkLoad => new + { + + DoctorId = gWorkLoad.Key, + //TrialId = Guid.Empty, + Downtime = gWorkLoad.Sum(t => t.Downtime), + Training = gWorkLoad.Sum(t => t.Training), + Timepoint = gWorkLoad.Sum(t => t.Timepoint), + TimepointIn24H = gWorkLoad.Sum(t => t.TimepointIn24H), + TimepointIn48H = gWorkLoad.Sum(t => t.TimepointIn48H), + Global = gWorkLoad.Sum(t => t.Global), + Adjudication = gWorkLoad.Sum(t => t.Adjudication), + AdjudicationIn24H = gWorkLoad.Sum(t => t.AdjudicationIn24H), + AdjudicationIn48H = gWorkLoad.Sum(t => t.AdjudicationIn48H), + }).ToListAsync(); + var workloadPayList = payQuery.ToList(); + //var workloadIncomeList = workloadIncomeQuery.ToList(); + + var returnList = new List(); + + #region 处理找到缺失的项目 + + //有项目价格的trial Id 集合 + var hasIncomePriceTrialIds = await _trialRevenuePriceRepository.Select(t => t.TrialId).ToListAsync(); + + workloadLambda = workloadLambda.And(t => !hasIncomePriceTrialIds.Contains(t.TrialId)); + var doctorMissingTrialCodesQuery = (from workLoad in _workloadRepository.Where(workloadLambda) + join trial in _trialRepository.AsQueryable() on workLoad.TrialId equals trial.Id + select new + { + DoctorId = workLoad.DoctorId, + TrialCode = trial.TrialCode + }).Distinct(); + + var doctorMissingTrialCodes = ((await doctorMissingTrialCodesQuery.ToListAsync()).GroupBy(t => t.DoctorId).Select(g => new + { + DoctorId = g.Key, + TrialCodes = g.Select(u => u.TrialCode).ToList() + })).ToList(); + + + #endregion + + workloadPayList.ForEach(pay => + { + var doctor = workloadIncomeList.FirstOrDefault(t => t.DoctorId == pay.ReviewerId); + + var doctorMissingCode = doctorMissingTrialCodes.FirstOrDefault(t => t.DoctorId == pay.ReviewerId); + + returnList.Add(new ReviewerAnalysisDTO() + { + ReviewerId = pay.ReviewerId, + ChineseName = pay.ChineseName, + FirstName = pay.FirstName, + LastName = pay.LastName, + ReviewerCode = pay.ReviewerCode, + PaymentUSD = pay.PaymentUSD + pay.AdjustmentUSD, + + //付费表的医生数量肯定事全的 工作量的不一定全 (只有调整) + RevenusUSD = doctor != null + ? doctor.Adjudication + doctor.AdjudicationIn24H + doctor.AdjudicationIn48H + doctor.Global + + doctor.Downtime + doctor.Training + + doctor.Timepoint + doctor.TimepointIn24H + doctor.TimepointIn48H + : 0, + MissingTrialCodes = doctorMissingCode != null ? doctorMissingCode.TrialCodes : new List() + }); + }); + return returnList.OrderBy(t => t.ReviewerCode).ToList(); + } + + /// + /// 获取劳务费用列表 + /// + [HttpPost] + public async Task> GetLaborPaymentList(List paymentIds) + { + var query = from payment in _paymentRepository.Where(t => paymentIds.Contains(t.Id)) + join reviewer in _doctorRepository.AsQueryable() on payment.DoctorId equals reviewer.Id + join payInfo in _doctorPayInfoRepository.AsQueryable() on payment.DoctorId equals payInfo.DoctorId + select new LaborPayment() + { + + ChineseName = reviewer.ChineseName, + FirstName = reviewer.FirstName, + LastName = reviewer.LastName, + ResidentId = payInfo.IDCard, + PaymentCNY = payment.PaymentCNY + payment.AdjustmentCNY, + YearMonth = payment.YearMonth, + Phone = reviewer.Phone, + AccountNumber = payInfo.BankCardNumber, + Bank = payInfo.BankName + + //TaxCNY = payment.TaxCNY, + //ActuallyPaidCNY = payment.ActuallyPaidCNY, + //BankTransferCNY = payment.BankTransferCNY, + }; + var result = await query.ToListAsync(); + + result.ForEach(t => + { + t.TaxCNY =GetTax2(t.PaymentCNY); + + t.ActuallyPaidCNY = t.PaymentCNY - t.TaxCNY; + t.BankTransferCNY = t.PaymentCNY - t.TaxCNY; + }); + + return result; + + } + + /// + /// 锁定医生费用,锁定后,无法变更该医生对应月份的费用和工作量[New] + /// + [HttpPost] + public async Task LockMonthlyPayment(LockPaymentDTO param) + { + var reviewerIds = param.ReviewerIdList; + var yearMonth = param.Month.ToString("yyyy-MM"); + var isLock = param.IsLock; + + + var paymentLockSuccess = await _paymentRepository.BatchUpdateNoTrackingAsync(u => reviewerIds.Contains(u.DoctorId) && u.YearMonth == yearMonth, t => new Payment + { + IsLock = isLock + }); + + var adjustmentLockSuccess = await _payAdjustmentRepository.BatchUpdateNoTrackingAsync( + t => t.YearMonth == yearMonth && reviewerIds.Contains(t.ReviewerId), u => + new PaymentAdjustment() + { + IsLock = true + }); + await _doctorWorkloadRepository.BatchUpdateNoTrackingAsync(u => reviewerIds.Contains(u.DoctorId) && u.YearMonth == yearMonth, + t => new Workload { IsLock = true }); + + return ResponseOutput.Result(paymentLockSuccess || adjustmentLockSuccess); + } + + [NonDynamicMethod] + private static decimal GetTax2(decimal paymentCNY) + { + if (paymentCNY >= 0 && paymentCNY <= 800) + { + return 0; + } + + var calculateTaxPart = paymentCNY <= 4000 ? paymentCNY - 800 : paymentCNY * (1 - 0.2m); + + + if (calculateTaxPart <= 20000) + { + return calculateTaxPart * 0.2m; + } + + else if (calculateTaxPart > 20000 && calculateTaxPart <= 50000) + { + return calculateTaxPart * 0.3m - 2000; + } + + else + { + return calculateTaxPart * 0.4m - 7000; + } + + + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/Interface/ICalculateService.cs b/IRaCIS.Core.Application/Service/Financial/Interface/ICalculateService.cs new file mode 100644 index 0000000..0e4ab8e --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/Interface/ICalculateService.cs @@ -0,0 +1,16 @@ +using IRaCIS.Application.Contracts; +using System; +using System.Collections.Generic; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface ICalculateService + { + Task CalculateMonthlyPayment(CalculateDoctorAndMonthDTO param, string token); + //IResponseOutput LockMonthlyPayment(LockPaymentDTO param); + Task> GetNeedCalculateReviewerList(Guid reviewerId, string yearMonth); + Task IsLock(Guid reviewerId, string yearMonth); + //bool ResetMonthlyPayment(Guid reviewerId, Guid trialId,string yearMonth); + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/Interface/IDoctorPayInfoService.cs b/IRaCIS.Core.Application/Service/Financial/Interface/IDoctorPayInfoService.cs new file mode 100644 index 0000000..87aae6d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/Interface/IDoctorPayInfoService.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IReviewerPayInfoService + { + Task AddOrUpdateReviewerPayInfo(ReviewerPayInfoCommand addOrUpdateModel, Guid userId); + Task> GetReviewerPayInfoList(DoctorPaymentInfoQueryDTO queryParam); + Task GetReviewerPayInfo(Guid doctorId); + Task> GetReviewerIdByRankId(Guid rankId); + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/Interface/IExchangeRateService.cs b/IRaCIS.Core.Application/Service/Financial/Interface/IExchangeRateService.cs new file mode 100644 index 0000000..de5b56b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/Interface/IExchangeRateService.cs @@ -0,0 +1,15 @@ +using IRaCIS.Application.Contracts; +using System; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IExchangeRateService + { + Task AddOrUpdateExchangeRate(ExchangeRateCommand model); + Task GetExchangeRateByMonth(string month); + Task> GetExchangeRateList(ExchangeRateQueryDTO queryParam); + + Task DeleteExchangeRate(Guid id); + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/Interface/IPaymentAdjustmentService.cs b/IRaCIS.Core.Application/Service/Financial/Interface/IPaymentAdjustmentService.cs new file mode 100644 index 0000000..4cf9640 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/Interface/IPaymentAdjustmentService.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Application.Contracts.Pay; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IPaymentAdjustmentService + { + Task> GetPaymentAdjustmentList(PaymentAdjustmentQueryDTO queryParam); + Task AddOrUpdatePaymentAdjustment(PaymentAdjustmentCommand addOrUpdateModel); + Task DeletePaymentAdjustment(Guid id); + Task CalculateCNY(string yearMonth, decimal rate); + Task> GetReviewerSelectList(); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/Interface/IPaymentService.cs b/IRaCIS.Core.Application/Service/Financial/Interface/IPaymentService.cs new file mode 100644 index 0000000..8ddbf2b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/Interface/IPaymentService.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Application.Contracts; +using IRaCIS.Application.Contracts.Pay; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IPaymentService + { + Task LockMonthlyPayment(LockPaymentDTO param); + + Task> GetMonthlyPaymentList(MonthlyPaymentQueryDTO queryParam); + Task GetMonthlyPaymentDetailList(Guid PaymentId, Guid doctorId, DateTime yearMonth); + + Task> GetLaborPaymentList(List paymentId); + + //导出多个医生的付费详细 + Task> GetReviewersMonthlyPaymentDetail(List manyReviewers); + + Task> GetPaymentHistoryList(PaymentQueryDTO param); + Task> GetPaymentHistoryDetailList(VolumeQueryDTO param); + + Task> GetRevenuesStatistics(StatisticsQueryDTO param); + Task> GetTrialAnalysisList(TrialAnalysisQueryDTO param); + Task> GetReviewerAnalysisList(AnalysisQueryDTO param); + + + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/Interface/IRankPriceService.cs b/IRaCIS.Core.Application/Service/Financial/Interface/IRankPriceService.cs new file mode 100644 index 0000000..93ddbdb --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/Interface/IRankPriceService.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IRankPriceService + { + Task AddOrUpdateRankPrice(RankPriceCommand addOrUpdateModel, Guid userId); + + Task> GetRankPriceList(RankPriceQueryDTO queryParam); + + Task DeleteRankPrice( Guid id); + + Task> GetRankDic(); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/Interface/ITrialPaymentPriceService.cs b/IRaCIS.Core.Application/Service/Financial/Interface/ITrialPaymentPriceService.cs new file mode 100644 index 0000000..6ce2a42 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/Interface/ITrialPaymentPriceService.cs @@ -0,0 +1,21 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface ITrialPaymentPriceService + { + Task AddOrUpdateTrialPaymentPrice(TrialPaymentPriceCommand addOrUpdateModel);//新增也不需要返回Id,TrialId 也是唯一 + Task> GetTrialPaymentPriceList(TrialPaymentPriceQueryDTO queryParam); + + + /// + /// 上传入组后的Ack-SOW + /// + Task UploadTrialSOW( TrialSOWPathDTO trialSowPath); + + + + Task DeleteTrialSOW( DeleteSowPathDTO trialSowPath); + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/Interface/ITrialRevenuesPriceService.cs b/IRaCIS.Core.Application/Service/Financial/Interface/ITrialRevenuesPriceService.cs new file mode 100644 index 0000000..f4d0c44 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/Interface/ITrialRevenuesPriceService.cs @@ -0,0 +1,14 @@ +using IRaCIS.Application.Contracts; +using System; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface ITrialRevenuesPriceService + { + Task AddOrUpdateTrialRevenuesPrice(TrialRevenuesPriceDTO model); + Task DeleteTrialCost(Guid Id); + Task> GetTrialRevenuesPriceList(TrialRevenuesPriceQueryDTO param); + + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/Interface/ITrialRevenuesPriceVerificationService.cs b/IRaCIS.Core.Application/Service/Financial/Interface/ITrialRevenuesPriceVerificationService.cs new file mode 100644 index 0000000..19355c7 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/Interface/ITrialRevenuesPriceVerificationService.cs @@ -0,0 +1,17 @@ + +using IRaCIS.Core.Application.Contracts; +using System.Collections.Generic; + + + +namespace IRaCIS.Application.Interfaces +{ + public interface ITrialRevenuesPriceVerificationService + { + //List GetRevenuesVerifyResultList(RevenusVerifyQueryDTO param); + + Task GetAnalysisVerifyList(RevenusVerifyQueryDTO param); + + Task> GetRevenuesVerifyList(RevenusVerifyQueryDTO param); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/Interface/IVolumeRewardService.cs b/IRaCIS.Core.Application/Service/Financial/Interface/IVolumeRewardService.cs new file mode 100644 index 0000000..fa25acd --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/Interface/IVolumeRewardService.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IVolumeRewardService + { + Task AddOrUpdateVolumeRewardPriceList(IEnumerable addOrUpdateModels); + Task> GetVolumeRewardPriceList(AwardPriceQueryDTO queryParam); + Task> GetVolumeRewardPriceList(); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/PaymentAdjustmentService.cs b/IRaCIS.Core.Application/Service/Financial/PaymentAdjustmentService.cs new file mode 100644 index 0000000..2cd692e --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/PaymentAdjustmentService.cs @@ -0,0 +1,313 @@ +using AutoMapper; +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts.Pay; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Financial")] + public class PaymentAdjustmentService : BaseService, IPaymentAdjustmentService + { + private readonly IRepository _payAdjustmentRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _exchangeRateRepository; + private readonly IRepository _paymentRepository; + + + public PaymentAdjustmentService(IRepository costAdjustmentRepository, IRepository doctorRepository, + IRepository exchangeRateRepository, IRepository paymentRepository, IMapper mapper) + { + _payAdjustmentRepository = costAdjustmentRepository; + _doctorRepository = doctorRepository; + _exchangeRateRepository = exchangeRateRepository; + _paymentRepository = paymentRepository; + + } + + /// + /// 添加或更新费用调整[AUTH] + /// + + [HttpPost] + public async Task AddOrUpdatePaymentAdjustment(PaymentAdjustmentCommand addOrUpdateModel) + { + + var yearMonthDate = new DateTime(addOrUpdateModel.YearMonth.Year, addOrUpdateModel.YearMonth.Month, 1); + var yearMonth = addOrUpdateModel.YearMonth.ToString("yyyy-MM"); + + var payment = await _paymentRepository.FirstOrDefaultAsync(u => u.DoctorId == addOrUpdateModel.ReviewerId + && u.YearMonth == yearMonth); + + //判断付费表中是否有记录 + if (payment == null) + { + //没有 添加仅有的调整费用记录 + payment = new Payment + { + DoctorId = addOrUpdateModel.ReviewerId, + YearMonth = yearMonth, + YearMonthDate = yearMonthDate, + PaymentCNY = 0, + PaymentUSD = 0, + AdjustmentCNY = 0, + AdjustmentUSD = 0 + }; + await _paymentRepository.AddAsync(payment); + await _paymentRepository.SaveChangesAsync(); + } + else + { + if (payment.IsLock) + { + return ResponseOutput.NotOk("Doctor payment has confirmed lock"); + } + } + + var exchangeRate = await _exchangeRateRepository.FirstOrDefaultAsync(t => t.YearMonth == yearMonth); + if (addOrUpdateModel.Id == Guid.Empty || addOrUpdateModel.Id == null) + { + var costAdjustment = _mapper.Map(addOrUpdateModel); + + //视图模型和领域模型没对应 重新赋值 + costAdjustment.ExchangeRate = exchangeRate?.Rate ?? 0; + costAdjustment.AdjustmentCNY = addOrUpdateModel.AdjustPaymentUSD * (exchangeRate?.Rate ?? 0); + costAdjustment.AdjustmentUSD = addOrUpdateModel.AdjustPaymentUSD; + + await _payAdjustmentRepository.AddAsync(costAdjustment); + + //添加的时候,每个月调整汇总费用 需要加上本次调整的费用 + payment.AdjustmentCNY += costAdjustment.AdjustmentCNY; + payment.AdjustmentUSD += costAdjustment.AdjustmentUSD; + + //await _paymentRepository.UpdateAsync(payment); + + await _paymentRepository.UpdateAsync(payment, u => new Payment() + { + AdjustmentCNY = payment.AdjustmentCNY + costAdjustment.AdjustmentCNY, + AdjustmentUSD = payment.AdjustmentUSD + costAdjustment.AdjustmentUSD + }); + + await _payAdjustmentRepository.SaveChangesAsync(); + + + return ResponseOutput.Ok(costAdjustment.Id.ToString()); + + } + else + { + + + + // 更新的时候,先查出来,更新前的调整费用数据 + + var paymentAdjust = await _payAdjustmentRepository.FirstOrDefaultAsync(t => t.Id == addOrUpdateModel.Id); + + + _mapper.Map(addOrUpdateModel, paymentAdjust); + + paymentAdjust.ExchangeRate = exchangeRate?.Rate ?? 0; + paymentAdjust.AdjustmentUSD = addOrUpdateModel.AdjustPaymentUSD; + paymentAdjust.AdjustmentCNY = addOrUpdateModel.AdjustPaymentUSD * (exchangeRate?.Rate ?? 0); + + //await _payAdjustmentRepository.UpdateAsync(paymentAdjust); + + var success = await _payAdjustmentRepository.SaveChangesAsync(); + + if (success) + { + + var adjustmentList = await _payAdjustmentRepository.Where(u => u.ReviewerId == addOrUpdateModel.ReviewerId && u.YearMonth == yearMonth).ToListAsync(); + + + await _paymentRepository.UpdateAsync(payment, u => new Payment() + { + AdjustmentCNY = adjustmentList.Sum(t => t.AdjustmentCNY), + AdjustmentUSD = adjustmentList.Sum(t => t.AdjustmentUSD) + }); + + //payment.AdjustmentCNY = adjustmentList.Sum(t => t.AdjustmentCNY); + //payment.AdjustmentUSD = adjustmentList.Sum(t => t.AdjustmentUSD); + + //await _paymentRepository.UpdateAsync(payment); + + await _paymentRepository.SaveChangesAsync(); + } + + //查询得到历史汇总 + + + + return ResponseOutput.Ok(success); + + #region 逻辑存在错误 问题待查 + + //// 更新的时候,先查出来,更新前的调整费用数据 + //var paymentAdjust = _payAdjustmentRepository.FindSingleOrDefault(t => t.Id == addOrUpdateModel.Id); + + ////减去数据库本条记录的值 + //payment.AdjustmentCNY = -paymentAdjust.AdjustmentCNY; + //payment.AdjustmentUSD = -paymentAdjust.AdjustmentUSD; + + //_mapper.Map(addOrUpdateModel, paymentAdjust); + + //paymentAdjust.ExchangeRate = exchangeRate?.Rate ?? 0; + //paymentAdjust.AdjustmentUSD = addOrUpdateModel.AdjustPaymentUSD; + //paymentAdjust.AdjustmentCNY = addOrUpdateModel.AdjustPaymentUSD * (exchangeRate?.Rate ?? 0); + + //_payAdjustmentRepository.Update(paymentAdjust); + + ////查询得到历史汇总 + //var adjustment = _payAdjustmentRepository.Find(u => u.ReviewerId == addOrUpdateModel.ReviewerId && u.YearMonth == yearMonth) + // .GroupBy(u => new { u.ReviewerId, u.YearMonth }).Select(g => new + // { + // AdjustCNY = g.Sum(t => t.AdjustmentCNY), + // AdjustUSD = g.Sum(t => t.AdjustmentUSD) + // }).FirstOrDefault(); + + ////最终的值 等于历史汇总 减去更新前的加上当前更新的值 + //payment.AdjustmentCNY += (adjustment.AdjustCNY + paymentAdjust.AdjustmentCNY); + //payment.AdjustmentUSD += (adjustment.AdjustUSD + paymentAdjust.AdjustmentUSD); + + //_paymentRepository.Update(payment); + + //var success = _payAdjustmentRepository.SaveChanges(); + //return ResponseOutput.Result(success, success ? string.Empty : StaticData.UpdateFailed); + + #endregion + + + + } + } + /// + /// 删除费用调整记录 + /// + + [HttpDelete("{id:guid}")] + public async Task DeletePaymentAdjustment(Guid id) + { + var adjustPayment = await _payAdjustmentRepository.FirstOrDefaultAsync(u => u.Id == id); + + + await _payAdjustmentRepository.DeleteAsync(new PaymentAdjustment() { Id = id }); + + var success = await _payAdjustmentRepository.SaveChangesAsync(); + + if (success) + { + + var adjustmentList = await _payAdjustmentRepository.Where(u => + u.ReviewerId == adjustPayment.ReviewerId && u.YearMonth == adjustPayment.YearMonth).ToListAsync(); + + + var monthPay = await _paymentRepository.FirstOrDefaultAsync(t => + t.DoctorId == adjustPayment.ReviewerId && t.YearMonth == adjustPayment.YearMonth); + + + + await _paymentRepository.UpdateAsync(monthPay, u => new Payment() + { + AdjustmentCNY = adjustmentList.Sum(t => t.AdjustmentCNY), + AdjustmentUSD = adjustmentList.Sum(t => t.AdjustmentUSD) + }); + + + //monthPay.AdjustmentCNY = adjustmentList.Sum(t => t.AdjustmentCNY); + //monthPay.AdjustmentUSD = adjustmentList.Sum(t => t.AdjustmentUSD); + //await _paymentRepository.UpdateAsync(monthPay); + + await _paymentRepository.SaveChangesAsync(); + } + + + + return ResponseOutput.Result(success); + } + + /// + /// 获取费用调整列表 + /// + [HttpPost] + public async Task> GetPaymentAdjustmentList(PaymentAdjustmentQueryDTO queryParam) + { + + var beginYearMonth = queryParam.BeginMonth.AddDays(1 - queryParam.BeginMonth.Day); + var endYearMonth = queryParam.EndMonth.AddDays(1 - queryParam.EndMonth.Day).AddMonths(1).AddDays(-1); + + var costAdjustmentQueryable = from costAdjustment in _payAdjustmentRepository + .Where(t => t.YearMonthDate >= beginYearMonth && t.YearMonthDate <= endYearMonth) + join doctor in _doctorRepository.AsQueryable(). + WhereIf(!string.IsNullOrWhiteSpace(queryParam.Reviewer), + u => u.ChineseName.Contains(queryParam.Reviewer) || + (u.LastName + u.FirstName).Contains(queryParam.Reviewer) || + u.ReviewerCode.Contains(queryParam.Reviewer)) + on costAdjustment.ReviewerId equals doctor.Id + + select new PaymentAdjustmentDetailDTO() + { + AdjustPaymentCNY = costAdjustment.AdjustmentCNY, + AdjustPaymentUSD = costAdjustment.AdjustmentUSD, + IsLock = costAdjustment.IsLock, + Id = costAdjustment.Id, + YearMonth = costAdjustment.YearMonth, + YearMonthDate = costAdjustment.YearMonthDate, + Note = costAdjustment.Note, + ReviewerId = costAdjustment.ReviewerId, + ReviewerCode = doctor.ReviewerCode, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + ChineseName = doctor.ChineseName + }; + + return await costAdjustmentQueryable.ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize, string.IsNullOrWhiteSpace(queryParam.SortField) ? "YearMonthDate" : queryParam.SortField, queryParam.Asc); + + + } + + public async Task> GetReviewerSelectList() + { + return await _doctorRepository.Where(t => t.CooperateStatus == ContractorStatusEnum.Cooperation && t.ResumeStatus == ResumeStatusEnum.Pass).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + [NonDynamicMethod] + public async Task CalculateCNY(string yearMonth, decimal rate) + { + + //如果是double 不会保留两位小数 + await _payAdjustmentRepository.BatchUpdateNoTrackingAsync(u => u.YearMonth == yearMonth && + !u.IsLock, t => new PaymentAdjustment + { + AdjustmentCNY = t.AdjustmentUSD * rate, + ExchangeRate = rate, + UpdateTime = DateTime.Now + }); + + var adjustList = await _payAdjustmentRepository.Where(u => u.YearMonth == yearMonth && + !u.IsLock).ToListAsync(); + + + + var needUpdatePayment = adjustList.GroupBy(t => t.ReviewerId).Select(g => new + { + ReviewerId = g.Key, + AdjustCNY = g.Sum(t => t.AdjustmentCNY), + AdjustUSD = g.Sum(t => t.AdjustmentUSD) + }); + + foreach (var reviewer in needUpdatePayment) + { + await _paymentRepository.BatchUpdateNoTrackingAsync(u => u.YearMonth == yearMonth && + !u.IsLock && u.DoctorId == reviewer.ReviewerId, t => new Payment() + { + AdjustmentUSD = reviewer.AdjustUSD, + AdjustmentCNY = reviewer.AdjustCNY + + }); + } + + + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/RankPriceService.cs b/IRaCIS.Core.Application/Service/Financial/RankPriceService.cs new file mode 100644 index 0000000..6ec7036 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/RankPriceService.cs @@ -0,0 +1,104 @@ +using AutoMapper; +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Application.Services +{ + [ ApiExplorerSettings(GroupName = "Financial")] + public class RankPriceService : BaseService, IRankPriceService + { + private readonly IRepository _rankPriceRepository; + private readonly IRepository _reviewerPayInfoRepository; + + + public RankPriceService(IRepository rankPriceRepository, IRepository reviewerPayInfoRepository,IMapper mapper) + { + _rankPriceRepository = rankPriceRepository; + _reviewerPayInfoRepository = reviewerPayInfoRepository; + + } + + [NonDynamicMethod] + public async Task AddOrUpdateRankPrice(RankPriceCommand addOrUpdateModel, Guid userId) + { + if (addOrUpdateModel.Id == Guid.Empty|| addOrUpdateModel.Id ==null) + { + var rankPrice = _mapper.Map(addOrUpdateModel); + rankPrice = await _rankPriceRepository.AddAsync(rankPrice); + if (await _rankPriceRepository.SaveChangesAsync()) + { + return ResponseOutput.Ok(rankPrice.Id.ToString()); + } + else + { + return ResponseOutput.NotOk(); + } + + } + else + { + var success =await _rankPriceRepository.BatchUpdateNoTrackingAsync(t => t.Id == addOrUpdateModel.Id, u => new RankPrice() + { + UpdateUserId = userId, + UpdateTime = DateTime.Now, + RefresherTraining=addOrUpdateModel.RefresherTraining, + RankName = addOrUpdateModel.RankName, + Timepoint = addOrUpdateModel.Timepoint, + TimepointIn24H = addOrUpdateModel.TimepointIn24H, + TimepointIn48H = addOrUpdateModel.TimepointIn48H, + Adjudication = addOrUpdateModel.Adjudication, + AdjudicationIn24H = addOrUpdateModel.AdjudicationIn24H, + AdjudicationIn48H = addOrUpdateModel.AdjudicationIn48H, + Global = addOrUpdateModel.Global, + Training = addOrUpdateModel.Training, + Downtime = addOrUpdateModel.Downtime + + }); + + + return ResponseOutput.Result(success); + } + } + + + [HttpDelete("{id:guid}")] + public async Task DeleteRankPrice(Guid id) + { + + if (await _reviewerPayInfoRepository.AnyAsync(t => t.RankId == id)) + { + return ResponseOutput.NotOk("This title has been used by reviewer payment information"); + } + + var success = await _rankPriceRepository.BatchDeleteNoTrackingAsync(t => t.Id == id); + + return ResponseOutput.Result(success); + } + + /// + /// 获取职称单价列表 + /// + [HttpPost] + public async Task> GetRankPriceList(RankPriceQueryDTO queryParam) + { + var rankPriceQueryable = _rankPriceRepository.ProjectTo(_mapper.ConfigurationProvider); + return await rankPriceQueryable.ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize, "ShowOrder", queryParam.Asc); + + } + + + public async Task> GetRankDic() + { + var rankQueryable = _rankPriceRepository.ProjectTo(_mapper.ConfigurationProvider); + return await rankQueryable.ToListAsync(); + + } + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/ReviewerPayInfoService.cs b/IRaCIS.Core.Application/Service/Financial/ReviewerPayInfoService.cs new file mode 100644 index 0000000..3f12a31 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/ReviewerPayInfoService.cs @@ -0,0 +1,144 @@ +using AutoMapper; +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using System.Linq.Expressions; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Application.Services +{ + [ ApiExplorerSettings(GroupName = "Financial")] + public class ReviewerPayInfoService : BaseService, IReviewerPayInfoService + { + private readonly IRepository _doctorPayInfoRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _rankPriceRepository; + private readonly IRepository _hospitalRepository; + + + public ReviewerPayInfoService(IRepository doctorRepository, IRepository doctorPayInfoRepository, + IRepository rankPriceRepository, IRepository hospitalRepository, IMapper mapper) + { + _doctorPayInfoRepository = doctorPayInfoRepository; + _doctorRepository = doctorRepository; + _rankPriceRepository = rankPriceRepository; + _hospitalRepository = hospitalRepository; + + } + [NonDynamicMethod] + public async Task AddOrUpdateReviewerPayInfo(ReviewerPayInfoCommand addOrUpdateModel, Guid userId) + { + var success = false; + var doctorPayInfoExistedItem = await _doctorPayInfoRepository.FirstOrDefaultAsync(u => u.DoctorId == addOrUpdateModel.DoctorId); + if (doctorPayInfoExistedItem == null)//insert + { + + await _doctorPayInfoRepository.InsertFromDTOAsync(addOrUpdateModel); + + } + else//update + { + await _doctorPayInfoRepository.UpdateFromDTOAsync(addOrUpdateModel); + + } + success = await _doctorPayInfoRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + + /// + /// 获取医生支付信息列表 + /// + [HttpPost] + public async Task> GetReviewerPayInfoList(DoctorPaymentInfoQueryDTO queryParam) + { + + + var doctorQueryable = from doctor in _doctorRepository.AsQueryable() + .WhereIf(queryParam.HospitalId != null, o => o.HospitalId == queryParam.HospitalId) + .WhereIf(!string.IsNullOrEmpty(queryParam.SearchName), + u => u.ChineseName.Contains(queryParam.SearchName)|| (u.LastName+ u.FirstName).Contains(queryParam.SearchName)) + join hospitalItem in _hospitalRepository.AsQueryable() on doctor.HospitalId equals hospitalItem.Id into gt + from hospital in gt.DefaultIfEmpty() + join trialPayInfo in _doctorPayInfoRepository.Where() + on doctor.Id equals trialPayInfo.DoctorId into payInfo + from doctorPayInfo in payInfo.DefaultIfEmpty() + join rankPrice in _rankPriceRepository.Where() + on doctorPayInfo.RankId equals rankPrice.Id into rankPriceInfo + from rankPrice in rankPriceInfo.DefaultIfEmpty() + select new DoctorPayInfoQueryListDTO + { + //Id = doctorPayInfo.Id, + DoctorId = doctor.Id, + Code = doctor.ReviewerCode, + LastName = doctor.LastName, + FirstName = doctor.FirstName, + ChineseName = doctor.ChineseName, + Phone = doctor.Phone, + DoctorNameInBank = doctorPayInfo.DoctorNameInBank, + IDCard = doctorPayInfo.IDCard, + BankCardNumber = doctorPayInfo.BankCardNumber, + BankName = doctorPayInfo.BankName, + RankId = doctorPayInfo.RankId, + RankName = rankPrice.RankName, + Additional = doctorPayInfo.Additional, + Hospital = hospital.HospitalName, + CreateTime = doctor.CreateTime + }; + + return await doctorQueryable.ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize, "Code", queryParam.Asc); + + + } + + + /// + /// 根据医生Id获取支付信息 + /// + /// 医生Id + /// + [HttpGet("{doctorId:guid}")] + public async Task GetReviewerPayInfo(Guid doctorId) + { + var doctorQueryable = from doctor in _doctorRepository.Where(u => u.Id == doctorId) + join trialPayInfo in _doctorPayInfoRepository.Where() + on doctor.Id equals trialPayInfo.DoctorId into payInfo + from doctorPayInfo in payInfo.DefaultIfEmpty() + join rankPrice in _rankPriceRepository.Where() + on doctorPayInfo.RankId equals rankPrice.Id into rankPriceInfo + from rankPrice in rankPriceInfo.DefaultIfEmpty() + select new DoctorPayInfoQueryListDTO + { + //Id = doctorPayInfo.Id, + DoctorId = doctor.Id, + Code = doctor.ReviewerCode, + LastName = doctor.LastName, + FirstName = doctor.FirstName, + ChineseName = doctor.ChineseName, + Phone = doctor.Phone, + DoctorNameInBank = doctorPayInfo.DoctorNameInBank, + IDCard = doctorPayInfo.IDCard, + BankCardNumber = doctorPayInfo.BankCardNumber, + BankName = doctorPayInfo.BankName, + RankId = doctorPayInfo.RankId, + RankName = rankPrice.RankName, + Additional = doctorPayInfo.Additional, + CreateTime = doctor.CreateTime + }; + + return (await doctorQueryable.FirstOrDefaultAsync()).IfNullThrowException(); + } + + /// + /// 根据rankId 获取ReviewerId,用于当Rank的单价信息改变时,触发费用计算 + /// + /// + /// + public async Task> GetReviewerIdByRankId(Guid rankId) + { + return await _doctorPayInfoRepository.Where(u => u.RankId == rankId).Select(u => u.DoctorId).ToListAsync(); + } + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/TrialPaymentPriceService.cs b/IRaCIS.Core.Application/Service/Financial/TrialPaymentPriceService.cs new file mode 100644 index 0000000..3f8091b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/TrialPaymentPriceService.cs @@ -0,0 +1,188 @@ +using AutoMapper; +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Financial")] + public class TrialPaymentPriceService : BaseService, ITrialPaymentPriceService + { + private readonly IRepository _trialExtRepository; + private readonly IRepository _enrollRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _croRepository; + + + private readonly IRepository _trialRepository; + public TrialPaymentPriceService(IRepository trialRepository, IRepository trialExtRepository, + IRepository enrollRepository, + IRepository doctorRepository, + IRepository croCompanyRepository, IMapper mapper) + { + _trialExtRepository = trialExtRepository; + _croRepository = croCompanyRepository; + _enrollRepository = enrollRepository; + _doctorRepository = doctorRepository; + + _trialRepository = trialRepository; + } + + /// + /// 添加或更新项目支付价格信息 + /// + [NonDynamicMethod] + public async Task AddOrUpdateTrialPaymentPrice(TrialPaymentPriceCommand addOrUpdateModel) + { + + var trialExistedItem = await _trialExtRepository.FirstOrDefaultAsync(u => u.TrialId == addOrUpdateModel.TrialId); + if (trialExistedItem == null)//insert + { + + await _trialExtRepository.InsertFromDTOAsync(addOrUpdateModel); + + } + else//update + { + await _trialExtRepository.UpdateFromDTOAsync(addOrUpdateModel); + + } + var success = await _trialExtRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + + + [HttpPost] + public async Task UploadTrialSOW(TrialSOWPathDTO trialSowPath) + { + var trialPaymentPrice = await _trialExtRepository.FirstOrDefaultAsync(u => u.TrialId == trialSowPath.TrialId); + + if (trialPaymentPrice == null)//添加 + { + await _trialExtRepository.InsertFromDTOAsync(trialSowPath); + } + else//更新 + { + await _trialExtRepository.UpdateFromDTOAsync(trialSowPath); + } + + var success = await _trialExtRepository.SaveChangesAsync(); + + return ResponseOutput.Result(success); + } + + + [HttpPost] + public async Task DeleteTrialSOW(DeleteSowPathDTO trialSowPath) + { + var success = await _trialExtRepository.BatchUpdateNoTrackingAsync(u => u.TrialId == trialSowPath.TrialId, s => new TrialPaymentPrice + { + SowPath = "", + SowName = "", + UpdateTime = DateTime.Now, + UpdateUserId = _userInfo.Id + }); + + return ResponseOutput.Result(success); + } + + /// + /// 获取项目支付价格信息列表 + /// + [HttpPost] + public async Task> GetTrialPaymentPriceList(TrialPaymentPriceQueryDTO queryParam) + { + #region hwt + //var trialQueryable = from trial in _trialRepository.AsQueryable() + // .WhereIf(queryParam.CroId != null, o => o.CROId == queryParam.CroId) + // .WhereIf(!string.IsNullOrEmpty(queryParam.KeyWord), o => o.TrialCode.Contains(queryParam.KeyWord) || o.Indication.Contains(queryParam.KeyWord)) + // join cro in _croRepository.AsQueryable() on trial.CROId equals cro.Id into CRO + // from croInfo in CRO.DefaultIfEmpty() + // join trialExt in _trialExtRepository.Where() + // on trial.Id equals trialExt.TrialId into trialInfo + // from trialExt in trialInfo.DefaultIfEmpty() + // select new TrialPaymentPriceDTO + // { + // //Id = trialExt.Id , + // IsNewTrial = trialExt.IsNewTrial, + // TrialId = trial.Id, + // TrialCode = trial.TrialCode, + // Cro = croInfo.CROName, + // Indication = trial.Indication, + // Expedited = trial.Expedited, + // TrialAdditional = trialExt.TrialAdditional, + // AdjustmentMultiple = trialExt.AdjustmentMultiple, + // SowName = trialExt.SowName, + // SowPath = trialExt.SowPath, + // CreateTime = trial.CreateTime, + // }; + + //var namelist = (from enroll in _enrollRepository.AsQueryable() + // join doctor in _doctorRepository.Where() on enroll.DoctorId equals doctor.Id + // select new DtoDoctorList() + // { + // TrialId = enroll.TrialId, + // Name = doctor.ChineseName + // }).ToList().GroupBy(x => new { x.TrialId }, + //(key, lst) => new DtoDoctorList + //{ + // TrialId = key.TrialId, + // Name = string.Join(',', lst.Select(x => x.Name)) + //}); + + //var returndata = trialQueryable.ToPagedList(queryParam.PageIndex, queryParam.PageSize, "CreateTime", queryParam.Asc); + + //returndata.CurrentPageData.ForEach(x => { + // x.DoctorsNames = namelist.Where(y => y.TrialId == x.TrialId).Select(y => y.Name).FirstOrDefault() ?? string.Empty; + //}); + //return returndata; + + #endregion + + #region byzhouhang 方式一 + + //var trialQueryable = _trialExtRepository.Where(t => t.Trial.IsDeleted == false) + // .WhereIf(queryParam.CroId != null, o => o.Trial.CROId == queryParam.CroId) + // .WhereIf(!string.IsNullOrEmpty(queryParam.KeyWord), o => o.Trial.TrialCode.Contains(queryParam.KeyWord) || o.Trial.Indication.Contains(queryParam.KeyWord)) + // .Select(trialExt => new TrialPaymentPriceDTO() + // { + // TrialCode = trialExt.Trial.TrialCode, + // Cro = trialExt.Trial.CRO.CROName, + // Indication = trialExt.Trial.Indication, + // Expedited = trialExt.Trial.Expedited, + + // TrialId = trialExt.TrialId, + // IsNewTrial = trialExt.IsNewTrial, + // SowName = trialExt.SowName, + // SowPath = trialExt.SowPath, + // TrialAdditional = trialExt.TrialAdditional, + // AdjustmentMultiple = trialExt.AdjustmentMultiple, + // CreateTime = trialExt.CreateTime, + // DoctorsNames = string.Join(',', trialExt.Trial.EnrollList.Select(t => t.Doctor.ChineseName)) + // }); + + //return trialQueryable.ToPagedList(queryParam.PageIndex, queryParam.PageSize, string.IsNullOrEmpty(queryParam.SortField) ? "CreateTime" : queryParam.SortField, queryParam.Asc); + + #endregion + + + #region byzhouhang 方式二 + + var trialQueryable2 = _trialExtRepository.Where(t => t.Trial.IsDeleted == false) + .WhereIf(queryParam.CroId != null, o => o.Trial.CROId == queryParam.CroId) + .WhereIf(!string.IsNullOrEmpty(queryParam.KeyWord), o => o.Trial.TrialCode.Contains(queryParam.KeyWord) || o.Trial.Indication.Contains(queryParam.KeyWord)) + .ProjectTo(_mapper.ConfigurationProvider); + + return await trialQueryable2.ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize, string.IsNullOrEmpty(queryParam.SortField) ? "CreateTime" : queryParam.SortField, queryParam.Asc); + + #endregion + + + + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/TrialRevenuesPriceService.cs b/IRaCIS.Core.Application/Service/Financial/TrialRevenuesPriceService.cs new file mode 100644 index 0000000..48968b7 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/TrialRevenuesPriceService.cs @@ -0,0 +1,151 @@ +using AutoMapper; +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using System.Linq.Expressions; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Application.Services +{ + [ ApiExplorerSettings(GroupName = "Financial")] + public class TrialRevenuesPriceService : BaseService, ITrialRevenuesPriceService + { + private readonly IRepository _trialRepository; + private readonly IRepository _trialRevenuesPriceRepository; + private readonly IRepository _croRepository; + private readonly IRepository _dictionaryRepository; + private readonly IRepository _trialRevenuesPriceVerificationRepository; + + + public TrialRevenuesPriceService(IRepository trialRepository, IRepository trialCostRepository, IRepository croCompanyRepository, IRepository dictionaryRepository, IRepository trialRevenuesPriceVerificationRepository, IMapper mapper) + { + _trialRepository = trialRepository; + _trialRevenuesPriceRepository = trialCostRepository; + _croRepository = croCompanyRepository; + _dictionaryRepository = dictionaryRepository; + _trialRevenuesPriceVerificationRepository = trialRevenuesPriceVerificationRepository; + + } + + public async Task AddOrUpdateTrialRevenuesPrice(TrialRevenuesPriceDTO model) + { + var count = model.Timepoint + + model.TimepointIn24H + + model.TimepointIn48H + + model.Adjudication + + model.AdjudicationIn24H + + model.AdjudicationIn48H + + model.Downtime + + model.Global + + model.Training; + + if (count <= 0) + { + return ResponseOutput.NotOk("Please add meaningful data"); + } + var trialExistedItem = await _trialRevenuesPriceRepository.FirstOrDefaultAsync(u => u.TrialId == model.TrialId); + if (trialExistedItem == null)//insert + { + var trialCost = _mapper.Map(model); + await _trialRevenuesPriceRepository.AddAsync(trialCost); + var success = await _trialRevenuesPriceRepository.SaveChangesAsync(); + return ResponseOutput.Result(success, trialCost.Id.ToString()); + } + else//update + { + //var trialRevenuesPrice = (await _trialRevenuesPriceRepository.AsQueryable().FirstOrDefaultAsync(u => u.TrialId == model.TrialId)).IfNullThrowException(); + + await _trialRevenuesPriceRepository.UpdateFromDTOAsync(model); + + // 完善价格的 将对应的列设置为true 变更为有价格了 + + var aaa = await _trialRevenuesPriceVerificationRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == model.TrialId, u => new TrialRevenuesPriceVerification() + { + //有价格 则设置为true 否则 该列不变 + Timepoint = model.Timepoint > 0 || u.Timepoint, + TimepointIn24H = model.TimepointIn24H > 0 || u.TimepointIn24H, + TimepointIn48H = model.TimepointIn48H > 0 || u.TimepointIn48H, + Adjudication = model.Adjudication > 0 || u.Adjudication, + AdjudicationIn24H = + model.AdjudicationIn24H > 0 || u.AdjudicationIn24H, + AdjudicationIn48H = + model.AdjudicationIn48H > 0 || u.AdjudicationIn48H, + Global = model.Global > 0 || u.Global, + Downtime = model.Downtime > 0 || u.Downtime, + Training = model.Training > 0 || u.Training, + RefresherTraining = model.RefresherTraining > 0 || u.RefresherTraining, + }); + + //删除所有有价格的记录 为true 表示有价格或者不需要价格 缺价格的为false + await _trialRevenuesPriceVerificationRepository.BatchDeleteNoTrackingAsync(t => t.TrialId == model.TrialId && + t.Timepoint&& + t.TimepointIn24H&& + t.TimepointIn48H && + t.Adjudication && + t.AdjudicationIn24H && + t.AdjudicationIn48H && + t.Global && + t.Training &&t.RefresherTraining); + + + + var success = await _trialRevenuesPriceRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + } + + [NonDynamicMethod] + public async Task DeleteTrialCost(Guid id) + { + return await _trialRevenuesPriceRepository.BatchDeleteNoTrackingAsync(u => u.Id == id); + } + + /// + /// 获取项目收入费用信息列表[New] + /// + [HttpPost] + public async Task> GetTrialRevenuesPriceList(TrialRevenuesPriceQueryDTO queryParam) + { + + var trialQueryable = from trial in _trialRepository.AsQueryable() + .Where(u => u.TrialCode.Contains(queryParam.KeyWord)|| u.Indication.Contains(queryParam.KeyWord)) + .WhereIf(queryParam.CroId != null, o => o.CROId == queryParam.CroId) + join cro in _croRepository.AsQueryable() on trial.CROId equals cro.Id into CRO + from croInfo in CRO.DefaultIfEmpty() + join dic in _dictionaryRepository.AsQueryable() on trial.ReviewModeId equals dic.Id into dict + from dic in dict.DefaultIfEmpty() + join trialCost in _trialRevenuesPriceRepository.AsQueryable() + on trial.Id equals trialCost.TrialId into trialInfo + from trialCostItem in trialInfo.DefaultIfEmpty() + select new TrialRevenuesPriceDetialDTO + { + Id = trialCostItem == null ? Guid.Empty : trialCostItem.Id, + TrialId = trial.Id, + TrialCode = trial.TrialCode, + Indication = trial.Indication, + Cro = croInfo == null ? string.Empty : croInfo.CROName, + ReviewMode = dic == null ? string.Empty : dic.Value, + Timepoint = trialCostItem == null ? 0 : trialCostItem.Timepoint, + TimepointIn24H = trialCostItem == null ? 0 : trialCostItem.TimepointIn24H, + TimepointIn48H = trialCostItem == null ? 0 : trialCostItem.TimepointIn48H, + Adjudication = trialCostItem == null ? 0 : trialCostItem.Adjudication, + AdjudicationIn24H = trialCostItem == null ? 0 : trialCostItem.AdjudicationIn24H, + AdjudicationIn48H = trialCostItem == null ? 0 : trialCostItem.AdjudicationIn48H, + Downtime = trialCostItem == null ? 0 : trialCostItem.Downtime, + Global = trialCostItem == null ? 0 : trialCostItem.Global, + Training = trialCostItem == null ? 0 : trialCostItem.Training, + RefresherTraining= trialCostItem == null ? 0 : trialCostItem.RefresherTraining, + Expedited = trial.Expedited + + }; + + return await trialQueryable.ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize, "TrialCode", queryParam.Asc); + + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Financial/TrialRevenuesPriceVerificationService.cs b/IRaCIS.Core.Application/Service/Financial/TrialRevenuesPriceVerificationService.cs new file mode 100644 index 0000000..f197f8b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/TrialRevenuesPriceVerificationService.cs @@ -0,0 +1,197 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Core.Domain.Models; +using System.Linq.Expressions; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Application.Contracts; + +namespace IRaCIS.Core.Application.Services +{ + /// + /// Financial---项目收入价格验证 + /// + [ ApiExplorerSettings(GroupName = "Financial")] + public class TrialRevenuesPriceVerificationService : BaseService, ITrialRevenuesPriceVerificationService + { + private readonly IRepository _trialRevenuesPriceVerificationRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _paymentRepository; + + public TrialRevenuesPriceVerificationService(IRepository trialRevenuesPriceVerificationRepository, + IRepository trialRepository,IRepository doctorRepository,IRepository paymentRepository) + { + _trialRevenuesPriceVerificationRepository = trialRevenuesPriceVerificationRepository; + _trialRepository = trialRepository; + _doctorRepository = doctorRepository; + _paymentRepository = paymentRepository; + } + [HttpPost] + public async Task GetAnalysisVerifyList(RevenusVerifyQueryDTO param) + { + AnalysisVerifyResultDTO result=new AnalysisVerifyResultDTO(); + result.RevenuesVerifyList = await GetRevenuesVerifyList(param); + + + var bDate = new DateTime(param.BeginDate.Year, param.BeginDate.Month, 1); + var eDate = new DateTime(param.EndDate.Year, param.EndDate.Month, 1); + eDate = eDate.AddMonths(1).AddSeconds(-1); + + Expression> paymentLambda = x => x.YearMonthDate >= bDate && x.YearMonthDate <= eDate&&!x.IsLock; + + var query = from payment in _paymentRepository.Where(paymentLambda) + join doctor in _doctorRepository.AsQueryable() on payment.DoctorId equals doctor.Id + select new AnalysisNeedLockDTO() + { + YearMonth = payment.YearMonth, + ReviewerCode = doctor.ReviewerCode, + ReviewerName = doctor.LastName + " / " + doctor.FirstName, + ReviewerNameCN = doctor.ChineseName + }; + + result.MonthVerifyResult = (await query.ToListAsync()).GroupBy(t => t.YearMonth).Select(g => new MonthlyResult + { + YearMonth = g.Key, + ReviewerNameList = g.Select(t => t.ReviewerName).ToList(), + ReviewerNameCNList = g.Select(t => t.ReviewerNameCN).ToList(), + ReviewerCodeList = g.Select(t => t.ReviewerCode).ToList() + + }).OrderBy(t=>t.YearMonth).ToList(); + + return result; + } + + + [HttpPost] + public async Task> GetRevenuesVerifyList(RevenusVerifyQueryDTO param) + { + var bDate = new DateTime(param.BeginDate.Year, param.BeginDate.Month, 1); + var eDate = new DateTime(param.EndDate.Year, param.EndDate.Month, 1); + Expression> trialRevenuesPriceVerificationLambda = x => x.WorkLoadDate >= bDate && x.WorkLoadDate <= eDate; + + var query = (from trialVerify in _trialRevenuesPriceVerificationRepository.Where(trialRevenuesPriceVerificationLambda) + join trail in _trialRepository.AsQueryable() on trialVerify.TrialId equals trail.Id + select new RevenusVerifyDTO + { + TrialCode = trail.TrialCode, + Timepoint = trialVerify.Timepoint, + TimepointIn24H = trialVerify.TimepointIn24H, + TimepointIn48H = trialVerify.TimepointIn48H, + Adjudication = trialVerify.Adjudication, + AdjudicationIn24H = trialVerify.AdjudicationIn24H, + AdjudicationIn48H = trialVerify.AdjudicationIn48H, + Global = trialVerify.Global, + Downtime = trialVerify.Downtime, + Training = trialVerify.Training + }).Distinct(); + + + return await query.ToListAsync(); + } + + + + //废弃 + [Obsolete] + public async Task> GetRevenuesVerifyResultList(RevenusVerifyQueryDTO param) + { + var bDate = new DateTime(param.BeginDate.Year, param.BeginDate.Month, 1); + var eDate = new DateTime(param.EndDate.Year, param.EndDate.Month, 1); + Expression> trialRevenuesPriceVerificationLambda = x => x.WorkLoadDate >= bDate && x.WorkLoadDate <= eDate; + + var query = (from trialVerify in _trialRevenuesPriceVerificationRepository.Where(trialRevenuesPriceVerificationLambda) + join trail in _trialRepository.AsQueryable() on trialVerify.TrialId equals trail.Id + select new RevenusVerifyDTO + { + TrialCode = trail.TrialCode, + Timepoint = trialVerify.Timepoint, + TimepointIn24H = trialVerify.TimepointIn24H, + TimepointIn48H = trialVerify.TimepointIn48H, + Adjudication = trialVerify.Adjudication, + AdjudicationIn24H = trialVerify.AdjudicationIn24H, + AdjudicationIn48H = trialVerify.AdjudicationIn48H, + Global = trialVerify.Global, + Downtime = trialVerify.Downtime, + Training = trialVerify.Training + }).Distinct(); + + + return await query.ToListAsync(); + #region 提示 old + //query = from trialVerify in _trialRevenuesPriceVerificationRepository.GetAll() + // join trail in _trialRepository.GetAll() on trialVerify.TrialId equals trail.Id + // join reviewer in _doctorRepository.GetAll() on trialVerify.ReviewerId equals reviewer.Id + // select new RevenusVerifyDTO() + // { + // ReviewerCode = reviewer.Code, + // TrialCode = trail.Code, + // YearMonth = trialVerify.YearMonth + // }; + + + + + + + + + + + ////0是Detail 1是按照项目 2是按照人 3按照月份 + //if (param.StatType == 0) + //{ + // query = from trialVerify in _trialRevenuesPriceVerificationRepository.GetAll() + // join trail in _trialRepository.GetAll() on trialVerify.TrialId equals trail.Id + // join reviewer in _doctorRepository.GetAll() on trialVerify.ReviewerId equals reviewer.Id + // select new RevenusVerifyDTO() + // { + // ReviewerCode = reviewer.Code, + // TrialCode = trail.Code, + // YearMonth = trialVerify.YearMonth + // }; + + //} + //else if (param.StatType == 1) + //{ + // query = (from trialVerify in _trialRevenuesPriceVerificationRepository.GetAll() + // join trail in _trialRepository.GetAll() on trialVerify.TrialId equals trail.Id + // select new RevenusVerifyDTO() + // { + // ReviewerCode = "", + // TrialCode = trail.Code, + // YearMonth = "" + // }).Distinct(); + //} + //else if (param.StatType == 2) + //{ + // query = (from trialVerify in _trialRevenuesPriceVerificationRepository.GetAll() + // join trail in _trialRepository.GetAll() on trialVerify.TrialId equals trail.Id + // join reviewer in _doctorRepository.GetAll() on trialVerify.ReviewerId equals reviewer.Id + // select new RevenusVerifyDTO() + // { + // ReviewerCode = reviewer.Code, + // TrialCode = trail.Code, + // YearMonth = "" + // }).Distinct(); + //} + //else + //{ + // query = from trialVerify in _trialRevenuesPriceVerificationRepository.GetAll() + // join trail in _trialRepository.GetAll() on trialVerify.TrialId equals trail.Id + // join reviewer in _doctorRepository.GetAll() on trialVerify.ReviewerId equals reviewer.Id + // select new RevenusVerifyDTO() + // { + // ReviewerCode = reviewer.Code, + // TrialCode = trail.Code, + // YearMonth = trialVerify.YearMonth + // }; + //} + + + #endregion + + + + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/VolumeRewardService.cs b/IRaCIS.Core.Application/Service/Financial/VolumeRewardService.cs new file mode 100644 index 0000000..35fc890 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/VolumeRewardService.cs @@ -0,0 +1,61 @@ +using AutoMapper; +using AutoMapper.QueryableExtensions; +using IRaCIS.Application.Interfaces; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Application.Services +{ + [ ApiExplorerSettings(GroupName = "Financial")] + public class VolumeRewardService : BaseService, IVolumeRewardService + { + private readonly IRepository _volumeRewardRepository; + + + public VolumeRewardService(IRepository volumeRewardRepository,IMapper mapper) + { + _volumeRewardRepository = volumeRewardRepository; + + } + /// + /// 批量添加或更新奖励费用单价 + /// + [NonDynamicMethod] + public async Task AddOrUpdateVolumeRewardPriceList(IEnumerable addOrUpdateModel) + { + await _volumeRewardRepository.BatchDeleteNoTrackingAsync(t => t.Id != Guid.Empty); + var temp = _mapper.Map>(addOrUpdateModel); + + await _volumeRewardRepository.AddRangeAsync(temp); + var success = await _volumeRewardRepository.SaveChangesAsync(); + + return ResponseOutput.Result(success); + } + + + /// + /// 获取所有奖励单价列表-用于计算时,一次性获取所有 + /// + [NonDynamicMethod] + public async Task> GetVolumeRewardPriceList() + { + return await _volumeRewardRepository.ProjectTo(_mapper.ConfigurationProvider).OrderBy(t => t.Min).ToListAsync(); + } + + /// + /// 分页获取奖励单价列表 + /// + [HttpPost] + public async Task> GetVolumeRewardPriceList(AwardPriceQueryDTO queryParam) + { + var awardPriceQueryable = _volumeRewardRepository.ProjectTo(_mapper.ConfigurationProvider); + + return await awardPriceQueryable.ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize, "Min"); + + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Financial/_MapConfig.cs b/IRaCIS.Core.Application/Service/Financial/_MapConfig.cs new file mode 100644 index 0000000..16d342d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Financial/_MapConfig.cs @@ -0,0 +1,62 @@ +using AutoMapper; +using IRaCIS.Application.Contracts; +using IRaCIS.Application.Contracts.Pay; +using IRaCIS.Application.Interfaces; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Application.Service +{ + public class FinancialConfig : Profile + { + public FinancialConfig() + { + CreateMap() + .ForMember(t => t.YearMonthDate, u => u.MapFrom(t => t.YearMonth)) + .ForMember(t => t.YearMonth, u => u.MapFrom(t => t.YearMonth.ToString("yyyy-MM"))); + + CreateMap(); + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); + + CreateMap(); + + + CreateMap() + .ForMember(t => t.TrialCode, u => u.MapFrom(t => t.Trial.Code)) + .ForMember(t => t.ReviewMode, u => u.MapFrom(t => t.Trial.ReviewMode.Value)) + .ForMember(t => t.Cro, u => u.MapFrom(t => t.Trial.CRO.CROName)) + .ForMember(t => t.Indication, u => u.MapFrom(t => t.Trial.Indication)) + .ForMember(t => t.Expedited, u => u.MapFrom(t => t.Trial.Expedited)) + .ForMember(t => t.DoctorsNames, u => u.MapFrom(t => string.Join(',', t.Trial.EnrollList.Select(t => t.Doctor.ChineseName)))) + + + ; + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomArchiveResult.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomArchiveResult.cs new file mode 100644 index 0000000..9eb0b06 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomArchiveResult.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace IRaCIS.Core.Application.Contracts.Dicom.DTO +{ + public sealed class DicomArchiveResult + { + public int ReceivedFileCount { get; set; } + public ICollection ErrorFiles { get; set; } + + public ICollection ArchivedDicomStudies { get; set; } + + public Guid ReuploadNewStudyId { get; set; } = Guid.Empty; + + public DicomArchiveResult() + { + ReceivedFileCount = 0; + ErrorFiles = new List(); + + ArchivedDicomStudies = new List(); + } + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomInstanceModel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomInstanceModel.cs new file mode 100644 index 0000000..ba6b488 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomInstanceModel.cs @@ -0,0 +1,70 @@ +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Contracts +{ + public class DicomInstanceDTO + { + public Guid Id { get; set;} + public Guid StudyId { get; set; } + public Guid SeriesId { get; set; } + + public int NumberOfFrames { get; set; } + public string StudyInstanceUid { get; set; } = string.Empty; + public string SeriesInstanceUid { get; set; } = string.Empty; + public string SopInstanceUid { get; set; } = string.Empty; + public int InstanceNumber { get; set; } + public DateTime? InstanceTime { get; set; } + public bool CPIStatus { get; set; } + public int ImageRows { get; set; } + public int ImageColumns { get; set; } + public int SliceLocation { get; set; } + + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + + public string Path { get; set; } + + + public bool IsDeleted { get; set; } + public bool IsReading { get; set; } = true; + + } + + + public class DicomTrialSiteSubjectInfo + { + public string TrialSiteCode { get; set; } = string.Empty; + public string SiteCode { get; set; } = string.Empty; + public string SiteName { get; set; } = string.Empty; + public string SubjectCode { get; set; } = string.Empty; + public int? SubjectAge { get; set; } + public string SubjectSex { get; set; } = string.Empty; + public string TrialCode { get; set; } = string.Empty; + public string ResearchProgramNo { get; set; } = string.Empty; + public string TrialIndication { get; set; } = string.Empty; + + public CheckStateEnum CheckState { get; set; } + + + public decimal VisitNum { get; set; } + public string SVUPDES { get; set; } = string.Empty; + public string VisitName { get; set; } = string.Empty; + public string Sponsor { get; set; } = string.Empty; + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + + public SubmitStateEnum SubmitState { get; set; } + + public AuditStateEnum AuditState { get; set; } + public ForwardStateEnum ForwardState { get; set; } + + //public string SubjectName => LastName + " / " + FirstName; + //public string FirstName { get; set; } = string.Empty; + //public string LastName { get; set; } = string.Empty; + //public bool IsDoubleReview { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomSeriesModel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomSeriesModel.cs new file mode 100644 index 0000000..eca8d7f --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomSeriesModel.cs @@ -0,0 +1,50 @@ +namespace IRaCIS.Core.Application.Contracts.Dicom.DTO +{ + public class DicomSeriesDTO + { + + public bool IsDicom { get; set; } = true; + + public bool IsBeMark { get; set; } = false; + + public Guid Id { get; set; } + public Guid StudyId { get; set; } + public string StudyInstanceUid { get; set; } = String.Empty; + public string SeriesInstanceUid { get; set; } = String.Empty; + public int SeriesNumber { get; set; } + public DateTime? SeriesTime { get; set; } + public string Modality { get; set; } = String.Empty; + public string Description { get; set; }=String.Empty; + public int InstanceCount { get; set; } + + public string NoneDicomFileFirstFile { get; set; } = String.Empty; + public string SliceThickness { get; set; } = String.Empty; + + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public bool IsDeleted { get; set; } + public bool IsReading { get; set; } = true; + + public List InstanceList { get; set; } = new List(); + + public List InstancePathList { get; set; } = new List(); + + //存放在instance 上面 + public string WindowCenter { get; set; } = string.Empty; + public string WindowWidth { get; set; } = string.Empty; + } + + public class DicomSeriesWithLabelDTO : DicomSeriesDTO + { + public bool HasLabel { get; set; } = false; + public bool KeySeries { get; set; } = false; + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomStudyModel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomStudyModel.cs new file mode 100644 index 0000000..ea1be81 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomStudyModel.cs @@ -0,0 +1,91 @@ +namespace IRaCIS.Core.Application.Contracts +{ + public class DicomStudyBasicDTO + { + public Guid Id { get; set; } + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + + public string StudyCode { get; set; } = string.Empty; + + public DateTime? StudyTime { get; set; } + } + + public class DicomStudyDTO + { + public Guid Id { get; set; } + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + + public string StudyCode { get; set; } = string.Empty; + + public int Status { get; set; } = 1; + + public string StudyInstanceUid { get; set; } = string.Empty; + public DateTime? StudyTime { get; set; } + public string Modalities { get; set; } = string.Empty; + + public string Description { get; set; } = string.Empty; + public int SeriesCount { get; set; } = 0; + public int InstanceCount { get; set; } = 0; + + public bool SoftDelete { get; set; } = false; + + public string InstitutionName { get; set; } = string.Empty; + public string PatientId { get; set; } = string.Empty; + public string PatientName { get; set; } = string.Empty; + public string PatientAge { get; set; } = string.Empty; + public string PatientSex { get; set; } = string.Empty; + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + } + + public class RelationVisitDTO + { + public string VisitName { get; set; } = string.Empty; + public string TpCode { get; set; } = string.Empty; + public Guid StudyId { get; set; } + } + + public class RelationStudyDTO + { + public string VisitName { get; set; } = string.Empty; + public string StudyCode { get; set; } = string.Empty; + public Guid StudyId { get; set; } + public int SeriesCount { get; set; } + public string Modalities { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + } + + + public class UploadViewInitDto + { + public Guid SubjectVisitId { get; set; } + + public Guid SubjectId { get; set; } + + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + //public string SubjectName { get; set; } + + public string SubjectCode { get; set; } = string.Empty; + + public string TrialSiteCode { get; set; } = string.Empty; + + public decimal VisitNum { get; set; } + + public string VisitName { get; set; } = string.Empty; + + + + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/ImageLabelModel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/ImageLabelModel.cs new file mode 100644 index 0000000..419304e --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/ImageLabelModel.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IRaCIS.Core.Application.Contracts.Dicom.DTO +{ + public class ImageLabelDTO + { + public Guid Id { get; set; } = Guid.Empty; + public string TpCode { get; set; } = string.Empty; + public Guid StudyId { get; set; } = Guid.Empty; + public Guid SeriesId { get; set; } = Guid.Empty; + public Guid InstanceId { get; set; } = Guid.Empty; + public string LabelValue { get; set; } = string.Empty; + } + public class ImageLabelInfo + { + public Guid StudyId { get; set; } = Guid.Empty; + public Guid SeriesId { get; set; } = Guid.Empty; + public Guid InstanceId { get; set; } = Guid.Empty; + public string LabelValue { get; set; } = string.Empty; + } + + public class ImageLabelCommand + { + public string TpCode { get; set; } = string.Empty; + public List ImageLabelList { get; set; } = new List(); + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/ImageShareModel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/ImageShareModel.cs new file mode 100644 index 0000000..f5ce052 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/ImageShareModel.cs @@ -0,0 +1,23 @@ +using System; + +namespace IRaCIS.Core.Application.Contracts.Dicom.DTO +{ + public class ImageShareCommand + { + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public Guid? StudyId { get; set; } + + public DateTime? ExpireTime { get; set; } + + public string Password { get; set; } = string.Empty; + } + + public class ResourceInfo + { + public Guid StudyId { get; set; } + + public string Token { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/StudyDTFModel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/StudyDTFModel.cs new file mode 100644 index 0000000..385bf04 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/StudyDTFModel.cs @@ -0,0 +1,30 @@ +namespace IRaCIS.Core.Application.Contracts.Dicom.DTO +{ + public class StudyDTFDTO + { + public Guid Id { get; set; } + public Guid StudyId { get; set; } + + public string FileName { get; set; } = string.Empty; + + public string Path { get; set; } = string.Empty; + public Guid CreateUserId { get; set; } + + public string UserName { get; set; } = string.Empty; + + public string FirstName { get; set; } = string.Empty; + + public string LastName { get; set; } = string.Empty; + public DateTime CreateTime { get; set; } + } + + public class StudyDTFAddOrUpdateCommand + { + public Guid? Id { get; set; } + public Guid StudyId { get; set; } + public string FileName { get; set; } = string.Empty; + public string Path { get; set; } = string.Empty; + + } + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/StudyReviewerModel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/StudyReviewerModel.cs new file mode 100644 index 0000000..ce32d3f --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/StudyReviewerModel.cs @@ -0,0 +1,46 @@ +namespace IRaCIS.Core.Application.Contracts.Dicom.DTO +{ + public class StudyReviewerDTO + { + public Guid Id { get; set; } + public Guid StudyId { get; set; } + public Guid ReviewerId { get; set; } + + } + + public class StudyDistributeInfo + { + public Guid StudyId { get; set; } + public int Status { get; set; } + //public int ReviewerCount { get; set; } + } + + public class StudyReviewerCommand + { + public List StudyList { get; set; } = new List(); + //public List StudyIdList { get; set; } + public Guid ReviewerId { get; set; } + //public int WorkloadType { get; set; } + public Guid TrialId { get; set; } + //public bool IsDoubleReview { get; set; } + } + + public class StudyReviewerEditCommand + { + public Guid TrialId { get; set; } + public Guid StudyId { get; set; } + public bool IsDoubleReview { get; set; } + + public Guid? ReviewerId1 { get; set; } + public Guid? ReviewerId2 { get; set; } + public Guid? ReviewerIdForAD { get; set; } + } + + public class ReviewerDistributionDTO + { + public Guid ReviewerId { get; set; } + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string ReviewerCode { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/StudyStatusDetailModel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/StudyStatusDetailModel.cs new file mode 100644 index 0000000..b8e5643 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/StudyStatusDetailModel.cs @@ -0,0 +1,27 @@ +using System; + +namespace IRaCIS.Core.Application.Contracts.Dicom.DTO +{ + public class StudyStatusDetailDTO + { + public Guid Id { get; set; } + public Guid StudyId { get; set; } + public int Status { get; set; } + public string OptUserName { get; set; }=string.Empty; + public DateTime OptTime { get; set; } + public string Note { get; set; } = string.Empty; + } + + public class StudyStatusDetailCommand + { + public Guid StudyId { get; set; } + + public int Status { get; set; } + + public DateTime? DeadlineTime { get; set; } + public string Note { get; set; } = string.Empty; + + //QA不通过的时候传递参数 + public string QAComment { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/SystemAnonymizationViewModel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/SystemAnonymizationViewModel.cs new file mode 100644 index 0000000..f657163 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/SystemAnonymizationViewModel.cs @@ -0,0 +1,63 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-03 15:28:20 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.ViewModel +{ + /// SystemAnonymizationView 列表视图模型 + public class SystemAnonymizationView: SystemAnonymizationAddOrEdit + { + + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + public DateTime CreateTime { get; set; } + + } + + ///SystemAnonymizationQuery 列表查询参数模型 + public class SystemAnonymizationQuery:PageInput + { + public string Group { get; set; } = string.Empty; + + public bool? IsAdd { get; set; } + + public string Element { get; set; } = string.Empty; + + public string TagDescription { get; set; } = string.Empty; + + public string TagDescriptionCN { get; set; } = string.Empty; + + public string ValueRepresentation { get; set; } = string.Empty; + + + + } + + /// SystemAnonymizationAddOrEdit 列表查询参数模型 + public class SystemAnonymizationAddOrEdit + { + public Guid? Id { get; set; } + public string Group { get; set; } = String.Empty; + public string Element { get; set; } = String.Empty; + public string TagDescription { get; set; } = String.Empty; + public string TagDescriptionCN { get; set; } = String.Empty; + public string ReplaceValue { get; set; } = String.Empty; + + public string ValueRepresentation { get; set; } = String.Empty; + + public bool IsAdd { get; set; } + + public bool IsEnable { get; set; } + + public bool IsFixed { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs new file mode 100644 index 0000000..476951f --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs @@ -0,0 +1,177 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Contracts +{ + + public class UnionStudyBaseModel + { + public Guid TrialId { get; set; } + + public Guid SiteId { get; set; } + + public Guid SubjectId { get; set; } + + public Guid SubjectVisitId { get; set; } + + + public string SubjectCode { get; set; } = String.Empty; + + public string VisitName { get; set; } = string.Empty; + + public decimal VisitNum { get; set; } + + public string TrialSiteCode { get; set; } = string.Empty; + + public string TrialSiteAliasName { get; set; } = string.Empty; + + public string Uploader { get; set; } = string.Empty; + + public DateTime UploadTime { get; set; } + + public string StudyCode { get; set; } + + + //[JsonIgnore] + //public string DicomStudyCode { get; set; } = string.Empty; + //[JsonIgnore] + //public int NoneDicomCode { get; set; } + + public bool IsDicom { get; set; } + } + + public class UnionStudyMonitorModel : UnionStudyBaseModel + { + public Guid StudyId { get; set; } + + + + + + public string UploadStartTimeStr => UploadStartTime.ToString("yyyy-MM-dd HH:mm:ss.fff"); + public string UploadFinishedTimeStr => UploadFinishedTime?.ToString("yyyy-MM-dd HH:mm:ss.fff"); + + public string ArchiveFinishedTimeStr => ArchiveFinishedTime?.ToString("yyyy-MM-dd HH:mm:ss.fff"); + + + public string UploadIntervalStr + { + get + { + var uploadTimeSpan = UploadFinishedTime - UploadStartTime; + + return $" {uploadTimeSpan?.Hours}:{uploadTimeSpan?.Minutes}:{uploadTimeSpan?.Seconds}.{uploadTimeSpan?.Milliseconds}"; + } + } + + public string ArchiveIntervalStr + { + get + { + var uploadTimeSpan = ArchiveFinishedTime - UploadFinishedTime; + + return $" {uploadTimeSpan?.Hours}:{uploadTimeSpan?.Minutes}:{uploadTimeSpan?.Seconds}.{uploadTimeSpan?.Milliseconds}"; + } + } + + + + public string TimeInterval + { + get + { + + var uploadTimeSpan = ArchiveFinishedTime - UploadStartTime; + + return $" {uploadTimeSpan?.Hours}:{uploadTimeSpan?.Minutes}:{uploadTimeSpan?.Seconds}.{uploadTimeSpan?.Milliseconds}"; + + #region 废弃 + //if (uploadTimeSpan.Seconds == 0 && uploadTimeSpan.Minutes==0 && uploadTimeSpan.Hours == 0) + //{ + // return $"{uploadTimeSpan.Milliseconds}毫秒"; + //} + //else if (uploadTimeSpan.Minutes == 0 && uploadTimeSpan.Hours == 0) + //{ + // return $"{uploadTimeSpan.Seconds}秒"; + //} + //else if (uploadTimeSpan.Hours == 0) + //{ + // return $"{uploadTimeSpan.Minutes} 分钟 {uploadTimeSpan.Seconds} 秒"; + //} + //else + //{ + // return $" {uploadTimeSpan.Hours} 小时 {uploadTimeSpan.Minutes} 分钟 {uploadTimeSpan.Seconds} 秒 {uploadTimeSpan.Milliseconds}毫秒"; + //} + #endregion + + } + } + + public DateTime UploadStartTime { get; set; } + + + public DateTime? UploadFinishedTime { get; set; } + + public DateTime? ArchiveFinishedTime { get; set; } + + + public decimal FileSize { get; set; } + + public string IP { get; set; } = String.Empty; + + + public bool IsDicomReUpload { get; set; } + + + public int FileCount { get; set; } + + public bool IsSuccess { get; set; } + + public string Note = string.Empty; + + } + + + public class UnionStudyViewModel:UnionStudyBaseModel + { + + public Guid Id { get; set; } + + + + public int? Count { get; set; } + + + public string Modalities { get; set; } = string.Empty; + + public string Bodypart { get; set; } = string.Empty; + + public DateTime? StudyTime { get; set; } + + + } + + + + public class StudyQuery:PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public Guid? SubjectId { get; set; } + + public Guid? SiteId { get; set; } + public Guid? SubjectVisitId { get; set; } + + public string SubjectInfo { get; set; } = String.Empty; + + + public string[]? VisitPlanArray { get; set; } + + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DicomArchiveService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DicomArchiveService.cs new file mode 100644 index 0000000..cd5f2de --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DicomArchiveService.cs @@ -0,0 +1,483 @@ +using Dicom; +using Dicom.Imaging.Codec; +using EasyCaching.Core; +using IRaCIS.Core.Application.Contracts.Dicom; +using IRaCIS.Core.Domain.Share; +using System.Text; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Helper; +using Microsoft.AspNetCore.Hosting; +using IRaCIS.Core.Infrastructure; + +namespace IRaCIS.Core.Application.Services +{ + public class DicomArchiveService : IDicomArchiveService + { + private readonly IRepository _studyRepository; + private readonly IRepository _seriesRepository; + private readonly IRepository _instanceRepository; + private readonly IRepository _dictionaryRepository; + private readonly IEasyCachingProvider _provider; + + private readonly IWebHostEnvironment _hostEnvironment; + + private static object lockCodeGenerate = new object(); + + + private List _instanceIdList = new List(); + + public DicomArchiveService(IRepository studyRepository, + IRepository seriesRepository, + IRepository instanceRepository, + IWebHostEnvironment hostEnvironment, + IRepository dictionaryRepository, + IEasyCachingProvider provider) + { + _hostEnvironment = hostEnvironment; + _studyRepository = studyRepository; + + _seriesRepository = seriesRepository; + + _instanceRepository = instanceRepository; + _dictionaryRepository = dictionaryRepository; + _provider = provider; + + } + + public async Task DicomDBDataSaveChange() + { + var success = await _studyRepository.SaveChangesAsync(); + return success; + } + + + + + public async Task<(Guid StudyId, string StudyCode)> ArchiveDicomStreamAsync(Stream dicomStream, + DicomTrialSiteSubjectInfo addtionalInfo, List seriesInstanceUidList, List instanceUidList) + { + + + DicomFile dicomFile = await DicomFile.OpenAsync(dicomStream, Encoding.Default); + + DicomDataset dataset = dicomFile.Dataset; + + //如果数据库存在该instance 记录 那么就不处理 + string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID); + string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID); + + if (instanceUidList.Any(t => t == sopInstanceUid)) + { + return (IdentifierHelper.CreateGuid(studyInstanceUid, addtionalInfo.TrialId.ToString()), string.Empty); + } + + + var anonymize_AddFixedFiledList = _provider.Get>(StaticData.Anonymize.Anonymize_AddFixedFiled).Value ?? new List(); + var anonymize_AddIRCInfoFiledList = _provider.Get>(StaticData.Anonymize.Anonymize_AddIRCInfoFiled).Value ?? new List(); + var anonymize_FixedFieldList = _provider.Get>(StaticData.Anonymize.Anonymize_FixedField).Value ?? new List(); + var anonymize_IRCInfoFieldList = _provider.Get>(StaticData.Anonymize.Anonymize_IRCInfoField).Value ?? new List(); + + + if (anonymize_AddFixedFiledList.Union(anonymize_AddIRCInfoFiledList).Union(anonymize_FixedFieldList).Union(anonymize_IRCInfoFieldList).Count() == 0) + { + throw new BusinessValidationFailedException("未取到缓存匿名化配置数据,上传停止,请联系开发人员核实"); + } + + + foreach (var item in anonymize_AddFixedFiledList.Union(anonymize_FixedFieldList)) + { + + var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16)); + + dataset.AddOrUpdate(dicomTag, item.ReplaceValue); + } + + foreach (var item in anonymize_AddIRCInfoFiledList.Union(anonymize_IRCInfoFieldList)) + { + + var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16)); + + if (dicomTag == DicomTag.ClinicalTrialProtocolID) + { + dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialCode); + + } + if (dicomTag == DicomTag.ClinicalTrialSiteID) + { + dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialSiteCode); + + } + if (dicomTag == DicomTag.ClinicalTrialSubjectID) + { + dataset.AddOrUpdate(dicomTag, addtionalInfo.SubjectCode); + + } + if (dicomTag == DicomTag.ClinicalTrialTimePointID) + { + dataset.AddOrUpdate(dicomTag, addtionalInfo.VisitNum.ToString()); + + } + if (dicomTag == DicomTag.PatientID) + { + dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialCode + "_" + addtionalInfo.SubjectCode); + + } + + } + + + DicomStudy dicomStudy = CreateDicomStudy(dataset, addtionalInfo, out bool isStudyNeedAdd); + DicomSeries dicomSeries = CreateDicomSeries(dataset, dicomStudy, out bool isSeriesNeedAdd); + DicomInstance dicomInstance = CreateDicomInstance(dataset, dicomStudy, dicomSeries,out bool isInstanceNeedAdd); + + dicomSeries.DicomStudy = dicomStudy; + + + var createtime = DateTime.Now; + + if (isStudyNeedAdd) + { + // 添加检查 + await _studyRepository.AddAsync(dicomStudy); + } + + if (isSeriesNeedAdd) + { + dicomSeries.DicomStudy = dicomStudy; + // 添加序列 + await _seriesRepository.AddAsync(dicomSeries); + } + + + var (physicalPath, relativePath) = FileStoreHelper.GetDicomInstanceFilePath(_hostEnvironment, dicomStudy.TrialId, dicomStudy.SiteId, dicomStudy.SubjectId, dicomStudy.SubjectVisitId, dicomStudy.Id, dicomInstance.Id); + + dicomInstance.Path = relativePath; + + if (isInstanceNeedAdd) + { + await _instanceRepository.AddAsync(dicomInstance); + + } + + + var samplesPerPixel = dataset.GetSingleValueOrDefault(DicomTag.SamplesPerPixel, string.Empty); + var photometricInterpretation = dataset.GetSingleValueOrDefault(DicomTag.PhotometricInterpretation, string.Empty); + + if (samplesPerPixel == "1" && (photometricInterpretation.ToUpper() == "MONOCHROME2" || photometricInterpretation.ToUpper() == "MONOCHROME1"))//MONOCHROME2 + { + if (dataset.InternalTransferSyntax.IsEncapsulated) + { + //正常保存 不做处理 + await dicomFile.SaveAsync(physicalPath); + } + else + { //JPEGLSLossless 保存 + await dicomFile.Clone(DicomTransferSyntax.JPEGLSLossless).SaveAsync(physicalPath); + } + } + else + { + if (dataset.InternalTransferSyntax.IsEncapsulated) + { + //正常保存 不做处理 + await dicomFile.SaveAsync(physicalPath); + } + else + { + //RLELossless 保存 + await dicomFile.Clone(DicomTransferSyntax.RLELossless).SaveAsync(physicalPath); //RLELossless + + } + } + return (dicomInstance.StudyId, dicomStudy.StudyCode); + } + + + private DicomStudy CreateDicomStudy(DicomDataset dataset, DicomTrialSiteSubjectInfo addtionalInfo, out bool isStudyNeedAdd) + { + + string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID); + Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, addtionalInfo.TrialId.ToString()); + + + + // 每个线程都查询数据库最大的,和缓存中最大的,取最大值为基数生成Code + //var id = Thread.CurrentThread.ManagedThreadId.ToString("00"); + + //虽然每个文件都会进来,但是只要查询过,就会跟踪,不会再次查询数据库 这里线程并发会有问题,得加锁,不然生成Code 出错 + DicomStudy dicomStudy = _studyRepository.ImageFind(studyId, typeof(DicomStudy)); + + if (dicomStudy != null) + { + isStudyNeedAdd = false; + return dicomStudy; + } + + //_logger.LogWarning($"Thread {id} ,studyUid{studyInstanceUid}, 生成StudyId:{studyId}"); + + isStudyNeedAdd = true; + + //dataset.GetSingleValue(DicomTag.StudyDate) + dataset.GetSingleValue(DicomTag.StudyTime) + + var modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty); + + var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList(); + + var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty; + + if (modality == "MR") + { + modalityForEdit = "MRI"; + } + + if (modality == "PT") + { + modalityForEdit = "PET"; + } + if(modality== "PT、CT") + { + modalityForEdit = "PET-CT"; + } + + dicomStudy = new DicomStudy + { + Id = studyId, + StudyInstanceUid = studyInstanceUid, + /* StudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, DateTime.Now).TimeOfDay),*///dataset.GetDateTime(DicomTag.StudyDate, DicomTag.StudyTime), + StudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.StudyTime).TimeOfDay), + Modalities = modality, + ModalityForEdit = modalityForEdit, + Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty), + InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty), + PatientId = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty), + PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty), + PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty), + PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty), + BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty), + + StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty), + AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty), + + //需要特殊处理 + PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty), + + + AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty), + AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty), + TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty), + + + SiteId = addtionalInfo.SiteId, + TrialId = addtionalInfo.TrialId, + SubjectId = addtionalInfo.SubjectId, + SubjectVisitId = addtionalInfo.SubjectVisitId, + //IsDoubleReview = addtionalInfo.IsDoubleReview, + SeriesCount = 0, + InstanceCount = 0 + }; + + if (dicomStudy.PatientBirthDate.Length == 8) + { + dicomStudy.PatientBirthDate = $"{dicomStudy.PatientBirthDate[0]}{dicomStudy.PatientBirthDate[1]}{dicomStudy.PatientBirthDate[2]}{dicomStudy.PatientBirthDate[3]}-{dicomStudy.PatientBirthDate[4]}{dicomStudy.PatientBirthDate[5]}-{dicomStudy.PatientBirthDate[6]}{dicomStudy.PatientBirthDate[7]}"; + } + + lock (lockCodeGenerate) + { + + //查询数据库获取最大的Code 没有记录则为0 + var dbStudyCodeIntMax = _studyRepository.Where(s => s.TrialId == addtionalInfo.TrialId).Select(t => t.Code).DefaultIfEmpty().Max(); + + //获取缓存中的值 并发的时候,需要记录,已被占用的值 这样其他线程在此占用的最大的值上递增 + var cacheMaxCodeInt = _provider.Get($"{addtionalInfo.TrialId}_{StaticData.CacheKey.StudyMaxCode}").Value; + + int currentNextCodeInt = cacheMaxCodeInt > dbStudyCodeIntMax ? cacheMaxCodeInt + 1 : dbStudyCodeIntMax + 1; + + dicomStudy.Code = currentNextCodeInt; + + dicomStudy.StudyCode = AppSettings.GetCodeStr(currentNextCodeInt, nameof(DicomStudy)); + + _provider.Set($"{addtionalInfo.TrialId}_{StaticData.CacheKey.StudyMaxCode}", dicomStudy.Code, TimeSpan.FromMinutes(30)); + + } + + #region Setting Code old + + //var studyCode = _studyRepository.Where(s => s.TrialId == addtionalInfo.TrialId).Select(t => t.StudyCode).OrderByDescending(c => c).FirstOrDefault(); + + //var cacheMaxCode = _provider.Get($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}").Value; + + //if (studyCode == null && string.IsNullOrEmpty(cacheMaxCode)) + //{ + // dicomStudy.StudyCode = "ST" + 1.ToString().PadLeft(5, '0'); + + // _logger.LogWarning($"Thread {id} DB:{studyCode} 生成{ dicomStudy.StudyCode}"); + //} + //else + //{ + // int dbNum = 0; + + // int cacheNum = 0; + + // if (studyCode != null) + // { + // int.TryParse(studyCode.Substring(studyCode.Length - 5, 5), out dbNum); + // } + + // if (!string.IsNullOrEmpty(cacheMaxCode)) + // { + // int.TryParse(cacheMaxCode.Substring(cacheMaxCode.Length - 5, 5), out cacheNum); + // } + + // dbNum = cacheNum > dbNum ? cacheNum : dbNum; + + + // dicomStudy.StudyCode = "ST" + (++dbNum).ToString().PadLeft(5, '0'); + + // _logger.LogWarning($" Thread {id} DB:{studyCode} cache:{cacheNum} 生成{ dicomStudy.StudyCode}"); + //} + + //_provider.Set($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}", dicomStudy.StudyCode, TimeSpan.FromMinutes(30)); + + + #endregion + + + + return dicomStudy; + + + + + + } + + private DicomSeries CreateDicomSeries(DicomDataset dataset, DicomStudy dicomStudy, out bool isSeriesNeedAdd) + { + string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID); + Guid seriesId = IdentifierHelper.CreateGuid(dicomStudy.StudyInstanceUid, seriesInstanceUid, dicomStudy.TrialId.ToString()); + + //有几个序列会查询几次 + + DicomSeries dicomSeries = _seriesRepository.ImageFind(seriesId, typeof(DicomSeries)); + + if (dicomSeries != null) + { + isSeriesNeedAdd = false; + return dicomSeries; + } + else + { + //var id = Thread.CurrentThread.ManagedThreadId.ToString("00"); + + isSeriesNeedAdd = true; + dicomSeries = new DicomSeries + { + Id = seriesId, + StudyId = dicomStudy.Id, + + StudyInstanceUid = dicomStudy.StudyInstanceUid, + SeriesInstanceUid = seriesInstanceUid, + SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1), + //SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay), + //SeriesTime = DateTime.TryParse(dataset.GetSingleValue(DicomTag.SeriesDate) + dataset.GetSingleValue(DicomTag.SeriesTime), out DateTime dt) ? dt : null, + SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.SeriesTime).TimeOfDay), + Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty), + Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty), + SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty), + + ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty), + ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty), + BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty), + SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty), + ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty), + ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty), + + AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty), + AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty), + TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty), + + SiteId = dicomStudy.SiteId, + TrialId = dicomStudy.TrialId, + SubjectId = dicomStudy.SubjectId, + SubjectVisitId = dicomStudy.SubjectVisitId, + + InstanceCount = 0 + }; + + ++dicomStudy.SeriesCount; + + + //_logger.LogWarning($"线程:{id},sericeId:{seriesId},count:{SeriesDic.Keys.Count}"); + + return dicomSeries; + } + + + } + + private DicomInstance CreateDicomInstance(DicomDataset dataset, DicomStudy dicomStudy, DicomSeries dicomSeries, out bool isInstanceNeedAdd) + { + string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID); + Guid instanceId = IdentifierHelper.CreateGuid(dicomStudy.StudyInstanceUid, dicomSeries.SeriesInstanceUid, sopInstanceUid, dicomStudy.TrialId.ToString()); + + + + + DicomInstance dicomInstance = new DicomInstance + { + Id = instanceId, + StudyId = dicomStudy.Id, + SeriesId = dicomSeries.Id, + + SiteId = dicomStudy.SiteId, + TrialId = dicomStudy.TrialId, + SubjectId = dicomStudy.SubjectId, + SubjectVisitId = dicomStudy.SubjectVisitId, + + StudyInstanceUid = dicomStudy.StudyInstanceUid, + SeriesInstanceUid = dicomSeries.SeriesInstanceUid, + SopInstanceUid = sopInstanceUid, + InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1), + InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.ContentTime).TimeOfDay), + //InstanceTime = DateTime.TryParse(dataset.GetSingleValue(DicomTag.ContentDate) + dataset.GetSingleValue(DicomTag.ContentTime), out DateTime dt) ? dt : null, + //InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate,(DateTime?)null)?.Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, TimeSpan.Zero)), + //dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime) + CPIStatus = false, + ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0), + ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0), + SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0), + + SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty), + NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0), + PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty), + ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty), + FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty), + WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty), + WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty), + }; + + isInstanceNeedAdd = false; + + if (!_instanceIdList.Contains(instanceId)) + { + ++dicomStudy.InstanceCount; + ++dicomSeries.InstanceCount; + + isInstanceNeedAdd = true; + + _instanceIdList.Add(instanceId); + } + + return dicomInstance; + + } + + + + + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/ImageShareService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/ImageShareService.cs new file mode 100644 index 0000000..2dfb177 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/ImageShareService.cs @@ -0,0 +1,131 @@ +using IRaCIS.Core.Application.Contracts.Dicom.DTO; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using IRaCIS.Core.Application.Auth; + +namespace IRaCIS.Core.Application.Services +{ + [AllowAnonymous, ApiExplorerSettings(GroupName = "Image")] + public class ImageShareService : BaseService, IImageShareService + { + + private readonly IRepository _imageShareRepository; + private readonly IRepository _studyRepository; + private readonly IConfiguration _configuration; + private readonly ITokenService _tokenService; + + public ImageShareService(IRepository imageShareRepository, IRepository studyRepository, IConfiguration configuration, ITokenService tokenService) + { + + _imageShareRepository = imageShareRepository; + _studyRepository = studyRepository; + _configuration = configuration; + _tokenService = tokenService; + } + + [HttpPost] + public async Task CreateImageShare(ImageShareCommand imageShareCommand) + { + if (imageShareCommand.StudyId == null) + { + + #region 上传不按照检查批次上传,基线没传,数据可能出错 + + //var subjectVisit1 = _subjectVisitRepository.FirstOrDefault(t => + // t.TrialId == imageShareCommand.TrialId && t.SubjectId == imageShareCommand.SubjectId && + // t.VisitNum == 1); + + //if (subjectVisit1 == null) + //{ + // return ResponseOutput.NotOk("当前无影像数据,无法分享!"); + //} + + //imageShareCommand.StudyId = + // _studyRepository.GetAll().First(t => t.SubjectVisitId == subjectVisit1.Id&&t.Status != (int)StudyStatus.Abandon).Id; + + #endregion + + var studyIds = await _studyRepository.AsQueryable() + .Where(t => t.TrialId == imageShareCommand.TrialId && t.SubjectId == imageShareCommand.SubjectId && + t.SiteId == imageShareCommand.SiteId) + .Select(u => u.Id).ToListAsync(); + + if (!studyIds.Any()) + { + return ResponseOutput.NotOk("当前检查没有影像可以分享。 "); + } + + imageShareCommand.StudyId = studyIds.First(); + + } + + //验证码 4位 + int verificationPassWord = new Random().Next(1000, 10000); + + imageShareCommand.Password = verificationPassWord.ToString(); + + //配置文件读取过期时间 + + var days = AppSettings.ImageShareExpireDays; + + + imageShareCommand.ExpireTime = DateTime.Now.AddDays(days); + + var imageShare = _mapper.Map(imageShareCommand); + + await _imageShareRepository.AddAsync(imageShare); + + var success = await _imageShareRepository.SaveChangesAsync(); + + return ResponseOutput.Result(success, new { ResourceId = imageShare.Id, Password = verificationPassWord.ToString() }); + + + } + + [HttpGet, Route("{resourceId:guid}/{password}")] + public async Task VerifyShareImage(Guid resourceId, string password) + { + var pWord = password.Trim(); + var imageShare = await _imageShareRepository.FirstOrDefaultAsync(t => t.Id == resourceId); + + if (imageShare == null) + { + return ResponseOutput.NotOk("资源不存在。"); + } + + if (pWord != imageShare.Password.Trim()) + { + return ResponseOutput.NotOk("分享密码错误。"); + } + + if (DateTime.Now > imageShare.ExpireTime) + { + return ResponseOutput.NotOk("资源分享过期。"); + } + + var resource = new ResourceInfo() + { + StudyId = imageShare.StudyId, + Token = _tokenService.GetToken(IRaCISClaims.Create(new UserBasicInfo() + { + Id = Guid.Empty, + IsReviewer = false, + IsAdmin = false, + RealName = "Share001", + UserName = "Share001", + Sex = 0, + //UserType = "ShareType", + UserTypeEnum = UserTypeEnum.ShareImage, + Code = "ShareCode001", + })) + }; + + + return ResponseOutput.Ok(resource); + + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/InstanceService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/InstanceService.cs new file mode 100644 index 0000000..7a19123 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/InstanceService.cs @@ -0,0 +1,108 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Helper; + +namespace IRaCIS.Core.Application.Services +{ + [ApiExplorerSettings(GroupName = "Image")] + [AllowAnonymous] + public class InstanceService : BaseService, IInstanceService + { + private readonly IRepository _instanceRepository; + private readonly IRepository _studyRepository; + private readonly IRepository _keyInstanceRepository; + public InstanceService(IRepository instanceRepository, IRepository studyRepository, + IRepository keyInstanceRepository ) + { + _instanceRepository = instanceRepository; + _studyRepository = studyRepository; + _keyInstanceRepository = keyInstanceRepository; + } + + /// 指定资源Id,获取Dicom序列所属的实例信息列表 + /// Dicom序列的Id + [HttpGet("{seriesId:guid}")] + public async Task> List(Guid seriesId) + { + return await _instanceRepository.Where(s => s.SeriesId == seriesId).OrderBy(s => s.InstanceNumber). + ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + + /// 指定资源Id,获取Dicom序列所属的实例Id列表 + /// Dicom序列的Id + /// + /// + [HttpGet, Route("{seriesId:guid}/{tpCode?}/{key?}")] + public IEnumerable List(Guid seriesId, string tpCode, bool? key) + { + if (key != null && key.HasValue && key.Value) + { + return _keyInstanceRepository.Where(s => s.TpCode == tpCode).Select(t => t.InstanceId).Distinct(); + } + else + return _instanceRepository.Where(s => s.SeriesId == seriesId).OrderBy(s => s.InstanceNumber).Select(t => t.Id); + } + + [AllowAnonymous] + [HttpGet, Route("{instanceId:guid}")] + public async Task Preview(Guid instanceId) + { + var path = string.Empty; + + path = (await _instanceRepository.Where(s => s.Id == instanceId).Select(t => t.Path).FirstOrDefaultAsync()).IfNullThrowException(); + + //DicomInstance dicomInstance = await _instanceRepository.FirstOrDefaultAsync(s => s.Id == instanceId).IfNullThrowException(); + + //DicomStudy dicomStudy = await _studyRepository.FirstOrDefaultAsync(s => s.Id == dicomInstance.StudyId).IfNullThrowException(); + + //var (physicalPath, relativePath) = FileStoreHelper.GetDicomInstanceFilePath(_hostEnvironment, dicomStudy.TrialId, dicomStudy.SiteId, dicomStudy.SubjectId, dicomStudy.SubjectVisitId, dicomStudy.Id, dicomInstance.Id); + + var physicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, path); + + using (var sw = ImageHelper.RenderPreviewJpeg(physicalPath)) + { + var bytes = new byte[sw.Length]; + sw.Read(bytes, 0, bytes.Length); + sw.Close(); + return new FileContentResult(bytes, "image/jpeg"); + } + + + } + + [AllowAnonymous] + [HttpGet, Route("{instanceId:guid}")] + public async Task Content(Guid instanceId) + { + var filePath = string.Empty; + var path = (await _instanceRepository.Where(s => s.Id == instanceId).Select(t=>t.Path).FirstOrDefaultAsync()).IfNullThrowException(); + + //DicomStudy dicomStudy = await _studyRepository.FirstOrDefaultAsync(s => s.Id == dicomInstance.StudyId).IfNullThrowException(); + + ////if (dicomInstance.Anonymize) //被匿名化 + ////{ + //// filePath = _dicomFileStoreHelper.GetInstanceFilePath(dicomStudy, dicomInstance.SeriesId, dicomInstance.Id + ".Anonymize"); + ////} + + ////else + //var (physicalPath, relativePath) = FileStoreHelper.GetDicomInstanceFilePath(_hostEnvironment, dicomStudy.TrialId, dicomStudy.SiteId, dicomStudy.SubjectId, dicomStudy.SubjectVisitId, dicomStudy.Id, dicomInstance.Id); + + + var physicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, path); + + + using (var sw = new FileStream(physicalPath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + var bytes = new byte[sw.Length]; + sw.Read(bytes, 0, bytes.Length); + sw.Close(); + return new FileContentResult(bytes, "application/octet-stream"); + } + } + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IDicomArchiveService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IDicomArchiveService.cs new file mode 100644 index 0000000..f78222d --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IDicomArchiveService.cs @@ -0,0 +1,24 @@ +namespace IRaCIS.Core.Application.Contracts.Dicom +{ + public interface IDicomArchiveService + { + Task<(Guid StudyId, string StudyCode)> ArchiveDicomStreamAsync(Stream dicomStream, DicomTrialSiteSubjectInfo addtionalInfo, List seriesInstanceUidList, List instanceUidList); + //ICollection GetArchivedStudyList(List archivedStudyIds); + + Task DicomDBDataSaveChange(); + + + //[EasyCachingAble(Expiration = 6000)] + + + + //IEnumerable GetSeriesList(Guid studyId); + //IEnumerable GetSeriesWithLabelList(Guid studyId,string tpCode); + + ////[EasyCachingAble(Expiration = 6000)] + //string GetSeriesPreview(Guid seriesId); + + + + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IImageShareService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IImageShareService.cs new file mode 100644 index 0000000..ec47c6d --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IImageShareService.cs @@ -0,0 +1,10 @@ +using IRaCIS.Core.Application.Contracts.Dicom.DTO; + +namespace IRaCIS.Core.Application.Services +{ + public interface IImageShareService + { + Task CreateImageShare(ImageShareCommand imageShareCommand); + Task VerifyShareImage(Guid resourceId, string password); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IInstanceService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IInstanceService.cs new file mode 100644 index 0000000..24a3f63 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IInstanceService.cs @@ -0,0 +1,13 @@ +using IRaCIS.Core.Application.Contracts; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Services +{ + public interface IInstanceService + { + Task Content(Guid instanceId); + Task> List(Guid seriesId); + IEnumerable List(Guid seriesId, string tpCode, bool? key); + Task Preview(Guid instanceId); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IStudyService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IStudyService.cs new file mode 100644 index 0000000..9d6da47 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IStudyService.cs @@ -0,0 +1,17 @@ +using IRaCIS.Core.Application.Contracts; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IStudyService + { + IResponseOutput> GetAllRelationStudyList(Guid subjectVisitId); + Task> GetDicomAndNoneDicomStudyList(StudyQuery studyQuery); + Task> GetDicomAndNoneDicomStudyMonitorList(StudyQuery studyQuery); + (List SeriesInstanceUid, List SopInstanceUid) GetHasUploadSeriesAndInstance(Guid studyId); + DicomTrialSiteSubjectInfo GetSaveToDicomInfo(Guid subjectVisitId); + IResponseOutput Item(Guid studyId); + Task Preview(Guid studyId); + IResponseOutput> VerifyStudyAllowUpload(VerifyUploadOrReupload verifyInfo); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/ISystemAnonymizationService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/ISystemAnonymizationService.cs new file mode 100644 index 0000000..3beb86a --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/ISystemAnonymizationService.cs @@ -0,0 +1,24 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-03 15:28:27 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// ISystemAnonymizationService + /// + public interface ISystemAnonymizationService + { + + + Task> GetSystemAnonymizationList(SystemAnonymizationQuery querySystemAnonymization); + + Task AddOrUpdateSystemAnonymization(SystemAnonymizationAddOrEdit addOrEditSystemAnonymization); + + Task DeleteSystemAnonymization(Guid systemAnonymizationId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/SeriesService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/SeriesService.cs new file mode 100644 index 0000000..a325bf1 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/SeriesService.cs @@ -0,0 +1,171 @@ +using IRaCIS.Core.Application.Contracts.Dicom.DTO; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Helper; +using Org.BouncyCastle.Utilities; + +namespace IRaCIS.Core.Application.Services +{ + [ApiExplorerSettings(GroupName = "Image")] + [AllowAnonymous] + public class SeriesService : BaseService + { + private readonly IRepository _imageLabelRepository; + private readonly IRepository _seriesRepository; + private readonly IRepository _instanceRepository; + private readonly IRepository _studyRepository; + private readonly IRepository _keyInstanceRepository; + public SeriesService(IRepository instanceRepository, IRepository studyRepository, + IRepository keyInstanceRepository, IRepository seriesRepository, IRepository imageLabelRepository) + { + _seriesRepository = seriesRepository; + _instanceRepository = instanceRepository; + _studyRepository = studyRepository; + _keyInstanceRepository = keyInstanceRepository; + _imageLabelRepository = imageLabelRepository; + } + + + + //医生读片那一块有耦合,关键序列 这里暂时留存 + /// 指定资源Id,获取Dicom检查所属序列信息列表 + /// Dicom检查的Id + [HttpGet, Route("{studyId:guid}")] + public async Task>> List(Guid studyId) + { + + var seriesList = await _seriesRepository.Where(s => s.StudyId == studyId).OrderBy(s => s.SeriesNumber). + ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var idList = await _instanceRepository.Where(s => s.StudyId == studyId).OrderBy(t => t.SeriesId).ThenBy(t => t.InstanceNumber) + .ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime) + .Select(t => new { t.SeriesId, t.Id, t.Path, t.NumberOfFrames,t.InstanceNumber }).ToListAsync();//.GroupBy(u => u.SeriesId); + + foreach (var item in seriesList) + { + item.InstanceList = idList.Where(s => s.SeriesId == item.Id).Select(u => u.Id).ToList(); + + //处理多帧 + item.InstancePathList = idList.Where(s => s.SeriesId == item.Id).OrderBy(t => t.InstanceNumber) + .SelectMany(u => + { + + if (u.NumberOfFrames > 1) + { + var pathList = new List(); + + for (int i = 1; i <= u.NumberOfFrames; i++) + { + pathList.Add(u.Path + "?frame=" + (i - 1)); + } + return pathList; + } + else + { + return new List { u.Path }; + + } + }) + .ToList(); + } + + #region 暂时废弃 + + //bool hasKeyInstance = false; + //var SeriesIdList = _imageLabelRepository.Where(u => u.TpCode == tpCode).Select(s => s.SeriesId).Distinct().ToList(); + //var instanceIdList = _imageLabelRepository.Where(u => u.TpCode == tpCode).Select(s => s.InstanceId).Distinct().ToList(); + //foreach (var item in seriesList) + //{ + // if (SeriesIdList.Contains(item.Id)) + // { + // item.HasLabel = true; + // hasKeyInstance = true; + // } + // else item.HasLabel = false; + //} + //if (hasKeyInstance) + //{ + // seriesList.Add(new DicomSeriesWithLabelDTO + // { + // KeySeries = true, + // Id = SeriesIdList[0], + // InstanceCount = instanceIdList.Count, + // HasLabel = true, + // Modality = seriesList[0].Modality, + // Description = "Key Series" + // }); + //} + + //var idList = await _instanceRepository.Where(s => s.StudyId == studyId).OrderBy(t => t.SeriesId).ThenBy(t => t.InstanceNumber) + // .ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime) + // .Select(t => new { t.SeriesId, t.Id, t.Path }).ToListAsync();//.GroupBy(u => u.SeriesId); + + //foreach (var item in seriesList) + //{ + // if (item.KeySeries) + // { + // item.InstanceList = instanceIdList; + // } + // else + // { + // //item.InstanceList = idList.Where(s => s.SeriesId == item.Id).OrderBy(t => t.InstanceNumber) + // // .ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime).Select(u => u.Id).ToList(); + + // item.InstanceList = idList.Where(s => s.SeriesId == item.Id).Select(u => u.Id).ToList(); + + // item.InstancePathList = idList.Where(s => s.SeriesId == item.Id).Select(u => u.Path).ToList(); + // } + //} + + + #endregion + + + + return ResponseOutput.Ok(seriesList); + } + + + + + + /// 指定资源Id,渲染Dicom序列的Jpeg预览图像 + /// Dicom序列的Id + [HttpGet, Route("{seriesId:guid}")] + [AllowAnonymous] + public async Task Preview(Guid seriesId) + { + string path = string.Empty; + + path = (await _instanceRepository.Where(s => s.SeriesId == seriesId).Select(t => t.Path).FirstOrDefaultAsync()).IfNullThrowException(); + + var physicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, path); + + //DicomInstance dicomInstance = await _instanceRepository.FirstOrDefaultAsync(s => s.SeriesId == seriesId).IfNullThrowException(); + + //DicomStudy dicomStudy = await _studyRepository.FirstOrDefaultAsync(s => s.Id == dicomInstance.StudyId).IfNullThrowException(); + + //var (physicalPath, relativePath) = FileStoreHelper.GetDicomInstanceFilePath(_hostEnvironment, dicomStudy.TrialId, dicomStudy.SiteId, dicomStudy.SubjectId, dicomStudy.SubjectVisitId, dicomStudy.Id, dicomInstance.Id); + + if (File.Exists(physicalPath)) + { + using (var sw = ImageHelper.RenderPreviewJpeg(physicalPath)) + { + var bytes = new byte[sw.Length]; + sw.Read(bytes, 0, bytes.Length); + sw.Close(); + return new FileContentResult(bytes, "image/jpeg"); + } + } + else + { + return new FileContentResult(new byte[0], "image/jpeg"); + } + + } + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs new file mode 100644 index 0000000..e6a4afd --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs @@ -0,0 +1,548 @@ +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Services; +using EasyCaching.Core; +using System.Linq.Expressions; +using IRaCIS.Core.Application.Helper; +using IRaCIS.Core.Infrastructure; + +namespace IRaCIS.Core.Application.Service.ImageAndDoc +{ + [ApiExplorerSettings(GroupName = "Image")] + public class StudyService : BaseService, IStudyService + { + + private readonly IEasyCachingProvider _provider; + + + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _dicomInstanceRepository; + private readonly IRepository _dicomSeriesRepository; + + public StudyService( IEasyCachingProvider provider + , IRepository subjectVisitRepository, + IRepository dicomInstanceRepository, + IRepository dicomSeriesRepository) + { + _provider = provider; + _subjectVisitRepository = subjectVisitRepository; + _dicomInstanceRepository = dicomInstanceRepository; + _dicomSeriesRepository = dicomSeriesRepository; + } + + + + + + + [HttpPost] + public async Task> GetDicomAndNoneDicomStudyList(StudyQuery studyQuery) + { + + var svExpression = QCCommon.GetDicomStudySubjectVisitFilter(studyQuery.VisitPlanArray); + + var dicomStudyQuery = _repository.Where(t => t.TrialId == studyQuery.TrialId) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + //.WhereIf(!string.IsNullOrEmpty(studyQuery.VisitPlanInfo), studyQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(studyQuery.VisitPlanInfo)) + .WhereIf(studyQuery.VisitPlanArray != null && studyQuery.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo)) + .Select(t => new UnionStudyViewModel() + { + TrialId = t.TrialId, + SiteId = t.SiteId, + SubjectId = t.SubjectId, + SubjectVisitId = t.SubjectVisitId, + VisitName = t.SubjectVisit.VisitName, + VisitNum = t.SubjectVisit.VisitNum, + + IsDicom = true, + + SubjectCode = t.Subject.Code, + + Id = t.Id, + + Bodypart = t.BodyPartExamined, + + Modalities = t.Modalities, + + Count = t.SeriesCount, + + StudyCode = t.StudyCode, + + //DicomStudyCode = t.StudyCode, + //NoneDicomCode = 0, + + StudyTime = t.StudyTime, + + TrialSiteAliasName = t.TrialSite.TrialSiteAliasName, + + TrialSiteCode = t.TrialSite.TrialSiteCode, + + Uploader = t.Uploader.UserName, + + UploadTime = t.CreateTime + + }); + + + //.ProjectTo(_mapper.ConfigurationProvider); + var svExpression2 = QCCommon.GetNoneDicomStudySubjectVisitFilter(studyQuery.VisitPlanArray); + + + var nodeDicomStudyQuery = _repository.Where(t => t.TrialId == studyQuery.TrialId) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + //.WhereIf(!string.IsNullOrEmpty(studyQuery.VisitPlanInfo), studyQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(studyQuery.VisitPlanInfo)) + .WhereIf(studyQuery.VisitPlanArray != null && studyQuery.VisitPlanArray?.Length > 0, svExpression2) + .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo)) + + .Select(t => new UnionStudyViewModel() + { + TrialId = t.TrialId, + SiteId = t.SiteId, + SubjectId = t.SubjectId, + SubjectVisitId = t.SubjectVisitId, + VisitName = t.SubjectVisit.VisitName, + VisitNum = t.SubjectVisit.VisitNum, + + IsDicom = false, + + SubjectCode = t.Subject.Code, + + Id = t.Id, + + Bodypart = t.BodyPart, + + Modalities = t.Modality, + + Count = t.NoneDicomFileList.Count(), + + StudyCode = t.StudyCode, + + //NoneDicomCode = t.Code, + //DicomStudyCode = string.Empty, + + StudyTime = t.ImageDate, + + TrialSiteAliasName = t.TrialSite.TrialSiteAliasName, + + TrialSiteCode = t.TrialSite.TrialSiteCode, + + Uploader = t.CreateUser.UserName, + + UploadTime = t.CreateTime + + }); + + //.ProjectTo(_mapper.ConfigurationProvider); + + + var unionQuery = dicomStudyQuery.Union(nodeDicomStudyQuery) + .WhereIf(studyQuery.SubjectId != null, t => t.SubjectId == studyQuery.SubjectId) + .WhereIf(studyQuery.SubjectVisitId != null, t => t.SubjectId == studyQuery.SubjectVisitId) + .WhereIf(studyQuery.SiteId != null, t => t.SiteId == studyQuery.SiteId); + + return await unionQuery.ToPagedListAsync(studyQuery.PageIndex, studyQuery.PageSize, studyQuery.SortField, studyQuery.Asc); + } + + + [HttpPost] + public async Task> GetDicomAndNoneDicomStudyMonitorList(StudyQuery studyQuery) + { + var svExpression = QCCommon.GetStudyMonitorSubjectVisitFilter(studyQuery.VisitPlanArray); + var StudyMonitorQuery = _repository.Where(t => t.TrialId == studyQuery.TrialId,ignoreQueryFilters:true) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + //.WhereIf(!string.IsNullOrEmpty(studyQuery.VisitPlanInfo), studyQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(studyQuery.VisitPlanInfo)) + .WhereIf(studyQuery.VisitPlanArray != null && studyQuery.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo)) + .WhereIf(studyQuery.SubjectId != null, t => t.SubjectId == studyQuery.SubjectId) + .WhereIf(studyQuery.SubjectVisitId != null, t => t.SubjectId == studyQuery.SubjectVisitId) + .WhereIf(studyQuery.SiteId != null, t => t.SiteId == studyQuery.SiteId) + .Select(t => new UnionStudyMonitorModel() + { + TrialId = t.TrialId, + SiteId = t.SiteId, + SubjectId = t.SubjectId, + SubjectVisitId = t.SubjectVisitId, + VisitName = t.SubjectVisit.VisitName, + VisitNum = t.SubjectVisit.VisitNum, + + + + SubjectCode = t.Subject.Code, + + + TrialSiteAliasName = t.TrialSite.TrialSiteAliasName, + + TrialSiteCode = t.TrialSite.TrialSiteCode, + + Uploader = t.Uploader.UserName, + + UploadTime = t.CreateTime, + + IsSuccess=t.IsSuccess, + Note=t.Note, + IP = t.IP, + FileCount = t.FileCount, + FileSize = t.FileSize, + UploadFinishedTime = t.UploadFinishedTime, + UploadStartTime = t.UploadStartTime, + ArchiveFinishedTime=t.ArchiveFinishedTime, + + + + + IsDicomReUpload = t.IsDicomReUpload, + StudyId = t.Id, + IsDicom = t.IsDicom, + + StudyCode = t.StudyCode + + + }); + + return await StudyMonitorQuery.ToPagedListAsync(studyQuery.PageIndex, studyQuery.PageSize, string.IsNullOrEmpty(studyQuery.SortField) ? "UploadTime" : studyQuery.SortField, studyQuery.Asc); + + + + #region 冗余查询 + //var dicomStudyQuery = _repository.Where(t => t.TrialId == studyQuery.TrialId && t.IsDicom) + // .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + // .WhereIf(!string.IsNullOrEmpty(studyQuery.VisitPlanInfo), studyQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(studyQuery.VisitPlanInfo)) + // .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo)) + // .Select(t => new UnionStudyMonitorModel() + // { + // TrialId = t.TrialId, + // SiteId = t.SiteId, + // SubjectId = t.SubjectId, + // SubjectVisitId = t.SubjectVisitId, + // VisitName = t.SubjectVisit.VisitName, + // VisitNum = t.SubjectVisit.VisitNum, + + + + // SubjectCode = t.Subject.Code, + + + // TrialSiteAliasName = t.TrialSite.TrialSiteAliasName, + + // TrialSiteCode = t.TrialSite.TrialSiteCode, + + // Uploader = t.Uploader.FullName, + + // UploadTime = t.CreateTime, + + + // IP = t.IP, + // FileCount = t.FileCount, + // FileSize = t.FileSize, + // UploadFinishedTime = t.UploadFinishedTime, + // UploadStartTime = t.UploadStartTime, + + // TotalMillisecondsInterval = t.TotalMillisecondsInterval, + + // IsDicomReUpload = t.IsDicomReUpload, + // StudyId = t.Id, + // IsDicom = t.IsDicom, + + // StudyCode = t.DicomStudy.StudyCode + // //DicomStudyCode = t.DicomStudy.StudyCode, + // //NoneDicomCode = 0, + + // }); + + ////.ProjectTo(_mapper.ConfigurationProvider); + + + + //var nodeDicomStudyQuery = _repository.Where(t => t.TrialId == studyQuery.TrialId && t.IsDicom == false) + // .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + // .WhereIf(!string.IsNullOrEmpty(studyQuery.VisitPlanInfo), studyQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(studyQuery.VisitPlanInfo)) + // .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo)) + + // .Select(t => new UnionStudyMonitorModel() + // { + + // TrialId = t.TrialId, + // SiteId = t.SiteId, + // SubjectId = t.SubjectId, + // SubjectVisitId = t.SubjectVisitId, + // VisitName = t.SubjectVisit.VisitName, + // VisitNum = t.SubjectVisit.VisitNum, + // SubjectCode = t.Subject.Code, + // TrialSiteAliasName = t.TrialSite.TrialSiteAliasName, + // TrialSiteCode = t.TrialSite.TrialSiteCode, + // Uploader = t.Uploader.FullName, + // UploadTime = t.CreateTime, + + // IP = t.IP, + // FileCount = t.FileCount, + // FileSize = t.FileSize, + // UploadFinishedTime = t.UploadFinishedTime, + // UploadStartTime = t.UploadStartTime, + + // TotalMillisecondsInterval = t.TotalMillisecondsInterval, + + // IsDicomReUpload = t.IsDicomReUpload, + // StudyId = t.Id, + // IsDicom = t.IsDicom, + + // StudyCode = t.NoneDicomStudy.StudyCode + + // //DicomStudyCode = string.Empty, + // //NoneDicomCode = t.NoneDicomStudy.Code, + + // }); + + ////.ProjectTo(_mapper.ConfigurationProvider); + + + //var unionQuery = dicomStudyQuery.Union(nodeDicomStudyQuery) + // .WhereIf(studyQuery.SubjectId != null, t => t.SubjectId == studyQuery.SubjectId) + // .WhereIf(studyQuery.SubjectVisitId != null, t => t.SubjectId == studyQuery.SubjectVisitId) + // .WhereIf(studyQuery.SiteId != null, t => t.SiteId == studyQuery.SiteId); + + //return await unionQuery.ToPagedListAsync(studyQuery.PageIndex, studyQuery.PageSize, string.IsNullOrEmpty(studyQuery.SortField) ? "UploadTime" : studyQuery.SortField, studyQuery.Asc); + + #endregion + + + + } + + + /// 指定资源Id,渲染Dicom检查的Jpeg预览图像 + /// Dicom检查的Id + [HttpGet("{studyId:guid}")] + public async Task Preview(Guid studyId) + { + string path = String.Empty; + + path = (await _dicomInstanceRepository.Where(s => s.StudyId == studyId).Select(t => t.Path).FirstOrDefaultAsync()).IfNullThrowException(); + + //DicomInstance dicomInstance = await _repository.FirstOrDefaultAsync(s => s.StudyId == studyId); + + //if (dicomInstance != null) + //{ + // DicomStudy dicomStudy = await _repository.FirstOrDefaultAsync(s => s.Id == dicomInstance.StudyId); + // if (dicomStudy != null) + // { + // var( physicalPath ,relativePath )= FileStoreHelper.GetDicomInstanceFilePath(_hostEnvironment, dicomStudy.TrialId, dicomStudy.SiteId, dicomStudy.SubjectId, dicomStudy.SubjectVisitId, dicomStudy.Id, dicomInstance.Id); + + // path = physicalPath; + // } + //} + + using (var sw = ImageHelper.RenderPreviewJpeg(path)) + { + var bytes = new byte[sw.Length]; + sw.Read(bytes, 0, bytes.Length); + sw.Close(); + return new FileContentResult(bytes, "image/jpeg"); + } + } + + + /// + /// 获取某个检查的关联检查列表(该患者在这个想项目下的所有检查) + /// 点击检查检查列表中的一个检查获取对应的序列列表(调用之前的接口:/series/list/,根据StudyId,获取检查批次的序列列表) + /// + /// + [HttpGet("{subjectVisitId:guid}")] + [AllowAnonymous] + public IResponseOutput> GetAllRelationStudyList(Guid subjectVisitId) + { + #region 废弃 + //var studylist = _studyRepository.Where(u => u.SubjectVisitId == subjectVisitId && u.IsDeleted == false).Select(t => new { StudyId = t.Id, t.SubjectId, t.TrialId }).ToList(); + //var subjectId = studylist.FirstOrDefault().SubjectId; + //var trialId = studylist.FirstOrDefault().TrialId; + //var studyIds = studylist.Select(t => t.StudyId).ToList(); + + + //var query = from studyItem in _studyRepository.Where(u => u.SubjectId == subjectId + // && u.TrialId == trialId && u.IsDeleted == false && + // !studyIds.Contains(u.Id) + // /* && u.Status != (int)StudyStatus.Abandon*/) + // join visitItem in _subjectVisitRepository.AsQueryable() + // on studyItem.SubjectVisitId equals visitItem.Id + // select new RelationStudyDTO + // { + // StudyId = studyItem.Id, + // StudyCode = studyItem.StudyCode, + // VisitName = visitItem.VisitName, + // Modalities = studyItem.Modalities, + // Description = studyItem.Description, + // SeriesCount = studyItem.SeriesCount + // }; + #endregion + + var studyInfo = _repository.Where(u => u.SubjectVisitId == subjectVisitId).Select(t => new { t.SubjectId, t.TrialId }).FirstOrDefault().IfNullThrowException(); + + var query = _repository.Where(t => t.SubjectVisitId != subjectVisitId && t.TrialId == studyInfo.TrialId && t.SubjectId == studyInfo.SubjectId) + .ProjectTo(_mapper.ConfigurationProvider).ToList(); + + var list = query.OrderBy(u => u.VisitName).ThenBy(s => s.StudyCode).ToList(); + + return ResponseOutput.Ok(list); + } + + + /// 指定资源Id,获取Dicom检查信息 + /// Dicom检查的Id + [HttpGet, Route("{studyId:guid}")] + [AllowAnonymous] + public IResponseOutput Item(Guid studyId) + { + return ResponseOutput.Ok(_mapper.Map(_repository.Where().FirstOrDefault(s => s.Id == studyId))); + } + + + + /// + /// 批量验证 检查是否可以上传 并告知原因 + /// + [HttpPost] + public IResponseOutput> VerifyStudyAllowUpload(VerifyUploadOrReupload verifyInfo) + { + var trialInfo = _repository.Where().FirstOrDefault(t => t.Id == verifyInfo.TrialId).IfNullThrowException(); + + var result = new List(); + + var visitList = _repository.Where(t => t.SubjectId == verifyInfo.SubjectId).Select(t => new { t.VisitNum, t.Subject.VisitOverTime, t.EarliestScanDate, t.LatestScanDate, t.Id }).ToList(); + + var visitOverTime = visitList.Select(t=>t.VisitOverTime).FirstOrDefault(); + + verifyInfo.StudyInstanceUidList.ForEach(waitUploadItem => + { + + if (trialInfo.IsVerifyVisitImageDate) + { + + if(visitOverTime!= null && waitUploadItem.StudyDate != null && waitUploadItem.StudyDate> visitOverTime) + { + result.Add(new VerifyStudyUploadResult() { ErrorMesseage = $"当前检查批次检查时间{waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")}不能晚于患者访视检查结束日期{visitOverTime?.ToString("yyyy-MM-dd")},请核对检查数据是否有误", StudyInstanceUid = waitUploadItem.StudyInstanceUid }); + return; + } + + //小于当前检查批次 最近的最晚拍片 + var before = visitList.Where(u => u.VisitNum < verifyInfo.VisitNum).Max(k => k.LatestScanDate); + + if (before != null && waitUploadItem.StudyDate != null && before > waitUploadItem.StudyDate) + { + result.Add(new VerifyStudyUploadResult() { ErrorMesseage = $"当前检查批次检查时间{waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")}不能早于前序检查批次检查时间{before?.ToString("yyyy-MM-dd")},请核对检查数据是否有误", StudyInstanceUid = waitUploadItem.StudyInstanceUid }); + return; + } + + //大于当前检查批次 最近的最早拍片日期 + var after = visitList.Where(u => u.VisitNum > verifyInfo.VisitNum).Min(k => k.EarliestScanDate); + + if (after != null && waitUploadItem.StudyDate != null && after < waitUploadItem.StudyDate) + { + result.Add(new VerifyStudyUploadResult() { ErrorMesseage = $"当前检查批次检查时间{waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")}不能晚于该检查批次之后的检查时间{after?.ToString("yyyy-MM-dd")},请核对检查数据是否有误", StudyInstanceUid = waitUploadItem.StudyInstanceUid }); + return; + } + + + + } + + var temp = VerifyStudyUpload(waitUploadItem.StudyInstanceUid, verifyInfo.TrialId, verifyInfo.SubjectVisitId, verifyInfo.SubjectId); + + result.Add(temp); + }); + + return ResponseOutput.Ok(result); + } + + + private VerifyStudyUploadResult VerifyStudyUpload(string studyInstanceUid, Guid trialId, Guid currentSubjectVisitId, Guid SubjectId) + { + + var result = new VerifyStudyUploadResult(); + + if (_provider.Exists("StudyUid_" + studyInstanceUid)) + { + result.AllowUpload = false; + + result.AllowReUpload = false; + result.StudyInstanceUid = studyInstanceUid; + result.ErrorMesseage = "当前有人正在上传归档该检查!"; + + return result; + } + + if (_repository.Where(t => t.Id == SubjectId).Select(t => t.Status).FirstOrDefault() == SubjectStatus.EndOfVisit) + { + result.AllowUpload = false; + + result.AllowReUpload = false; + result.StudyInstanceUid = studyInstanceUid; + result.ErrorMesseage = "患者检查批次结束,不允许上传!"; + + return result; + } + + Guid expectStudyId = IdentifierHelper.CreateGuid(studyInstanceUid.Trim(), trialId.ToString()); + + + + var verifyStudyInfo = _repository.Where(t => t.TrialId == trialId && t.Id == expectStudyId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + result.StudyInfo = verifyStudyInfo; + + + //数据库不存在该检查 允许上传 + if (verifyStudyInfo == null) + { + result.AllowUpload = true; + } + //数据库该项目有该检查 看是否支持重传 + else + { + //是同一个患者 支持重传 + if (verifyStudyInfo.SubjectId == SubjectId && verifyStudyInfo.SubjectVisitId == currentSubjectVisitId) + { + result.AllowReUpload = true; + } + //不是同一个患者 + else + { + //有默认值,其实不用写,这里为了好理解 + result.AllowUpload = false; + + result.AllowReUpload = false; + + result.ErrorMesseage = $"此处不可以上传。当前影像检查已经上传给患者{verifyStudyInfo.SubjectCode}的{verifyStudyInfo.VisitName}"; + } + } + result.StudyInstanceUid = studyInstanceUid; + return result; + } + + + + + /// + /// 获取保存到Dicom文件中的信息 + /// + /// + /// + public DicomTrialSiteSubjectInfo GetSaveToDicomInfo(Guid subjectVisitId) + { + //6表连接 subject trial trialSite sponsor subjectVisit site + var info = _subjectVisitRepository.Where(t => t.Id == subjectVisitId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault().IfNullThrowException(); + + return info; + } + + public (List SeriesInstanceUid, List SopInstanceUid) GetHasUploadSeriesAndInstance(Guid studyId) + { + var seriesInstanceUidList = _dicomSeriesRepository.Where(t => t.StudyId == studyId).Select(t => t.SeriesInstanceUid).ToList(); + + var sopInstanceUidList = _dicomInstanceRepository.Where(t => t.StudyId == studyId).Select(t => t.SopInstanceUid).ToList(); + + return (seriesInstanceUidList, sopInstanceUidList); + } + + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/SystemAnonymizationService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/SystemAnonymizationService.cs new file mode 100644 index 0000000..82efbe2 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/SystemAnonymizationService.cs @@ -0,0 +1,78 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-03 15:28:32 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using Dicom; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.MediatR.Handlers; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Infra.EFCore; +using MediatR; +using Microsoft.AspNetCore.Mvc; +namespace IRaCIS.Core.Application.Service +{ + /// + /// SystemAnonymizationService + /// + [ApiExplorerSettings(GroupName = "Image")] + public class SystemAnonymizationService : BaseService, ISystemAnonymizationService + { + private readonly IMediator _mediator; + private readonly IRepository systemAnonymizationRepository; + + public SystemAnonymizationService(IMediator mediator, IRepository systemAnonymizationRepository) + { + _mediator = mediator; + this.systemAnonymizationRepository = systemAnonymizationRepository; + } + + [HttpPost] + public async Task> GetSystemAnonymizationList(SystemAnonymizationQuery querySystemAnonymization) + { + + var systemAnonymizationQueryable = systemAnonymizationRepository + .WhereIf(!string.IsNullOrEmpty(querySystemAnonymization.Group), t => t.Group.Contains(querySystemAnonymization.Group)) + .WhereIf(!string.IsNullOrEmpty(querySystemAnonymization.Element), t => t.Element.Contains(querySystemAnonymization.Element)) + .WhereIf(!string.IsNullOrEmpty(querySystemAnonymization.TagDescription), t => t.TagDescription.Contains(querySystemAnonymization.TagDescription)) + .WhereIf(!string.IsNullOrEmpty(querySystemAnonymization.TagDescriptionCN), t => t.TagDescriptionCN.Contains(querySystemAnonymization.TagDescriptionCN)) + .WhereIf(querySystemAnonymization.IsAdd != null, t => t.IsAdd == querySystemAnonymization.IsAdd) + .WhereIf(!string.IsNullOrEmpty(querySystemAnonymization.ValueRepresentation), t => t.ValueRepresentation.Contains(querySystemAnonymization.ValueRepresentation)) + .ProjectTo(_mapper.ConfigurationProvider); + + return await systemAnonymizationQueryable.ToPagedListAsync(querySystemAnonymization.PageIndex, querySystemAnonymization.PageSize, querySystemAnonymization.SortField, querySystemAnonymization.Asc); + } + + + public async Task AddOrUpdateSystemAnonymization(SystemAnonymizationAddOrEdit addOrEditSystemAnonymization) + { + + ushort result; + if ((UInt16.TryParse(addOrEditSystemAnonymization.Group, System.Globalization.NumberStyles.HexNumber, null, out result)==false) || (UInt16.TryParse(addOrEditSystemAnonymization.Element, System.Globalization.NumberStyles.HexNumber, null, out result) == false)) + { + return ResponseOutput.NotOk("请核对DicomTag 配置的元素号或者组号是否符合要求"); + } + + + + var entity = await _repository.InsertOrUpdateAsync(addOrEditSystemAnonymization, true); + + await _mediator.Send(new AnonymizeCacheRequest()); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + + [HttpDelete("{systemAnonymizationId:guid}")] + public async Task DeleteSystemAnonymization(Guid systemAnonymizationId) + { + var success = await systemAnonymizationRepository.BatchDeleteNoTrackingAsync(t => t.Id == systemAnonymizationId); + + return ResponseOutput.Result(success); + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/_MapConfig.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/_MapConfig.cs new file mode 100644 index 0000000..6743eaa --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/_MapConfig.cs @@ -0,0 +1,121 @@ +using AutoMapper; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.Dicom.DTO; +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Application.Service +{ + public class ImageAndDocConfig : Profile + { + public ImageAndDocConfig() + { + CreateMap(); + CreateMap(); + CreateMap(); + + //影像上传 检查 + CreateMap() + .ForMember(d => d.SubjectId, u => u.MapFrom(s => s.Subject.Id)) + .ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)) + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) + .ForMember(d => d.FirstName, u => u.MapFrom(s => s.Subject.FirstName)) + .ForMember(d => d.LastName, u => u.MapFrom(s => s.Subject.LastName)); + + + + CreateMap().IncludeMembers(t => t.Subject, u => u.SubjectVisit) + .ForMember(d => d.UploaderFirstName, u => u.MapFrom(s => s.Site.SiteName)) + .ForMember(d => d.SiteName, u => u.MapFrom(s => s.Site.SiteName)) + .ForMember(d => d.UploaderFirstName, u => u.MapFrom(s => s.Uploader.FirstName)) + .ForMember(d => d.UploaderLastName, u => u.MapFrom(s => s.Uploader.LastName)) + .ForMember(d => d.StudyStatus, u => u.MapFrom(s => s.Status)) + .ForMember(d => d.UploadedTime, u => u.MapFrom(s => s.CreateTime)) + + .ForMember(d => d.DTFCount, u => u.MapFrom(s => s.StudyDTFList.Count())); + + CreateMap(); + + CreateMap(); + + CreateMap() + .ForMember(d => d.StudyId, u => u.MapFrom(s => s.Id)) + .ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)); + + CreateMap(); + + + CreateMap() + .ForMember(o => o.StudyTime, t => t.MapFrom(u => u.DicomStudy.StudyTime)) + + .ForMember(o => o.StudyCode, t => t.MapFrom(u => u.DicomStudy.StudyCode)) + //.ForMember(o => o.InstanceList, t => t.MapFrom(u => u.DicomInstanceList.Select(t => t.Id).ToArray())) + //.ForMember(o => o.InstancePathList, t => t.MapFrom(u => u.DicomInstanceList.OrderBy(t => t.InstanceNumber).Select(t => t.Path))) + ; + + CreateMap() + .ForMember(o => o.UploadedTime, t => t.MapFrom(u => u.CreateTime)) + .ForMember(o => o.Uploader, t => t.MapFrom(u => u.Uploader.LastName + " / " + u.Uploader.FirstName)) + .ForMember(o => o.StudyId, t => t.MapFrom(u => u.Id)); + + + + CreateMap(); + + //CreateMap(); + + CreateMap(); + + CreateMap(); + + + + CreateMap(); + + CreateMap() + .ForMember(o => o.IsDeleted, t => t.MapFrom(u => u.DicomSerie.IsDeleted)) + .ForMember(o => o.IsReading, t => t.MapFrom(u => u.DicomSerie.IsReading)); + CreateMap(); + CreateMap(); + + + + CreateMap().ReverseMap(); + + CreateMap(); + + + + CreateMap() + //.ForMember(d => d.DicomStudyCode, u => u.MapFrom(s => s.StudyCode)) + //.ForMember(d => d.Modality, u => u.MapFrom(s => s.Modalities)) + .ForMember(d => d.Bodypart, u => u.MapFrom(s => s.BodyPartExamined)) + .ForMember(d => d.StudyTime, u => u.MapFrom(s => s.StudyTime)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + .ForMember(d => d.TrialSiteAliasName, u => u.MapFrom(s => s.TrialSite.TrialSiteAliasName)) + .ForMember(d => d.IsDicom, u => u.MapFrom(s => true)) + .ForMember(d => d.Count, u => u.MapFrom(s => s.SeriesCount)) + .ForMember(d => d.VisitNum, u => u.MapFrom(s => s.SubjectVisit.VisitNum)) + .ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)); + + CreateMap() + //.ForMember(d => d.NoneDicomCode, u => u.MapFrom(s => s.Code)) + //.ForMember(d => d.Modality, u => u.MapFrom(s => s.Modalities)) + .ForMember(d => d.Bodypart, u => u.MapFrom(s => s.BodyPart)) + .ForMember(d => d.StudyTime, u => u.MapFrom(s => s.ImageDate)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + .ForMember(d => d.TrialSiteAliasName, u => u.MapFrom(s => s.TrialSite.TrialSiteAliasName)) + .ForMember(d => d.IsDicom, u => u.MapFrom(s => false)) + .ForMember(d => d.Count, u => u.MapFrom(s => s.NoneDicomFileList.Count())) + .ForMember(d => d.VisitNum, u => u.MapFrom(s => s.SubjectVisit.VisitNum)) + .ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)); + + + + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/Inspection/DTO/InspectionModel.cs b/IRaCIS.Core.Application/Service/Inspection/DTO/InspectionModel.cs new file mode 100644 index 0000000..7658341 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Inspection/DTO/InspectionModel.cs @@ -0,0 +1,506 @@ +using Castle.Core.Internal; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.Inspection.DTO +{ + + + + + public class DataInspectionAddDTO + { + + public Guid? TrialId { get; set; } + public Guid? SiteId { get; set; } + public Guid? SubjectId { get; set; } + public Guid? SubjectVisitId { get; set; } + + /// + /// 子类 + /// + public Guid? ChildrenType { get; set; } + /// + /// 对象名称 + /// + public Guid? ObjectType { get; set; } + /// + /// 操作类型 + /// + public Guid? OptType { get; set; } + /// + /// 功能模块 + /// + public Guid? ModuleType { get; set; } + + public string BlindName { get; set; } = string.Empty; + public string Reason { get; set; } = string.Empty; + + + public bool IsSign { get; set; } + public string JsonDetail { get; set; } = string.Empty; + + /// + /// 创建人名称 + /// + public string CreateUserName { get; set; } = string.Empty; + + /// + /// 项目名称 + /// + public string TrialName { get; set; } = string.Empty; + + /// + /// 中心名称 + /// + public string SiteName { get; set; } = string.Empty; + + /// + /// 患者Code + /// + public string SubjectCode { get; set; } = string.Empty; + + /// + /// 检查批次名称 + /// + public string SubjectVisitName { get; set; } = string.Empty; + + /// + /// 角色名称 + /// + public string RoleName { get; set; } = string.Empty; + + + /// + /// 中心Code + /// + public string SiteCode { get; set; } = string.Empty; + + /// + /// 项目编码 + /// + public string ResearchProgramNo { get; set; } = string.Empty; + + public List EnumList { get; set; } = new List { }; + + + /// + /// 标识 + /// + public string Identification { get; set; } = string.Empty; + + /// + /// 检查批次计划ID + /// + public Guid? VisitStageId { get; set; } + + + /// + /// 通用ID + /// + public Guid? GeneralId { get; set; } + + + + ////需要单独处理 + //public string IP { get; set; } + + public DateTime? CreateTime { get; set; } = DateTime.Now; + + public bool NeedSava { get; set; } = true; + + + + + } + + + public class EnumList + { + public string Key { get; set; } = string.Empty; + + public string Code { get; set; } = string.Empty; + + public string Type { get; set; } = string.Empty; + } + + public interface IInspectionDTO + { + public DataInspectionAddDTO AuditInfo { get; set; } + } + + public interface ISignDTO + { + public SignDTO SignInfo { get; set; } + + } + + #region Setting + /// + /// 配置 基础逻辑信息 + /// + public class InsBasicTrialConfig : InspectionBase, IInspectionDTO, ISignDTO + { + public BasicTrialConfig OptCommand { get; set; } + } + + /// + /// 配置流程 + /// + public class InsTrialProcessConfig : InspectionBase, IInspectionDTO, ISignDTO + { + public TrialProcessConfig OptCommand { get; set; } + } + + /// + /// 配置加急信息 + /// + public class InsTrialUrgentConfig : InspectionBase, IInspectionDTO, ISignDTO + { + public TrialUrgentConfig OptCommand { get; set; } + } + #endregion + + public class SetCheckPassDto + { + public Guid qcChallengeId { get; set; } + public Guid subjectVisitId { get; set; } + public QCChallengeCloseEnum closeEnum { get; set; } + public string closeReason { get; set; } + } + + + public class SetNeedReuploadDto + { + public Guid trialId { get; set; } + public Guid qcChallengeId { get; set; } + } + + public class SetVisitUrgentDto + { + public Guid trialId { get; set; } + public Guid subjectVisitId { get; set; } + public bool setOrCancel { get; set; } + } + + + public class UpdateTrialStateDto + { + public Guid trialId {get; set; } + public string trialStatusStr { get; set; } + + public string? reason { get; set; } + } + public class QCPassedOrFailedDto + { + public Guid trialId { get; set; } + public Guid subjectVisitId { get; set; } + + public AuditStateEnum auditState { get; set; } + } + + public class SetSeriesStateDto + { + public Guid subjectVisitId { get; set; } + public Guid studyId { get; set; } + public Guid seriesId { get; set; } + public int state { get; set; } + } + + public class UpdateModalityDto + { + public Guid id { get; set; } + public int type { get; set; } + public string modality { get; set; } + public string bodyPart { get; set; } + } + + public class CloseQCChallengeDto + { + public Guid qcChallengeId { get; set; } + public Guid subjectVisitId { get; set; } + public QCChallengeCloseEnum closeEnum { get; set; } + public string closeReason { get; set; } + } + + public class AddOrUpdateQCChallengeDto + { + public QCChallengeCommand qaQuestionCommand { get; set; } + public Guid trialId { get; set; } + public TrialQCProcess trialQCProcess { get; set; } + public CurrentQC currentQCType { get; set; } + } + + public class AddOrUpdateQCQuestionAnswerListDto + { + public QCQuestionAnswerCommand[] qcQuestionAnswerCommands { get; set; } + public Guid trialId { get; set; } + public Guid subjectVisitId { get; set; } + public TrialQCProcess trialQCProcess { get; set; } + public CurrentQC currentQCType { get; set; } + + } + + public class SetCheckPassDt + { + public string ManualPassReason { get; set; } = string.Empty; + + public Guid Id { get; set; } + } + + + public class ObtainOrCancelQCTaskDto + { + public Guid trialId { get; set; } + public Guid subjectVisitId { get; set; } + public bool obtaionOrCancel { get; set; } + } + + public class ReadingClinicalDataSignIndto + { + /// + /// 阅片临床数据ID + /// + public Guid ReadingClinicalDataId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + /// + /// 稽查泛型Dto + /// + /// 泛型 + public class DataInspectionDto + { + + + public T Data { get; set; } + + + public SignDTO SignInfo { get; set; } + + + } + + #region Qc + public class DeleteStudyList + { + public Guid[] ids { get; set; } + public Guid subjectVisitId { get; set; } + public Guid trialId { get; set; } + } + #endregion + + #region 患者 + public class InsSubjectCommand : InspectionBase, IInspectionDTO, ISignDTO + { + public SubjectCommand OptCommand { get; set; } + } + + public class InsUpdateSubjectStatus : InspectionBase, IInspectionDTO, ISignDTO + { + public SubjectStatusChangeCommand OptCommand { get; set; } + } + + + public class InsDeleteSubjectDto : InspectionBase, IInspectionDTO, ISignDTO + { + public DeleteSubjectDto OptCommand { get; set; } + } + + + public class DeleteSubjectDto + { + public Guid SubjectId { get; set; } + + public Guid TrialId { get; set; } + + public Guid SiteId { get; set; } + + public string Code { get; set; } = string.Empty; + } + + #endregion + + + + + /// + /// 用户 签名某个文档 Dto + /// + public class TrialDocumentConfirmDTO : InspectionBase, IInspectionDTO, ISignDTO + { + public UserConfirmCommand OptCommand { get; set; } + + } + + public class GetDataInspectionOutDto : DataInspection + { + public Guid? TrialReadingCriterionId { get; set; } + public string TrialReadingCriterionName { get; set; } + public string BlindName { get; set; } + + public string TaskName { get; set; } + + //public string FirstName { get; set; } = string.Empty; + + //public string LastName { get; set; } = string.Empty; + + public string VisitName { get; set; } = string.Empty; + + //public string ParentJson { get; set; } = string.Empty; + + + public string CreateUserRealName { get; set; } = string.Empty; + + public string Description { get; set; } = string.Empty; + + public string ModuleTypeName { get; set; } = string.Empty; + + + public string SignText { get; set; } = string.Empty; + + public decimal? VisitNum { get; set; } + + public bool? InPlan { get; set; } + + public string OptType { get; set; } = string.Empty; + + public Guid? FrontAuditConfigId { get; set; } + + + /// + /// 父标识 + /// + public string ParentIdentification { get; set; } + + + public string ExperimentName { get; set; } + + + public string SiteCode { get; set; } + + public string ResearchProgramNo { get; set; } + + + public string SiteName { get; set; } + + + public string SubjectCode { get; set; } + + + public string SubjectVisitName { get; set; } + + + + } + + public class UpdateTrialState + { + public Guid trialId { get; set; } + public string trialStatusStr { get; set; } + public string reason { get; set; } + } + + public class AbandonTrial + { + public Guid trialId { get; set; } + public bool isAbandon { get; set; } + } + + + public class GetDataInspectionDto : PageInput + { + + + /// + /// 项目iD + /// + public Guid? TrialId { get; set; } + + /// + /// 中心 + /// + public Guid? SiteId { get; set; } + + /// + /// 患者 + /// + public string SubjectInfo { get; set; } = string.Empty; + + /// + /// + /// + public decimal? VisitPlanInfo { get; set; } + + /// + /// 开始时间 + /// + public DateTime? StartTime { get; set; } + + /// + /// 结束时间 + /// + public DateTime? EndTime { get; set; } + + + + /// + /// 功能模块 + /// + public Guid? ModuleType { get; set; } + + /// + /// Description + /// + public string Description { get; set; } = string.Empty; + + /// + /// 操作人名称模糊查询 + /// + public string OpByUserName { get; set; } = string.Empty; + + /// + /// 阅读片人 + /// + public string ReaderUser { get; set; } = string.Empty; + + /// + /// 是否有签名 + /// + public bool? IsSign { get; set; } + + /// + /// 批次Id + /// + public Guid? BatchId { get; set; } + + public Guid? GeneralId { get; set; } + + public DateTime? RelationDeadlineTime { get; set; } + + public Guid? ObjectRelationParentId { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + + } + + public class InspectionBase + { + public DataInspectionAddDTO AuditInfo { get; set; } + + public SignDTO SignInfo { get; set; } = new SignDTO(); + } +} diff --git a/IRaCIS.Core.Application/Service/Inspection/FrontAuditConfigService.cs b/IRaCIS.Core.Application/Service/Inspection/FrontAuditConfigService.cs new file mode 100644 index 0000000..2ec831e --- /dev/null +++ b/IRaCIS.Core.Application/Service/Inspection/FrontAuditConfigService.cs @@ -0,0 +1,1330 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-28 16:46:23 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using Castle.Core.Internal; +using MassTransit; +using IRaCIS.Core.Infra.EFCore.Common.Dto; +using Microsoft.Data.SqlClient; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Domain.Share.Management; +using System.Text.Json.Nodes; +using IRaCIS.Application.Contracts; + +namespace IRaCIS.Core.Application.Service +{ + + /// + /// FrontAuditConfigService + /// + [ApiExplorerSettings(GroupName = "Reviewer")] + public class FrontAuditConfigService : BaseService, IFrontAuditConfigService + { + + private readonly IRepository _frontAuditConfigRepository; + private readonly IRepository _qCChallengeDialogRepository; + private readonly IRepository _dataInspectionRepository; + private readonly IRepository _qCChallengeRepository; + private readonly IRepository _dictionaryRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _checkChallengeDialogRepository; + + public FrontAuditConfigService(IRepository frontAuditConfigRepository, + IRepository qCChallengeDialogRepository, + IRepository dataInspectionRepository, + IRepository qCChallengeRepository, + IRepository dictionaryRepository, + IRepository trialRepository, + IRepository checkChallengeDialogRepository + ) + { + _frontAuditConfigRepository = frontAuditConfigRepository; + this._qCChallengeDialogRepository = qCChallengeDialogRepository; + this._dataInspectionRepository = dataInspectionRepository; + this._qCChallengeRepository = qCChallengeRepository; + this._dictionaryRepository = dictionaryRepository; + this._trialRepository = trialRepository; + this._checkChallengeDialogRepository = checkChallengeDialogRepository; + } + + /// + /// 获取数据库所有表 + /// + /// + [HttpPost] + public async Task> GetDatabaseTables() + { + return await _frontAuditConfigRepository._dbContext.GetTableList().ToListAsync(); + } + + /// + /// 获取查询对象 + /// + /// + /// + [HttpPost] + public async Task> GetDialogList(AccessToDialogueInDto inDto) + { + List data = new List(); + + switch (inDto.Type) + { + case AccessToDialogueEnum.Question: + + AccessToDialogueOutDto title = (await _qCChallengeRepository.Where(x => x.Id == inDto.Id).Include(x => x.CreateUser).Select(x => new AccessToDialogueOutDto() + { + CreateTime = x.CreateTime, + CreateUserName = x.CreateUser.UserName, + TalkContent = x.Content, + IsTitle = true, + }).FirstOrDefaultAsync()) ?? new AccessToDialogueOutDto(); + + data = await _qCChallengeDialogRepository.Where(x => x.QCChallengeId == inDto.Id && x.CreateTime <= inDto.Createtime).Include(x => x.CreateUser).Select( + x => new AccessToDialogueOutDto() + { + CreateTime = x.CreateTime, + CreateUserName = x.CreateUser.UserName, + TalkContent = x.TalkContent + } + ).OrderBy(x => x.CreateTime).ToListAsync(); + + data.Insert(0, title); + + break; + case AccessToDialogueEnum.Consistency: + data = await _checkChallengeDialogRepository.Where(x => x.SubjectVisitId == inDto.Id && x.CreateTime <= inDto.Createtime).Include(x => x.CreateUser).Select( + x => new AccessToDialogueOutDto() + { + CreateTime = x.CreateTime, + CreateUserName = x.CreateUser.UserName, + TalkContent = x.TalkContent + } + ).OrderBy(x => x.CreateTime).ToListAsync(); + break; + + } + + return data; + } + + /// + /// 获取表列名 + /// + /// + [HttpPost] + public async Task> GetTableColumn(string tableName) + { + return await _frontAuditConfigRepository._dbContext.GetTableColumn(tableName).ToListAsync(); + } + + /// + /// 复制配置项及其子项 + /// + /// 传入对象 + /// + [HttpPost] + public async Task CopyFrontAuditConfigItem(CopyFrontAuditConfigItemDto input) + { + var id = NewId.NextGuid(); + List frontAudits = new List(); + + var frontAuditConfig = (await _frontAuditConfigRepository.FirstOrDefaultAsync(x => x.Id == input.ChildId)).Clone(); + var fronts = await _frontAuditConfigRepository.Where(x => x.ParentId == frontAuditConfig.Id).ToListAsync(); + fronts.ForEach(x => + { + x.Id = NewId.NextGuid(); + x.ParentId = id; + }); + frontAuditConfig.ParentId = input.ParentId; + frontAuditConfig.Id = id; + frontAudits.Add(frontAuditConfig); + frontAudits.AddRange(fronts); + + await _frontAuditConfigRepository.AddRangeAsync(frontAudits); + await _frontAuditConfigRepository.SaveChangesAsync(); + + } + + /// + /// 批量添加字段 + /// + /// 数据集 + /// + public async Task BatchAddFrontAudit(BatchAddFrontAudit data) + { + var maxSort = await _frontAuditConfigRepository.Where(x => x.ParentId == data.ParentId).MaxAsync(x => x.Sort); + + List fronts = new List(); + foreach (var item in data.Columns) + { + maxSort++; + fronts.Add(new FrontAuditConfig() + { + Sort = maxSort, + Code = item.Name, + ValueCN = item.Remake, + IsEnable = true, + ParentId = data.ParentId + }); + } + + await _frontAuditConfigRepository.AddRangeAsync(fronts); + } + + /// + /// 翻译稽查数据 + /// + /// + /// + [HttpPost] + public async Task> SetInspectionEnumValue(SetInspectionEnumValueDto dto) + { + return await SetInspectionEnumValueDataList(dto, dto.AuditDataIds.FirstOrDefault()); + } + + + public async Task> GetInspectionJsonDataList(Guid trialId, Guid id) + { + //找到上一条Id + + + var currentInspection = await _dataInspectionRepository.Where(t => t.Id == id).Select(t => new { t.GeneralId, t.ObjectRelationParentId, t.CreateTime }).FirstOrDefaultAsync(); + + var beforeId = await _dataInspectionRepository.Where(x => x.GeneralId == currentInspection.GeneralId && x.CreateTime <= currentInspection.CreateTime && x.Id != id).OrderByDescending(x => x.CreateTime).Select(t => t.Id) + .FirstOrDefaultAsync(); + + List searchGuidList = new List() { id }; + + if (beforeId != Guid.Empty) + { + searchGuidList.Add(beforeId); + } + + return await SetInspectionEnumValueDataList(new SetInspectionEnumValueDto() { TrialId = trialId, AuditDataIds = searchGuidList }, id); + } + + + + + /// + /// 翻译稽查数据 + /// + /// 传入Dto + /// + private async Task> SetInspectionEnumValueDataList(SetInspectionEnumValueDto dto, Guid currentInspectionId) + { + var auditDatas = await _dataInspectionRepository.AsQueryable().Where(x => dto.AuditDataIds.Contains(x.Id)).Select(x => new SetInspectionEnumDataDto() + { + Id = x.Id, + JsonStr = x.JsonDetail, + Identification = x.Identification, + ObjectRelationParentId = x.ObjectRelationParentId, + ObjectRelationParentId2 = x.ObjectRelationParentId2, + ObjectRelationParentId3 = x.ObjectRelationParentId3, + CreateTime = x.CreateTime, + BatchId = x.BatchId, + + }).ToListAsync(); + + + + + var listIdentification = auditDatas.Select(x => x.Identification).Distinct().ToList(); + foreach (var item in auditDatas) + { + Dictionary jsonDict = JsonConvert.DeserializeObject>(item.JsonStr); + + if (!jsonDict.ContainsKey(nameof(InspectionJsonDetail.CommonData))) + { + jsonDict.Add(nameof(InspectionJsonDetail.CommonData), new { }); + } + + //查询关联父层级数据 + if (item.Id == currentInspectionId) + { + //把父层级的数据的 CommonData 数据合并(每一个层级把下面层级需要的数据放在CommonData 里面) 麻烦点是每个层级都需要记录一些信息,而且名称不能重复 + + var objectLsit = new List(); + + var relationParentDataObjList = await GetRelationParentData(item.Id, item.ObjectRelationParentId, item.ObjectRelationParentId2, item.ObjectRelationParentId3, item.CreateTime, item.BatchId, objectLsit); + + + + var currentCommonDataDic = JsonConvert.DeserializeObject>(jsonDict[nameof(InspectionJsonDetail.CommonData)].ToJsonStr()); + + foreach (var relationParentDataObj in relationParentDataObjList) + { + var otherDic = JsonConvert.DeserializeObject>(relationParentDataObj.ToJsonStr()); + + foreach (var valuePair in otherDic) + { + //关联层级的数据 + if (valuePair.Key.Contains("_")) + { + var entityProperName = valuePair.Key.Split("_").ToList().Last(); + + if (!currentCommonDataDic.ContainsKey(entityProperName)) + { + currentCommonDataDic.Add(entityProperName, valuePair.Value); + } + else + { + if (!currentCommonDataDic.ContainsKey(valuePair.Key)) + { + currentCommonDataDic.Add(valuePair.Key, valuePair.Value); + + } + } + } + else + { + if (!currentCommonDataDic.ContainsKey(valuePair.Key)) + { + currentCommonDataDic.Add(valuePair.Key, valuePair.Value); + } + } + } + } + + jsonDict[nameof(InspectionJsonDetail.CommonData)] = currentCommonDataDic; + } + + + #region Old + //item.JsonStr = jsonDict[nameof(InspectionJsonDetail.Data)].ToString(); + //if (item.Identification == string.Empty || item.JsonStr == string.Empty) + //{ + // continue; + //} + //item.JsonStr = await GetInspectionEnumValue(listIdentification, item.JsonStr); + //item.JsonStr = await SetEnum(dto.TrialId, listIdentification, item.JsonStr); + //item.JsonStr = await SetDataInspectionDateType(listIdentification, item.JsonStr); + + //jsonDict[nameof(InspectionJsonDetail.Data)] = JsonConvert.DeserializeObject(item.JsonStr); + #endregion + + #region New + + var str = jsonDict[nameof(InspectionJsonDetail.Data)].ToString(); + if (item.Identification == string.Empty || str == string.Empty) + { + continue; + } + str = await GetInspectionEnumValue(listIdentification, str); + str = await SetEnum(dto.TrialId, listIdentification, str); + str = await SetDataInspectionDateType(listIdentification, str); + + jsonDict[nameof(InspectionJsonDetail.Data)] = JsonConvert.DeserializeObject(str); + + + var str2 = jsonDict[nameof(InspectionJsonDetail.CommonData)].ToJsonStr(); + if (item.Identification == string.Empty || str2 == string.Empty) + { + continue; + } + str2 = await GetInspectionEnumValue(listIdentification, str2); + str2 = await SetEnum(dto.TrialId, listIdentification, str2); + str2 = await SetDataInspectionDateType(listIdentification, str2); + + jsonDict[nameof(InspectionJsonDetail.CommonData)] = JsonConvert.DeserializeObject(str2); + #endregion + + + #region 后续移除 避免前端看到的不统一 因为采用了新的关联方式,之前数据在Data里面取 现在配置在CommonData 里取 + //var dataDic = JsonConvert.DeserializeObject>(str); + //var commonDic = JsonConvert.DeserializeObject>(str2); + //foreach (var valuePair in dataDic) + //{ + // if (!commonDic.ContainsKey(valuePair.Key)) + // { + // commonDic.Add(valuePair.Key, valuePair.Value); + // } + //} + + //jsonDict[nameof(InspectionJsonDetail.CommonData)] = JsonConvert.DeserializeObject(commonDic.ToJsonStr()); + #endregion + + + item.JsonStr = JsonConvert.SerializeObject(jsonDict); + } + + var resultJsonStrList = new List(); + + dto.AuditDataIds.ForEach(x => + { + var auditData = auditDatas.FirstOrDefault(y => y.Id == x); + + resultJsonStrList.Add(auditData?.JsonStr); + }); + + if (resultJsonStrList.Count() < 2) + { + resultJsonStrList.Add(String.Empty); + } + return resultJsonStrList; + } + + + + private async Task AddJsonObjectToDic(Guid id, Guid? objectRelationParentId, DateTime createTime, Guid batchId, List objectLsit) + { + if (objectRelationParentId != null) + { + //父子层级的数据可能在同一个批次 进行更新 但是后插入的是父层级的数据 找父层级的稽查应该优先同一批次的 + var relationParentInspection = await _dataInspectionRepository.Where(t => t.GeneralId == objectRelationParentId && (t.CreateTime <= createTime || t.BatchId == batchId) && t.Id != id).OrderByDescending(x => x.CreateTime).Select(t => new { t.ObjectRelationParentId, t.CreateTime, t.JsonDetail, t.BatchId, t.ObjectRelationParentId2, t.ObjectRelationParentId3, t.EntityName, t.Id }).FirstOrDefaultAsync(); + + + if (relationParentInspection != null) + { + + Dictionary jsonDic = JsonConvert.DeserializeObject>(relationParentInspection.JsonDetail); + + + // //CommonData 仅仅存放关联对象的 Data里面的数据 + //if (jsonDic.ContainsKey(nameof(InspectionJsonDetail.CommonData))) + //{ + // var commonDataDicObj = jsonDic[nameof(InspectionJsonDetail.CommonData)]; + + // objectLsit.Add(commonDataDicObj); + //} + + + //避免对象信息记录 把 Data里面的信息也取过去 但是加上稽查对象的前缀 + var dataDicObj = jsonDic[nameof(InspectionJsonDetail.Data)]; + + if (dataDicObj != null) + { + var entityName = relationParentInspection.EntityName; + + IDictionary newNamepDic = new Dictionary(); + + var tempDic = JsonConvert.DeserializeObject>(dataDicObj.ToJsonStr()); + + foreach (var valuePair in tempDic) + { + newNamepDic.Add(entityName + "_" + valuePair.Key, valuePair.Value); + } + + + objectLsit.Add(newNamepDic); + } + + + + await AddJsonObjectToDic(relationParentInspection.Id, relationParentInspection.ObjectRelationParentId, relationParentInspection.CreateTime, relationParentInspection.BatchId, objectLsit); + await AddJsonObjectToDic(relationParentInspection.Id, relationParentInspection.ObjectRelationParentId2, relationParentInspection.CreateTime, relationParentInspection.BatchId, objectLsit); + await AddJsonObjectToDic(relationParentInspection.Id, relationParentInspection.ObjectRelationParentId3, relationParentInspection.CreateTime, relationParentInspection.BatchId, objectLsit); + } + + else + { + //用户的数据稽查没有 临时处理 + + + var userObj = await _repository.Where(t => t.Id == objectRelationParentId).Select(t => new { UserRealName = t.FullName, t.Phone, t.UserName, UserType = t.UserTypeRole.UserTypeShortName, t.EMail, t.OrganizationName }).FirstOrDefaultAsync(); + + if (userObj != null) + { + objectLsit.Add(userObj); + + } + } + + } + } + + + + private async Task> GetRelationParentData(Guid id, Guid? objectRelationParentId, Guid? objectRelationParentId2, Guid? objectRelationParentId3, DateTime createTime, Guid batchId, List objectLsit) + { + await AddJsonObjectToDic(id, objectRelationParentId, createTime, batchId, objectLsit); + await AddJsonObjectToDic(id, objectRelationParentId2, createTime, batchId, objectLsit); + + await AddJsonObjectToDic(id, objectRelationParentId3, createTime, batchId, objectLsit); + + + + #region 废弃 + //if (objectRelationParentId != null) + //{ + // //父子层级的数据可能在同一个批次 进行更新 但是后插入的是父层级的数据 找父层级的稽查应该优先同一批次的 + // var relationParentInspection = await _dataInspectionRepository.Where(t => t.GeneralId == objectRelationParentId && (t.CreateTime <= createTime || t.BatchId == batchId)).OrderByDescending(x => x.CreateTime).Select(t => new { t.ObjectRelationParentId, t.CreateTime, t.JsonDetail, t.BatchId, t.ObjectRelationParentId2, t.EntityName }).FirstOrDefaultAsync(); + + + // if (relationParentInspection != null) + // { + // Dictionary jsonDic = JsonConvert.DeserializeObject>(relationParentInspection.JsonDetail); + + + // var commonDataDicObj = jsonDic[nameof(InspectionJsonDetail.CommonData)]; + + // objectLsit.Add(commonDataDicObj); + + // //避免对象信息记录 把 Data里面的信息也取过去 但是加上稽查对象的前缀 + // var dataDicObj = jsonDic[nameof(InspectionJsonDetail.Data)]; + + // if (dataDicObj != null) + // { + // var entityName = relationParentInspection.EntityName; + + // IDictionary newNamepDic = new Dictionary(); + + // var tempDic = JsonConvert.DeserializeObject>(dataDicObj.ToJsonStr()); + + // foreach (var valuePair in tempDic) + // { + // newNamepDic.Add(entityName + "_" + valuePair.Key, valuePair.Value); + // } + + + // objectLsit.Add(newNamepDic); + // } + + + + // await GetRelationParentData(relationParentInspection.ObjectRelationParentId, relationParentInspection.ObjectRelationParentId2, relationParentInspection.CreateTime, relationParentInspection.BatchId, objectLsit); + // } + + + + //} + + //if (objectRelationParentId2 != null) + //{ + // //父子层级的数据可能在同一个批次 进行更新 但是后插入的是父层级的数据 找父层级的稽查应该优先同一批次的 + // var relationParentInspection = await _dataInspectionRepository.Where(t => t.GeneralId == objectRelationParentId2 && (t.CreateTime <= createTime || t.BatchId == batchId)).OrderByDescending(x => x.CreateTime).Select(t => new { t.ObjectRelationParentId, t.CreateTime, t.JsonDetail, t.BatchId, t.ObjectRelationParentId2, t.EntityName }).FirstOrDefaultAsync(); + + + // if (relationParentInspection != null) + // { + // Dictionary jsonDic = JsonConvert.DeserializeObject>(relationParentInspection.JsonDetail); + + + // var commonDataDicObj = jsonDic[nameof(InspectionJsonDetail.CommonData)]; + + // objectLsit.Add(commonDataDicObj); + + + // //避免对象信息记录 把 Data里面的信息也取过去 但是加上稽查对象的前缀 + // var dataDicObj = jsonDic[nameof(InspectionJsonDetail.Data)]; + + // if (dataDicObj != null) + // { + // var entityName = relationParentInspection.EntityName; + + // IDictionary newNamepDic = new Dictionary(); + + // var tempDic = JsonConvert.DeserializeObject>(dataDicObj.ToJsonStr()); + + // foreach (var valuePair in tempDic) + // { + // newNamepDic.Add(entityName + "_" + valuePair.Key, valuePair.Value); + // } + + + // objectLsit.Add(newNamepDic); + // } + + + + + // await GetRelationParentData(relationParentInspection.ObjectRelationParentId, relationParentInspection.ObjectRelationParentId2, relationParentInspection.CreateTime, relationParentInspection.BatchId, objectLsit); + // } + // else + // { + // //用户的数据稽查没有 临时处理 + + + + // var userObj = await _repository.Where(t => t.Id == objectRelationParentId2).Select(t => new { UserRealName = t.FullName, t.Phone, t.UserName, UserType = t.UserTypeRole.UserTypeShortName, t.EMail, t.OrganizationName }).FirstOrDefaultAsync(); + + // objectLsit.Add(userObj); + // } + + + + //} + + #endregion + + + return objectLsit; + } + + + + + + + /// + /// 格式化日期和时间 + /// + /// + /// + /// + private async Task SetDataInspectionDateType(List identificationList, string jsonStr) + { + var list = await (from parent in _frontAuditConfigRepository.AsQueryable().Where(x => identificationList.Contains(x.Identification)) + join child in _frontAuditConfigRepository.AsQueryable().Where(x => x.EnumType == "Date" && x.IsEnable) on parent.Id equals child.ParentId + select new DateDto() + { + Code = child.Code, + DateType = child.DateType, + }).ToListAsync(); + + list = list.GroupBy(x => new { x.Code }, (key, lst) => new DateDto() + { + Code = key.Code, + DateType = lst.Max(x => x.DateType), + }).ToList(); + + var jsonDataDic = JsonConvert.DeserializeObject>(jsonStr); + + if (jsonDataDic == null) + { + return jsonStr; + } + + foreach (var item in jsonDataDic.Keys) + { + var datefirst = list.FirstOrDefault(x => x.Code.ToLower() == item.ToLower()); + if (datefirst != null && !IsNullOrEmpty(jsonDataDic[item])) + { + try + { + if (datefirst.DateType == FrontAuditDateType.Date.GetDescription()) + { + jsonDataDic[item] = DateTime.Parse(jsonDataDic[item].ToString()).ToString("yyyy-MM-dd"); + } + + if (datefirst.DateType == FrontAuditDateType.DateTime.GetDescription()) + { + jsonDataDic[item] = DateTime.Parse(jsonDataDic[item].ToString()).ToString("yyyy-MM-dd HH:mm:ss"); + } + } + catch (Exception) + { + continue; + } + } + + } + + return JsonConvert.SerializeObject(jsonDataDic); + + + } + + /// + /// 获取外键表数据 + /// + /// + /// + /// + ///// 表名称 + ///// 外键value + ///// 要查询的外键值 + ///// 传入的纸 + private async Task GetInspectionEnumValue(List identificationList, string jsonStr) + { + var list = await (from u in _frontAuditConfigRepository.Where(x => identificationList.Contains(x.Identification)) + join p in _frontAuditConfigRepository.Where(x => x.EnumType == "Foreign" && x.IsEnable) on u.Id equals p.ParentId + select new + { + Key = p.Code, + ForeignKeyValue = p.ForeignKeyValue, + ForeignKeyText = p.ForeignKeyText, + ForeignKeyTable = p.ForeignKeyTableName + }).ToListAsync(); + list = list.GroupBy(x => new { x.Key }, (key, lst) => new + { + Key = key.Key, + ForeignKeyValue = lst.Max(x => x.ForeignKeyValue), + ForeignKeyText = lst.Max(x => x.ForeignKeyText), + ForeignKeyTable = lst.Max(x => x.ForeignKeyTable), + + }).ToList(); + + var jsonDataValueDic = JsonConvert.DeserializeObject>(jsonStr); + foreach (var item in list) + { + if (!jsonDataValueDic.ContainsKey(item.Key)) + { + continue; + } + string Table = item.ForeignKeyTable; + string ForeignKeyValue = item.ForeignKeyValue; + string ForeignKeyText = item.ForeignKeyText; + if (jsonDataValueDic[item.Key] != null) + { + string value = jsonDataValueDic[item.Key].ToString(); + string para = string.Empty; + string sql = string.Empty; + var JsonData = JsonConvert.DeserializeObject>(JsonConvert.SerializeObject(new { item = value })); + if (JsonData["item"].GetType() == typeof(JArray)) + { + foreach (var v in JsonData["item"] as JArray) + { + para += para == string.Empty ? $"'{v.ToString()}'" : $",'{v.ToString()}'"; + } + sql = $"select {ForeignKeyText} Text from [{Table}] where {ForeignKeyValue} in (@para)"; + } + else + { + para = $"{JsonData["item"].ToString()}"; + sql = $"select {ForeignKeyText} Text from [{Table}] where {ForeignKeyValue} = @para"; + } + SqlParameter[] paravalue = new SqlParameter[] { + new SqlParameter("@para",para) + }; + jsonDataValueDic[item.Key] = string.Join(",", _frontAuditConfigRepository._dbContext.Database.SqlQuery(sql, paravalue).Select(x => x.Text).ToList()); + } + } + return JsonConvert.SerializeObject(jsonDataValueDic); + } + + + /// + /// 获取枚举 + /// + /// 标识 + /// 标识 + /// Json对象 + /// + private async Task SetEnum(Guid trialId, List identificationList, string jsonStr) + { + if (jsonStr == null || jsonStr == "null") + { + return null; + } + + + //DictionaryCode='' and EnumType='Dictionary' 是审核状态 + var list = await (from u in _frontAuditConfigRepository.Where(x => identificationList.Contains(x.Identification)) + join p in _frontAuditConfigRepository.Where(x => (x.DictionaryCode != string.Empty && x.EnumType == "Dictionary") || (x.DataType == "Table") && x.IsEnable) on u.Id equals p.ParentId + select new + { + //前端展示类型 + DataType = p.DataType, + TableConfigJsonStr = p.TableConfigJsonStr, + Key = p.Code, + Code = p.DictionaryCode, + Type = p.DictionaryType + }).ToListAsync(); + + //两条不同的标识 但是里面配置有相同的翻译字典 + list = list.Distinct().ToList(); + + // 添加单双审 + var trialtype = await _trialRepository.AsQueryable().Where(x => x.Id == trialId).Select(x => x.QCProcessEnum).FirstOrDefaultAsync(); + + if (!list.Any(x => x.Key == "AuditState")) + { + list.Add(new + { + DataType = string.Empty, + TableConfigJsonStr = string.Empty, + Key = "AuditState", + Code = trialtype == TrialQCProcess.SingleAudit ? "AuditStatePE" : "AuditStateRC", + Type = "Code", + }); + } + + var jsonDataDic = JsonConvert.DeserializeObject>(jsonStr); + foreach (var item in list) + { + try + { + + if (!jsonDataDic.ContainsKey(item.Key) || jsonDataDic[item.Key] == null) + { + continue; + } + var value = jsonDataDic[item.Key]; + + //翻译的是数组 + if (value.GetType() == typeof(JArray)) + { + JArray arrays = (JArray)value; + + + if (item.DataType == "Table") + { + var tableConfigList = JsonConvert.DeserializeObject>(item.TableConfigJsonStr) ?? new List(); + + + //处理静态翻译 + var translateInfoList = tableConfigList.Where(t => t.IsNeedTransalate).Select(t => new { t.ColumnValue, t.TranslateDictionaryName }).Distinct().ToList(); + var dictionaryNameList = translateInfoList.Select(t => t.TranslateDictionaryName).Distinct().ToList(); + + + + //处理动态翻译 会在数组中提供 一个属性 “DictionaryCode” 这个是默认约束,做稽查的时候记得注意,免得配置麻烦 + var dynamicTranslateInfoList = tableConfigList.Where(t => t.IsDynamicTranslate && t.IsList).Select(t => new { t.ListName, t.ColumnValue }).Distinct().ToList(); + + var dynamicDictionaryNameList = new List(); + + foreach (var dynamicTranslateInfo in dynamicTranslateInfoList) + { + var tempNameList = arrays[0][dynamicTranslateInfo.ListName].Select(t => t["DictionaryCode"].ToString()).Where(t => !string.IsNullOrEmpty(t)).ToList(); + + dynamicDictionaryNameList.AddRange(tempNameList); + + dynamicDictionaryNameList = dynamicDictionaryNameList.Distinct().ToList(); + } + + + var allDictionaryNameList = dictionaryNameList.Union(dynamicDictionaryNameList).Distinct(); + + + var searchList = await _dictionaryRepository.Where(t => allDictionaryNameList.Contains(t.Parent.Code) && t.ParentId != null && t.IsEnable).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + //翻译的字典数据 + var translateDataList = searchList.GroupBy(t => t.ParentCode).ToDictionary(g => g.Key, g => g.OrderBy(t => t.ShowOrder).ToList()); + + List jsonList = new List(); + foreach (JToken arraysItem in arrays) + { + var jsonObject = JObject.Parse(arraysItem.ToString()); + + //处理静态翻译 + foreach (var translateInfo in translateInfoList) + { + //Json 解析后 true 变为了True + jsonObject[translateInfo.ColumnValue] = translateDataList[translateInfo.TranslateDictionaryName].Where(t => t.Code.ToLower() == jsonObject[translateInfo.ColumnValue]?.ToString().ToLower()).Select(t => t.ValueCN).FirstOrDefault(); + } + + //处理动态翻译 + + foreach (var dynamicTranslateInfo in dynamicTranslateInfoList) + { + + var innerArrays = (JArray)jsonObject[dynamicTranslateInfo.ListName]; + + + List innberJsonList = new List(); + + foreach (var innerItem in innerArrays) + { + var innerObject = JObject.Parse(innerItem.ToString()); + + + var dicName = innerObject["DictionaryCode"]?.ToString(); + + if(dicName!=null && !string.IsNullOrEmpty(dicName)) + { + innerObject[dynamicTranslateInfo.ColumnValue] = translateDataList[dicName].Where(t => t.Code.ToLower() == innerObject[dynamicTranslateInfo.ColumnValue].ToString().ToLower()).Select(t => t.ValueCN).FirstOrDefault(); + + } + innberJsonList.Add(innerObject); + + } + + jsonObject[dynamicTranslateInfo.ListName] = JToken.FromObject(innberJsonList) ; + + } + + jsonList.Add(jsonObject); + + } + + jsonDataDic[item.Key] = JToken.FromObject(jsonList); + + continue; + + } + + + if (item.Type.ToLower() == FrontAudit.Id.GetDescription().ToLower()) + { + List guids = new List(); + arrays.ForEach(x => + { + guids.Add(Guid.Parse(x.ToString())); + }); + jsonDataDic[item.Key] = string.Join(',', await _dictionaryRepository.Where(x => guids.Contains(x.Id)).Select(x => x.ValueCN).ToListAsync()); + } + else if (item.Type.ToLower() == FrontAudit.ChildGroup.GetDescription().ToLower()) + { + List guids = new List(); + arrays.ForEach(x => + { + guids.Add(x.ToString()); + }); + jsonDataDic[item.Key] = string.Join(',', await + _dictionaryRepository.Where(x => x.Code == item.Code).GroupJoin( + _dictionaryRepository.Where(x => guids.Contains(x.ChildGroup)), a => a.Id, b => b.ParentId, (a, b) => new + { + parent = b + }).SelectMany(a => a.parent, (m, n) => new + { + value = n.ValueCN + }).Select(x => x.value).ToListAsync() + ); + } + + //稽查 后端查询记录出表格数据,但是表格数据需要翻译,显示给出了翻译字典名,翻译的配置 + else if (item.Type.ToLower() == FrontAudit.DictionaryType.GetDescription().ToLower()) + { + + List jsonList = new List(); + + foreach (JToken arraysItem in arrays) + { + var jsonObject = JObject.Parse(arraysItem.ToString()); + try + { + if (jsonObject["DictionaryCode"] != null && jsonObject["DictionaryCode"].ToString() != string.Empty) + { + + jsonObject[item.Code] = await _dictionaryRepository.Where(x => x.Code == jsonObject["DictionaryCode"].ToString()).Join(_dictionaryRepository.Where(x => x.Code == jsonObject[item.Code].ToString()), a => a.Id, b => b.ParentId, (a, b) => new + { + value = b.ValueCN + }).Select(x => x.value).FirstOrDefaultAsync(); + + } + jsonList.Add(jsonObject); + + + } + catch (Exception) + { + jsonList.Add(jsonObject); + + } + } + + + jsonDataDic[item.Key] = JToken.FromObject(jsonList); + } + else + { + List guids = new List(); + arrays.ForEach(x => + { + guids.Add(x.ToString()); + }); + jsonDataDic[item.Key] = string.Join(',', await + _dictionaryRepository.Where(x => x.Code == item.Code).GroupJoin( + _dictionaryRepository.Where(x => guids.Contains(x.Code)), a => a.Id, b => b.ParentId, (a, b) => new + { + parent = b + }).SelectMany(a => a.parent, (m, n) => new + { + value = n.ValueCN + }).Select(x => x.value).ToListAsync() + ); + } + } + + //翻译的是单个字段 + else + { + //通过字典项的Guid 翻译 + if (item.Type.ToLower() == FrontAudit.Id.GetDescription().ToLower()) + { + Guid guid = Guid.Parse(value.ToString()); + jsonDataDic[item.Key] = await _dictionaryRepository.Where(x => x.Id == guid).Select(x => x.ValueCN).FirstOrDefaultAsync(); + } + else if (item.Type.ToLower() == FrontAudit.ChildGroup.GetDescription().ToLower()) + { + jsonDataDic[item.Key] = await _dictionaryRepository.Where(x => x.Code == item.Code).Join(_dictionaryRepository.Where(x => x.ChildGroup == value.ToString()), a => a.Id, b => b.ParentId, (a, b) => new + { + value = b.ValueCN + }).Select(x => x.value).FirstOrDefaultAsync(); + } + //通过字典项的code 翻译 枚举或者 bool + else + { + jsonDataDic[item.Key] = await _dictionaryRepository.Where(x => x.Code == item.Code).Join(_dictionaryRepository.Where(x => x.Code == value.ToString()), a => a.Id, b => b.ParentId, (a, b) => new + { + value = b.ValueCN + }).Select(x => x.value).FirstOrDefaultAsync(); + } + } + } + catch (Exception) + { + } + } + return JsonConvert.SerializeObject(jsonDataDic); + } + + + + + /// + /// 格式化日期和时间 + /// + /// 稽查数据 + /// + private async Task SetDataInspectionDateType(DataInspection Data) + { + var list = await (from parent in _frontAuditConfigRepository.AsQueryable().Where(x => x.Identification == Data.Identification) + join child in _frontAuditConfigRepository.AsQueryable().Where(x => x.EnumType == "Date") on parent.Id equals child.ParentId + select new DateDto() + { + Code = child.Code, + DateType = child.DateType, + }).ToListAsync(); + + var JsonData = JsonConvert.DeserializeObject>(Data.JsonDetail); + + foreach (var item in JsonData.Keys) + { + var datefirst = list.FirstOrDefault(x => x.Code.ToLower() == item.ToLower()); + if (datefirst != null && !IsNullOrEmpty(JsonData[item])) + { + try + { + if (datefirst.DateType == FrontAuditDateType.Date.GetDescription()) + { + JsonData[item] = DateTime.Parse(JsonData[item].ToString()).ToString("yyyy-MM-dd"); + } + + if (datefirst.DateType == FrontAuditDateType.DateTime.GetDescription()) + { + JsonData[item] = DateTime.Parse(JsonData[item].ToString()).ToString("yyyy-MM-dd HH:mm:ss"); + } + } + catch (Exception) + { + continue; + } + } + + } + Data.JsonDetail = JsonConvert.SerializeObject(JsonData); + + return Data; + } + + /// + /// IsNullOrEmpty + /// + /// + /// + private bool IsNullOrEmpty(object value) + { + if (value == null || value.ToString() == string.Empty) + { + return true; + } + else + { + return false; + } + } + + + + /// + /// 获取子数据 + /// + /// + /// + [HttpGet] + public async Task> GetAuditConfigChildList(Guid frontAuditConfigId) + { + //var list = await (from data in _repository.GetQueryable().Where(x => x.Id == frontAuditConfigId) + // join childrenType in _repository.GetQueryable() on data.Id equals childrenType.ParentId + // select childrenType).OrderBy(x => x.Sort).ToListAsync(); + //return list; + + var list = await _frontAuditConfigRepository.Where(t => t.ParentId == frontAuditConfigId).OrderBy(x => x.Sort).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + foreach (var item in list) + { + item.TableConfigList = JsonConvert.DeserializeObject>(item.TableConfigJsonStr) ?? new List(); + item.UrlConfig = JsonConvert.DeserializeObject(item.UrlConfigJsonStr) ?? new UrlConfig(); + } + + return list; + } + + /// + /// 完全复制其他子项到当前项 + /// + /// + /// + [HttpPost] + public async Task FullyReplicated(FullyReplicated fully) + { + await _frontAuditConfigRepository.DeleteFromQueryAsync(x => x.ParentId == fully.ToItemId); + var list = await _frontAuditConfigRepository.Where(x => x.ParentId == fully.FromItemId).ToListAsync(); + list.ForEach(x => + { + x.Id = NewId.NextGuid(); + x.ParentId = fully.ToItemId; + }); + await _frontAuditConfigRepository.AddRangeAsync(list); + await _frontAuditConfigRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } + + + /// + /// Cope子项数据 + /// + /// + /// + [HttpPost] + public async Task CopyOtherToThisItem(CopyOtherToThisItem item) + { + + var lists = _frontAuditConfigRepository.Where(x => x.ParentId == item.AddItemGuid).ToList(); + + var additem = await _frontAuditConfigRepository.FirstOrDefaultAsync(x => x.Id == item.AddItemGuid); + + var alllist = _frontAuditConfigRepository.Where(x => item.DataSourceGuids.Contains(x.ParentId)).ToList().GroupBy(x => new { x.ValueCN }, (key, lst) => new FrontAuditConfig + { + Sort = lst.Select(x => x.Sort).FirstOrDefault(), + TableConfigJsonStr = lst.Select(x => x.TableConfigJsonStr).FirstOrDefault(), + UrlConfigJsonStr = lst.Select(x => x.UrlConfigJsonStr).FirstOrDefault(), + IsShowByTrialConfig = lst.Select(x => x.IsShowByTrialConfig).FirstOrDefault(), + TrialConfigRelyFieldName = lst.Select(x => x.TrialConfigRelyFieldName).FirstOrDefault(), + Code = lst.Max(x => x.Code), + ConfigType = lst.Select(x => x.ConfigType).FirstOrDefault(), + CreateTime = DateTime.Now, + Description = lst.Select(x => x.Description).FirstOrDefault(), + //EnumList = lst.Select(x => x.EnumList).FirstOrDefault(), + //IsConfig = lst.Select(x => x.IsConfig).FirstOrDefault(), + IsShowParent = lst.Select(x => x.IsShowParent).FirstOrDefault(), + ParentId = item.AddItemGuid, + CreateUserId = _userInfo.Id, + IsEnable = lst.Select(x => x.IsEnable).FirstOrDefault(), + DictionaryKey = lst.Select(x => x.DictionaryKey).FirstOrDefault(), + EnumType = lst.Select(x => x.EnumType).FirstOrDefault(), + UpdateTime = DateTime.Now, + ValueCN = lst.Select(x => x.ValueCN).FirstOrDefault(), + Value = lst.Max(x => x.Value), + UpdateUserId = _userInfo.Id, + ChildrenTypeId = additem?.ChildrenTypeId, + ModuleTypeId = additem?.ModuleTypeId, + ObjectTypeId = additem?.ObjectTypeId, + OptTypeId = additem?.OptTypeId, + DictionaryCode = lst.Max(x => x.DictionaryCode), + DictionaryType = lst.Max(x => x.DictionaryType), + DateType = lst.Select(x => x.DateType).FirstOrDefault(), + ForeignKeyValue = lst.Select(x => x.ForeignKeyValue).FirstOrDefault(), + ForeignKeyText = lst.Select(x => x.ForeignKeyText).FirstOrDefault(), + ForeignKeyTableName = lst.Select(x => x.ForeignKeyTableName).FirstOrDefault(), + DataType = lst.Select(x => x.DataType).FirstOrDefault(), + Id = NewId.NextGuid()//新id, + }).ToList(); + + // 获取已存在的所有名称 + var names = lists.Select(x => x.ValueCN).ToList(); + + // 获取不存在的数据 + var list = alllist.Where(x => !names.Contains(x.ValueCN)).ToList(); + + // 获取要添加的name + var addvaluecns = list.Select(x => x.ValueCN); + + // 获取要修改的数据 + var neewupdate = lists.Where(x => !addvaluecns.Contains(x.ValueCN)); + + neewupdate.ForEach(x => + { + var item = alllist.FirstOrDefault(y => y.ValueCN == x.ValueCN); + if (item != null) + { + x.Code = item.Code; + x.Value = !item.Code.IsNullOrEmpty() ? item.Value : x.Value; + x.DictionaryType = !item.DictionaryType.IsNullOrEmpty() ? item.DictionaryType : x.DictionaryType; + x.DictionaryCode = !item.DictionaryCode.IsNullOrEmpty() ? item.DictionaryCode : x.DictionaryCode; + x.DataType = !item.DataType.IsNullOrEmpty() ? item.DataType : x.DataType; + x.DateType = !item.DateType.IsNullOrEmpty() ? item.DateType : x.DateType; + x.DictionaryKey = !item.DictionaryKey.IsNullOrEmpty() ? item.DictionaryKey : x.DictionaryKey; + x.IsShowParent = /*!item.IsShowParent == null ?*/ item.IsShowParent /*: x.IsShowParent*/; + x.ForeignKeyTableName = !item.ForeignKeyTableName.IsNullOrEmpty() ? item.ForeignKeyTableName : x.ForeignKeyTableName; + x.ForeignKeyText = !item.ForeignKeyText.IsNullOrEmpty() ? item.ForeignKeyText : x.ForeignKeyText; + x.ForeignKeyValue = !item.ForeignKeyValue.IsNullOrEmpty() ? item.ForeignKeyValue : x.ForeignKeyValue; + x.EnumType = !item.EnumType.IsNullOrEmpty() ? item.EnumType : x.EnumType; + } + + }); + + + await _repository.UpdateRange(neewupdate); + await _repository.AddRangeAsync(list); + await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + /// + /// 获取Description + /// + /// + /// + [HttpGet] + public async Task> GetModuleTypeDescriptionList(Guid moduleTypeId) + { + var result = await _frontAuditConfigRepository.Where(x => x.ModuleTypeId == moduleTypeId && x.ObjectTypeId != null && x.OptTypeId != null && x.Description.Length > 0).Select(x => new { x.Description, x.Sort }).OrderBy(t => t.Sort).ToListAsync(); + return result.Select(t => t.Description).Distinct().ToList(); + } + + /// + /// 获取列表 + /// + /// + /// + [HttpPost] + public async Task> GetFrontAuditConfigList(FrontAuditConfigQuery iq) + { + var query = from data in _repository.GetQueryable() + join childrenType in _repository.GetQueryable() on data.ChildrenTypeId equals childrenType.Id into childrenTypetemp + from leftchildrenType in childrenTypetemp.DefaultIfEmpty() + join ModuleType in _repository.GetQueryable() on data.ModuleTypeId equals ModuleType.Id into ModuleTypetemp + from leftModuleType in ModuleTypetemp.DefaultIfEmpty() + join OptTypeId in _repository.GetQueryable() on data.OptTypeId equals OptTypeId.Id into OptTypeIdtemp + from leftOptTypeId in OptTypeIdtemp.DefaultIfEmpty() + join ObjectTypeId in _repository.GetQueryable() on data.ObjectTypeId equals ObjectTypeId.Id into ObjectTypeIdtemp + from leftObjectTypeIdtemp in ObjectTypeIdtemp.DefaultIfEmpty() + select new FrontAuditConfigView() + { + IsShowParent = data.IsShowParent, + ChildrenTypeId = data.ChildrenTypeId, + Code = data.Code, + ConfigType = data.ConfigType, + CreateTime = data.CreateTime, + CreateUserId = data.CreateUserId, + Description = data.Description, + IsConfig = data.IsConfig, + IsEnable = data.IsEnable, + ModuleTypeId = data.ModuleTypeId, + Id = data.Id, + ParentId = data.ParentId, + UpdateTime = data.UpdateTime, + Value = data.Value, + ChildrenTypeValueCN = leftchildrenType.ValueCN, + ModuleTypeValue = leftModuleType.Value, + ModuleTypeValueCN = leftModuleType.ValueCN, + OptTypeId = data.OptTypeId, + OptTypeValue = leftOptTypeId.Value, + OptTypeValueCN = leftOptTypeId.ValueCN, + UpdateUserId = data.UpdateUserId, + Sort = data.Sort, + ValueCN = data.ValueCN, + ChildrenTypeValue = leftchildrenType.Value, + + DictionaryKey = data.DictionaryKey, + EnumType = data.EnumType, + ObjectTypeId = data.ObjectTypeId, + ObjectTypeValue = leftObjectTypeIdtemp.Value, + ObjectTypeValueCN = leftObjectTypeIdtemp.ValueCN, + IsShowByTrialConfig = data.IsShowByTrialConfig, + TrialConfigRelyFieldName = data.TrialConfigRelyFieldName, + + Identification = data.Identification, + IsHaveReason = data.IsHaveReason, + IsHaveSign = data.IsHaveSign, + IsFinish = data.IsFinish, + IsJoinPlan = data.IsJoinPlan, + DataType = data.DataType, + ChildDataLabel = data.ChildDataLabel, + ChildDataValue = data.ChildDataValue, + IsSpecialType = data.IsSpecialType, + DateType = data.DataType, + DictionaryCode = data.DictionaryCode, + DictionaryType = data.DictionaryType, + InterfaceName = data.InterfaceName, + + UrlConfigJsonStr = data.UrlConfigJsonStr, + TableConfigJsonStr = data.TableConfigJsonStr, + }; + + query = query + .WhereIf(!iq.Value.IsNullOrEmpty(), x => x.Value == iq.Value) + .WhereIf(!iq.ValueCN.IsNullOrEmpty(), x => x.ValueCN == iq.ValueCN) + .WhereIf(!iq.Description.IsNullOrEmpty(), x => x.Description == iq.Description) + .WhereIf(iq.OptTypeId != null, x => x.OptTypeId == iq.OptTypeId) + .WhereIf(!iq.Code.IsNullOrEmpty(), x => x.Code == iq.Code) + .WhereIf(iq.ChildrenTypeId != null, x => x.ChildrenTypeId == iq.ChildrenTypeId) + .WhereIf(iq.ModuleTypeId != null, x => x.ModuleTypeId == iq.ModuleTypeId) + .WhereIf(iq.ObjectTypeId != null, x => x.ObjectTypeId == iq.ObjectTypeId) + .WhereIf(!iq.ConfigType.IsNullOrEmpty(), x => x.ConfigType == iq.ConfigType); + + return await query.OrderBy(x => x.Sort).ToListAsync(); + } + + + /// + /// 修改排序 + /// + /// + /// + public async Task ChangeFrontAuditSort(List sortDataList) + { + foreach (var item in sortDataList) + { + await _frontAuditConfigRepository.BatchUpdateNoTrackingAsync(x => x.Id == item.Id, x => new FrontAuditConfig + { + Sort = item.Sort + }); + } + await _frontAuditConfigRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } + + /// + /// 新增或者修改 + /// + /// + /// + public async Task AddOrUpdateFrontAuditConfig(FrontAuditConfigAddOrEdit addOrEditFrontAuditConfig) + { + + if (await _frontAuditConfigRepository.AnyAsync(x => x.Identification != string.Empty && x.Identification == addOrEditFrontAuditConfig.Identification && x.Id != addOrEditFrontAuditConfig.Id && x.ConfigType == "M" && addOrEditFrontAuditConfig.ConfigType == "M")) + { + return ResponseOutput.NotOk("标识重复"); + } + + if (await _frontAuditConfigRepository.AnyAsync(x => x.Description == addOrEditFrontAuditConfig.Description && x.Id != addOrEditFrontAuditConfig.Id && x.ConfigType == "M" && addOrEditFrontAuditConfig.ConfigType == "M")) + { + return ResponseOutput.NotOk("名称重复"); + } + + if (addOrEditFrontAuditConfig.ConfigType == "C") + { + addOrEditFrontAuditConfig.Description = ""; + } + + addOrEditFrontAuditConfig.TableConfigJsonStr = JsonConvert.SerializeObject(addOrEditFrontAuditConfig.TableConfigList); + addOrEditFrontAuditConfig.UrlConfigJsonStr = JsonConvert.SerializeObject(addOrEditFrontAuditConfig.UrlConfig); + + var entity = await _frontAuditConfigRepository.InsertOrUpdateAsync(addOrEditFrontAuditConfig, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + /// + /// 删除 + /// + /// + /// + [HttpDelete("{frontAuditConfigId:guid}")] + public async Task DeleteFrontAuditConfig(Guid frontAuditConfigId) + { + if (await _frontAuditConfigRepository.AnyAsync(x => x.ParentId == frontAuditConfigId)) + { + return ResponseOutput.NotOk("存在子类 不能删除"); + } + var success = await _repository.BatchDeleteAsync(t => t.Id == frontAuditConfigId); + return ResponseOutput.Result(success); + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Inspection/InspectionService.cs b/IRaCIS.Core.Application/Service/Inspection/InspectionService.cs new file mode 100644 index 0000000..2828e97 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Inspection/InspectionService.cs @@ -0,0 +1,274 @@ +using Castle.Core.Internal; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Service.Inspection.DTO; +using IRaCIS.Core.Application.Service.Inspection.Interface; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using Panda.DynamicWebApi.Attributes; + + +namespace IRaCIS.Core.Application.Service.Inspection +{ + [NonDynamicWebApi] + public class InspectionService : BaseService, IInspectionService + { + private readonly IRepository _dataInspectionRepository; + private readonly IRepository _frontAuditConfigRepository; + + public InspectionService(IRepository dataInspectionRepository, + IRepository frontAuditConfigRepository + ) + { + this._dataInspectionRepository = dataInspectionRepository; + this._frontAuditConfigRepository = frontAuditConfigRepository; + } + + + + public async Task> GetInspectionList(GetDataInspectionDto dto) + { + //_repository.GetQueryable.GetQueryable < DataInspection > + + var trialData = await _repository.GetQueryable().Where(x => x.Id == dto.TrialId).AsNoTracking().FirstOrDefaultAsync(); + + + trialData.TrialFinishTime = trialData.TrialFinishTime == null ? DateTime.Now : trialData.TrialFinishTime; + + #region 逻辑代码 + var query = from data in _repository.GetQueryable() + //.Where(x => (x.TrialId == dto.TrialId)||(x.TrialId==null&&x.CreateTime>= trialData.CreateTime && x.CreateTime <= trialData.TrialFinishTime)) + + + join trial in _repository.GetQueryable().IgnoreQueryFilters() on data.TrialId equals trial.Id into trialtemp + from leftrial in trialtemp.DefaultIfEmpty() + join site in _repository.GetQueryable().IgnoreQueryFilters() on data.SiteId equals site.Id into sitetemp + from leftsite in sitetemp.DefaultIfEmpty() + + join trialSite in _repository.GetQueryable().IgnoreQueryFilters() on new { SiteId = leftsite.Id, Trialid = leftrial.Id } equals new { SiteId = trialSite.SiteId, Trialid = trialSite.TrialId } into trialSitetemp + from lefttrialSite in trialSitetemp.DefaultIfEmpty() + + + join subject in _repository.GetQueryable().IgnoreQueryFilters() on data.SubjectId equals subject.Id into subtemp + from leftsubject in subtemp.DefaultIfEmpty() + join subjectVisit in _repository.GetQueryable().IgnoreQueryFilters() on data.SubjectVisitId equals subjectVisit.Id into subjectVisittemp + from leftsubjectVisit in subjectVisittemp.DefaultIfEmpty() + join parent in _repository.GetQueryable() on data.ParentId equals parent.Id into parenttemp + from leftparent in parenttemp.DefaultIfEmpty() + join user in _repository.GetQueryable().IgnoreQueryFilters() on data.CreateUserId equals user.Id into usertemp + from leftuser in usertemp.DefaultIfEmpty() + join usertype in _repository.GetQueryable().IgnoreQueryFilters() on leftuser.UserTypeId equals usertype.Id into usertypetemp + from leftusertype in usertypetemp.DefaultIfEmpty() + + //join trialCriterion in _repository.GetQueryable().IgnoreQueryFilters() on data.TrialReadingCriterionId equals trialCriterion.Id into criterion + //from leftCriterion in criterion.DefaultIfEmpty() + + //join moduleTyped in _repository.GetQueryable().Where(x => x.Code == "ModuleType") on 1 equals 1 + //join moduleTypec in _repository.GetQueryable() on new { ParentId = moduleTyped.Id, ModuleType = data.ModuleType } equals new { ParentId = moduleTypec.ParentId.Value, ModuleType = moduleTypec.Value } into moduleTypectemp + + //join childrenTyped in _repository.GetQueryable().Where(x => x.Code == "ChildrenType") on 1 equals 1 + //join childrenTypec in _repository.GetQueryable() on new { ParentId = childrenTyped.Id, ModuleType = data.ChildrenType } equals new { ParentId = childrenTypec.ParentId.Value, ModuleType = childrenTypec.Value } into childrenTypectemp + //from leftchildrenTypec in childrenTypectemp.DefaultIfEmpty() + + //join ObjectTyped in _repository.GetQueryable().Where(x => x.Code == "ObjectType") on 1 equals 1 + //join ObjectTypec in _repository.GetQueryable() on new { ParentId = ObjectTyped.Id, ModuleType = data.ObjectType } equals new { ParentId = ObjectTypec.ParentId.Value, ModuleType = ObjectTypec.Value } into objectTypetemp + //from leftObjectType in objectTypetemp.DefaultIfEmpty() + + //join OptTyped in _repository.GetQueryable().Where(x => x.Code == "OptType") on 1 equals 1 + //join OptTypec in _repository.GetQueryable() on new { ParentId = OptTyped.Id, ModuleType = data.OptType } equals new { ParentId = OptTypec.ParentId.Value, ModuleType = OptTypec.Value } into optTypetemp + //from leftOptType in optTypetemp.DefaultIfEmpty() + + + join trialSign in _repository.GetQueryable().IgnoreQueryFilters() on data.SignId equals trialSign.Id into trialSigntemp + from lefttrialSign in trialSigntemp.DefaultIfEmpty() + join leftfrontAuditConfig in _repository.GetQueryable().Where(x => x.ConfigType == "M" && x.Identification != null && x.IsEnable == true) on + data.Identification.ToLower() + equals + leftfrontAuditConfig.Identification.ToLower() + join moduleTypec in _repository.GetQueryable() on new { ModuleType = leftfrontAuditConfig.ModuleTypeId.Value } equals new { ModuleType = moduleTypec.Id } into moduleTypectemp + from leftmoduleTypec in moduleTypectemp.DefaultIfEmpty() + join OptTypec in _repository.GetQueryable() on new { ModuleType = leftfrontAuditConfig.OptTypeId.Value } equals new { ModuleType = OptTypec.Id } into optTypetemp + from leftOptType in optTypetemp.DefaultIfEmpty() + + select new GetDataInspectionOutDto() + { + CreateTime = data.CreateTime, + CreateUserId = data.CreateUserId, + ModuleTypeId = leftmoduleTypec.Id, + BlindName = data.VisitTask.TaskBlindName, + TaskName = data.VisitTask.TaskName, + TrialId = data.TrialId, + SiteId = data.SiteId, + SubjectId = data.SubjectId, + SubjectVisitId = data.SubjectVisitId, + //OptType = data.OptType, + IP = data.IP, + Reason = data.Reason, + IsSign = leftfrontAuditConfig.IsHaveSign, + SignId = data.SignId, + ParentId = data.ParentId, + ChildrenTypeId = data.ChildrenTypeId, + //JsonDetail = data.JsonDetail, + //SiteName = data.SiteName, + //ExperimentName = data.TrialName, + //FirstName = leftsubject.FirstName, + //LastName = leftsubject.LastName, + + Id = data.Id, + //ParentJson = leftparent.JsonDetail, + VisitName = leftsubjectVisit.VisitName, + SubjectVisitName = leftsubjectVisit.VisitName, + + + //CreateUserName = leftuser.UserName, + //RoleName = leftusertype.UserTypeShortName, + + CreateUserRealName=data.CreateUserRealName, + CreateUserName = data.CreateUserName, + RoleName=data.RoleName, + + + //UserFirstName = leftuser.FirstName, + //UserLastName = leftuser.LastName, + ExperimentName = leftrial.ExperimentName, + + + SubjectCode = leftsubject.Code, + SiteCode = lefttrialSite.TrialSiteCode, + + ResearchProgramNo = leftrial.ResearchProgramNo, + ObjectTypeId = data.ObjectTypeId, + Description = leftfrontAuditConfig.Description, + ModuleTypeName = leftmoduleTypec.ValueCN, + SignText = lefttrialSign.SignText, + Identification = leftfrontAuditConfig.Identification, + FrontAuditConfigId = leftfrontAuditConfig.Id, + ParentIdentification = leftparent.Identification, + OptTypeId = leftOptType.Id, + VisitNum = leftsubjectVisit.VisitNum, + InPlan = leftsubjectVisit.InPlan, + //IsFrontAdd=data.IsFrontAdd, + BatchId = data.BatchId, + OptType = leftOptType.Value, + ObjectRelationParentId = data.ObjectRelationParentId, + GeneralId = data.GeneralId, + + TrialReadingCriterionId=data.TrialReadingCriterionId, + TrialReadingCriterionName = data.TrialReadingCriterion.CriterionName + }; + + query = query.WhereIf(dto.SiteId != null, x => x.SiteId == dto.SiteId) + .Where(x => (x.TrialId == dto.TrialId) || (x.TrialId == null && x.CreateTime >= trialData.CreateTime && x.CreateTime <= trialData.TrialFinishTime)) + + #region 废弃 + // .WhereIf(dto.BatchId != null && dto.ObjectRelationParentId == null && dto.GeneralId == null, x => x.BatchId == dto.BatchId) + // .WhereIf(dto.BatchId != null && dto.GeneralId != null && dto.ObjectRelationParentId == null, x => x.BatchId == dto.BatchId || + // ((x.GeneralId == dto.GeneralId || x.ObjectRelationParentId == dto.GeneralId) && x.CreateTime <= dto.RelationDeadlineTime.Value.AddSeconds(1))) + // .WhereIf(dto.ObjectRelationParentId != null && dto.BatchId != null && dto.GeneralId != null, + // x => + // x.BatchId == dto.BatchId || //同一事务批次 + // //x.ObjectRelationParentId == dto.ObjectRelationParentId || //不同对象 但是同一层级 适用于子对象 + + + //((x.GeneralId == dto.ObjectRelationParentId || //子稽查 查询父记录 + // x.GeneralId == dto.GeneralId || //同一对象 + // x.ObjectRelationParentId == dto.GeneralId //父稽查 查询子记录 + // ) && x.CreateTime <= dto.RelationDeadlineTime.Value.AddSeconds(1))) + #endregion + .WhereIf(dto.BatchId != null, x => x.BatchId == dto.BatchId) + .WhereIf(dto.BatchId == null && dto.GeneralId != null, x => x.GeneralId == dto.GeneralId) + .WhereIf(dto.TrialReadingCriterionId != null, x => x.TrialReadingCriterionId == dto.TrialReadingCriterionId) + + + .WhereIf(!dto.SubjectInfo.IsNullOrEmpty(), x => x.SubjectCode.Contains(dto.SubjectInfo)) + //.WhereIf(dto.VisitPlanInfo != null&& dto.VisitPlanInfo!=(decimal) 1.11, x => x.VisitNum == dto.VisitPlanInfo) + //.WhereIf(dto.VisitPlanInfo != (decimal)1.11,x=>x.InPlan!=null&& x.InPlan==false) + .WhereIf(dto.StartTime != null, x => x.CreateTime >= dto.StartTime) + .WhereIf(dto.EndTime != null, x => x.CreateTime <= dto.EndTime) + .WhereIf(dto.ModuleType != null, x => x.ModuleTypeId == dto.ModuleType) + .WhereIf(!dto.Description.IsNullOrEmpty(), x => x.Description == dto.Description) + .WhereIf(!dto.OpByUserName.IsNullOrEmpty(), x => x.CreateUserName.Contains(dto.OpByUserName)) + //.WhereIf(!dto.SubjectInfo.IsNullOrEmpty(), x => x.SubjectCode.Contains(dto.SubjectInfo)) + .WhereIf(dto.IsSign != null, x => x.IsSign == dto.IsSign); + #endregion + + if (dto.VisitPlanInfo != null && dto.VisitPlanInfo.Value != (decimal)1.11) + { + query = query.Where(x => x.VisitNum == dto.VisitPlanInfo.Value); + } + else if (dto.VisitPlanInfo != null) + { + query = query.Where(x => x.InPlan == false); + } + return await query.ToPagedListAsync(dto.PageIndex, dto.PageSize, dto.SortField.IsNullOrEmpty() ? nameof(GetDataInspectionOutDto.CreateTime) : dto.SortField, dto.Asc); + } + + + + + /// + /// 传入参数记录ID + /// + /// + /// + public async Task RecordSing(SignDTO SignInfo) + { + if (SignInfo != null) + { + var verifyResult = await VerifySignatureAsync(SignInfo); + var signId = await AddSignRecordAsync(SignInfo); + _userInfo.SignId = signId; + return signId; + } + else + { + return default(Guid); + } + + } + + /// + /// 完成签名 + /// + /// + /// + /// + public async Task CompletedSign(Guid signId, IResponseOutput response) + { + if (response.IsSuccess) + { + await _repository.BatchUpdateAsync(t => t.Id == signId, u => new TrialSign() { IsCompleted = true }); + } + } + + + + /// 验证用户签名信息 /// + public async Task VerifySignatureAsync(SignDTO signDTO) + { + var user = await _repository.FirstOrDefaultAsync(u => u.UserName == signDTO.UserName && u.Password == signDTO.PassWord); + if (user == null) + { + throw new BusinessValidationFailedException(_localizer["User_CheckNameOrPw"]); + } + else if (user.Status == UserStateEnum.Disable) + { + throw new BusinessValidationFailedException("当前用户已被禁用。"); + } + return ResponseOutput.Ok(); + + } + + /// 添加签名记录 /// + public async Task AddSignRecordAsync(SignDTO signDTO) + { + var add = await _repository.AddAsync(_mapper.Map(signDTO)); + + var success = await _repository.SaveChangesAsync(); + + return add.Id; + + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Inspection/Interface/IInspectionService.cs b/IRaCIS.Core.Application/Service/Inspection/Interface/IInspectionService.cs new file mode 100644 index 0000000..74c0d7c --- /dev/null +++ b/IRaCIS.Core.Application/Service/Inspection/Interface/IInspectionService.cs @@ -0,0 +1,28 @@ +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Service.Inspection.DTO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.Inspection.Interface +{ + public interface IInspectionService + { + + Task> GetInspectionList(GetDataInspectionDto dto); + + + + Task AddSignRecordAsync(SignDTO signDTO); + + Task RecordSing(SignDTO SignInfo); + + + Task CompletedSign(Guid signId, IResponseOutput response); + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Inspection/_MapConfig.cs b/IRaCIS.Core.Application/Service/Inspection/_MapConfig.cs new file mode 100644 index 0000000..efa0b9b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Inspection/_MapConfig.cs @@ -0,0 +1,33 @@ +using AutoMapper; +using AutoMapper.EquivalencyExpression; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Core.Application.Service.Inspection.DTO; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Service +{ + public class InspectionConfig : Profile + { + public InspectionConfig() + { + + + + CreateMap(); + + CreateMap(); + + + + + + + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/Institution/CROService.cs b/IRaCIS.Core.Application/Service/Institution/CROService.cs new file mode 100644 index 0000000..949e6e2 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/CROService.cs @@ -0,0 +1,80 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Application.Services +{ + [ ApiExplorerSettings(GroupName = "Institution")] + public class CroService : BaseService, ICroService + { + private readonly IRepository _croRepository; + private readonly IRepository _trialRepository; + + public CroService(IRepository croRepository, IRepository trialRepository) + { + _croRepository = croRepository; + this._trialRepository = trialRepository; + } + + /// 分页获取CRO列表 + [HttpPost] + public async Task> GetCroList(CROCompanyQueryDTO croCompanySearchModel) + { + var croQueryable = _croRepository + .WhereIf(!string.IsNullOrEmpty(croCompanySearchModel.CROName), t => t.CROName.Contains(croCompanySearchModel.CROName) || t.CRONameCN.Contains(croCompanySearchModel.CROName)) + .WhereIf(!string.IsNullOrEmpty(croCompanySearchModel.CROCode), t => t.CROCode.Contains(croCompanySearchModel.CROCode) ) + .ProjectTo(_mapper.ConfigurationProvider); + + return await croQueryable.ToPagedListAsync(croCompanySearchModel.PageIndex, croCompanySearchModel.PageSize, + string.IsNullOrWhiteSpace(croCompanySearchModel.SortField) ? "CROName": croCompanySearchModel.SortField, croCompanySearchModel.Asc); + + + } + + /// 根据CRO 名称查询所有CRO 列表 + public async Task> GetAllCROList() + { + return await _croRepository.ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + + + /// 添加CRO信息 + + public async Task AddOrUpdateCro(CROCompanyDTO addCroCompanyCommand) + { + + var exp = new EntityVerifyExp() + { + VerifyExp = cro => cro.CROName.Equals(addCroCompanyCommand.CROName) , + VerifyMsg = "已经存在同名的CRO,请确认。" + }; + + var cro = await _croRepository.InsertOrUpdateAsync(addCroCompanyCommand, true, exp); + + + + return ResponseOutput.Ok( cro.Id.ToString()); + + } + + /// 删除CRO信息 + + [HttpDelete("{croCompanyId:guid}")] + public async Task DeleteCro(Guid cROCompanyId) + { + if (await _trialRepository.AnyAsync(t => t.CROId == cROCompanyId)) + { + return ResponseOutput.NotOk("该CRO已经参与项目,不能被删除。"); + } + //if (_userRepository.Find().Any(t => t.OrganizationId == cROCompanyId)) + //{ + // return ResponseOutput.NotOk("该CRO下存在用户,暂时无法删除。"); + //} + var success = await _croRepository.BatchDeleteNoTrackingAsync(x=>x.Id== cROCompanyId); + + return ResponseOutput.Result(success); + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Institution/DTO/CROCompanyModel.cs b/IRaCIS.Core.Application/Service/Institution/DTO/CROCompanyModel.cs new file mode 100644 index 0000000..da90775 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/DTO/CROCompanyModel.cs @@ -0,0 +1,35 @@ +using System; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + public class CROCompanyDTO + { + public Guid? Id { get; set; } + public string CROName { get; set; } = string.Empty; + public string CROCode { get; set; } = string.Empty; + + public string CRONameCN { get; set; } = string.Empty; + + } + + public class CroSelectDTO + { + public Guid Id { get; set; } + public string CROName { get; set; } = string.Empty; + public string CROCode { get; set; } = string.Empty; + + public string CRONameCN { get; set; } = string.Empty; + } + + public class CROCompanyQueryDTO : PageInput + { + public string CROCode { get; set; } = string.Empty; + public string CROName { get; set; } = string.Empty; + } + + public class CROCompanySelectSearchDTO + { + public string CROName { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Institution/DTO/HospitalModel.cs b/IRaCIS.Core.Application/Service/Institution/DTO/HospitalModel.cs new file mode 100644 index 0000000..3bf2666 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/DTO/HospitalModel.cs @@ -0,0 +1,36 @@ +using System; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + public class HospitalDTO : HospitalCommand + { + + } + + public class HospitalCommand + { + public Guid? Id { get; set; } + public string HospitalName { get; set; } = String.Empty; + public string UniversityAffiliated { get; set; } = String.Empty; + public string Country { get; set; } = String.Empty; + public string Province { get; set; } = String.Empty; + public string City { get; set; } = String.Empty; + + + public string HospitalNameCN { get; set; } = String.Empty; + public string UniversityAffiliatedCN { get; set; } = String.Empty; + public string CountryCN { get; set; } = String.Empty; + public string ProvinceCN { get; set; } = String.Empty; + public string CityCN { get; set; } = String.Empty; + + public Guid? SiteId { get; set; } + } + + public class HospitalQueryDTO : PageInput + { + public string HospitalName { get; set; } = string.Empty; + public string Province { get; set; } = string.Empty; + public string City { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Application/Service/Institution/DTO/SiteModel.cs b/IRaCIS.Core.Application/Service/Institution/DTO/SiteModel.cs new file mode 100644 index 0000000..43f0272 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/DTO/SiteModel.cs @@ -0,0 +1,60 @@ +using System; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + public class SiteDTO: SiteCommand + { + + } + + public class SiteCommand + { + public Guid? Id { get; set; } + public string SiteName { get; set; } = String.Empty; + + public int Code { get; set; } + public string SiteCode { get; set; } = String.Empty; + + public string Address { get; set; } = String.Empty; + + public string Province { get; set; } = string.Empty; + public string UniqueCode { get; set; } = string.Empty; + + public string AliasName { get; set; } = string.Empty; + public string City { get; set; } = string.Empty; + public string Country { get; set; } = string.Empty; + public Guid? HospitalId { get; set; } + public string DirectorName { get; set; } = string.Empty; + public string DirectorPhone { get; set; } = string.Empty; + public string ContactName { get; set; } = string.Empty; + public string ContactPhone { get; set; } = string.Empty; + } + + public class SiteSelectionDTO + { + public Guid Id { get; set; } + public string SiteName { get; set; } = String.Empty; + public string City { get; set; } = String.Empty; + + public string Province { get; set; } = string.Empty; + public string Country { get; set; } = String.Empty; + } + + public class SiteSelectDTO : SiteDTO + { + public string HospitalName { get; set; } = String.Empty; + public bool IsSelect { get; set; } + } + + public class SiteQueryParam : PageInput + { + public string SiteName { get; set; } = String.Empty; + public string AliasName { get; set; } = string.Empty; + + public string City { get; set; } = string.Empty; + + + public string Country { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Institution/DTO/SponsorModel.cs b/IRaCIS.Core.Application/Service/Institution/DTO/SponsorModel.cs new file mode 100644 index 0000000..7bde890 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/DTO/SponsorModel.cs @@ -0,0 +1,34 @@ +using System; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + public class SponsorDTO:SponsorCommand + { + + } + + public class SponsorCommand + { + public Guid? Id { get; set; } + public string SponsorName { get; set; } = string.Empty; + + public string SponsorNameCN { get; set; } = String.Empty; + + public string SponsorCode { get; set; } = String.Empty; + + } + + public class SponsorQueryDTO : PageInput + { + public string SponsorName { get; set; } = string.Empty; + + public string SponsorCode { get; set; } = String.Empty; + } + + public class SponsorSelectDTO + { + public Guid Id { get; set; } + public string SponsorName { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Institution/HospitalService.cs b/IRaCIS.Core.Application/Service/Institution/HospitalService.cs new file mode 100644 index 0000000..485b7d6 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/HospitalService.cs @@ -0,0 +1,85 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Infrastructure; + +namespace IRaCIS.Application.Services +{ + [ ApiExplorerSettings(GroupName = "Institution")] + public class HospitalService : BaseService, IHospitalService + { + private readonly IRepository _hospitalRepository; + private readonly IRepository _doctorRepository; + + public HospitalService(IRepository hospitalRepository, IRepository doctorRepository) + { + _hospitalRepository = hospitalRepository; + this._doctorRepository = doctorRepository; + } + + /// 获取所有医院列表 + public async Task> GetHospitalList() + { + return await _hospitalRepository.ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + /// 添加医院 + [HttpPost] + public async Task AddOrUpdateHospital(HospitalCommand hospitalCommand) + { + var exp = new EntityVerifyExp() + { + VerifyExp = h => h.HospitalName.Equals(hospitalCommand.HospitalName), + VerifyMsg = "已经存在同名的医院,请确认。" + }; + + if (await _hospitalRepository.AnyAsync(x => x.SiteId == hospitalCommand.SiteId && hospitalCommand.SiteId != null)) + { + throw new BusinessValidationFailedException("当前Site已经添加到其他Hospital了"); + } + + var hospital = await _hospitalRepository.InsertOrUpdateAsync(hospitalCommand, true, exp); + + return ResponseOutput.Ok(hospital.Id.ToString()); + + } + + + + /// 删除医院信息 + [HttpDelete("{hospitalId:guid}")] + public async Task DeleteHospital(Guid hospitalId) + { + if (await _doctorRepository.AnyAsync(t => t.Id == hospitalId)) + { + return ResponseOutput.NotOk("该医院下已经注册有医生,不可以删除。"); + } + + + var success = await _hospitalRepository.BatchDeleteNoTrackingAsync(x => x.Id == hospitalId); + + return ResponseOutput.Result(success); + } + + /// 分页获取医院列表 + [HttpPost] + public async Task> GetHospitalPageList(HospitalQueryDTO hospitalSearchModel) + { + + var hospitalQueryable = + _hospitalRepository + .WhereIf(hospitalSearchModel.HospitalName!=null, t => t.HospitalName.Contains(hospitalSearchModel.HospitalName!) || t.HospitalNameCN.Contains(hospitalSearchModel.HospitalName!)) + .WhereIf(hospitalSearchModel.City != null, t => t.City.Contains(hospitalSearchModel.City!) || t.HospitalNameCN.Contains(hospitalSearchModel.City!)) + .WhereIf(hospitalSearchModel.Province != null, t => t.Province.Contains(hospitalSearchModel.Province!) || t.HospitalNameCN.Contains(hospitalSearchModel.Province!)) + .ProjectTo(_mapper.ConfigurationProvider); + + //优化后 + return await hospitalQueryable.ToPagedListAsync(hospitalSearchModel.PageIndex, hospitalSearchModel.PageSize, string.IsNullOrWhiteSpace(hospitalSearchModel.SortField) ? "HospitalName" : hospitalSearchModel.SortField, + hospitalSearchModel.Asc); + + + + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Institution/Interface/ICROService.cs b/IRaCIS.Core.Application/Service/Institution/Interface/ICROService.cs new file mode 100644 index 0000000..961a70b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/Interface/ICROService.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface ICroService + { + Task> GetCroList(CROCompanyQueryDTO queryModel); + Task> GetAllCROList(); + Task AddOrUpdateCro(CROCompanyDTO addCroCompanyViewModel); + Task DeleteCro(Guid croCompanyId); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Institution/Interface/IHospitalService.cs b/IRaCIS.Core.Application/Service/Institution/Interface/IHospitalService.cs new file mode 100644 index 0000000..c3694a9 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/Interface/IHospitalService.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IHospitalService + { + /// 获取医院列表 + Task> GetHospitalList(); + Task> GetHospitalPageList(HospitalQueryDTO queryModel); + Task AddOrUpdateHospital(HospitalCommand model); + Task DeleteHospital(Guid hospitalId); + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Institution/Interface/ISiteService.cs b/IRaCIS.Core.Application/Service/Institution/Interface/ISiteService.cs new file mode 100644 index 0000000..2d392e5 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/Interface/ISiteService.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface ISiteService + { + Task> GetSiteList(SiteQueryParam queryModel); + Task> GetAllSiteList(); + Task AddOrUpdateSite(SiteCommand AddModel); + Task DeleteSite(Guid researchCenterId); + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Institution/Interface/ISponsorService.cs b/IRaCIS.Core.Application/Service/Institution/Interface/ISponsorService.cs new file mode 100644 index 0000000..6020d07 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/Interface/ISponsorService.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface ISponsorService + { + Task> GetSponsorList(SponsorQueryDTO queryParam); + Task> GetAllSponsorList(); + Task AddOrUpdateSponsor(SponsorCommand model); + Task DeleteSponsor(Guid sponsorId); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Institution/SiteService.cs b/IRaCIS.Core.Application/Service/Institution/SiteService.cs new file mode 100644 index 0000000..a01360d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/SiteService.cs @@ -0,0 +1,97 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Share; +using Nito.AsyncEx; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Institution")] + public class SiteService : BaseService, ISiteService + { + private readonly IRepository _siteRepository; + private readonly IRepository _trialSiteUserRepository; + + private readonly AsyncLock _mutex = new AsyncLock(); + + public SiteService(IRepository siteRepository, IRepository trialSiteUserRepository) + { + _siteRepository = siteRepository; + this._trialSiteUserRepository = trialSiteUserRepository; + } + + /// 分页获取研究中心列表 + [HttpPost] + public async Task> GetSiteList(SiteQueryParam searchModel) + { + + var siteQueryable = _siteRepository + .WhereIf(!string.IsNullOrWhiteSpace(searchModel.SiteName), t => t.SiteName.Contains(searchModel.SiteName)) + .WhereIf(!string.IsNullOrWhiteSpace(searchModel.AliasName), t => t.AliasName.Contains(searchModel.AliasName)) + .WhereIf(!string.IsNullOrWhiteSpace(searchModel.City), t => t.City.Contains(searchModel.City)) + .WhereIf(!string.IsNullOrWhiteSpace(searchModel.Country), t => t.Country.Contains(searchModel.Country)) + .ProjectTo(_mapper.ConfigurationProvider); + + + return await siteQueryable.ToPagedListAsync(searchModel.PageIndex, searchModel.PageSize, string.IsNullOrWhiteSpace(searchModel.SortField) ? "SiteName" : searchModel.SortField, searchModel.Asc); + + + } + + public async Task> GetAllSiteList() + { + return await _siteRepository.ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + + /// 添加研究中心 + + public async Task AddOrUpdateSite(SiteCommand siteCommand) + { + + var exp = new EntityVerifyExp() + { + VerifyExp = h => h.SiteName.Equals(siteCommand.SiteName) || h.SiteCode.Equals(siteCommand.SiteCode), + VerifyMsg = "已经存在同名的中心,请确认。" + }; + + using (await _mutex.LockAsync()) + { + if (siteCommand.Id == null) + { + + siteCommand.Code = await _siteRepository.Select(t => t.Code).DefaultIfEmpty().MaxAsync() + 1; + + siteCommand.SiteCode = AppSettings.GetCodeStr(siteCommand.Code, nameof(User)); + } + + var site = await _siteRepository.InsertOrUpdateAsync(siteCommand, true, exp); + + return ResponseOutput.Ok(site.Id.ToString()); + + } + + + + + + + } + + /// 删除研究中心 + + [HttpDelete("{siteId:guid}")] + public async Task DeleteSite(Guid siteId) + { + + if (await _trialSiteUserRepository.AnyAsync(t => t.SiteId == siteId)) + { + return ResponseOutput.NotOk("该中心已经加入项目,不可以被删除。"); + } + + var success = await _siteRepository.BatchDeleteNoTrackingAsync(x => x.Id == siteId); + return ResponseOutput.Result(success); + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Institution/SponsorService.cs b/IRaCIS.Core.Application/Service/Institution/SponsorService.cs new file mode 100644 index 0000000..9381753 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/SponsorService.cs @@ -0,0 +1,83 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Application.Services +{ + [ ApiExplorerSettings(GroupName = "Institution")] + public class SponsorService : BaseService, ISponsorService + { + private readonly IRepository _sponsorRepository; + private readonly IRepository _trialRepository; + + public SponsorService(IRepository sponsorRepository, IRepository trialRepository) + { + _sponsorRepository = sponsorRepository; + this._trialRepository = trialRepository; + } + + /// 分页获取申办方列表 + [HttpPost] + public async Task> GetSponsorList(SponsorQueryDTO sponsorSearchModel) + { + + var sponsorQueryable = _sponsorRepository + .WhereIf(!string.IsNullOrWhiteSpace(sponsorSearchModel.SponsorName),t => t.SponsorName.Contains(sponsorSearchModel.SponsorName)|| t.SponsorNameCN.Contains(sponsorSearchModel.SponsorName)) + .WhereIf(!string.IsNullOrEmpty(sponsorSearchModel.SponsorCode), t => t.SponsorCode.Contains(sponsorSearchModel.SponsorCode)) + .ProjectTo(_mapper.ConfigurationProvider); + + return await sponsorQueryable.ToPagedListAsync(sponsorSearchModel.PageIndex, + sponsorSearchModel.PageSize, string.IsNullOrWhiteSpace(sponsorSearchModel.SortField) ? "Id" : sponsorSearchModel.SortField, sponsorSearchModel.Asc); + + } + + /// 分页获取申办方列表 + public async Task> GetAllSponsorList() + { + //Expression> sponsorLambda = x => true; + //if (!string.IsNullOrWhiteSpace(sponsorSearchModel.SponsorName)) + //{ + // sponsorLambda = sponsorLambda.And(t => t.SponsorName.Contains(sponsorSearchModel.SponsorName.Trim())); + //} + var sponsorQueryable = _sponsorRepository.ProjectTo(_mapper.ConfigurationProvider); + return await sponsorQueryable.ToListAsync(); + } + + /// 添加申办方 + public async Task AddOrUpdateSponsor(SponsorCommand sponsorCommand) + { + var exp = new EntityVerifyExp() + { + VerifyExp = h => h.SponsorName.Equals(sponsorCommand.SponsorName), + VerifyMsg = "已经存在同名的申办方,请确认。" + }; + + var sponsor = await _sponsorRepository.InsertOrUpdateAsync(sponsorCommand, true, exp); + + return ResponseOutput.Ok(sponsor.Id.ToString()); + + + + } + + /// 删除申办方 + [HttpDelete("{sponsorId:guid}")] + + public async Task DeleteSponsor(Guid sponsorId) + { + if (await _trialRepository.AnyAsync(t => t.CROId == sponsorId)) + { + return ResponseOutput.NotOk("该申办方已经加入项目,不允许删除。"); + } + //if (_userRepository.Find().Any(t => t.OrganizationId == sponsorId)) + //{ + // return ResponseOutput.NotOk("该申办方下存在用户,暂时无法删除。"); + //} + + + var success = await _sponsorRepository.BatchDeleteNoTrackingAsync(x => x.Id == sponsorId); + return ResponseOutput.Result(success); + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Institution/_MapConfig.cs b/IRaCIS.Core.Application/Service/Institution/_MapConfig.cs new file mode 100644 index 0000000..605901c --- /dev/null +++ b/IRaCIS.Core.Application/Service/Institution/_MapConfig.cs @@ -0,0 +1,34 @@ +using AutoMapper; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Application.Service +{ + public class InstitutionConfig : Profile + { + public InstitutionConfig() + { + CreateMap(); + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap().ReverseMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + + + //CreateMap() + // .ForMember(o => o.InstitutionName, t => t.MapFrom(u => u.CROName)); + //CreateMap() + // .ForMember(o => o.InstitutionName, t => t.MapFrom(u => u.SponsorName)); + //CreateMap() + // .ForMember(o => o.InstitutionName, t => t.MapFrom(u => u.HospitalName)); + //CreateMap() + // .ForMember(o => o.InstitutionName, t => t.MapFrom(u => u.SiteName)); + } + } + +} diff --git a/IRaCIS.Core.Application/Service/Management/DTO/MenuModel.cs b/IRaCIS.Core.Application/Service/Management/DTO/MenuModel.cs new file mode 100644 index 0000000..afdf671 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/DTO/MenuModel.cs @@ -0,0 +1,102 @@ +namespace IRaCIS.Application.Contracts +{ + public class MenuCommand + { + public Guid? MenuId { get; set; } + + //上级菜单 + public Guid? ParentId { get; set; } = Guid.Empty; + + // 类型(M目录 C菜单 F按钮 L链接) + public string MenuType { get; set; } = string.Empty; + + public string MenuIcon { get; set; } = String.Empty; + + public string MenuName { get; set; } = string.Empty; + + //路由地址 + public string Path { get; set; } = string.Empty; + + //组件路径 + public string Component { get; set; } = string.Empty; + + public int ShowOrder { get; set; } + + //启用 禁用 + public bool IsEnable { get; set; } = true; + + public bool IsCache { get; set; } = false; + + public bool IsDisplay { get; set; } + + public bool IsInTabDisplay { get; set; } + + public bool IsExternalLink { get; set; } + + //权限点 + public string PermissionStr { get; set; } = String.Empty; + + //Api 接口地址 + public string ApiPath { get; set; } = String.Empty; + + public string Meta { get; set; } = string.Empty; + + public string Note { get; set; } + + public string Redirect { get; set; } = string.Empty; + + public string LanguageMark { get; set; } + } + + + public class MenuDTO : MenuCommand + { + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + } + + public class MenuQueyDTO + { + public Guid? ParentId { get; set; } + + public string? MenuType { get; set; } + + public bool? IsEnable { get; set; } + + public bool? IsCache { get; set; } + + public bool? IsDisplay { get; set; } + + public bool? IsInTabDisplay { get; set; } + + public bool? IsExternalLink { get; set; } + } + + + public class MenuFunctionDTO:MenuCommand + { + public List Children { get; set; }=new List(); + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + } + + public class RoleMenuFunctionSelectDTO + { + public Guid RoleId { get; set; } + public List MenuFunctionId { get; set; }=new List(); + } + + + public class FunctionSelectDTO + { + public Guid RoleId { get; set; } + public Guid FunctionId { get; set; } + public bool IsSelect { get; set; } + } + +} diff --git a/IRaCIS.Core.Application/Service/Management/DTO/RoleModel.cs b/IRaCIS.Core.Application/Service/Management/DTO/RoleModel.cs new file mode 100644 index 0000000..c34b469 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/DTO/RoleModel.cs @@ -0,0 +1,30 @@ +using System; + +namespace IRaCIS.Application.Contracts +{ + public class RoleDTO + { + public Guid? Id { get; set; } = Guid.Empty; + public string RoleName { get; set; } = string.Empty; + public string RoleDescription { get; set; } = string.Empty; + public int PrivilegeLevel { get; set; } + } + + public class UserRoleSelectDTO + { + public Guid userId { get; set; } + public Guid roleId { get; set; } + public bool isSelect { get; set; } + } + + + public class UserSelectRoleDTO : RoleDTO + { + public bool IsSelect { get; set; } + } + + public class RoleCommand : RoleDTO + { + + } +} diff --git a/IRaCIS.Core.Application/Service/Management/DTO/SystemNoticeViewModel.cs b/IRaCIS.Core.Application/Service/Management/DTO/SystemNoticeViewModel.cs new file mode 100644 index 0000000..48be64d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/DTO/SystemNoticeViewModel.cs @@ -0,0 +1,153 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-04-25 09:46:34 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Domain.Share.Management; + +namespace IRaCIS.Core.Application.ViewModel +{ + /// SystemNoticeView 列表视图模型 + public class SystemNoticeView : SystemNoticeBasicInfo + { + public Guid? PublishedUserId { get; set; } + + public DateTime? PublishedTime { get; set; } + + public string PublishUserName { get; set; } + + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + + public string CreateUserName { get; set; } + + + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + public string FullFilePath { get; set; } + + public SystemNotice_NoticeStateEnum ActualNoticeStateEnum + + { + get + { + if (NoticeStateEnum == SystemNotice_NoticeStateEnum.HavePublished && EndDate != null && EndDate < DateTime.Now) + { + return SystemNotice_NoticeStateEnum.HaveExpired; + } + else + { + return NoticeStateEnum; + } + } + } + + public List NoticeUserTypeList { get; set; } + + } + + ///SystemNoticeQuery 列表查询参数模型 + public class SystemNoticeQuery:PageInput + { + public string NoticeContent { get; set; } = string.Empty; + + public string FileName { get; set; } = string.Empty; + + public SystemNotice_NoticeTypeEnum? NoticeTypeEnum { get; set; } + + public SystemNotice_NoticeLevelEnum? NoticeLevelEnum { get; set; } + + public SystemNotice_ApplicableProjectEnum? ApplicableProjectEnum { get; set; } + + public SystemNotice_NoticeModeEnum? NoticeModeEnum { get; set; } + + public SystemNotice_NoticeStateEnum? NoticeStateEnum { get; set; } + + + } + + public class SystemNoticeBasicInfo + { + public Guid? Id { get; set; } + public string NoticeContent { get; set; } + + public SystemNotice_NoticeTypeEnum NoticeTypeEnum { get; set; } + + public SystemNotice_NoticeLevelEnum NoticeLevelEnum { get; set; } + + public SystemNotice_ApplicableProjectEnum ApplicableProjectEnum { get; set; } + + public SystemNotice_NoticeModeEnum NoticeModeEnum { get; set; } + + public SystemNotice_NoticeStateEnum NoticeStateEnum { get; set; } + + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } + public string FileName { get; set; } + public string Path { get; set; } + } + + /// SystemNoticeAddOrEdit 列表查询参数模型 + public class SystemNoticeAddOrEdit: SystemNoticeBasicInfo + { + + + public Guid[] NoticeUserTypeIdList { get; set; } + } + + + public class SystemNoticeReadDTO: SystemNoticeBasicInfo + { + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + + public string CreateUserName { get; set; } + + public string FullFilePath { get; set; } + + public bool IsRead { get; set; } + + + public Guid? PublishedUserId { get; set; } + + public DateTime? PublishedTime { get; set; } + + public string PublishUserName { get; set; } + + public SystemNotice_NoticeStateEnum ActualNoticeStateEnum + + { + get + { + if (NoticeStateEnum == SystemNotice_NoticeStateEnum.HavePublished && EndDate != null && EndDate < DateTime.Now) + { + return SystemNotice_NoticeStateEnum.HaveExpired; + } + else + { + return NoticeStateEnum; + } + } + } + + } + + public class SystemNoticeUserTypeView + { + public Guid Id { get; set; } + public Guid SystemNoticeId { get; set; } + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public Guid UserTypeId { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Management/DTO/TreeNode.cs b/IRaCIS.Core.Application/Service/Management/DTO/TreeNode.cs new file mode 100644 index 0000000..3d5e440 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/DTO/TreeNode.cs @@ -0,0 +1,26 @@ +namespace IRaCIS.Application.Contracts +{ + + public class DictionaryTreeNode + { + public Guid Id { get; set; } + public string KeyName { get; set; } = string.Empty; + public string Type { get; set; } = string.Empty; + public List Children { get; set; }=new List(); + } + + public class MenuTreeNodeSelect: MenuCommand + { + + public List Children { get; set; } = new List(); + public bool IsSelect { get; set; } = false; + } + + + public class MenuTreeNode : MenuCommand + { + + public List Children { get; set; } = new List(); + } + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs b/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs new file mode 100644 index 0000000..34d6597 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs @@ -0,0 +1,254 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Core.Infrastructure.Extention; +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; + + +namespace IRaCIS.Application.Contracts +{ + + public class UserTypeViewModel + { + public Guid Id { get; set; } + + public string UserType { get; set; } = string.Empty; + public UserTypeEnum UserTypeEnum { get; set; } + public string Description { get; set; } = string.Empty; + } + + public class UserLoginDTO + { + public string UserName { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + } + + public class LoginReturnDTO + { + public UserBasicInfo BasicInfo { get; set; } = new UserBasicInfo(); + public string JWTStr { get; set; }=string.Empty; + + } + + public class UserBasicInfo + { + public Guid Id { get; set; } + public string UserName { get; set; } = string.Empty; + public string RealName { get; set; } = string.Empty; + public int Sex { get; set; } // 1-男 2-女 + + public UserTypeEnum UserTypeEnum { get; set; } + + + public bool IsTestUser { get; set; } + public bool IsAdmin { get; set; } = false; + public string UserTypeShortName { get; set; } = string.Empty; + public bool PasswordChanged { get; set; } + public int Status { get; set; } + public Guid UserTypeId { get; set; } + + public string Code { get; set; } = String.Empty; + + public string PermissionStr { get; set; } = String.Empty; + + + public bool IsFirstAdd { get; set; } + public bool IsReviewer { get; set; } = false; + + } + + public class MenuFuncTreeNodeView + { + [JsonIgnore] + public Guid Id { get; set; } + [JsonIgnore] + public Guid ParentId { get; set; } = Guid.Empty; + [JsonIgnore] + public int ShowOrder { get; set; } + + public string routeName { get; set; } = string.Empty; + public string component { get; set; } = string.Empty; + public string redirect { get; set; } = string.Empty; + public string path { get; set; } = string.Empty; + public Meta meta { get; set; } = new Meta(); + public bool hidden { get; set; } + public List Childrens { get; set; }=new List(); + } + + public class Meta + { + public string MetaTitle { get; set; } = string.Empty; + public bool MetaBreadcrumb { get; set; } = false; + public string MetaIcon { get; set; } = string.Empty; + public string MetaActiveMenu { get; set; } = string.Empty; + } + + + public class FunctionTreeNodeDTO + { + [JsonIgnore] + public Guid Id { get; set; } + [JsonIgnore] + public Guid ParentId { get; set; } = Guid.Empty; + [JsonIgnore] + public int ShowOrder { get; set; } + + public string RouteName { get; set; } = string.Empty; + public string FunctionName { get; set; } = string.Empty; + + public List Childrens { get; set; } = new List(); + } + + + + public class UserDetailDTO : UserInfo + { + public bool CanEditUserType { get; set; } + } + + public class UserInfo + { + public Guid Id { get; set; } + public string UserName { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + public string RealName { get; set; } = string.Empty; + + + public string FirstName { get; set; } = string.Empty; + + public string LastName { get; set; } = string.Empty; + public int Sex { get; set; } // 1-男 2-女 + + public int Status { get; set; } = 1; // 0-已删除 1-正常 + + public string Phone { get; set; } = string.Empty; + public string EMail { get; set; } = string.Empty; + public Guid UserTypeId { get; set; } = Guid.Empty; + + public string UserCode { get; set; } = string.Empty; + + public string UserType { get; set; } = string.Empty; + + public string UserTypeShortName { get; set; } = string.Empty; + + + public UserTypeEnum UserTypeEnum { get; set; } + + public bool? IsZhiZhun { get; set; } + + //public Guid OrganizationTypeId { get; set; } = Guid.Empty; + //public string OrganizationType { get; set; } = String.Empty; + //public Guid OrganizationId { get; set; } + public string OrganizationName { get; set; } = string.Empty; + + + + public string DepartmentName { get; set; } = String.Empty; + public string PositionName { get; set; } = String.Empty; + + public bool IsTestUser { get; set; } + } + + /// + /// 添加用户是的返回模型 + /// + public class UserAddedReturnDTO + { + public Guid Id { get; set; } + public string UserCode { get; set; } = string.Empty; + } + + + public class UserCommand : UserInfo + { + public string BaseUrl { get; set; } = string.Empty; + public string RouteUrl { get; set; } = string.Empty; + + //public string FirstName { get; set; } + //public string LastName { get; set; } + } + + public class EditPasswordCommand + { + public string NewUserName { get; set; } = string.Empty; + + public string NewPassWord { get; set; } = string.Empty; + public string OldPassWord { get; set; } = string.Empty; + } + + + + public class UserAccountDto + { + public Guid UserId { get; set; } + + public string UserName { get; set; } = string.Empty; + + public string UserRealName { get; set; } = string.Empty; + + public string UserType { get; set; } = string.Empty; + } + + public class UserListQueryDTO : PageInput + { + public string UserName { get; set; } = string.Empty; + public string RealName { get; set; } + public string Phone { get; set; } = string.Empty; + public string OrganizationName { get; set; } = string.Empty; + public Guid? UserType { get; set; } + + public bool? IsTestUser { get; set; } + + public bool ? IsZhiZhun { get; set; } + public UserStateEnum? UserState { get; set; } + } + + public class UserRoleInfoDTO + { + public List RoleList { get; set; } = new List(); + public int MaxPrivilegeLevel { get; set; } + } + + public class UserListDTO : UserInfo + { + [JsonIgnore] + public Guid testGuid { get; set; } + public bool CanEditUserType { get; set; } + + public List RoleNameArray { get; set; } = new List(); + public IEnumerable RoleNameList { get; set; } = new List(); + } + + + public class UserIdRoleName : RoleDTO + { + public Guid UserId { get; set; } + } + public class UserIdRoleNameList + { + public Guid UserId { get; set; } + public IEnumerable RoleList { get; set; }=new List(); + } + + + public class AllowAnonymousResetPasswordCommand + { + public string Email { get; set; } = string.Empty; + + public string VerificationCode { get; set; } = string.Empty; + + public string NewPwd { get; set; } = string.Empty; + + public string UserName { get; set; } = string.Empty; + } + + public class ResetPasswordCommand + { + public string EmailOrPhone { get; set; } = string.Empty; + public VerifyType VerificationType { get; set; } + public string VerificationCode { get; set; } = string.Empty; + public string NewPwd { get; set; } = string.Empty; + + public bool IsReviewer { get; set; } = false; + } +} diff --git a/IRaCIS.Core.Application/Service/Management/DTO/UserTypeRoleModel.cs b/IRaCIS.Core.Application/Service/Management/DTO/UserTypeRoleModel.cs new file mode 100644 index 0000000..ef13f02 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/DTO/UserTypeRoleModel.cs @@ -0,0 +1,94 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-03 09:38:51 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Domain.Share; +namespace IRaCIS.Core.Application.Contracts +{ + /// UserTypeRoleView 列表视图模型 + public class UserTypeRoleView : UserTypeMenuAddOrEdit + { + public List UserTypeGroupList { get; set; } = new List(); + + public new List UserTypeGroupIdList => UserTypeGroupList.Select(t=>t.DictionaryId).ToList(); + + } + + public class UserTypeGroupInfo + { + public Guid UserTypeId { get; set; } + + public Guid DictionaryId { get; set; } + + public string GroupName { get; set; }=string.Empty; + + public string GroupNameCN { get; set; } = string.Empty; + } + + ///UserTypeRoleQuery 列表查询参数模型 + public class UserTypeQuery + { + public Guid? GroupId { get; set; } + + public string? SearchFilter { get; set; } = string.Empty; + + } + + public class UserTypeMenuAddOrEdit: UserTypeRoleAddOrEdit + { + public List MenuIds { get; set; } = new List(); + + } + + public class UserTypeRoleAddOrEdit + { + + public Guid? Id { get; set; } + public string UserTypeName { get; set; } = string.Empty; + public string UserTypeShortName { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string PermissionStr { get; set; } = string.Empty; + public int Order { get; set; } + + + public List UserTypeGroupIdList { get; set; } = new List(); + + + + + + } + + + public class TrialUserType + { + public Guid Id { get; set; } + public string UserType { get; set; } = string.Empty; + public UserTypeEnum UserTypeEnum { get; set; } + public string UserTypeShortName { get; set; } = string.Empty; + public string PermissionStr { get; set; } = String.Empty; + + + + //public UserTypeGroup Type { get; set; } + + //public bool IsInternal { get; set; } + + + + } + + + public class UserTypeSimpleDTO + { + public Guid Id { get; set; } + public string UserTypeName { get; set; } = string.Empty; + public UserTypeEnum UserTypeEnum { get; set; } + public string UserTypeShortName { get; set; } = string.Empty; + public string PermissionStr { get; set; } = String.Empty; + } + +} + + diff --git a/IRaCIS.Core.Application/Service/Management/Interface/IMenuService.cs b/IRaCIS.Core.Application/Service/Management/Interface/IMenuService.cs new file mode 100644 index 0000000..7f001dc --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/Interface/IMenuService.cs @@ -0,0 +1,13 @@ +using IRaCIS.Application.Contracts; + +namespace IRaCIS.Application.Services +{ + public interface IMenuService + { + Task AddOrUpdateMenu(MenuCommand menuAddOrUpdateModel); + Task DeleteMenu(Guid menuId); + Task> GetMenuList(MenuQueyDTO menuQueyDTO); + Task> GetMenuTree(); + Task> GetUserTypeMenuTree(Guid userTypeId); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Management/Interface/ISystemNoticeService.cs b/IRaCIS.Core.Application/Service/Management/Interface/ISystemNoticeService.cs new file mode 100644 index 0000000..5f7610d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/Interface/ISystemNoticeService.cs @@ -0,0 +1,25 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-04-25 09:46:39 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// ISystemNoticeService + /// + public interface ISystemNoticeService + { + + + Task> GetSystemNoticeList(SystemNoticeQuery querySystemNotice); + + Task AddOrUpdateSystemNotice(SystemNoticeAddOrEdit addOrEditSystemNotice); + + Task DeleteSystemNotice(Guid systemNoticeId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/Management/Interface/IUserService.cs b/IRaCIS.Core.Application/Service/Management/Interface/IUserService.cs new file mode 100644 index 0000000..cc44a25 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/Interface/IUserService.cs @@ -0,0 +1,22 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Application.Services +{ + public interface IUserService + { + Task> AddUser(UserCommand userAddModel); + Task DeleteUser(Guid userId); + Task GetUser(Guid id); + Task> GetUserList(UserListQueryDTO param); + Task> Login(string userName, string password); + Task ModifyPassword(EditPasswordCommand editPwModel); + Task ResetPassword(Guid userId); + + Task UpdateUser(UserCommand model); + Task UpdateUserState(Guid userId, UserStateEnum state); + + //Task SendVerificationCode(string emailOrPhone, VerifyType verificationType, bool isReviewer = false); + //Task SetNewPassword(ResetPasswordCommand resetPwdModel); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Management/Interface/IUserTypeService.cs b/IRaCIS.Core.Application/Service/Management/Interface/IUserTypeService.cs new file mode 100644 index 0000000..f91b2a3 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/Interface/IUserTypeService.cs @@ -0,0 +1,18 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-03 09:38:11 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IUserTypeService + { + Task AddOrUpdateUserTypeRole(UserTypeMenuAddOrEdit addOrEditUserTypeRole); + Task DeleteUserTypeRole(Guid userTypeRoleId); + Task> GetTrialUserTypeList(); + Task> GetUserTypeList(UserTypeSelectEnum userTypeSelectEnum); + Task> GetUserTypeRoleList(UserTypeQuery userTypeQuery); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Management/MenuService.cs b/IRaCIS.Core.Application/Service/Management/MenuService.cs new file mode 100644 index 0000000..51fa9e2 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/MenuService.cs @@ -0,0 +1,361 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Management")] + public class MenuService : BaseService, IMenuService + { + private readonly IRepository menuRepository; + private readonly IRepository _userTypeMenuRepository; + + public MenuService(IRepository menuRepository, IRepository userTypeMenuRepository) + { + this.menuRepository = menuRepository; + this._userTypeMenuRepository = userTypeMenuRepository; + } + + /// + /// 添加菜单 New + /// + /// + /// + public async Task AddOrUpdateMenu(MenuCommand menuAddOrUpdateModel) + { + var exp = new EntityVerifyExp() + { + VerifyExp = u => u.ParentId == menuAddOrUpdateModel.ParentId && u.MenuName == menuAddOrUpdateModel.MenuName, + VerifyMsg = "该父节点下已经存在同名的子节点。" + }; + + var menu = await _repository.InsertOrUpdateAsync(menuAddOrUpdateModel, true, exp); + + return ResponseOutput.Ok(menu.Id.ToString()); + + #region 废弃前 + + //if (menuAddOrUpdateModel.Id == null) + //{ + // bool exist = _menuFunctionRepository.Any(u => u.ParentId == menuAddOrUpdateModel.ParentId && u.MenuName == menuAddOrUpdateModel.MenuName); + // if (exist) + // { + // return ResponseOutput.NotOk("A son node with the same name already existed under this parent node."); + // } + + // if (!string.IsNullOrWhiteSpace(menuAddOrUpdateModel.FunctionName)) + // { + // menuAddOrUpdateModel.IsFunction = true; + // } + + // var result = _menuFunctionRepository.Add(_mapper.Map(menuAddOrUpdateModel)); + + // if (_menuFunctionRepository.SaveChanges()) + // { + // return ResponseOutput.Ok(result.Id.ToString()); + // } + // return ResponseOutput.NotOk(); + //} + //else + //{ + // var menuUpdateModel = menuAddOrUpdateModel; + // bool exist = _menuFunctionRepository.Any(u => u.ParentId == menuUpdateModel.ParentId && + // u.MenuName == menuUpdateModel.MenuName && u.Id != menuUpdateModel.Id); + // if (exist) + // { + // return ResponseOutput.NotOk("A son node with the same name already existed under this parent node."); + // } + // var updateModel = _mapper.Map(menuUpdateModel); + + // if (!string.IsNullOrWhiteSpace(updateModel.FunctionName)) + // { + // updateModel.IsFunction = true; + // } + // _menuFunctionRepository.Update(updateModel); + + // var success = _menuFunctionRepository.SaveChanges(); + // return ResponseOutput.Result(success); + //} + #endregion + + } + + /// + /// 删除菜单 已验证 父节点不允许删除 New + /// + /// + /// + [HttpDelete("{menuId:guid}")] + public async Task DeleteMenu(Guid menuId) + { + if (await menuRepository.AnyAsync(t => t.ParentId == menuId)) + { + return ResponseOutput.NotOk("该节点存在子节点,请在删除子节点后,再删除该节点。"); + } + + var success =await menuRepository.BatchDeleteNoTrackingAsync(u => u.Id == menuId); + + return ResponseOutput.Result(success); + } + + + /// + /// 所有的菜单树 包括禁用的 New + /// + /// + [HttpGet] + public async Task> GetMenuTree() + { + var allMenuList = (await menuRepository.ProjectTo(_mapper.ConfigurationProvider).ToListAsync()).IfNullThrowException(); + + var list = allMenuList.ToTree((root, child) => child.ParentId == Guid.Empty, + (root, child) => child.ParentId == root.MenuId, + (child, datalist) => + { + child.Children ??= new List(); + child.Children.AddRange(datalist); + }, item => item.ShowOrder); + + return list; + } + + /// + /// 菜单列表 参数都是可传的 根据需要传递 New + /// + /// + /// + [HttpPost] + public async Task> GetMenuList(MenuQueyDTO menuQueyDTO) + { + return await menuRepository + .WhereIf(menuQueyDTO.ParentId != null, t => t.ParentId == menuQueyDTO.ParentId) + .WhereIf(menuQueyDTO.IsEnable != null, t => t.IsEnable == menuQueyDTO.IsEnable) + .WhereIf(menuQueyDTO.IsCache != null, t => t.IsCache == menuQueyDTO.IsCache) + .WhereIf(menuQueyDTO.IsDisplay != null, t => t.IsDisplay == menuQueyDTO.IsDisplay) + .WhereIf(menuQueyDTO.IsExternalLink != null, t => t.IsExternalLink == menuQueyDTO.IsExternalLink) + .WhereIf(menuQueyDTO.IsInTabDisplay != null, t => t.IsInTabDisplay == menuQueyDTO.IsInTabDisplay) + .WhereIf(menuQueyDTO.MenuType != null, t => t.MenuType == menuQueyDTO.MenuType) + + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(t=>t.ShowOrder).ToListAsync(); + } + + + /// + /// 获取某用户类型(角色) 菜单树 勾选情况 New + /// + /// + /// + [HttpGet("{userTypeId:guid}")] + public async Task> GetUserTypeMenuTree(Guid userTypeId) + { + var menuFunctionlist = (await menuRepository + .ProjectTo(_mapper.ConfigurationProvider, new { userTypeId = userTypeId }).ToListAsync()).IfNullThrowException(); + + + var list = menuFunctionlist.ToTree((root, child) => child.ParentId == Guid.Empty, + (root, child) => child.ParentId == root.MenuId, + (child, datalist) => + { + child.Children ??= new List(); + child.Children.AddRange(datalist); + }, item => item.ShowOrder); + + return list; + + } + + [AllowAnonymous] + public async Task> GetUserMenuTree() + { + + var menuFunctionlist = await _userTypeMenuRepository + .Where(t => t.UserTypeId == _userInfo.UserTypeId && (t.Menu.MenuType == "M" || t.Menu.MenuType == "C") && t.Menu.IsEnable) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var list = menuFunctionlist.ToTree((root, child) => child.ParentId == Guid.Empty, + (root, child) => child.ParentId == root.MenuId, + (child, datalist) => + { + child.Children ??= new List(); + child.Children.AddRange(datalist); + }, item => item.ShowOrder); + + return list; + } + + [AllowAnonymous] + public async Task> GetUserPermissions() + { + + var list = await _userTypeMenuRepository + .Where(t => t.UserTypeId == _userInfo.UserTypeId && t.Menu.MenuType == "F") + .Select(t => t.Menu.PermissionStr).ToListAsync(); + + //var str = await _repository.Where(t => t.Id == _userInfo.UserTypeId).Select(t => t.PermissionStr).FirstOrDefaultAsync(); + + + list.Add(_userInfo.PermissionStr); + + return list; + + } + + + + #region 废弃 生成树废弃前 + + + + + //public List GetMenuFunction() + //{ + // var allMenuList = _menuFunctionRepository.ProjectTo(_mapper.ConfigurationProvider).ToList(); + + // return GetTreeList(Guid.Empty, allMenuList); + //} + + ///// + ///// 根据父节点ID递归生成树 + ///// + ///// + ///// + ///// + //[NonDynamicMethod] + //public List GetTreeList(Guid? parentId, List allMenuList) + //{ + // //树节点集合 每个节点包含一个菜单项 和一个子菜单集合 + // List treeList = new List(); + + // // 根据父菜单节点获取子菜单节点 并且进行排序 + // List menuList = allMenuList.Where(x => x.ParentId == parentId).OrderBy(t => t.ShowOrder).ToList(); + + // foreach (var menuItem in menuList) + // { + // MenuTreeNode treeItem = new MenuTreeNode() + // { + // Id = menuItem.Id, + // ParentId = menuItem.ParentId, + // RouteName = menuItem.RouteName, + // MenuName = menuItem.MenuName, + // Path = menuItem.Path, + // Hidden = menuItem.Hidden, + // Component = menuItem.Component, + // FunctionName = menuItem.FunctionName, + // IsFunction = menuItem.IsFunction, + // MetaActiveMenu = menuItem.MetaActiveMenu, + // MetaBreadcrumb = menuItem.MetaBreadcrumb, + // MetaIcon = menuItem.MetaIcon, + // MetaTitle = menuItem.MetaTitle, + // Redirect = menuItem.Redirect, + // ShowOrder = menuItem.ShowOrder, + // Note = menuItem.Note, + // Status = menuItem.Status, + + // Children = GetTreeList(menuItem.Id, allMenuList) + // }; + + // treeList.Add(treeItem); + // } + // return treeList; + //} + + + ///// + ///// 获取某角色 菜单树 勾选情况 和 功能勾选情况 + ///// + ///// + ///// + //[HttpGet("{roleId:guid}")] + //public List GetRoleMenuFunction(Guid roleId) + //{ + + + + // var menuFuncQuery = from function in _menuFunctionRepository.Where(u => !u.IsFunction) + // join roleMenuFunctionItem in _roleMenuFunctionRepository.AsQueryable() + // .Where(t => t.UserTypeId == roleId) + // on function.Id equals roleMenuFunctionItem.MenuFunctionId into t + // from roleFunction in t.DefaultIfEmpty() + // select new MenuTreeNodeSelect() + // { + // IsSelect = roleFunction != null, + // ShowOrder = function.ShowOrder, + // MenuName = function.MenuName, + // Note = function.Note, + // FunctionName = function.FunctionName, + // ParentId = function.ParentId, + // Id = function.Id, + // }; + // var list = menuFuncQuery.ToList(); + + // return GetSelectTreeList(Guid.Empty, list); + //} + + //[NonDynamicMethod] + //private List GetSelectTreeList(Guid parentId, List allMenuList) + //{ + // List TreeList = new List(); + // List menuList = allMenuList.Where(x => x.ParentId == parentId).OrderBy(t => t.ShowOrder).ToList(); + + // foreach (var menuItem in menuList) + // { + // MenuTreeNodeSelect treeItem = new MenuTreeNodeSelect() + // { + // Id = menuItem.Id, + // ParentId = menuItem.ParentId, + // RouteName = menuItem.RouteName, + // MenuName = menuItem.MenuName, + // Note = menuItem.Note, + // IsSelect = menuItem.IsSelect, + // ShowOrder = menuItem.ShowOrder, + // Children = GetSelectTreeList(menuItem.Id, allMenuList) + // }; + + // TreeList.Add(treeItem); + // } + // return TreeList; + //} + + //[NonDynamicMethod] + //private List GetTreeList(Guid? parentId, List allMenuList) + //{ + // List TreeList = new List(); + // List menuList = allMenuList.Where(x => x.ParentId == parentId).OrderBy(t => t.ShowOrder).ToList(); + + // foreach (var menuItem in menuList) + // { + // MenuTreeNode treeItem = new MenuTreeNode() + // { + // Id = menuItem.Id, + // ParentId = menuItem.ParentId, + // RouteName = menuItem.RouteName, + // MenuName = menuItem.MenuName, + // Path = menuItem.Path, + // Hidden = menuItem.Hidden, + // Component = menuItem.Component, + // FunctionName = menuItem.FunctionName, + // IsFunction = menuItem.IsFunction, + // MetaActiveMenu = menuItem.MetaActiveMenu, + // MetaBreadcrumb = menuItem.MetaBreadcrumb, + // MetaIcon = menuItem.MetaIcon, + // MetaTitle = menuItem.MetaTitle, + // Redirect = menuItem.Redirect, + // ShowOrder = menuItem.ShowOrder, + // Note = menuItem.Note, + // Status = menuItem.Status, + + // Children = GetTreeList(menuItem.Id, allMenuList) + // }; + + // TreeList.Add(treeItem); + // } + // return TreeList; + //} + + #endregion + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Management/SystemNoticeService.cs b/IRaCIS.Core.Application/Service/Management/SystemNoticeService.cs new file mode 100644 index 0000000..9bbe122 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/SystemNoticeService.cs @@ -0,0 +1,147 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-04-25 09:46:43 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Service +{ + /// + /// SystemNoticeService + /// + [ApiExplorerSettings(GroupName = "Management")] + public class SystemNoticeService : BaseService, ISystemNoticeService + { + + private readonly IRepository _systemNoticeRepository; + + public SystemNoticeService(IRepository systemNoticeRepository) + { + _systemNoticeRepository = systemNoticeRepository; + } + + [HttpPost] + public async Task> GetSystemNoticeList(SystemNoticeQuery querySystemNotice) + { + + + var systemNoticeQueryable = _systemNoticeRepository + .WhereIf(querySystemNotice.ApplicableProjectEnum != null, t => t.ApplicableProjectEnum == querySystemNotice.ApplicableProjectEnum) + .WhereIf(querySystemNotice.NoticeLevelEnum != null, t => t.NoticeLevelEnum == querySystemNotice.NoticeLevelEnum) + .WhereIf(querySystemNotice.NoticeModeEnum != null, t => t.NoticeModeEnum == querySystemNotice.NoticeModeEnum) + .WhereIf(querySystemNotice.NoticeStateEnum != null && querySystemNotice.NoticeStateEnum != Domain.Share.Management.SystemNotice_NoticeStateEnum.HaveExpired, t => t.NoticeStateEnum == querySystemNotice.NoticeStateEnum) + .WhereIf(querySystemNotice.NoticeModeEnum != null && querySystemNotice.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HaveExpired, t => t.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished && t.EndDate !=null && t.EndDate < DateTime.Now) + .WhereIf(querySystemNotice.NoticeTypeEnum != null, t => t.NoticeTypeEnum == querySystemNotice.NoticeTypeEnum) + .WhereIf(!string.IsNullOrWhiteSpace(querySystemNotice.FileName), t => t.FileName.Contains(querySystemNotice.FileName)) + .WhereIf(!string.IsNullOrWhiteSpace(querySystemNotice.NoticeContent), t => t.NoticeContent.Contains(querySystemNotice.NoticeContent)) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken }); + + return await systemNoticeQueryable.ToPagedListAsync(querySystemNotice.PageIndex, querySystemNotice.PageSize, querySystemNotice.SortField, querySystemNotice.Asc); + } + + + public async Task AddOrUpdateSystemNotice(SystemNoticeAddOrEdit addOrEditSystemNotice) + { + + if (addOrEditSystemNotice.Id == null) + { + var entity = await _systemNoticeRepository.InsertFromDTOAsync(addOrEditSystemNotice); + + if (entity.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished) + { + entity.PublishedUserId = _userInfo.Id; + entity.PublishedTime = DateTime.Now; + } + await _systemNoticeRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id.ToString()); + + } + else + { + var systemNotice = (await _systemNoticeRepository.Where(t => t.Id == addOrEditSystemNotice.Id, true, true).Include(t => t.NoticeUserTypeList).FirstOrDefaultAsync()).IfNullThrowException(); + + if (addOrEditSystemNotice.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished && systemNotice.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.NotPublish) + { + systemNotice.PublishedUserId = _userInfo.Id; + systemNotice.PublishedTime = DateTime.Now; + } + else if (addOrEditSystemNotice.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.NotPublish && systemNotice.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished) + { + systemNotice.PublishedUserId = null; + systemNotice.PublishedTime = null; + } + + + + _mapper.Map(addOrEditSystemNotice, systemNotice); + + + await _systemNoticeRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + + } + + + } + + + [HttpDelete("{systemNoticeId:guid}")] + public async Task DeleteSystemNotice(Guid systemNoticeId) + { + var success = await _systemNoticeRepository.BatchDeleteNoTrackingAsync(t => t.Id == systemNoticeId); + + return ResponseOutput.Result(success); + } + + + /// 设置已读 + [HttpPut("{systemNoticeId:guid}")] + public async Task SetSystemNoticeHaveRead(Guid systemNoticeId) + { + var systemNotice = (await _systemNoticeRepository.FirstOrDefaultAsync(t => t.Id == systemNoticeId)).IfNullThrowException(); + + systemNotice.NoticeUserReadList.Add(new SystemNoticeUserRead() { SystemNoticeId = systemNoticeId }); + + var success = await _systemNoticeRepository.SaveChangesAsync(); + + + return ResponseOutput.Result(success); + } + + /// 获取登陆用户的系统通知列表 只是过滤了用户类型 和已经发布的 + [HttpPost] + public async Task> GetUserSystemNoticeList(SystemNoticeQuery querySystemNotice) + { + var systemNoticeQueryable = _systemNoticeRepository + .Where(t => t.NoticeUserTypeList.Any(t => t.UserTypeId == _userInfo.UserTypeId) && t.NoticeStateEnum==Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished) + .WhereIf(querySystemNotice.ApplicableProjectEnum != null, t => t.ApplicableProjectEnum == querySystemNotice.ApplicableProjectEnum) + .WhereIf(querySystemNotice.NoticeLevelEnum != null, t => t.NoticeLevelEnum == querySystemNotice.NoticeLevelEnum) + .WhereIf(querySystemNotice.NoticeModeEnum != null, t => t.NoticeModeEnum == querySystemNotice.NoticeModeEnum) + .WhereIf(querySystemNotice.NoticeStateEnum != null && querySystemNotice.NoticeStateEnum != Domain.Share.Management.SystemNotice_NoticeStateEnum.HaveExpired, t => t.NoticeStateEnum == querySystemNotice.NoticeStateEnum) + .WhereIf(querySystemNotice.NoticeModeEnum != null && querySystemNotice.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HaveExpired, t => t.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished && t.EndDate != null && t.EndDate < DateTime.Now) + .WhereIf(querySystemNotice.NoticeTypeEnum != null, t => t.NoticeTypeEnum == querySystemNotice.NoticeTypeEnum) + .WhereIf(!string.IsNullOrWhiteSpace(querySystemNotice.FileName), t => t.FileName.Contains(querySystemNotice.FileName)) + .WhereIf(!string.IsNullOrWhiteSpace(querySystemNotice.NoticeContent), t => t.NoticeContent.Contains(querySystemNotice.NoticeContent)) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken, userId = _userInfo.Id }); + + return await systemNoticeQueryable.ToPagedListAsync(querySystemNotice.PageIndex, querySystemNotice.PageSize, querySystemNotice.SortField, querySystemNotice.Asc); + } + + + public async Task> GetUserNoticeList() + { + var query = _systemNoticeRepository + .Where(t => t.NoticeUserTypeList.Any(t => t.UserTypeId == _userInfo.UserTypeId) && t.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished && !t.NoticeUserReadList.Any(t => t.CreateUserId == _userInfo.Id)) + .Where(t=>t.EndDate==null || t.EndDate != null && t.EndDate > DateTime.Now) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken, userId = _userInfo.Id }); + + return await query.ToListAsync(); + } + + } +} diff --git a/IRaCIS.Core.Application/Service/Management/UserService.cs b/IRaCIS.Core.Application/Service/Management/UserService.cs new file mode 100644 index 0000000..06c11fb --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/UserService.cs @@ -0,0 +1,645 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using System.Text.RegularExpressions; + +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Management")] + public class UserService : BaseService, IUserService + { + private readonly IRepository _userRepository; + private readonly IMailVerificationService _mailVerificationService; + private readonly IRepository _verificationCodeRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _userTrialRepository; + + private readonly IMemoryCache _cache; + + private ILogger _logger; + + private readonly IOptionsMonitor _verifyConfig; + public UserService(IRepository userRepository, + + IMailVerificationService mailVerificationService, + IRepository verificationCodeRepository, + IRepository doctorRepository, + IMemoryCache cache, + IRepository userTrialRepository, + IOptionsMonitor verifyConfig, + ILogger logger + + ) + { + _logger=logger; + _verifyConfig = verifyConfig; + _cache = cache; + _userRepository = userRepository; + _mailVerificationService = mailVerificationService; + _verificationCodeRepository = verificationCodeRepository; + _doctorRepository = doctorRepository; + _userTrialRepository = userTrialRepository; + } + + + + private async Task VerifyUserNameAsync(Guid? userId, string userName) + { + if (await _userRepository.WhereIf(userId != null, t => t.Id != userId).AnyAsync(t => t.UserName == userName)) + { + throw new BusinessValidationFailedException("用户名已经存在。"); + } + } + + private async Task VerifyUserPhoneAsync(Guid? userId, Guid userTypeId, string phone) + { + if (await _userRepository.WhereIf(userId != null, t => t.Id != userId).AnyAsync(t => (t.Phone == phone && t.UserTypeId == userTypeId))) + { + throw new BusinessValidationFailedException("该用户类型中已存在具有相同的电话的用户。"); + } + } + + + private async Task VerifyUserEmailAsync(Guid? userId, Guid userTypeId, string email) + { + if (await _userRepository.WhereIf(userId != null, t => t.Id != userId).AnyAsync(t => (t.EMail == email && t.UserTypeId == userTypeId))) + { + throw new BusinessValidationFailedException("该用户类型中已存在具有相同邮箱的用户。"); + } + } + + private async Task VerifyUserPwdAsync(Guid userId, string newPwd, string? oldPwd = null) + { + //var dbUser = (await _userRepository.FirstOrDefaultAsync(t => t.Id == userId)).IfNullThrowException(); + + if (_verifyConfig.CurrentValue.OpenUserComplexPassword) + { + + if (oldPwd != null && oldPwd == newPwd) + { + throw new BusinessValidationFailedException("新密码与旧密码相同。"); + } + + + var dbUser = (await _userRepository.Where(t => t.Id == userId).FirstOrDefaultAsync()).IfNullThrowException(); + + if (oldPwd != null && dbUser.Password != oldPwd) + { + throw new BusinessValidationFailedException("旧密码验证失败。"); + } + + if (dbUser.Password == newPwd) + { + throw new BusinessValidationFailedException("新密码与旧密码相同。"); + } + + + } + + await Task.CompletedTask; + + + } + + + /// 发送验证码 修改邮箱(已经登陆修改) New + + [HttpGet("{email}")] + public async Task SendVerificationCode(string email) + { + + //检查手机或者邮箱是否有效 + if (!Regex.IsMatch(email, @"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$")) + { + return ResponseOutput.NotOk("Please input a legal email"); + } + + + //验证码 6位 + int verificationCode = new Random().Next(100000, 1000000); + + await _mailVerificationService.SendMailEditEmail(_userInfo.Id, _userInfo.RealName, email, verificationCode); + + return ResponseOutput.Ok(); + + } + + + [HttpPut("{newEmail}/{verificationCode}")] + [UnitOfWork] + public async Task SetNewEmail(string newEmail, string verificationCode) + { + + + var verificationRecord = await _verificationCodeRepository + .FirstOrDefaultAsync(t => t.UserId == _userInfo.Id && t.Code == verificationCode && t.CodeType == 0); + + //检查数据库是否存在该验证码 + if (verificationRecord == null) + { + + return ResponseOutput.NotOk("验证码错误。"); + + } + else + { + //检查验证码是否失效 + if (verificationRecord.ExpirationTime < DateTime.Now) + { + return ResponseOutput.NotOk("验证码已经过期。"); + + } + else //验证码正确 并且 没有超时 + { + //更新密码 + //var pwd = MD5Helper.Md5(newPwd); + //var count = _doctorRepository.Update().Where(t => t.Id == doctor.Id).Set(d => d.Password == pwd).ExecuteAffrows(); + + await VerifyUserEmailAsync(_userInfo.Id, _userInfo.UserTypeId, newEmail); + + + await _userRepository.UpdatePartialNowNoQueryAsync(_userInfo.Id, u => new User() + { + EMail = newEmail + }); + + //删除验证码历史记录 + await _verificationCodeRepository.BatchDeleteNoTrackingAsync(t => t.UserId == _userInfo.Id && t.CodeType == 0); + + return ResponseOutput.Ok(); + + } + } + } + + + [HttpPut("{newPhone}")] + public async Task SetNewPhone(string newPhone) + { + + await VerifyUserPhoneAsync(_userInfo.Id, _userInfo.UserTypeId, newPhone); + + await _userRepository.UpdatePartialNowNoQueryAsync(_userInfo.Id, u => new User() + { + Phone = newPhone + }); + + return ResponseOutput.Ok(); + } + + + [HttpPut("{newUserName}")] + public async Task SetNewUserName(string newUserName) + { + await VerifyUserNameAsync(_userInfo.Id, newUserName); + + + await _userRepository.UpdatePartialNowNoQueryAsync(_userInfo.Id, u => new User() + { + UserName = newUserName + }); + + return ResponseOutput.Ok(); + } + + ///// + ///// Result 为true 的时候 认为链接没有失效 + ///// + ///// + ///// + //[HttpGet] + //public async Task VerifyCanInitSetUserNameAndPwd(Guid userId) + //{ + // return ResponseOutput.Ok(await _userRepository.AnyAsync(t => t.Id == userId && t.EmailToken == _userInfo.UserToken && t.IsFirstAdd)); + //} + + + [HttpGet] + public async Task InitSetUserNameAndPwd(Guid userId, string newUserName, string newPWd) + { + + + + + await VerifyUserPwdAsync(userId, newPWd); + + await VerifyUserNameAsync(userId, newUserName); + + await _userRepository.UpdatePartialFromQueryAsync(userId, u => new User() + { + UserName = newUserName, + + Password = newPWd, + + IsFirstAdd = false, + + EmailToken = String.Empty + + }, true); + + return ResponseOutput.Ok(); + } + + + /// + /// 重置密码为 默认密码 + /// + /// + /// + [HttpGet("{userId:guid}")] + [UnitOfWork] + public async Task ResetPassword(Guid userId) + { + + var pwd = AppSettings.DefaultPassword; + + if (_hostEnvironment.EnvironmentName != "Development") + { + pwd = "EIImage." + new Random().Next(100, 1000); + + } + + try + { + await _mailVerificationService.AdminResetPwdSendEmailAsync(userId, pwd); + } + catch (Exception ex) + { + throw new BusinessValidationFailedException("请检查邮箱地址或者联系维护人员, 邮件发送失败"); + } + + + await _userRepository.UpdatePartialNowNoQueryAsync(userId, u => new User() + { + Password = MD5Helper.Md5(pwd), + IsFirstAdd = true + }); + + return ResponseOutput.Ok(); + } + + + + /// + /// 重置密码发邮件 (未登陆修改) + /// + /// + /// + [AllowAnonymous] + [HttpGet("{email}")] + public async Task AnonymousSendVerificationCode(string email) + { + + //检查手机或者邮箱是否有效 + if (!Regex.IsMatch(email, @"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$")) + { + + return ResponseOutput.NotOk("请输入一个正确的邮箱。"); + + } + + ////查找改邮箱或者手机的用户 + var exist = await _userRepository.AnyAsync(t => t.EMail == email); + + if (!exist) + { + return ResponseOutput.NotOk("邮箱错误。"); + + } + + + //验证码 6位 + int verificationCode = new Random().Next(100000, 1000000); + + await _mailVerificationService.AnolymousSendEmailForResetAccount(email, verificationCode); + + return ResponseOutput.Ok(); + + } + + /// + /// 验证验证码,没问题就返回用户所有的账户 + /// + /// + /// + /// + /// + [AllowAnonymous] + [HttpGet("{email}/{verifyCode}")] + public async Task> VerifyAnonymousVerifyCode(string email, string verifyCode) + { + var verificationRecord = await _verificationCodeRepository + .Where(t => t.UserId == Guid.Empty && t.Code == verifyCode && t.CodeType == VerifyType.Email && t.EmailOrPhone == email).OrderByDescending(t => t.CreateTime).FirstOrDefaultAsync(); + + //检查数据库是否存在该验证码 + if (verificationRecord == null) + { + + throw new BusinessValidationFailedException("验证码错误。"); + } + else + { + //检查验证码是否失效 + if (verificationRecord.ExpirationTime < DateTime.Now) + { + + throw new BusinessValidationFailedException("验证码已经过期。"); + } + else //验证码正确 并且 没有超时 + { + + //删除验证码历史记录 + await _verificationCodeRepository.BatchDeleteNoTrackingAsync(t => t.Id == verificationRecord.Id); + } + } + + var list = await _userRepository.Where(t => t.EMail == email).Select(t => new UserAccountDto() { UserId = t.Id, UserName = t.UserName, UserRealName = t.FullName, UserType = t.UserTypeRole.UserTypeShortName }).ToListAsync(); + + + + return list; + } + + + /// + /// (未登陆) 设置新密码 + /// + /// + /// + /// + [AllowAnonymous] + [HttpGet("{userId:guid}/{newPwd}")] + public async Task AnonymousSetPassword(Guid userId, string newPwd) + { + + + await VerifyUserPwdAsync(userId, newPwd); + + var success = await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == userId, u => new User() + { + Password = newPwd, + IsFirstAdd = false + }); + + return ResponseOutput.Result(success); + + } + + /// + /// 修改密码,当前支持旧密码修改密码 + /// + /// + [HttpPost] + [UnitOfWork] + public async Task ModifyPassword(EditPasswordCommand editPwModel) + { + + await VerifyUserPwdAsync(_userInfo.Id, editPwModel.NewPassWord, editPwModel.OldPassWord); + + + if (!string.IsNullOrEmpty(editPwModel.NewUserName)) + { + + await VerifyUserNameAsync(_userInfo.Id, editPwModel.NewUserName); + + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == _userInfo.Id, u => new User() + { + UserName = editPwModel.NewUserName, + }); + + } + + var success = await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == _userInfo.Id, u => new User() + { + Password = editPwModel.NewPassWord, + IsFirstAdd = false + }); + + + return ResponseOutput.Result(success); + + + ////医生密码 + //if (await _doctorRepository.AnyAsync(t => t.Id == _userInfo.Id && t.Password == editPwModel.OldPassWord)) + //{ + // var success = await _doctorRepository.BatchUpdateNoTrackingAsync(t => t.Id == _userInfo.Id, u => new Doctor() + // { + + // Password = editPwModel.NewPassWord + // }); + + // return ResponseOutput.Result(success); + //} + + //return ResponseOutput.NotOk("Old password is wrong."); + + } + + + + /// + /// 获取用户列表 + /// + /// + /// + [HttpPost] + public async Task> GetUserList(UserListQueryDTO param) + { + var userQueryable = _userRepository.Where(x => x.UserTypeEnum != UserTypeEnum.SuperAdmin) + .WhereIf(!string.IsNullOrWhiteSpace(param.UserName), t => t.UserName.Contains(param.UserName) ) + .WhereIf(!string.IsNullOrWhiteSpace(param.RealName), t => t.FullName.Contains(param.RealName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.Phone), t => t.Phone.Contains(param.Phone)) + .WhereIf(!string.IsNullOrWhiteSpace(param.OrganizationName), t => t.OrganizationName.Contains(param.OrganizationName)) + .WhereIf(param.UserType != null, t => t.UserTypeId == param.UserType) + .WhereIf(param.UserState != null, t => t.Status == param.UserState) + .WhereIf(param.IsTestUser != null, t => t.IsTestUser == param.IsTestUser) + .WhereIf(param.IsZhiZhun != null, t => t.IsZhiZhun == param.IsZhiZhun) + .ProjectTo(_mapper.ConfigurationProvider); + + return await userQueryable.ToPagedListAsync(param.PageIndex, param.PageSize, param.SortField == string.Empty ? "UserName" : param.SortField, param.Asc); + + + } + + /// + /// 根据用户Id获取用户详细信息[New] + /// + /// + /// xiuga + [HttpGet("{id:guid}")] + public async Task GetUser(Guid id) + { + var userQuery = _userRepository.Where(t => t.Id == id).ProjectTo(_mapper.ConfigurationProvider); + return await (userQuery.FirstOrDefaultAsync()).IfNullThrowException(); + } + + /// + /// 添加用户 + /// + /// + /// + [UnitOfWork] + public async Task> AddUser(UserCommand userAddModel) + { + + + await VerifyUserNameAsync(null, userAddModel.UserName); + + await VerifyUserEmailAsync(null, userAddModel.UserTypeId, userAddModel.EMail); + + //await VerifyUserPhoneAsync(null, userAddModel.UserTypeId, userAddModel.Phone); + + + var saveItem = _mapper.Map(userAddModel); + + saveItem.Code = await _userRepository.Select(t => t.Code).DefaultIfEmpty().MaxAsync() + 1; + + saveItem.UserCode = AppSettings.GetCodeStr(saveItem.Code, nameof(User)); + + + + saveItem.Password = MD5Helper.Md5(AppSettings.DefaultPassword); + + await _userRepository.AddAsync(saveItem); + + var success = await _userRepository.SaveChangesAsync(); + + try + { + await _mailVerificationService.AddUserSendEmailAsync(saveItem.Id, userAddModel.BaseUrl, userAddModel.RouteUrl); + + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + throw new BusinessValidationFailedException("发送邮件失败, 未能创建账户成功"); + } + + + + return ResponseOutput.Result(success, new UserAddedReturnDTO { Id = saveItem.Id, UserCode = saveItem.UserCode }); + + } + + /// + /// 更新用户 + /// + /// + /// + public async Task UpdateUser(UserCommand model) + { + + + await VerifyUserNameAsync(model.Id, model.UserName); + + await VerifyUserEmailAsync(model.Id, model.UserTypeId, model.EMail); + + //await VerifyUserPhoneAsync(model.Id, model.UserTypeId, model.Phone); + + var user = await _userRepository.FirstOrDefaultAsync(t => t.Id == model.Id); + + if (user == null) return Null404NotFound(user); + + _mapper.Map(model, user); + + + var success = await _userRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(success); + + } + + /// + /// 删除用户 + /// + /// + /// + [HttpDelete("{userId:guid}")] + public async Task DeleteUser(Guid userId) + { + if (await _userTrialRepository.AnyAsync(t => t.Id == userId)) + { + return ResponseOutput.NotOk("该用户已经参加项目,不能够删除。"); + } + + var success = await _userRepository.BatchDeleteNoTrackingAsync(t => t.Id == userId); + + return ResponseOutput.Result(success); + } + + /// + /// 禁用或者启用账户 + /// + /// + /// + /// + + [HttpPost("{userId:guid}/{state:int}")] + public async Task UpdateUserState(Guid userId, UserStateEnum state) + { + var success = await _userRepository.BatchUpdateNoTrackingAsync(u => u.Id == userId, t => new User + { + Status = state + }); + return ResponseOutput.Result(success); + } + + + + + /// + /// 用户登陆 + /// + /// + /// + /// + [NonDynamicMethod] + public async Task> Login(string userName, string password) + { + var userLoginReturnModel = new LoginReturnDTO(); + + + var loginUser = await _userRepository.Where(u => u.UserName.Equals(userName) && u.Password == password).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + + if (loginUser == null) + { + //此处下面 代码 为了支持医生也能登录 而且前端不加选择到底是管理用户 还是医生用户 奇怪的需求 无法理解 + + var loginDoctor = await _doctorRepository.Where(u => u.Phone == userName && u.Password == password).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + + if (loginDoctor == null) + { + return ResponseOutput.NotOk(_localizer["User_CheckNameOrPw"], new LoginReturnDTO()); + + } + + userLoginReturnModel.BasicInfo = loginDoctor; + + // 登录 清除缓存 + _cache.Remove(userLoginReturnModel.BasicInfo.Id.ToString()); + return ResponseOutput.Ok(userLoginReturnModel); + + } + + if (loginUser.Status == 0) + { + return ResponseOutput.NotOk("该用户已经被禁用。", new LoginReturnDTO()); + } + + userLoginReturnModel.BasicInfo = loginUser; + + // 登录 清除缓存 + _cache.Remove(userLoginReturnModel.BasicInfo.Id.ToString()); + return ResponseOutput.Ok(userLoginReturnModel); + + } + + } +} diff --git a/IRaCIS.Core.Application/Service/Management/UserTypeService.cs b/IRaCIS.Core.Application/Service/Management/UserTypeService.cs new file mode 100644 index 0000000..6509988 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/UserTypeService.cs @@ -0,0 +1,168 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-03 09:38:11 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore; + +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// UserTypeRoleService + /// + [ApiExplorerSettings(GroupName = "Management")] + public class UserTypeRoleService : BaseService, IUserTypeService + { + private readonly IRepository userTypeServiceRepository; + + public UserTypeRoleService(IRepository userTypeServiceRepository) + { + this.userTypeServiceRepository = userTypeServiceRepository; + } + + [HttpPost] + public async Task> GetUserTypeRoleList(UserTypeQuery userTypeQuery) + { + + var userTypeRoleQueryable = userTypeServiceRepository + .WhereIf(!string.IsNullOrWhiteSpace(userTypeQuery.SearchFilter), t => t.Description.Contains(userTypeQuery.SearchFilter!) || t.UserTypeName.Contains(userTypeQuery.SearchFilter!) || t.UserTypeShortName.Contains(userTypeQuery.SearchFilter!)) + + .WhereIf(userTypeQuery.GroupId!=null,t=>t.UserTypeGroupList.Any(t=>t.DictionaryId== userTypeQuery.GroupId)) + .OrderBy(t => t.UserTypeEnum).ProjectTo(_mapper.ConfigurationProvider); + + return await userTypeRoleQueryable.ToListAsync(); + } + + + + public async Task AddOrUpdateUserTypeRole(UserTypeMenuAddOrEdit addOrEditUserTypeRole) + { + + var entity = new UserType(); + + if (addOrEditUserTypeRole.Id == null) + { + entity = _mapper.Map(addOrEditUserTypeRole); + + entity.UserTypeEnum = userTypeServiceRepository.Select(t => t.UserTypeEnum).DefaultIfEmpty().Max() + 1; + + await _repository.AddAsync(entity); + + + } + else + { + + if (addOrEditUserTypeRole.MenuIds.Count > 0) + { + entity = userTypeServiceRepository.Where(t => t.Id == addOrEditUserTypeRole.Id, true).Include(t => t.UserTypeMenuList).Include(t=>t.UserTypeGroupList).FirstOrDefault().IfNullThrowException(); + + } + else + { + entity = userTypeServiceRepository.Where(t => t.Id == addOrEditUserTypeRole.Id, true).FirstOrDefault().IfNullThrowException(); + } + _mapper.Map(addOrEditUserTypeRole, entity); + } + + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok( entity.Id.ToString()); + + } + + + [HttpDelete("{userTypeId:guid}")] + public async Task DeleteUserTypeRole(Guid userTypeId) + { + if ( await _repository.AnyAsync(t => t.UserTypeId == userTypeId)) + { + return ResponseOutput.NotOk("该用户类型中已存在用户,不能删除"); + } + + var success = await userTypeServiceRepository.BatchDeleteNoTrackingAsync(t => t.Id == userTypeId); + + return ResponseOutput.Result(success); + } + + + + + /// + /// 通过传递场景枚举 返回对应的下拉框数据 1:是外部 2:是内部 3:是Site调研 4: 邮件接收人,5:邮件抄送人 + /// + /// + /// + [HttpGet("{userTypeSelectEnum:int}")] + public async Task> GetUserTypeList(UserTypeSelectEnum userTypeSelectEnum) + { + var userTypeEnums = new List(); + + if (userTypeSelectEnum == UserTypeSelectEnum.ExternalUser) + { + userTypeEnums = new List() { UserTypeEnum.CPM, UserTypeEnum.SPM, UserTypeEnum.CPM, UserTypeEnum.SMM, UserTypeEnum.CMM, UserTypeEnum.EA }; + } + + + if (userTypeSelectEnum == UserTypeSelectEnum.InnerUser) + { + //userTypeEnums = new List() { UserTypeEnum.IQC, UserTypeEnum.APM, UserTypeEnum.MIM, UserTypeEnum.QA ,UserTypeEnum.MW}; + + userTypeEnums = new List() { UserTypeEnum.IndependentReviewer, UserTypeEnum.ClinicalResearchCoordinator }; + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin) + { + userTypeEnums.Add(UserTypeEnum.ProjectManager); + } + } + + if (userTypeSelectEnum == UserTypeSelectEnum.SiteSurvey) + { + userTypeEnums = new List() { UserTypeEnum.CRA, UserTypeEnum.ClinicalResearchCoordinator }; + + } + + + if (userTypeSelectEnum == UserTypeSelectEnum.EnrollOrPD_EMailCopy) + { + userTypeEnums = new List() { UserTypeEnum.ProjectManager, UserTypeEnum.APM, UserTypeEnum.CPM, UserTypeEnum.SPM, UserTypeEnum.SMM, UserTypeEnum.CMM }; + + } + + if (userTypeSelectEnum == UserTypeSelectEnum.EnrollOrPD_EmailReceive) + { + userTypeEnums = new List() { UserTypeEnum.CRA, UserTypeEnum.ClinicalResearchCoordinator }; + + + } + + + + var query = userTypeServiceRepository.Where(x => x.UserTypeEnum != UserTypeEnum.SuperAdmin) + .WhereIf(userTypeSelectEnum != UserTypeSelectEnum.None, t => userTypeEnums.Contains(t.UserTypeEnum)) + .OrderBy(t => t.Order).ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToListAsync(); + } + + + + + + /// + /// 项目文档 配置 哪里用户类型下拉 + /// + /// + public async Task> GetTrialUserTypeList() + { + var query = userTypeServiceRepository.Where(x => x.UserTypeEnum != UserTypeEnum.SuperAdmin) + //.Where(t => t.Type == UserTypeGroup.TrialUser) + .OrderBy(t => t.Order).ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToListAsync(); + } + + } +} diff --git a/IRaCIS.Core.Application/Service/Management/_MapConfig.cs b/IRaCIS.Core.Application/Service/Management/_MapConfig.cs new file mode 100644 index 0000000..fa77bf4 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/_MapConfig.cs @@ -0,0 +1,126 @@ +using AutoMapper; +using AutoMapper.EquivalencyExpression; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Application.Service +{ + public class ManagementConfig : Profile + { + public ManagementConfig() + { + + CreateMap().IncludeMembers(t => t.Menu); + + + + + CreateMap(); + + CreateMap().ForMember(d => d.UserCode, x => x.Ignore()); + CreateMap().ReverseMap() + .ForMember(t => t.UserTypeMenuList, u => u.MapFrom(c => c.MenuIds)) + .ForMember(t => t.UserTypeGroupList, u => u.MapFrom(c => c.UserTypeGroupIdList)); + + CreateMap().ForMember(t => t.DictionaryId, u => u.MapFrom(c => c)) + .EqualityComparison((odto, o) => odto == o.DictionaryId); + + CreateMap().ForMember(t => t.MenuId, u => u.MapFrom(c => c)) + .EqualityComparison((odto, o) => odto == o.MenuId); + + CreateMap() + .ForMember(t => t.GroupName, u => u.MapFrom(c => c.Group.Value)) + .ForMember(t => t.GroupNameCN, u => u.MapFrom(c => c.Group.ValueCN)); + + + CreateMap().ForMember(d => d.Id, u => u.MapFrom(t => t.MenuId)).ReverseMap(); + + CreateMap() + .ForMember(d => d.MenuId, u => u.MapFrom(t => t.Id)); + + CreateMap().ForMember(d => d.MenuId, u => u.MapFrom(t => t.Id)); + + + CreateMap() + .ForMember(t => t.MenuIds, u => u.MapFrom(c => c.UserTypeMenuList.Select(t => t.MenuId))); + + CreateMap(); + CreateMap(); + + + CreateMap(); + + + //用户类型菜单勾选 + var userTypeId = Guid.Empty; + CreateMap() + .ForMember(d => d.IsSelect, u => u.MapFrom(s => s.UserTypeMenuList.Any(t => t.UserTypeId == userTypeId))); + + //菜单树 + CreateMap() + /*.ForMember(d => d.meta, u => u.MapFrom(s => new Meta() { MetaTitle = s.MetaTitle, MetaBreadcrumb = s.MetaBreadcrumb, MetaIcon = s.MetaIcon, MetaActiveMenu = s.MetaActiveMenu }))*/; + + //功能树 + CreateMap(); + + //普通用户菜单树 + CreateMap().IncludeMembers(t => t.Menu) + .ForMember(d => d.Id, u => u.MapFrom(t => t.Menu.Id)); + //.ForMember(d => d.meta, u => u.MapFrom(s => new Meta() { MetaTitle = s.MenuFunction.MetaTitle, MetaBreadcrumb = s.MenuFunction.MetaBreadcrumb, MetaIcon = s.MenuFunction.MetaIcon, MetaActiveMenu = s.MenuFunction.MetaActiveMenu })); + + + CreateMap().IncludeMembers(t => t.Menu) + .ForMember(d => d.Id, u => u.MapFrom(t => t.Menu.Id)); + + CreateMap() + .ForMember(d => d.UserType, u => u.MapFrom(t => t.UserTypeName)); + + CreateMap() + .ForMember(d => d.RealName, u => u.MapFrom(s => s.FullName)) + .ForMember(d => d.UserTypeId, u => u.MapFrom(s => s.UserTypeRole.Id)) + .ForMember(d => d.UserType, u => u.MapFrom(s => s.UserTypeRole.UserTypeName)) + .ForMember(d => d.UserTypeShortName, u => u.MapFrom(s => s.UserTypeRole.UserTypeShortName)) + .ForMember(d => d.CanEditUserType, u => u.MapFrom(s => !s.UserTrials.Any())); + + CreateMap() + .ForMember(d => d.RealName, u => u.MapFrom(s => s.FullName)) + .ForMember(d => d.UserTypeId, u => u.MapFrom(s => s.UserTypeRole.Id)) + .ForMember(d => d.UserType, u => u.MapFrom(s => s.UserTypeRole.UserTypeShortName)) + .ForMember(d => d.CanEditUserType, u => u.MapFrom(s => !s.UserTrials.Any())); + + var token = string.Empty; + var userId = Guid.Empty; + CreateMap() + .ForMember(t => t.PublishUserName, d => d.MapFrom(t => t.PublishedUser.UserName)) + .ForMember(t => t.CreateUserName, d => d.MapFrom(t => t.CreateUser.UserName)) + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path + "?access_token=" + token)); + + + + + CreateMap() + .ForMember(t => t.PublishUserName, d => d.MapFrom(t => t.PublishedUser.UserName)) + .ForMember(t => t.CreateUserName, d => d.MapFrom(t => t.CreateUser.UserName)) + .ForMember(t => t.IsRead, d => d.MapFrom(t => t.NoticeUserReadList.Any(t => t.CreateUserId == userId))) + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path + "?access_token=" + token)); + + CreateMap().ReverseMap() + .ForMember(t => t.NoticeUserTypeList, u => u.MapFrom(t => t.NoticeUserTypeIdList)); + + + CreateMap().IncludeMembers(t => t.NoticeUserType) + .ForMember(t => t.Id, u => u.MapFrom(u => u.NoticeUserType.Id)); + + CreateMap(); + + CreateMap().EqualityComparison((odto, o) => odto == o.UserTypeId) + .ForMember(d => d.UserTypeId, c => c.MapFrom(t => t)); + + + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/QC/ClinicalDataService.cs b/IRaCIS.Core.Application/Service/QC/ClinicalDataService.cs new file mode 100644 index 0000000..4f81bce --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/ClinicalDataService.cs @@ -0,0 +1,199 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-22 11:29:44 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service; + +namespace IRaCIS.Core.Application.Contracts +{ + /// + ///患者临床信息 + /// + [ApiExplorerSettings(GroupName = "Image")] + public class ClinicalDataService : BaseService, IClinicalDataService + { + public IRepository _previousOtherRepository { get; } + private readonly IRepository _previousHistoryRepository; + private readonly IRepository _previousSurgeryRepository; + private readonly IRepository _previousPdfRepository; + private readonly IRepository _subjectVisitRepository; + + + public ClinicalDataService(IRepository previousHistoryRepository, + IRepository previousOtherRepository, + IRepository previousSurgeryRepository, + IRepository previousPDFRepository, + IRepository subjectVisitRepository + ) + { + _previousOtherRepository = previousOtherRepository; + _previousHistoryRepository = previousHistoryRepository; + _previousSurgeryRepository = previousSurgeryRepository; + _previousPdfRepository = previousPDFRepository; + _subjectVisitRepository = subjectVisitRepository; + } + + + + + /// + /// 获取检查批次+患者级别的数据 + /// + /// + /// + [HttpGet("{subjectVisitId:guid}")] + public async Task GetSubjectVisitClinicalData(Guid subjectVisitId) + { + + // 最完美方式 其中TrialSite 通过导航属性 两个字段连接出来 + + var clinicalObj = await _repository.Where(t => t.Id == subjectVisitId) + .ProjectTo(_mapper.ConfigurationProvider, new { subjectVisitId = subjectVisitId, token = _userInfo.UserToken }).FirstOrDefaultAsync().IfNullThrowException(); + + return clinicalObj; + + #region 老方式 linq join 而且需要映射 + + + //var query = from sv in _subjectVisitRepository.GetAll() + // join subject in _subjectRepository.GetAll() on sv.SubjectId equals subject.Id + // join trialSite in _trialSiteRepository.Find(t => t.TrialId == subjectClinicalDataQuery.TrialId) on sv.SiteId equals trialSite.SiteId + // select new SubjectClinicalDataDto() + // { + // SubjectId = sv.SubjectId, + // SubjectCode = subject.Code, + // SubjectVisitId = sv.Id, + // VisitName = sv.VisitName, + // VisitNum = sv.VisitNum, + // TrialSiteCode = trialSite.TrialSiteCode + // }; + + //var clinicalObj = query.FirstOrDefault(t => t.SubjectVisitId == subjectClinicalDataQuery.SubjectVisitId); + + //clinicalObj.PreviousHistoryList = _previousHistoryRepository.Find(t => t.SubjectVisitId == subjectClinicalDataQuery.SubjectVisitId) + // .ProjectTo(_mapper.ConfigurationProvider).ToList(); + //clinicalObj.PreviousOtherList = _previousOtherRepository.Find(t => t.SubjectVisitId == subjectClinicalDataQuery.SubjectVisitId) + // .ProjectTo(_mapper.ConfigurationProvider).ToList(); + //clinicalObj.PreviousSurgeryList = _previousSurgeryRepository.Find(t => t.SubjectVisitId == subjectClinicalDataQuery.SubjectVisitId) + // .ProjectTo(_mapper.ConfigurationProvider).ToList(); + + #endregion + + } + + + public async Task> GetPreviousHistoryList(PreviousHistoryQuery queryPreviousHistory) + { + + var previousHistoryQueryable = _previousHistoryRepository.WhereIf(queryPreviousHistory.IsSubjectLevel != null, t => t.IsSubjectLevel == queryPreviousHistory.IsSubjectLevel) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken }); + + return await previousHistoryQueryable.ToListAsync(); + } + + [HttpPost("{trialId:guid}")] + public async Task> AddOrUpdatePreviousHistory(PreviousHistoryAddOrEdit addOrEditPreviousHistory) + { + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, addOrEditPreviousHistory.SubjectVisitId); + + var entity = await _previousHistoryRepository.InsertOrUpdateAsync(addOrEditPreviousHistory, true); + return ResponseOutput.Ok(entity.Id); + } + + + [HttpDelete("{trialId:guid}/{subjectVisitId:guid}/{previousHistoryId:guid}")] + public async Task DeletePreviousHistory(Guid previousHistoryId,Guid subjectVisitId) + { + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); + await _previousHistoryRepository.DeleteFromQueryAsync(t => t.Id == previousHistoryId,true); + return ResponseOutput.Ok(); + } + + + public async Task> GetPreviousOtherList(PreviousOtherQuery queryPreviousOther) + { + var previousOtherQueryable = _previousOtherRepository.WhereIf(queryPreviousOther.IsSubjectLevel != null, t => t.IsSubjectLevel == queryPreviousOther.IsSubjectLevel) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken }); + return await previousOtherQueryable.ToListAsync(); + } + + + [HttpPost("{trialId:guid}")] + public async Task> AddOrUpdatePreviousOther(PreviousOtherAddOrEdit addOrEditPreviousOther) + { + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, addOrEditPreviousOther.SubjectVisitId); + var entity = await _previousOtherRepository.InsertOrUpdateAsync(addOrEditPreviousOther, true); + return ResponseOutput.Ok(entity.Id); + + } + + + [HttpDelete("{trialId:guid}/{subjectVisitId:guid}/{previousOtherId:guid}")] + public async Task DeletePreviousOther(Guid previousOtherId, Guid subjectVisitId) + { + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); + + await _previousOtherRepository.DeleteFromQueryAsync(t => t.Id == previousOtherId,true); + return ResponseOutput.Ok(); + } + + + public async Task> GetPreviousSurgeryList(PreviousSurgeryQuery queryPreviousSurgery) + { + var previousSurgeryQueryable = _previousSurgeryRepository.WhereIf(queryPreviousSurgery.IsSubjectLevel != null, t => t.IsSubjectLevel == queryPreviousSurgery.IsSubjectLevel) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken }); + + return await previousSurgeryQueryable.ToListAsync(); + } + + [HttpPost("{trialId:guid}")] + public async Task> AddOrUpdatePreviousSurgery(PreviousSurgeryAddOrEdit addOrEditPreviousSurgery) + { + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, addOrEditPreviousSurgery.SubjectVisitId); + var entity = await _previousSurgeryRepository.InsertOrUpdateAsync(addOrEditPreviousSurgery, true); + return ResponseOutput.Ok(entity.Id); + } + + + [HttpDelete("{trialId:guid}/{subjectVisitId:guid}/{previousSurgeryId:guid}")] + public async Task DeletePreviousSurgery(Guid previousSurgeryId, Guid subjectVisitId) + { + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); + var success = await _previousSurgeryRepository.DeleteFromQueryAsync(t => t.Id == previousSurgeryId,true); + return ResponseOutput.Ok(); + } + + [HttpGet("{subjectVisitId:guid}")] + public async Task> GetPreviousPDFList(Guid subjectVisitId) + { + + var previousPDFQueryable = _previousPdfRepository.Where(t => t.SubjectVisitId == subjectVisitId).ProjectTo(_mapper.ConfigurationProvider); + + return await previousPDFQueryable.ToListAsync(); + } + + + public async Task AddOrUpdatePreviousPDF(PreviousPDFAddOrEdit addOrEditPreviousPDF) + { + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, addOrEditPreviousPDF.SubjectVisitId); + var entity = await _previousPdfRepository.InsertOrUpdateAsync(addOrEditPreviousPDF, true); + return ResponseOutput.Ok(entity.Id); + + } + + [HttpDelete("{trialId:guid}/{subjectVisitId:guid}/{previousPDFId:guid}")] + public async Task DeletePreviousPDF(Guid previousPDFId, Guid subjectVisitId) + { + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); + + await _previousPdfRepository.DeleteFromQueryAsync(t => t.Id == previousPDFId,true); + return ResponseOutput.Ok(); + } + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/QC/DTO/NoneDicomStudyFileViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/NoneDicomStudyFileViewModel.cs new file mode 100644 index 0000000..5ac67cd --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/DTO/NoneDicomStudyFileViewModel.cs @@ -0,0 +1,69 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-06 10:56:50 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Application.Service.Inspection.DTO; +using Microsoft.AspNetCore.Http; + +namespace IRaCIS.Core.Application.Contracts +{ + /// NoneDicomStudyFileView 列表视图模型 + public class NoneDicomStudyFileView + { + public Guid Id { get; set; } + public string Path { get; set; } = string.Empty; + public string FileName { get; set; } = string.Empty; + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public Guid NoneDicomStudyId { get; set; } + + public string FullFilePath => Path; + + public string PreviewPath => "/Common/LocalFilePreview?relativePath=" + Path; + + } + + + public class UploadNoneDicomFileDto + { + public Guid subjectVisitId { get; set; } + public Guid noneDicomStudyId { get; set; } + + public string AuditInfo { get; set; } = string.Empty; + } + + public class NoneDicomStudyAndFile + { + public string NoneDicomStudyCode { get; set; } = string.Empty; + + public List NoneDicomStudyFileList { get; set; } = new List(); + + } + + ///NoneDicomStudyFileQuery 列表查询参数模型 + public class NoneDicomStudyFileQuery + { + /// Path + public string Path { get; set; } = string.Empty; + + /// FileName + public string FileName { get; set; } = string.Empty; + + } + + /// NoneDicomStudyFileAddOrEdit 列表查询参数模型 + public class NoneDicomStudyFileAddOrEdit + { + public Guid Id { get; set; } + public string Path { get; set; } = string.Empty; + public string FileName { get; set; } = string.Empty; + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public Guid NoneDicomStudyId { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/QC/DTO/NoneDicomStudyViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/NoneDicomStudyViewModel.cs new file mode 100644 index 0000000..2fa70b7 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/DTO/NoneDicomStudyViewModel.cs @@ -0,0 +1,73 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-06 10:56:50 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.Contracts +{ + /// NoneDicomStudyView 列表视图模型 + public class NoneDicomStudyView:NoneDicomStudyAddOrEdit + { + public DateTime UpdateTime { get; set; } + public string CodeView { get; set; } + + public int FileCount { get; set; } + + public List NoneDicomStudyFileList { get; set; } = new List(); + + public List NoneDicomStudyFilePreviewList => NoneDicomStudyFileList.Select(t=> t.PreviewPath).ToList(); + + } + + ///NoneDicomStudyQuery 列表查询参数模型 + public class NoneDicomStudyQuery + { + /// BodyPart + public string BodyPart { get; set; } = string.Empty; + + /// Modality + public string Modality { get; set; } = string.Empty; + + /// Description + public string Description { get; set; } = string.Empty; + + } + + /// NoneDicomStudyAddOrEdit 列表查询参数模型 + public class NoneDicomStudyAddOrEdit + { + public Guid? Id { get; set; } + + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + public string BodyPart { get; set; } = string.Empty; + public string Modality { get; set; } = string.Empty; + public DateTime ImageDate { get; set; } + public string Description { get; set; } = string.Empty; + + public int Code { get; set; } + + + public string VideoName { get; set; } = string.Empty; + + public string VideoObjectName { get; set; } = string.Empty; + + public DateTime? UploadVideoTime { get; set; } + + public string VideoUrl { get; set; } = string.Empty; + } + + + public class NoneDicomStudyAddReturnDto + { + public Guid Id{ get; set; } + public string StudyCode { get; set; } = string.Empty; + } + +} + + diff --git a/IRaCIS.Core.Application/Service/QC/DTO/PreviousHistoryViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/PreviousHistoryViewModel.cs new file mode 100644 index 0000000..00fdb75 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/DTO/PreviousHistoryViewModel.cs @@ -0,0 +1,110 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-22 11:27:53 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +namespace IRaCIS.Core.Application.Contracts +{ + + public class SubjectClinicalDataDto + { + public string SubjectCode { get; set; } = String.Empty; + + //public string SubjectName { get; set; } + + public string TrialSiteCode { get; set; } = String.Empty; + + public Guid SubjectVisitId { get; set; } + + public decimal VisitNum { get; set; } + + public string VisitName { get; set; } = String.Empty; + + public Guid SubjectId { get; set; } + + + public List PreviousHistoryList { get; set; } = new List(); + + public List PreviousOtherList { get; set; } = new List(); + + public List PreviousSurgeryList { get; set; } = new List(); + + public List PreviousPDFList { get; set; } = new List(); + } + + + public class SubjectClinicalDataQuery + { + + //public Guid TrialId { get; set; } + + //public Guid SubjectId { get; set; } + + //public Guid SubjectId { get; set; } + + + //public bool? IsSubjectLevel { get; set; } + + + public Guid SubjectVisitId { get; set; } + } + + + + + /// PreviousHistoryView 列表视图模型 + public class PreviousHistoryView + { + public Guid Id { get; set; } + public DateTime CreateTime { get; set; } + public string CreateUser { get; set; } = String.Empty; + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + public int? IsPD { get; set; } + public Guid? SubjectVisitId { get; set; } + public bool IsSubjectLevel { get; set; } + public string Path { get; set; } = String.Empty; + public string FileName { get; set; } = String.Empty; + public string Position { get; set; } = String.Empty; + + public Guid CreateUserId { get; set; } + + public Guid ClinicalDataTrialSetId { get; set; } + + public string FullFilePath => Path; + + + } + + ///PreviousHistoryQuery 列表查询参数模型 + public class PreviousHistoryQuery + { + public bool? IsSubjectLevel { get; set; } + + public string Position { get; set; } = String.Empty; + + } + + public class PreviousHistoryAddOrEdit + { + + + public Guid? Id { get; set; } + + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + public int? IsPD { get; set; } + public Guid SubjectVisitId { get; set; } + public bool IsSubjectLevel { get; set; } = true; + public string Path { get; set; } = String.Empty; + public string FileName { get; set; } = String.Empty; + public string Position { get; set; } = String.Empty; + + public Guid ClinicalDataTrialSetId { get; set; } + //public Guid SubjectId { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/QC/DTO/PreviousOtherViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/PreviousOtherViewModel.cs new file mode 100644 index 0000000..3c35327 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/DTO/PreviousOtherViewModel.cs @@ -0,0 +1,65 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-22 11:27:53 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.Contracts +{ + /// PreviousOtherView 列表视图模型 + public class PreviousOtherView + { + public Guid Id { get; set; } + public DateTime CreateTime { get; set; } + public string CreateUser { get; set; } = String.Empty; + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + public bool IsPD { get; set; } + public Guid SubjectVisitId { get; set; } + public bool IsSubjectLevel { get; set; } + public string Path { get; set; } = String.Empty; + public string FileName { get; set; } = String.Empty; + public string TreatmentType { get; set; } = String.Empty; + //public Guid SubjectId { get; set; } + public Guid CreateUserId { get; set; } + + public Guid ClinicalDataTrialSetId { get; set; } + + public string FullFilePath => Path; + } + + ///PreviousOtherQuery 列表查询参数模型 + public class PreviousOtherQuery + { + public bool? IsSubjectLevel { get; set; } + + public string TreatmentType { get; set; } = String.Empty; + + } + + public class PreviousOtherAddOrEdit + { + public Guid? Id { get; set; } + + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + public bool IsPD { get; set; } + public Guid? SubjectVisitId { get; set; } + public bool IsSubjectLevel { get; set; } = true; + public string Path { get; set; }=String.Empty; + public string FileName { get; set; } = String.Empty; + public string TreatmentType { get; set; } = String.Empty; + + + /// + /// 临床数据类型Id + /// + public Guid ClinicalDataTrialSetId { get; set; } + //public Guid SubjectId { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/QC/DTO/PreviousSurgeryViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/PreviousSurgeryViewModel.cs new file mode 100644 index 0000000..ac53d30 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/DTO/PreviousSurgeryViewModel.cs @@ -0,0 +1,144 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-22 11:27:53 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Contracts +{ + /// PreviousSurgeryView 列表视图模型 + public class PreviousSurgeryView + { + public Guid Id { get; set; } + public DateTime CreateTime { get; set; } + public string CreateUser { get; set; } = string.Empty; + public DateTime? OperationTime { get; set; } + public Guid SubjectVisitId { get; set; } + public bool IsSubjectLevel { get; set; } + public string Path { get; set; } = string.Empty; + public string FileName { get; set; } = string.Empty; + public string OperationName { get; set; } = string.Empty; + //public Guid SubjectId { get; set; } + public Guid CreateUserId { get; set; } + + public Guid ClinicalDataTrialSetId { get; set; } + + public string FullFilePath => Path; + } + + ///PreviousSurgeryQuery 列表查询参数模型 + public class PreviousSurgeryQuery + { + public bool? IsSubjectLevel { get; set; } + public string OperationName { get; set; } = string.Empty; + + } + + public class PreviousSurgeryAddOrEdit + { + public Guid? Id { get; set; } + + public DateTime? OperationTime { get; set; } + public Guid? SubjectVisitId { get; set; } + public bool IsSubjectLevel { get; set; } = true; + public string Path { get; set; } = String.Empty; + public string FileName { get; set; } = String.Empty; + public string OperationName { get; set; } = string.Empty; + + + /// + /// 临床数据类型Id + /// + public Guid ClinicalDataTrialSetId { get; set; } + //public Guid SubjectId { get; set; } + } + + public class PreviousPDFView + { + public Guid Id { get; set; } + public DateTime CreateTime { get; set; } + public Guid SubjectVisitId { get; set; } + public string Path { get; set; } = string.Empty; + public string FileName { get; set; } = string.Empty; + public Guid CreateUserId { get; set; } + + public string FullFilePath => Path; + + + /// + /// 是否是检查批次 + /// + public bool? IsVisist { get; set; } + + /// + /// 临床级别 + /// + public ClinicalLevel? ClinicalLevel { get; set; } + + /// + /// 数据类型 + /// + public ClinicalDataType? DataType { get; set; } + + /// + /// 上传方式 + /// + public ClinicalUploadType? UploadType { get; set; } + + /// + /// 项目Id + /// + public Guid? TrialId { get; set; } + + /// + /// 患者ID + /// + public Guid? SubjectId { get; set; } + } + + + + /// PreviousPDFAddOrEdit 列表查询参数模型 + public class PreviousPDFAddOrEdit + { + public Guid? Id { get; set; } + public Guid SubjectVisitId { get; set; } + public string Path { get; set; } = string.Empty; + public string FileName { get; set; } = string.Empty; + + /// + /// 是否是检查批次 + /// + public bool? IsVisist { get; set; } + + /// + /// 临床级别 + /// + public ClinicalLevel? ClinicalLevel { get; set; } + + /// + /// 数据类型 + /// + public ClinicalDataType? DataType { get; set; } + + /// + /// 上传方式 + /// + public ClinicalUploadType? UploadType { get; set; } + + /// + /// 项目Id + /// + public Guid? TrialId { get; set; } + + /// + /// 患者ID + /// + public Guid? SubjectId { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs new file mode 100644 index 0000000..52a41a8 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs @@ -0,0 +1,543 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.MediatR.CommandAndQueries; +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Application.Contracts.DTO +{ + + + + + + + + + public class UpdateModalityCommand + { + public Guid Id { get; set; } + public Guid SubjectVisitId { get; set; } + + public int Type { get; set; } + + public string Modality { get; set; } = String.Empty; + + public string BodyPart { get; set; } = String.Empty; + + } + + public class QCQuestionAnswerCommand + { + public Guid? Id { get; set; } + public string Answer { get; set; } = String.Empty; + + //public string ChildAnswer { get; set; } = String.Empty; + + public Guid? TrialQCQuestionConfigureId { get; set; } + + } + + + public class QCQuestionAnswerItemDto + { + //public string ChildAnswer { get; set; } = String.Empty; + + [JsonIgnore] + public List QCQuestionAnswerList { get; set; } = new List(); + + + public TrialQCProcess? QCProcessEnum { get; set; } + + // 1代表第一个人QC数据 2 代表第二个人QC数据 + public CurrentQC? CurrentQCEnum { get; set; } + + public Guid? SubjectVisitId { get; set; } + + public Guid? Id { get; set; } + public string Answer { get; set; } = String.Empty; + + public Guid TrialQCQuestionConfigureId { get; set; } + public Guid TrialId { get; set; } + public string QuestionName { get; set; } = String.Empty; + public bool IsRequired { get; set; } + public bool IsEnable { get; set; } + public string Type { get; set; } = String.Empty; + public Guid? ParentId { get; set; } + public string TypeValue { get; set; } = String.Empty; + public string ParentTriggerValue { get; set; } = String.Empty; + public int ShowOrder { get; set; } + + public int? ParentShowOrder { get; set; } + + public bool IsChild => ParentId != null; + } + + + public class QATrialTemplateItemDto + { + public Guid QATrialTemplateId { get; set; } + public string TemplateItemName { get; set; } = String.Empty; + public Guid QATrialTemplateItemId { get; set; } + public int ShowOrder { get; set; } + } + + + public class QARecordItemQuery + { + public Guid QaTrialTemplateId { get; set; } + public Guid? QaRecordId { get; set; } + + } + + public class QATemplateQuery + { + public Guid TrialId { get; set; } + + public string Modalities { get; set; } = String.Empty; + } + + + public class CloseQCChallengeInDto + { + public Guid qcChallengeId { get; set; } + + public Guid trialId { get; set; } + + public Guid subjectVisitId { get; set; } + public QCChallengeCloseEnum closeEnum { get; set; } + public string closeReason { get; set; } + } + + + public class QCChallengeCommand + { + public Guid? Id { get; set; } + + public string ChallengeType { get; set; } = String.Empty; + + public Guid SubjectVisitId { get; set; } + + public string Content { get; set; } = string.Empty; + + public string ActionContent { get; set; } = string.Empty; + + public QCChanllengeReuploadEnum ReuploadEnum { get; set; } + + + public DateTime? DeadlineTime { get; set; } + + } + + + public class GetConsistencyCheckFileDto/*: ConsistencyCheckFile*/ + { + /// + /// 文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件路径 + /// + public string FilePath { get; set; } + + /// + /// 相对路径 + /// + public string RelativePath { get; set; } + + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 创建者名称 + /// + public string CreateUserName { get; set; } + } + + + public class GetConsistencyCheckFileInDto + { + public Guid TrialId { get; set; } + + public int PageIndex { get; set; } = 1; + public int PageSize { set; get; } = 10; + } + + + public class QCChanllengeCreatorDto + { + public Guid CreateUserId { get; set; } + + public string Creator { get; set; } = String.Empty; + + public string CreatorRealName { get; set; } = String.Empty; + + //public string FirstName { get; set; } + //public string LastName { get; set; } + + } + + public class ParticipantDTO + { + public Guid HandleUserId { get; set; } + public string HandleUser { get; set; } = String.Empty; + + public string HandleUserRealName { get; set; } = String.Empty; + } + + public class QCChallengeWithUser : QCChallengeCommand + { + + public string VisitName { get; set; } + public string BlindName { get; set; } = string.Empty; + public string SubjectCode { get; set; } + + public Guid? CurrentActionUserId { get; set; } + + public string CurrentActionUserName { get; set; } = String.Empty; + + public Guid SubjectId { get; set; } + public UserTypeEnum UserTypeEnum { get; set; } + public Guid CreateUserId { get; set; } + public string ChallengeCode { get; set; } = String.Empty; + + public DateTime? LatestMsgTime { get; set; } + + public string LatestReplyUserName { get; set; } = String.Empty; + + public DateTime? ClosedTime { get; set; } + + public bool IsClosed { get; set; } + public DateTime? ReUploadedTime { get; set; } + public string CreateUserName { get; set; } = String.Empty; + public DateTime CreateTime { get; set; } + + public bool IsOverTime => IsClosed ? ClosedTime > DeadlineTime : DateTime.Now > DeadlineTime; + + public string ChallengeDuration + { + get + { + if (!ClosedTime.HasValue) + return ""; + else return string.Format("{0}天{1}小时{2}分钟", (ClosedTime - CreateTime)?.Days, (ClosedTime - CreateTime)?.Hours, (ClosedTime - CreateTime)?.Minutes); + } + } + + + + } + + public class ChallengeAndDialog: QCChallengeWithUser + { + + public List DialogList { get; set; } = new List(); + } + + + + + + public class TrialVisitQADTO + { + + public SubjectClinicalDataDto SubjectClinicalData { get; set; } = new SubjectClinicalDataDto(); + + public List NoneDicomStudyList { get; set; } = new List(); + + public List QCQuestionAnswerList { get; set; } = new List(); + + public List StudyList { get; set; } = new List(); + + public List SeriesList { get; set; } = new List(); + + public QARelationInfo RelationInfo { get; set; } = new QARelationInfo(); + + } + + + public class QAStudySeriesInfo + { + public List StudyList { get; set; } = new List(); + + public List SeriesList { get; set; } = new List(); + } + + + public class QADictionaryCommand + { + + public string Note { get; set; } = String.Empty; + public Guid DictionaryId { get; set; } + + } + + public class QADictionaryDTO : QADictionaryCommand + { + public string DictionaryValue { get; set; } = String.Empty; + } + + public class QADicView + { + public Guid QARecordId { get; set; } + public string DictionaryValue { get; set; } = String.Empty; + public string Note { get; set; } = String.Empty; + public Guid DictionaryId { get; set; } + } + + + + + + public class QARelationInfo + { + public int TrialClinicalInformationTransmissionEnum { get; set; } + public int TrialChangeDefalutDays { get; set; } + public TrialQCProcess TrialQCProcessEnum { get; set; } + public string TrialChallengeTypes { get; set; } = String.Empty; + public string TrialBodyPartTypes { get; set; } = String.Empty; + public string TrialImageQCSignText { get; set; } = String.Empty; + public string TrialModalitys { get; set; } = String.Empty; + + //public string FirstName { get; set; } + //public string LastName { get; set; } + //public string Modalities { get; set; } + + public Guid SiteId { get; set; } + public string SiteName { get; set; } = String.Empty; + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + + public string SubjectCode { get; set; } = String.Empty; + public string SubjectName { get; set; } = String.Empty; + public int SubjectAge { get; set; } + public string SubjectSex { get; set; } = String.Empty; + + public DateTime? EarliestScanDate { get; set; } + + public DateTime? LatestScanDate { get; set; } + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + //public DateTime? SVSTDTC { get; set; } + //public DateTime? SVENDTC { get; set; } + public bool InPlan { get; set; } + public int? VisitDay { get; set; } + public bool IsBaseLine { get; set; } = false; + public string AnonymousVisitName { get; set; } = string.Empty; + public int? VisitWindowLeft { get; set; } + public int? VisitWindowRight { get; set; } + + public DateTime? SubjectFirstGiveMedicineTime { get; set; } + + public bool IsHaveFirstGiveMedicineDate { get; set; } = true; + + + + + public int? TotalChallengeCount { get; set; } + + public int? NotClosedChallengeCount { get; set; } + + + + } + public class QAStudyInfoDTO + { + + public bool IsDeleted { get; set; } + public string StudyInstanceUid { get; set; } = string.Empty; + + public Guid TrialId { get; set; } + + public Guid StudyId { get; set; } + + public int StudyStatus { get; set; } + + public string StudyCode { get; set; } = string.Empty; + + public string Modalities { get; set; } = String.Empty; + + public int SeriesCount { get; set; } + + public int InstanceCount { get; set; } + + public string Uploader { get; set; } = string.Empty; + public DateTime? StudyTime { get; set; } + + public DateTime? UploadedTime { get; set; } + public string BodyPartExamined { get; set; } = String.Empty; + + public string BodyPartForEdit { get; set; } = String.Empty; + + public string ModalityForEdit { get; set; } = string.Empty; + + //public string PatientName { get; set; } = string.Empty; + //public string PatientAge { get; set; } = string.Empty; + //public string PatientSex { get; set; } = string.Empty; + + //public string Comment { get; set; } + //public string QAComment { get; set; } + + //public string UploaderFirstName { get; set; } + //public string UploaderLastName { get; set; } + + + + } + + public class QASeriesInfoDto + { + public DateTime? StudyTime { get; set; } + + public string StudyCode { get; set; } = string.Empty; + public Guid Id { get; set; } + public Guid StudyId { get; set; } + + public string BodyPartForEdit { get; set; } = String.Empty; + + public string SeriesInstanceUid { get; set; } = String.Empty; + public int SeriesNumber { get; set; } + public DateTime? SeriesTime { get; set; } + public string Modality { get; set; } = String.Empty; + public string Description { get; set; } = String.Empty; + public int InstanceCount { get; set; } + + public DateTime CreateTime { get; set; } + + public string BodyPartExamined { get; set; } = String.Empty; + + public bool IsReading { get; set; } + + public bool IsDeleted { get; set; } + + //public bool IsDicomData { get; set; } = true; + + //public Guid[] InstanceList = new Guid[0]; + + //public List InstancePathList = new List(); + } + + + + public class QADialogCommand + { + public string TalkContent { get; set; } = String.Empty; + public Guid QCChallengeId { get; set; } + + public Guid SubjectVisitId { get; set; } + } + + + /// + /// 关闭一致性质疑Dto + /// + public class CloseCheckChallengeDto + { + public Guid subjectVisitId { get; set; } + + public string CloseCheckChallenge { get; set; } + } + + + public class CheckChallengeDialogCommand + { + public string TalkContent { get; set; } = String.Empty; + + public Guid SubjectVisitId { get; set; } + } + + + public class CheckChanllengeDialogDTO + { + public Guid Id { get; set; } + public string TalkContent { get; set; } = String.Empty; + + public DateTime CreateTime { get; set; } + + public Guid CreateUserId { get; set; } + + public string CreateUserName { get; set; } = String.Empty; + public string CreateUserFullName { get; set; } = String.Empty; + + public bool IsCurrentUser { get; set; } + + public bool? IsCRCNeedReply { get; set; } + + public UserTypeEnum UserTypeEnum { get; set; } + + public string ParamInfo { get; set; } + + public List ParamInfoList + { + get { + try + { + return JsonConvert.DeserializeObject>(ParamInfo); + } + catch (Exception e) + { + + return new List(); + } + } + } + } + + + + + public class QCChanllengeDialogDTO : CheckChanllengeDialogDTO + { + public Guid QCChallengeId { get; set; } + + } + + + + + + + public class DialogDTO + { + public Guid Id { get; set; } + public string TalkContent { get; set; } = String.Empty; + public string From { get; set; } = String.Empty; + public string To { get; set; } = string.Empty; + public Guid UserId { get; set; } + + public string PreviousContent { get; set; } = String.Empty; + + public bool HasReply { get; set; } + + public DateTime CreateTime { get; set; } + } + + public class DialogNode + { + public Guid Id { get; set; } + public Guid ParentId { get; set; } + public Guid UserId { get; set; } + public string UserName { get; set; } = String.Empty; + public bool HasReply { get; set; } + + public string TalkContent { get; set; } = String.Empty; + //public Guid QARecordId { get; set; } + public DateTime CreateTime { get; set; } + } + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs new file mode 100644 index 0000000..b98f594 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs @@ -0,0 +1,1446 @@ +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Core.Application.Helper; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Share; +using MiniExcelLibs.Attributes; +using Newtonsoft.Json; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Application.Contracts +{ + + public class CRCVisitSearchDTO : PageInput + { + + public AuditStateEnum[]? AuditStateArray { get; set; } + public SubmitStateEnum? SubmitState { get; set; } + public ChallengeStateEnum? ChallengeState { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + public Guid? SiteId { get; set; } + public Guid? SubjectId { get; set; } + + public bool? IsUrgent { get; set; } + public string SubjectInfo { get; set; } = string.Empty; + //public string VisitPlanInfo { get; set; } = string.Empty; + + public string[]? VisitPlanArray { get; set; } + + public DateTime? BeginSubmitTime { get; set; } + + public DateTime? EndSubmitTime { get; set; } + + } + + public class GetNextQCInfoInDto + { + public Guid TrialId { get; set; } + } + + public class QCVisitSearchDTO : PageInput + { + + //public AuditStateEnum? AuditState { get; set; } + + public Guid? CurrentActionUserId { get; set; } + + public AuditStateEnum[]? AuditStateArray { get; set; } + + public ChallengeStateEnum? ChallengeState { get; set; } + + public bool? IsUrgent { get; set; } + public Guid TrialId { get; set; } + public Guid? SiteId { get; set; } + public Guid? SubjectId { get; set; } + + public string SubjectInfo { get; set; } = String.Empty; + + public string[]? VisitPlanArray { get; set; } + //public string VisitPlanInfo { get; set; } = String.Empty; + + public Guid? HandleUserId { get; set; } + } + + + public class CRCRequestToQCCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + + public Guid[] SubjectVisitIds { get; set; } = new Guid[0]; + } + + + + public class CRCReuploadFinishedCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid QCChallengeId { get; set; } + + public bool SetOrCancel { get; set; } + + } + + public class UploadSubjectAndVisitCommand + { + public Guid SubjectVisitId { get; set; } + public Guid SubjectId { get; set; } + public PDStateEnum PDState { get; set; } + + //前端有可能不传递 不是基线的时候 + public bool? IsEnrollmentConfirm { get; set; } + public DateTime? SubjectFirstGiveMedicineTime { get; set; } + + } + + public class TrialSubjectAndSVConfig + { + public bool IsHaveFirstGiveMedicineDate { get; set; } + public bool IsSubjectSexView { get; set; } = false; + public bool IsSubjectExpeditedView { get; set; } = false; + + public bool IsEnrollementQualificationConfirm { get; set; } + public bool IsPDProgressView { get; set; } + + + public string OutEnrollmentVisitName { get; set; } = String.Empty; + + public string BodyPartTypes { get; set; } = String.Empty; + + public string Modalitys { get; set; } = String.Empty; + public string DocumentConfirmSignText { get; set; } = String.Empty; + + public string ImageQCSignText { get; set; } = String.Empty; + + public string PreliminaryAuditReuploadText { get; set; } = string.Empty; + + public string ReviewAuditReuploadText { get; set; } = string.Empty; + + public string CheckBackText { get; set; } = String.Empty; + + public string CheckPassText { get; set; } = String.Empty; + + public int ClinicalInformationTransmissionEnum { get; set; } + + public TrialQCProcess QCProcessEnum { get; set; } + + public bool IsHaveSubjectClinicalData { get; set; } = false; + + public bool IsHaveVisitClinicalData { get; set; } = false; + + } + + + #region 导出模型 + + public class CRCVisitExportDTO + { + public ChallengeStateEnum ChallengeState { get; set; } + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsUrgent { get; set; } + public string BlindName { get; set; } = String.Empty; + public string VisitName { get; set; } = string.Empty; + + public string SubjectCode { get; set; } = String.Empty; + + public String TrialSiteCode { get; set; } = String.Empty; + + public DateTime? EarliestScanDate { get; set; } + public DateTime? LatestScanDate { get; set; } + + public DateTime? SubmitTime { get; set; } + + + [DictionaryTranslateAttribute("SubmitState")] + public SubmitStateEnum SubmitState { get; set; } + + [DictionaryTranslateAttribute("AuditStateRC")] + public AuditStateEnum AuditState { get; set; } + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsHaveClinicalData { get; set; } + + public string SubmitUserName { get; set; } + + } + + + public class QCChanllengeExportDto + { + + [DictionaryTranslateAttribute("Subject_Visit_Status")] + public SubjectStatus SubjectState { get; set; } + + public string ChallengeCode { get; set; } = String.Empty; + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + public string VisitName { get; set; } = string.Empty; + public DateTime? CreateTime { get; set; } + public string CreateUserName { get; set; } = String.Empty; + public DateTime? LatestMsgTime { get; set; } + + + public string LatestReplyUserName { get; set; } = String.Empty; + + public string Content { get; set; } = string.Empty; + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsClosed { get; set; } + + public DateTime? ClosedTime { get; set; } + + public DateTime? DeadlineTime { get; set; } + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsOverTime => IsClosed ? ClosedTime > DeadlineTime : DateTime.Now > DeadlineTime; + + public string ChallengeDuration + { + get + { + if (!ClosedTime.HasValue) + return ""; + else return string.Format("{0}天{1}小时{2}分钟", (ClosedTime - CreateTime)?.Days, (ClosedTime - CreateTime)?.Hours, (ClosedTime - CreateTime)?.Minutes); + } + } + + + [DictionaryTranslateAttribute("ReuploadEnum")] + public QCChanllengeReuploadEnum ReuploadEnum { get; set; } + + + + public string DialogStr { get; set; } + + //public SubmitStateEnum SubmitState { get; set; } + //public string? CurrentActionUserName { get; set; } + + //public bool IsUrgent { get; set; } + + //public DateTime? ReUploadedTime { get; set; } + + //public RequestBackStateEnum RequestBackState { get; set; } + + //public string PreliminaryAuditUserName { get; set; } = String.Empty; + //public AuditStateEnum AuditState { get; set; } + //public CurrentQC CurrentQCEnum { get; set; } + //public string ChallengeType { get; set; } = string.Empty; + //public string Note { get; set; } = string.Empty; + + + + } + + + public class SubjectExportDTO + { + public string Code { get; set; } = String.Empty; + + //public int? Age { get; set; } + //public string Sex { get; set; } = string.Empty; + //public Guid SiteId { get; set; } + //public Guid TrialId { get; set; } + //public string MedicalNo { get; set; } = string.Empty; + //public string ShortName { get; set; } = string.Empty; + //public string Height { get; set; } = string.Empty; + //public string Weight { get; set; } = string.Empty; + //public DateTime? BirthDate { get; set; } + //public DateTime? SignDate { get; set; } + //public bool IsUrgent { get; set; } + + //[ExcelColumn(Name = "FirstGiveMedicineTime", Format = "yyyy-MM-dd")] + public DateTime? FirstGiveMedicineTime { get; set; } + + + public string FirstGiveMedicineTimeStr => FirstGiveMedicineTime?.ToString("yyyy-MM-dd"); + + public DateTime? OutEnrollmentTime { get; set; } + public DateTime? VisitOverTime { get; set; } + + [DictionaryTranslateAttribute("Subject_Visit_Status")] + + public SubjectStatus Status { get; set; } + + + public int? TotalVisitCount => OutPlanVisitCount + InPlanVisitCount; + public int? TotalVisitSubmmitCount => OutPlanVisitSubmmitCount + InPlanVisitSubmmitCount; + + public int? OutPlanVisitCount { get; set; } + public int? OutPlanVisitSubmmitCount { get; set; } + + public int? InPlanVisitCount { get; set; } + public int? InPlanVisitSubmmitCount { get; set; } + + public string? FinalSubjectVisitName { get; set; } + + + public int? MissingSubmmitCount { get; set; } + + public int? LostVisitCount { get; set; } + + + public string SiteName { get; set; } = string.Empty; + public string TrialSiteCode { get; set; } = string.Empty; + + public string LatestVisitName { get; set; } = string.Empty; + + public string LatestBlindName { get; set; } = string.Empty; + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsMissingImages => MissingSubmmitCount > 0; + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsEnrollmentConfirmed { get; set; } + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsPDProgress { get; set; } + + + public int? RadiologyClinicalDataCount { get; set; } + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsHaveRadiologyClinicalData => RadiologyClinicalDataCount != null && RadiologyClinicalDataCount > 0; + + public int? OncologyClinicalDataCount { get; set; } + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsHaveOncologyClinicalData => OncologyClinicalDataCount != null && OncologyClinicalDataCount > 0; + + + public int? ChallengeWaitReplyCount { get; set; } + + + public int? CheckWaitReplyCount { get; set; } + + + } + + + public class SubjectReadingPeriodExportDto + { + [DictionaryTranslateAttribute("Subject_Visit_Status")] + + public SubjectStatus Status { get; set; } + + public string Code { get; set; } = String.Empty; + + public string TrialSiteCode { get; set; } = string.Empty; + + + public CriterionType CriterionType { get; set; } + + public ReadingMethod ReadingType { get; set; } + + + ///// + ///// 任务展示检查批次 读片任务显示是否顺序 + ///// + //public bool IsReadingTaskViewInOrder { get; set; } = true; + } + + public class ReadingPeriodOrVisitInfo + { + + + public Guid SubjectVisitId { get; set; } + + //核查状态 + public CheckStateEnum CheckState { get; set; } + + //提交状态 + public SubmitStateEnum SubmitState { get; set; } + //审核状态 + public AuditStateEnum AuditState { get; set; } + + //阅片期或者检查批次名 + public string ReadingPeriodName { get; set; } + + + + + //public ForwardStateEnum ForwardState { get; set; } + } + + public class ReadPeriodQuery + { + public Guid TrialId { get; set; } + } + + public class SubjectProgressQuery + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + public class SubjectProgressDto + { + public string SubjectCode { get; set; } + + public string TrialSiteCode { get; set; } + + [DictionaryTranslateAttribute("Subject_Visit_Status")] + public SubjectStatus SubjectStatus { get; set; } + + + public List VisitList { get; set; } + + public List ModuleList { get; set; } + + + public List TotalList => VisitList.OrderByDescending(t => t.Inplan).Union(ModuleList.OrderBy(t => t.ReadingSetType).ThenBy(t => t.VisitTaskNum)).ToList(); + + + public class MouduleProgress + { + public string TaskName { get; set; } + + public ReadingStatusEnum ReadingStatus { get; set; } + + + + + //最终任务 + [Column(TypeName = "decimal(18,2)")] + public decimal VisitTaskNum { get; set; } + + //检查批次会有 + public bool? Inplan { get; set; } + + public bool? IsLostVisit { get; set; } + + public bool? IsFinalVisit { get; set; } + + //阅片期会有 + public ReadingSetType? ReadingSetType { get; set; } + } + + } + + public class ReadPeriodExportDto + { + public string SubjectCode { get; set; } + + public string TrialSiteCode { get; set; } + + [DictionaryTranslateAttribute("Subject_Visit_Status")] + public SubjectStatus SubjectStatus { get; set; } + public string TrialReadingCriterionName { get; set; } + + [ExcelFormat("yyyy-MM-dd")] + public DateTime? FirstGiveMedicineTime { get; set; } + + public string FirstGiveMedicineTimeStr => FirstGiveMedicineTime?.ToString("yyyy-MM-dd"); + + public string ModuleName { get; set; } + + + public string DeadlineVisitName { get; set; } + + + public DateTime? EarliestScanDate { get; set; } + public DateTime? LatestScanDate { get; set; } + + + + + + } + + + public class UnionStudyMonitorExportDto + { + public string SubjectCode { get; set; } = String.Empty; + + public string VisitName { get; set; } = string.Empty; + + + public string TrialSiteCode { get; set; } = string.Empty; + + public string TrialSiteAliasName { get; set; } = string.Empty; + + public string Uploader { get; set; } = string.Empty; + + public DateTime UploadTime { get; set; } + + public string StudyCode { get; set; } + + + public bool IsDicom { get; set; } + + public string IsDicomStr => IsDicom ? "DICOM" : "Non-DICOM"; + + public string UploadStartTimeStr => UploadStartTime.ToString("yyyy-MM-dd HH:mm:ss.fff"); + public string UploadFinishedTimeStr => UploadFinishedTime?.ToString("yyyy-MM-dd HH:mm:ss.fff"); + + public string ArchiveFinishedTimeStr => ArchiveFinishedTime?.ToString("yyyy-MM-dd HH:mm:ss.fff"); + + public string UploadIntervalStr + { + get + { + var uploadTimeSpan = UploadFinishedTime - UploadStartTime; + + return $" {uploadTimeSpan?.Hours}:{uploadTimeSpan?.Minutes}:{uploadTimeSpan?.Seconds}.{uploadTimeSpan?.Milliseconds}"; + } + } + + public string ArchiveIntervalStr + { + get + { + var uploadTimeSpan = ArchiveFinishedTime - UploadFinishedTime; + + return $" {uploadTimeSpan?.Hours}:{uploadTimeSpan?.Minutes}:{uploadTimeSpan?.Seconds}.{uploadTimeSpan?.Milliseconds}"; + } + } + + + public string TimeInterval + { + get + { + + var uploadTimeSpan = ArchiveFinishedTime - UploadStartTime; + + return $" {uploadTimeSpan?.Hours}:{uploadTimeSpan?.Minutes}:{uploadTimeSpan?.Seconds}.{uploadTimeSpan?.Milliseconds}"; + } + } + + public DateTime UploadStartTime { get; set; } + + public DateTime? ArchiveFinishedTime { get; set; } + public DateTime? UploadFinishedTime { get; set; } + + + public decimal FileSize { get; set; } + + public string FileSizeStr => $"{(FileSize / (decimal)Math.Pow(1024, 2)).ToString("F")}M"; + + public string IP { get; set; } = String.Empty; + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsDicomReUpload { get; set; } + + + public int FileCount { get; set; } + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsSuccess { get; set; } + + public string Note = string.Empty; + + } + + public class UnionStudyExportDTO + { + + public string Modality { get; set; } = string.Empty; + + [ExcelFormat("yyyy-MM-dd")] + public DateTime? StudyTime { get; set; } + + public string StudyTimeStr => StudyTime?.ToString("yyyy-MM-dd"); + + public string SubjectCode { get; set; } = String.Empty; + + public string VisitName { get; set; } = string.Empty; + + public decimal VisitNum { get; set; } + + public string TrialSiteCode { get; set; } = string.Empty; + + public string TrialSiteAliasName { get; set; } = string.Empty; + + + public string StudyCode { get; set; } + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsDicom { get; set; } + + + [DictionaryTranslateAttribute("CheckState")] + public CheckStateEnum CheckState { get; set; } + } + + + public class PMKCheckEXportDTO + { + public string? TalkContent { get; set; } = String.Empty; + public string BlindName { get; set; } = String.Empty; + public bool IsUrgent { get; set; } + + public DateTime? CheckPassedTime { get; set; } + + [DictionaryTranslateAttribute("AuditStateRC")] + public AuditStateEnum AuditState { get; set; } + + + [DictionaryTranslateAttribute("CheckState")] + public CheckStateEnum CheckState { get; set; } + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + [DictionaryTranslateAttribute("Subject_Visit_Status")] + public SubjectStatus SubjectStatus { get; set; } + + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + + public Guid SubjectId { get; set; } + public Guid SiteId { get; set; } + + [DictionaryTranslateAttribute("RequestBackState")] + public RequestBackStateEnum RequestBackState { get; set; } + + public DateTime? CheckTime { get; set; } + + + [DictionaryTranslateAttribute("CheckChallengeState")] + public CheckChanllengeTypeEnum CheckChallengeState { get; set; } + + [DictionaryTranslateAttribute("YesOrNo")] + public bool? IsCheckBack { get; set; } + + public string CheckResult { get; set; } = String.Empty; + + + public string CheckDialogStr { get; set; } + + public DateTime? CheckBackTime { get; set; } + + public DateTime? EarliestScanDate { get; set; } + + public DateTime? LatestScanDate { get; set; } + + public List ModalityList { get; set; } + + public string Modalitys => string.Join(',', ModalityList); + } + + + public class ReadingTaskExportDto + { + //public TaskAllocationState TaskAllocationState { get; set; } + + [DictionaryTranslateAttribute("Subject_Visit_Status")] + public SubjectStatus SubjectStatus { get; set; } + + + public DateTime? AllocateTime { get; set; } + + //public bool IsPMSetBack { get; set; } + + public string TaskCode { get; set; } + + public string TaskName { get; set; } + public string TaskBlindName { get; set; } + + public decimal VisitTaskNum { get; set; } + + [DictionaryTranslateAttribute("ReadingCategory")] + public ReadingCategory ReadingCategory { get; set; } + + + [DictionaryTranslateAttribute("TaskState")] + public TaskState TaskState { get; set; } + + public DateTime? SignTime { get; set; } + + public DateTime? CreateTime { get; set; } + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsUrgent { get; set; } + + [DictionaryTranslateAttribute("ArmEnum")] + public Arm ArmEnum { get; set; } + + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + public string TrialReadingCriterionName { get; set; } + + [DictionaryTranslateAttribute("ReadingTaskState")] + public ReadingTaskState ReadingTaskState { get; set; } + + [DictionaryTranslateAttribute("ReReadingApplyState")] + public ReReadingApplyState ReReadingApplyState { get; set; } + public DateTime? SuggesteFinishedTime { get; set; } + public string UserCode { get; set; } + public string UserName { get; set; } + public string FullName { get; set; } + + public string UserTypeShortName { get; set; } + } + + + public class ReReadingTaskExportDto : ReadingTaskExportDto + { + + + //public ReadingTaskExportDto ApplyTask { get; set; } + + + + public string? ReReadingNewTaskCode { get; set; } + + + + [DictionaryTranslateAttribute("RequestReReadingType")] + public RequestReReadingType RequestReReadingType { get; set; } + + public string RequestReReadingRejectReason { get; set; } = string.Empty; + + public DateTime? RequestReReadingTime { get; set; } + + public string RequestReReadingReason { get; set; } = string.Empty; + + public DateTime? SuggesteFinishedTime { get; set; } + + [DictionaryTranslateAttribute("RequestReReadingResult")] + public RequestReReadingResult RequestReReadingResultEnum { get; set; } + + + + + } + + + + public class TaskMedicalReviewExportDto : ReadingTaskExportDto + { + + public string MedicalNo { get; set; } = string.Empty; + + + [DictionaryTranslateAttribute("MedicalReviewAuditState")] + public MedicalReviewAuditState AuditState { get; set; } + public DateTime? AuditSignTime { get; set; } + + [DictionaryTranslateAttribute("MedicalReviewDoctorUserIdea")] + public MedicalReviewDoctorUserIdea DoctorUserIdeaEnum { get; set; } + + [DictionaryTranslateAttribute("AuditAdvice")] + public AuditAdvice AuditAdviceEnum { get; set; } + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsHaveQuestion { get; set; } + + + + //public UserSimpleInfo DoctorUser { get; set; } + + //public UserSimpleInfo MedicalManagerUser { get; set; } + + //public string DoctorUserName { get; set; } + public string MedicalManagerUserName { get; set; } + + /// + /// 医学审核对话关闭原因 + /// + public MedicalDialogClose MedicalDialogCloseEnum { get; set; } + + + /// + /// 无效的 为True无效 + /// + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsInvalid { get; set; } + } + + + public class AnalysisExortCommon + { + public Guid Id { get; set; } + public string TaskName { get; set; } + public string TaskBlindName { get; set; } + + public decimal VisitTaskNum { get; set; } + public bool? IsSelfAnalysis { get; set; } + + public string TrialReadingCriterionName { get; set; } + + public CriterionType CriterionType { get; set; } + + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + + [DictionaryTranslateAttribute("ArmEnum")] + public Arm ArmEnum { get; set; } + public string UserName { get; set; } + + + + public bool? IsBaseline { get; set; } + + + [DictionaryTranslateAttribute("ExistDisease", nameof(SelftAnalysisExport.IsBaseline), "true")] + [DictionaryTranslateAttribute("OverallAssessment", nameof(SelftAnalysisExport.IsBaseline), "false")] + public string EvaluateResult { get; set; } + + [DictionaryTranslateAttribute("ExistDisease", nameof(SelftAnalysisExport.IsBaseline), "true")] + [DictionaryTranslateAttribute("OverallAssessment", nameof(SelftAnalysisExport.IsBaseline), "false")] + public string AgainEvaluateResult { get; set; } = String.Empty; + } + + public class GroupAnalysisExport : AnalysisExortCommon + { + + [DictionaryTranslateAttribute("ArmEnum")] + public Arm? AgainArmEnum { get; set; } + + public string AgainUserName { get; set; } + + /// + /// 组件一致性和原Arm1是否有差异 + /// + public bool? IsGroupDiffArm1 { get; set; } + + /// + /// 组件一致性和原Arm2是否有差异 + /// + public bool? IsGroupDiffArm2 { get; set; } + + public bool? IsGroupAnalysisDiffToOriginalData { get; set; } + + } + + public class SelftAnalysisExport : AnalysisExortCommon + { + //自身一致性分析任务特有 + + [DictionaryTranslateAttribute("YesOrNo")] + public bool? IsAnalysisDiffToOriginalData { get; set; } + + + } + + public class OverallTumorEvaluationExport + { + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + public Guid Id { get; set; } + public string TaskName { get; set; } + public string TaskBlindName { get; set; } + + public decimal VisitTaskNum { get; set; } + + public bool? IsBaseline { get; set; } + + + [DictionaryTranslateAttribute("ArmEnum")] + public Arm ArmEnum { get; set; } + public string UserName { get; set; } + + + //[DictionaryTranslateAttribute("ExistDisease", CriterionType.RECIST1Pointt1, nameof(OverallTumorEvaluationExport.IsBaseline), "true")] + //[DictionaryTranslateAttribute("OverallAssessment", CriterionType.RECIST1Pointt1, nameof(OverallTumorEvaluationExport.IsBaseline), "false")] + + //[DictionaryTranslateAttribute("VisitTumorEvaluation", CriterionType.PCWG3)] + ////整体肿瘤评估结果 需要翻译 + //public string OverallTumorEvaluationResult { get; set; } + + public Guid? JudgeResultTaskId { get; set; } + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsGenerateJudge => JudgeResultTaskId != null; + + + + } + + + + public class RECIST1Point1EvaluationOfTumorEfficacyExport : OverallTumorEvaluationExport + { + //[DictionaryTranslateAttribute("TargetAssessment", CriterionType.RECIST1Pointt1)] + //// 靶病灶评估 + //public string TargetlesionEvaluationResult { get; set; } + + //[DictionaryTranslateAttribute("NoTargetAssessment", CriterionType.RECIST1Pointt1)] + //// 非靶病灶评估 + //public string NoneTargetlesionEvaluationResult { get; set; } + + //// 是否存在新病灶 + //[DictionaryTranslateAttribute("NewLesionAssessment", CriterionType.RECIST1Pointt1)] + //public string IsExistNewlesionEvaluationResult { get; set; } + + public string EvaluationSummary { get; set; } + + } + + public class PCWG3LessionInfo + { + //病灶编号 + public string LessionCode { get; set; } + + + [DictionaryTranslateAttribute("LesionType")] + //病灶类型 + public string LessionType { get; set; } + + //所在部位 + public string LessionLocation { get; set; } + + //所在器官 + [DictionaryTranslate("OrganType")] + public string LessionOrgan { get; set; } + + + //部位描述 + public string BodyPartDescription { get; set; } + + + [DictionaryTranslateAttribute("TargetState")] + //病灶状态 + public string LessionState { get; set; } + } + + public class PCWG3DetailedOfEvaluatedLesionExport : OverallTumorEvaluationExport + { + + [JsonIgnore] + public List LesionList = new List(); + + //病灶编号 + public string LessionCode { get; set; } + + + [DictionaryTranslateAttribute("LesionType")] + //病灶类型 + public string LessionType { get; set; } + + //所在部位 + public string LessionLocation { get; set; } + + //所在器官 + + public string LessionOrgan { get; set; } + + + //部位描述 + public string BodyPartDescription { get; set; } + + + [DictionaryTranslateAttribute("EvaluationOfState")] + //病灶状态 + public string LessionState { get; set; } + } + + + //RECIST1.1评估病灶明细表 + + public class RECIST1Point1DetailedOfEvaluatedLesionExport : RECIST1Point1EvaluationOfTumorEfficacyExport + { + //EF 查询 需要 最后转成导表需要的展开形式 + [JsonIgnore] + public List LesionList = new List(); + + + + + //病灶编号 + public string LessionCode { get; set; } + + + [DictionaryTranslateAttribute("LesionType")] + //病灶类型 + public string LessionType { get; set; } + + + + //是否淋巴结 + [DictionaryTranslateAttribute("IsLymph")] + public string IsLymph { get; set; } + + //所在部位 + public string LessionLocation { get; set; } + + + //所在器官 + + public string LessionOrgan { get; set; } + //部位描述 + public string BodyPartDescription { get; set; } + + //测量值 + //public string MeasurementResult { get; set; } + + //长径 + public string LongDiameter { get; set; } + + //短径 + public string ShortDiameter { get; set; } + + [DictionaryTranslateAttribute("TargetState")] + //病灶状态 + public string LessionState { get; set; } + + } + + + + + public class RECIST1Point1LessionInfo + { + //病灶编号 + public string LessionCode { get; set; } + + //病灶类型 + public string LessionType { get; set; } + + //所在部位 + public string LessionLocation { get; set; } + + //所在器官 + public string LessionOrgan { get; set; } + + //是否淋巴结 + public string IsLymph { get; set; } + + //部位描述 + public string BodyPartDescription { get; set; } + + //测量值 + //public string MeasurementResult { get; set; } + + + //长径 + public string LongDiameter { get; set; } + + //短径 + public string ShortDiameter { get; set; } + + //病灶状态 + public string LessionState { get; set; } + + } + + + public class PGW3DetailedOfEvaluatedLesionExport : OverallTumorEvaluationExport + { + //病灶编号 + public string LessionCode { get; set; } + + + //病灶类型 + public string LessionType { get; set; } + + //所在部位 + public string LessionLocation { get; set; } + + //部位描述 + public string BodyPartDescription { get; set; } + + //病灶状态 + public string LessionState { get; set; } + } + + #endregion + + + + + public class QCCRCVisitViewModel : QCVisitBasicListViewModel + { + public Guid? OutPlanPreviousVisitId { get; set; } + public bool IsOutEnromentVisit { get; set; } + + public PDStateEnum PDState { get; set; } + public bool IsEnrollmentConfirm { get; set; } + public DateTime? SubjectFirstGiveMedicineTime { get; set; } + + public SubjectStatus SubjectStatus { get; set; } + public string SubjectCode { get; set; } = String.Empty; + + //public int? StudyCount { get; set; } + public String TrialSiteCode { get; set; } = String.Empty; + + public DateTime? SubmitTime { get; set; } + public AuditStateEnum AuditState { get; set; } + + + public int? DicomStudyCount { get; set; } + public int? NoneDicomStudyCount { get; set; } + + public bool IsHaveClinicalData { get; set; } + + public string MedicalNo { get; set; } + + public int? Age { get; set; } + public string Sex { get; set; } = string.Empty; + + } + + + public class ChallengeQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public string[]? VisitPlanArray { get; set; } + + //public string VisitPlanInfo { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + public QCChanllengeReuploadEnum? ReuploadEnum { get; set; } + + public bool? IsUrgent { get; set; } + public Guid? SubjectId { get; set; } + public Guid? SiteId { get; set; } + public bool? IsClosed { get; set; } + public bool? IsOverTime { get; set; } + public Guid? CreateUserId { get; set; } + + } + + public class QCCRCChallengeViewModel + { + public Guid? PreliminaryAuditUserId { get; set; } + public Guid? CurrentActionUserId { get; set; } + + + public SubmitStateEnum SubmitState { get; set; } + + + + public string? CurrentActionUserName { get; set; } + + public bool IsBaseLine { get; set; } + public bool IsUrgent { get; set; } + public Guid? ClinicalDataSignUserId { get; set; } + + public bool IsQCConfirmedReupload { get; set; } + + //public string CreatorFirstName { get; set; } + //public string CreatorLastName { get; set; } + public string BlindName { get; set; } = String.Empty; + + //public string ReplyerRealName { get; set; } + + public DateTime? ReUploadedTime { get; set; } + public Guid SubjectVisitId { get; set; } + public Guid SubjectId { get; set; } + public Guid Id { get; set; } + public String TrialSiteCode { get; set; } = String.Empty; + + public string SubjectCode { get; set; } = String.Empty; + + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + + public RequestBackStateEnum RequestBackState { get; set; } + + public bool IsClosed { get; set; } + + public DateTime? ClosedTime { get; set; } + + public string PreliminaryAuditUserName { get; set; } = String.Empty; + + //public string ClosedUserUserName { get; set; } = String.Empty; + + public DateTime? CreateTime { get; set; } + + public Guid CreateUserId { get; set; } + + public string CreateUserName { get; set; } = String.Empty; + + //public bool NeedReUpload { get; set; } + public QCChanllengeReuploadEnum ReuploadEnum { get; set; } + public DateTime? DeadlineTime { get; set; } + + public Guid SiteId { get; set; } + public AuditStateEnum AuditState { get; set; } + public CurrentQC CurrentQCEnum { get; set; } + public TrialQCProcess QCProcessEnum { get; set; } + + public string ChallengeCode { get; set; } = String.Empty; + + public string ChallengeType { get; set; } = string.Empty; + + public string Note { get; set; } = string.Empty; + + + public bool IsOverTime => IsClosed ? ClosedTime > DeadlineTime : DateTime.Now > DeadlineTime; + + public Guid LatestReplyUserId { get; set; } + + + public string LatestReplyUserName { get; set; } = String.Empty; + + public string Content { get; set; } = string.Empty; + + public UserTypeEnum UserTypeEnum { get; set; } + + + public DateTime? LatestMsgTime { get; set; } + + public string ChallengeDuration + { + get + { + if (!ClosedTime.HasValue) + return ""; + else return string.Format("{0}天{1}小时{2}分钟", (ClosedTime - CreateTime)?.Days, (ClosedTime - CreateTime)?.Hours, (ClosedTime - CreateTime)?.Minutes); + } + } + + + } + + public class CheckQuery : PageInput + { + + //public string VisitPlanInfo { get; set; } = String.Empty; + public string[]? VisitPlanArray { get; set; } + + //核查状态 + public CheckStateEnum? CheckState { get; set; } + + public Guid TrialId { get; set; } + + public Guid? SiteId { get; set; } + public string SubjectInfo { get; set; } = String.Empty; + + + //public bool? IsClosed { get; set; } + } + + public class QCQuestionAnswer + { + public Guid Id { get; set; } + public string QuestionName { get; set; } = String.Empty; + public bool IsRequired { get; set; } + + public string Type { get; set; } = String.Empty; + + public string Answer { get; set; } = String.Empty; + public string ParentTriggerValue { get; set; } + public Guid? ParentId { get; set; } + public string TypeValue { get; set; } = String.Empty; + public int ShowOrder { get; set; } + + public int? ParentShowOrder { get; set; } + + + + public List Childrens = new List(); + } + public class GetQCQuestionAnswerInDto + { + public Guid SubjectVisitId { get; set; } + + public Guid TrialId { get; set; } + + public TrialQCProcess QCProcessEnum { get; set; } + + // 1代表第一个人QC数据 2 代表第二个人QC数据 + public CurrentQC CurrentQCEnum { get; set; } + } + + public class ForwardQuery : PageInput + { + public Guid TrialId { get; set; } + + public Guid? SiteId { get; set; } + public string SubjectInfo { get; set; } = String.Empty; + + public string[]? VisitPlanArray { get; set; } + + //public string VisitPlanInfo { get; set; } = String.Empty; + + public ForwardStateEnum? ForwardState { get; set; } + } + + public class ForWardViewModel + { + public Guid Id { get; set; } + public string BlindName { get; set; } = String.Empty; + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + + public Guid? ForwardUserId { get; set; } + + public string ForwardUserName { get; set; } = String.Empty; + + public DateTime? ForwardTime { get; set; } + + public ForwardStateEnum ForwardState { get; set; } + + public CheckStateEnum CheckState { get; set; } + + + } + + + public class CheckDialogDTO + { + public QCCheckViewModel SubjectVisitCheck { get; set; } + + public List DialogList { get; set; } + } + + public class QCCheckWithModalityView: QCCheckViewModel + { + public DateTime? EarliestScanDate { get; set; } + + public DateTime? LatestScanDate { get; set; } + + public List ModalityList { get; set; } + + public string Modalitys =>string.Join(',', ModalityList); + } + + public class QCCheckViewModel + { + public string? TalkContent { get; set; } = String.Empty; + public string BlindName { get; set; } = String.Empty; + public bool IsUrgent { get; set; } + + public DateTime? CheckPassedTime { get; set; } + public Guid Id { get; set; } + + public Guid? CheckChallengeLatestUserId { get; set; } + + public AuditStateEnum AuditState { get; set; } + + public CheckStateEnum CheckState { get; set; } + public String TrialSiteCode { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + + public Guid SubjectId { get; set; } + public Guid SiteId { get; set; } + + //public string Modality { get; set; } + + public RequestBackStateEnum RequestBackState { get; set; } + + [ExcelFormat("yyyy-MM-dd")] + public DateTime? StudyTime { get; set; } + + public string StudyTimeStr => StudyTime?.ToString("yyyy-MM-dd"); + + public DateTime? CheckTime { get; set; } + public CheckChanllengeTypeEnum CheckChallengeState { get; set; } + + public bool? IsCheckBack { get; set; } + + public string CheckResult { get; set; } = String.Empty; + } + + public class QCVisitViewModel : QCVisitBasicListViewModel + { + public int? DicomStudyCount { get; set; } + public int? NoneDicomStudyCount { get; set; } + public bool IsHaveClinicalData { get; set; } + + public SubjectStatus SubjectStatus { get; set; } + public int? StudyCount { get; set; } + + public string SubjectCode { get; set; } = String.Empty; + public String TrialSiteCode { get; set; } = String.Empty; + + + + public int? ChallengeCount { get; set; } + + + public AuditStateEnum AuditState { get; set; } + + public DateTime? SubmitTime { get; set; } + + public string CurrentActionUserName { get; set; } = String.Empty; + public string PreliminaryAuditUserName { get; set; } = String.Empty; + + + public string ReviewAuditUserName { get; set; } = String.Empty; + public DateTime? ReviewAuditTime { get; set; } + public DateTime? PreliminaryAuditTime { get; set; } + + public bool IsEnrollmentConfirm { get; set; } = false; + public DateTime? SubjectFirstGiveMedicineTime { get; set; } + + public PDStateEnum PDState { get; set; } = PDStateEnum.None; + } + + //public class QCHistoryChallengeViewModel: QCCRCChallengeViewModel + //{ + + + + //} + + public class QCChallengeViewModel : QCCRCChallengeViewModel + { + + + } + + + + public class QCVisitBasicListViewModel + { + public ChallengeStateEnum ChallengeState { get; set; } + public bool? IsConfirmedClinicalData { get; set; } + public bool IsQCConfirmedReupload { get; set; } + + public bool IsFinalVisit { get; set; } + public string BlindName { get; set; } = String.Empty; + public bool IsUrgent { get; set; } + //public bool IsReuploaded { get; set; } + //public bool NeedReUpload { get; set; } + public QCChanllengeReuploadEnum ReuploadEnum { get; set; } + public Guid? Id { get; set; } + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + public Guid SiteId { get; set; } + public bool InPlan { get; set; } = true; + public int VisitExecuted { get; set; } + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + public int VisitDay { get; set; } + public string SVUPDES { get; set; } = string.Empty; + public DateTime? SVSTDTC { get; set; } + public DateTime? SVENDTC { get; set; } + + public TrialQCProcess QCProcessEnum { get; set; } + public DateTime? EarliestScanDate { get; set; } + public DateTime? LatestScanDate { get; set; } + + public bool IsBaseLine { get; set; } = false; + public string AnonymousVisitName { get; set; } = string.Empty; + public int VisitWindowLeft { get; set; } + public int VisitWindowRight { get; set; } + + public bool IsTake { get; set; } + public Guid? CurrentActionUserId { get; set; } + public Guid? PreliminaryAuditUserId { get; set; } + + public Guid? ReviewAuditUserId { get; set; } + public DateTime? CurrentActionUserExpireTime { get; set; } + + public SubmitStateEnum SubmitState { get; set; } + + public RequestBackStateEnum RequestBackState { get; set; } + + public bool IsLostVisit { get; set; } + + //public Guid? ClinicalDataSignUserId { get; set; } + } + + + +} diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QCQuestionConfigureViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QCQuestionConfigureViewModel.cs new file mode 100644 index 0000000..98cbf55 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/DTO/QCQuestionConfigureViewModel.cs @@ -0,0 +1,106 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-11 11:48:52 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.Contracts +{ + + /// + /// + /// + public class QCQuestionViewInDto + { + public Guid? TrialId { get; set; } + } + + public class QCQuestionView + { + public Guid Id { get; set; } + public string QuestionName { get; set; } = String.Empty; + public bool IsRequired { get; set; } + public bool IsEnable { get; set; } + public string Type { get; set; } = String.Empty; + public string ParentTriggerValue { get; set; } + public Guid? ParentId { get; set; } + public string TypeValue { get; set; } = String.Empty; + public int ShowOrder { get; set; } + + public int? ParentShowOrder { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + + public List Childrens = new List(); + } + + /// QCQuestionConfigureView 列表视图模型 + public class QCQuestionConfigureView + { + public Guid Id { get; set; } + public string QuestionName { get; set; } = String.Empty; + public bool IsRequired { get; set; } + public bool IsEnable { get; set; } + public string Type { get; set; } = String.Empty; + public string ParentTriggerValue { get; set; } + public Guid? ParentId { get; set; } + public string TypeValue { get; set; } = String.Empty; + public int ShowOrder { get; set; } + + public string ParentQuestionName { get; set; } = String.Empty; + + + public int? ParentShowOrder { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + } + + ///QCQuestionQuery 列表查询参数模型 + public class QCQuestionQuery + { + /// QuestionName + public string QuestionName { get; set; } = String.Empty; + + /// TypeValue + public string Type { get; set; } = String.Empty; + + public bool? IsEnable { get; set; } + + + public bool IsDefeaultViewParent { get; set; } + + + + } + + /// QCQuestionAddOrEdit 列表查询参数模型 + public class QCQuestionAddOrEdit + { + public Guid? Id { get; set; } + public string QuestionName { get; set; } = String.Empty; + public bool IsRequired { get; set; } + public bool IsEnable { get; set; } + public string Type { get; set; } = String.Empty; + + public string ParentTriggerValue { get; set; } = String.Empty; + public Guid? ParentId { get; set; } + + + public string TypeValue { get; set; } = String.Empty; + public string ChildInvalidValue { get; set; } = String.Empty; + public int ShowOrder { get; set; } + + + + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/QC/DTO/TrialQCQuestionConfigureViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/TrialQCQuestionConfigureViewModel.cs new file mode 100644 index 0000000..33e5d79 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/DTO/TrialQCQuestionConfigureViewModel.cs @@ -0,0 +1,106 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-11 11:48:52 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Core.Application.Contracts +{ + /// TrialQCQuestionConfigureView 列表视图模型 + public class TrialQCQuestionConfigureView + { + public Guid Id { get; set; } + public Guid TrialId { get; set; } + public string QuestionName { get; set; } = string.Empty; + public bool IsRequired { get; set; } + public bool IsEnable { get; set; } + public string Type { get; set; } = string.Empty; + public string TypeValue { get; set; } = string.Empty; + + public Guid? ParentId { get; set; } + public string ParentTriggerValue { get; set; } = string.Empty; + public int? ParentShowOrder { get; set; } + + public int ShowOrder { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + } + + public class QCQuestionFilterSelect + { + public Guid? Id { get; set; } + + public string[] TypeArray { get; set; } = new string[0]; + } + + public class TrialQCQuestionFilterSelect + { + [NotDefault] + public Guid TrialId { get; set; } + + + public Guid? Id { get; set; } + + public string[] TypeArray { get; set; }=new string[0]; + } + + public class TrialQCQuestionSelect + { + public Guid Id { get; set; } + public string QuestionName { get; set; } = string.Empty; + + public Guid? ParentId { get; set; } + public int ShowOrder { get; set; } + + public string TypeValue { get; set; } + } + + ///TrialQCQuestionQuery 列表查询参数模型 + public class TrialQCQuestionQuery + { + + public Guid TrialId { get; set; } + /// QuestionName + public string QuestionName { get; set; } = string.Empty; + + /// TypeValue + public string Type { get; set; }=String.Empty; + + public bool? IsEnable { get; set; } + + public bool? IsRequired { get; set; } + + + } + + /// TrialQCQuestionAddOrEdit 列表查询参数模型 + public class TrialQCQuestionAddOrEdit:TrialQCQuestionConfigureBatchAdd + { + public Guid TrialId { get; set; } + + } + + public class TrialQCQuestionConfigureBatchAdd + { + + public Guid? Id { get; set; } + public string QuestionName { get; set; } = string.Empty; + public bool IsRequired { get; set; } + public bool IsEnable { get; set; } + public string Type { get; set; } = string.Empty; + public Guid? ParentId { get; set; } + public string TypeValue { get; set; } = string.Empty; + public string ParentTriggerValue { get; set; } = string.Empty; + public int ShowOrder { get; set; } + + + public Guid SystemQuestionId { get; set; } + } +} + + diff --git a/IRaCIS.Core.Application/Service/QC/Interface/IClinicalDataService.cs b/IRaCIS.Core.Application/Service/QC/Interface/IClinicalDataService.cs new file mode 100644 index 0000000..808940c --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/Interface/IClinicalDataService.cs @@ -0,0 +1,30 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-22 11:29:44 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IClinicalDataService + { + Task> AddOrUpdatePreviousHistory(PreviousHistoryAddOrEdit addOrEditPreviousHistory); + Task> AddOrUpdatePreviousOther(PreviousOtherAddOrEdit addOrEditPreviousOther); + Task AddOrUpdatePreviousPDF(PreviousPDFAddOrEdit addOrEditPreviousPDF); + Task> AddOrUpdatePreviousSurgery(PreviousSurgeryAddOrEdit addOrEditPreviousSurgery); + Task DeletePreviousHistory(Guid previousHistoryId, Guid subjectVisitId); + Task DeletePreviousOther(Guid previousOtherId, Guid subjectVisitId); + Task DeletePreviousPDF(Guid previousPDFId, Guid subjectVisitId); + Task DeletePreviousSurgery(Guid previousSurgeryId, Guid subjectVisitId); + Task> GetPreviousHistoryList(PreviousHistoryQuery queryPreviousHistory); + Task> GetPreviousOtherList(PreviousOtherQuery queryPreviousOther); + Task> GetPreviousPDFList(Guid subjectVisitId); + Task> GetPreviousSurgeryList(PreviousSurgeryQuery queryPreviousSurgery); + Task GetSubjectVisitClinicalData(Guid subjectVisitId); + //Task UploadVisitClinicalData(IFormCollection formCollection, Guid subjectVisitId, [FromServices] IWebHostEnvironment _hostEnvironment); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/QC/Interface/INoneDicomStudyService.cs b/IRaCIS.Core.Application/Service/QC/Interface/INoneDicomStudyService.cs new file mode 100644 index 0000000..a90a92c --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/Interface/INoneDicomStudyService.cs @@ -0,0 +1,26 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-06 10:54:55 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface INoneDicomStudyService + { + Task> AddOrUpdateNoneDicomStudy(NoneDicomStudyAddOrEdit addOrEditNoneDicomStudy); + Task DeleteNoneDicomStudy(Guid noneDicomStudyId, Guid subjectVisitId); + Task DeleteNoneDicomStudyFile(Guid noneDicomStudyFileId, Guid subjectVisitId); + Task> GetNoneDicomStudyFileList(Guid noneDicomStudyId); + Task> GetNoneDicomStudyList(Guid subjectVisitId, Guid? sujectVisitId = null); + Task> GetVisitNoneDicomStudyFileList(Guid subjectVisitId); + //Task UploadNoneDicomFile(IFormCollection formCollection, Guid subjectVisitId, Guid noneDicomStudyId); + + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/QC/Interface/IQCListService.cs b/IRaCIS.Core.Application/Service/QC/Interface/IQCListService.cs new file mode 100644 index 0000000..8c79249 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/Interface/IQCListService.cs @@ -0,0 +1,34 @@ +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Image.QA +{ + public interface IQCListService + { + Task GetCheckChallengeDialogList(Guid subjectVisitId); + //Task, TrialSubjectAndSVConfig>> GetConsistencyVerificationList(CheckQuery checkQuery); + //Task, TrialSubjectAndSVConfig>> GetCRCChallengeList(ChallengeQuery challengeQuery); + //Task, TrialSubjectAndSVConfig>> GetCRCVisitList(CRCVisitSearchDTO visitSearchDTO); + //Task, TrialSubjectAndSVConfig>> GetQCVisitList(QCVisitSearchDTO visitSearchDTO); + //Task, TrialSubjectAndSVConfig>> GetQCChallengeList(ChallengeQuery challengeQuery); + //Task>> GetForwardList(ForwardQuery forwardQuery); + + Task> GetCRCVisitChallengeAndDialog(Guid subjectVisitId, [FromRoute] TrialQCProcess trialQCProcess); + + Task> GetHistoryChallengeList(Guid subjectVisitId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType); + Task> GetQCChallengeCreatorList(Guid trialId); + Task> GetQCChallengeDialogList(Guid qaChallengeId); + + Task> GetQCParticipantList(Guid trialId); + Task> GetQCQuestionAnswerList(Guid subjectVisitId, Guid trialId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType); + + Task> GetSubjectVisitSelectList(Guid subjectId); + Task<(List, object)> GetSubjectVisitUploadedStudyList(Guid subjectVisitId); + Task GetUploadInitInfo(Guid subjectVisitId); + Task GetVisitQCInfo(Guid subjectVisitId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType); + Task GetVisitQCStudyAndSeriesList(Guid subjectVisitId); + Task GetVisitQCSubjectInfo(Guid subjectVisitId); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/QC/Interface/IQCOperationService.cs b/IRaCIS.Core.Application/Service/QC/Interface/IQCOperationService.cs new file mode 100644 index 0000000..3eeb445 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/Interface/IQCOperationService.cs @@ -0,0 +1,44 @@ +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Core.Application.Service.Inspection.DTO; +using IRaCIS.Core.Domain.Share; +using MediatR; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Image.QA +{ + public interface IQCOperationService + { + Task CheckBack(Guid subjectVisitId); + Task SetNeedReupload(Guid trialId, Guid qcChallengeId); + Task QCPassedOrFailed(Guid trialId, Guid subjectVisitId, [FromRoute] AuditStateEnum auditState); + Task SetCheckPass(SetCheckPassDt data); + + + + + Task AddCheckChallengeReply(CheckChallengeDialogCommand checkDialogCommand); + Task AddOrUpdateQCChallenge(QCChallengeCommand qaQuestionCommand, Guid trialId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType); + Task AddOrUpdateQCQuestionAnswerList(QCQuestionAnswerCommand[] qcQuestionAnswerCommands, Guid trialId, Guid subjectVisitId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType); + Task AddQCChallengeReply(QADialogCommand qaDialogCommand); + Task CloseCheckChallenge(CloseCheckChallengeDto input); + Task CloseQCChallenge(CloseQCChallengeInDto input); + Task CRCRequestReUpload(Guid qcChallengeId); + Task CRCRequestToQC(CRCRequestToQCCommand cRCRequestToQCCommand); + Task CRCRequstCheckBack(Guid subjectVisitId); + Task DeleteQCChallenge(Guid qcChallengeId); + Task DeleteStudyList(Guid[] ids, Guid subjectVisitId, Guid trialId); + Task ObtainOrCancelQCTask(Guid trialId, Guid subjectVisitId, bool obtaionOrCancel); + Task SetReuploadFinished(CRCReuploadFinishedCommand cRCReuploadFinishedCommand); + Task SetSeriesState(Guid subjectVisitId, Guid studyId, Guid seriesId, int state); + Task SetVisitUrgent(Guid trialId, Guid subjectVisitId, bool setOrCancel); + Task UpdateModality(UpdateModalityCommand updateModalityCommand); + Task UpdateSubjectAndSVInfo(UploadSubjectAndVisitCommand command); + //Task UploadVisitCheckExcel(IFormFile file, Guid trialId); + Task VerifyCanQCPassedOrFailed(Guid subjectVisitId); + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/QC/Interface/IQCQuestionService.cs b/IRaCIS.Core.Application/Service/QC/Interface/IQCQuestionService.cs new file mode 100644 index 0000000..21a102b --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/Interface/IQCQuestionService.cs @@ -0,0 +1,18 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-11 11:04:54 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IQCQuestionService + { + Task AddOrUpdateQCQuestionConfigure(QCQuestionAddOrEdit addOrEditQCQuestionConfigure); + Task DeleteQCQuestionConfigure(Guid qCQuestionConfigureId); + Task> GetQCQuestionConfigureList(QCQuestionQuery queryQCQuestionConfigure); + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/QC/Interface/ITrialQCQuestionConfigureService.cs b/IRaCIS.Core.Application/Service/QC/Interface/ITrialQCQuestionConfigureService.cs new file mode 100644 index 0000000..47e64f6 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/Interface/ITrialQCQuestionConfigureService.cs @@ -0,0 +1,17 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-11 11:04:54 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface ITrialQCQuestionConfigureService + { + Task AddOrUpdateTrialQCQuestionConfigure(TrialQCQuestionAddOrEdit addOrEditTrialQCQuestionConfigure); + Task BatchAddTrialQCQuestionConfigure(List batchList, Guid trialId); + Task DeleteTrialQCQuestionConfigure(Guid trialQCQuestionConfigureId, Guid trialId); + //Task> GetTrialQCQuestionConfigureList(TrialQCQuestionQuery queryTrialQCQuestionConfigure); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/QC/NoneDicomStudyService.cs b/IRaCIS.Core.Application/Service/QC/NoneDicomStudyService.cs new file mode 100644 index 0000000..c461842 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/NoneDicomStudyService.cs @@ -0,0 +1,158 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-06 10:54:55 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Filter; +using Nito.AsyncEx; +using System.ComponentModel.DataAnnotations; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// NoneDicomStudyService + /// + [ApiExplorerSettings(GroupName = "Image")] + public class NoneDicomStudyService : BaseService, INoneDicomStudyService + { + private readonly IRepository _noneDicomStudyRepository; + private readonly IRepository _noneDicomStudyFileRepository; + private readonly AsyncLock _mutex = new AsyncLock(); + + + public NoneDicomStudyService(IRepository noneDicomStudyRepository, + + IRepository noneDicomStudyFileRepository) + { + _noneDicomStudyRepository = noneDicomStudyRepository; + + _noneDicomStudyFileRepository = noneDicomStudyFileRepository; + + } + + + [HttpGet] + public async Task> GetNoneDicomStudyList([FromQuery, NotDefault] Guid subjectVisitId, Guid? nonedicomStudyId) + { + + var noneDicomStudyQueryable = _noneDicomStudyRepository.Where(t => t.SubjectVisitId == subjectVisitId).WhereIf(nonedicomStudyId != null, t => t.Id == nonedicomStudyId) + + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken }); + + return await noneDicomStudyQueryable.ToListAsync(); + } + + + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task> AddOrUpdateNoneDicomStudy(NoneDicomStudyAddOrEdit addOrEditNoneDicomStudy) + { + + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, addOrEditNoneDicomStudy.SubjectVisitId); + + if (_repository.Where(t => t.Id == addOrEditNoneDicomStudy.TrialId).Any(t => t.IsVerifyVisitImageDate==true)) + { + await QCCommon.VerifyStudyImageDataAsync(_repository, addOrEditNoneDicomStudy.SubjectId, addOrEditNoneDicomStudy.SubjectVisitId, addOrEditNoneDicomStudy.ImageDate); + + } + + NoneDicomStudy? optEntity = null; + using (await _mutex.LockAsync()) + { + if (addOrEditNoneDicomStudy.Id == Guid.Empty || addOrEditNoneDicomStudy.Id == null) + { + //默认会是0 + var code = await _noneDicomStudyRepository.Where(t => t.TrialId == addOrEditNoneDicomStudy.TrialId).Select(x => x.Code).DefaultIfEmpty().MaxAsync(); + + addOrEditNoneDicomStudy.Code = code + 1; + + optEntity = await _noneDicomStudyRepository.InsertFromDTOAsync(addOrEditNoneDicomStudy); + + optEntity.StudyCode = AppSettings.GetCodeStr(optEntity.Code, nameof(NoneDicomStudy)); + } + else + { + + optEntity = await _noneDicomStudyRepository.UpdateFromDTOAsync(addOrEditNoneDicomStudy); + } + + await _noneDicomStudyRepository.SaveChangesAsync(); + } + + + NoneDicomStudyAddReturnDto noneDicom = new NoneDicomStudyAddReturnDto() + { + StudyCode = optEntity.StudyCode, + Id = optEntity.Id + }; + return ResponseOutput.Ok(noneDicom); + + } + + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + [HttpDelete("{trialId:guid}/{subjectVisitId:guid}/{noneDicomStudyId:guid}")] + public async Task DeleteNoneDicomStudy(Guid noneDicomStudyId, Guid subjectVisitId) + { + //提交了 但是IQC同意的时候 是可以删除的 | 普通提交后也不能删除 + + + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); + + await _noneDicomStudyRepository.DeleteFromQueryAsync(noneDicomStudyId); + + await _noneDicomStudyFileRepository.DeleteFromQueryAsync(t => t.NoneDicomStudyId == noneDicomStudyId); + + //确认需求 不删除 + //await _studyMonitorRepository.BatchDeleteNoTrackingAsync(t => t.StudyId == noneDicomStudyId); + + await _noneDicomStudyRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + [HttpDelete("{trialId:guid}/{subjectVisitId:guid}/{noneDicomStudyFileId:guid}")] + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task DeleteNoneDicomStudyFile(Guid noneDicomStudyFileId, Guid subjectVisitId) + { + //提交了 但是IQC同意的时候 是可以删除的 | 普通提交后也不能删除 + await QCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); + + var noneDicomStudyFile = await _noneDicomStudyFileRepository.FirstOrDefaultAsync(t => t.Id == noneDicomStudyFileId); + + var success = await _noneDicomStudyFileRepository.DeleteAsync(noneDicomStudyFile, true); + + //维护文件数量数字 + var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync(t => t.Id == noneDicomStudyFile.NoneDicomStudyId); + noneDicomStudy.FileCount = await _noneDicomStudyFileRepository.CountAsync(t => t.NoneDicomStudyId == noneDicomStudyFile.NoneDicomStudyId); + await _noneDicomStudyRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + /// + /// 非Dicom检查 文件列表 + /// + /// + /// + [HttpGet("{noneDicomStudyId:guid}")] + public async Task> GetNoneDicomStudyFileList(Guid noneDicomStudyId) + { + return await _noneDicomStudyFileRepository.Where(t => t.NoneDicomStudyId == noneDicomStudyId) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken }).ToListAsync(); + } + + [HttpGet("{subjectVisitId:guid}")] + public async Task> GetVisitNoneDicomStudyFileList(Guid subjectVisitId) + { + return await _repository.Where(t => t.NoneDicomStudy.SubjectVisitId == subjectVisitId) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken }).ToListAsync(); + } + + + + } +} diff --git a/IRaCIS.Core.Application/Service/QC/QCCommon.cs b/IRaCIS.Core.Application/Service/QC/QCCommon.cs new file mode 100644 index 0000000..33be040 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/QCCommon.cs @@ -0,0 +1,268 @@ +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using System.Linq.Expressions; + +namespace IRaCIS.Core.Application.Service +{ + public static class QCCommon + { + /// + /// 验证IC 是否已提交 已提交 就不允许进行任何操作,如果是IQC 那么还验证是否是当前任务领取人 + /// + /// + /// + /// + /// + /// + public static async Task VerifyIsCRCSubmmitAsync(IRepository _repository, IUserInfo _userInfo, Guid? subjectVisitId = null) + { + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator) + { + //添加的时候不验证 + if (subjectVisitId != null) + { + if (await _repository.AnyAsync(t => t.Id == subjectVisitId && t.SubmitState == SubmitStateEnum.Submitted && + (!t.QCChallengeList.Any(u => u.ReuploadEnum == QCChanllengeReuploadEnum.QCAgreeUpload)))) + { + throw new BusinessValidationFailedException("IC 已提交影像,不能进行操作。"); + } + } + } + + + //IQC 的时候 验证是不是当前领取人 + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IQC) + { + + await VerifyIsCanQCAsync(_repository, _userInfo, null, subjectVisitId); + + } + + } + + public static async Task VerifyIsCanQCAsync(IRepository _repository, IUserInfo _userInfo, SubjectVisit? subjectVisit = null, Guid? subjectVisitId = null) + { + if (subjectVisitId != null) + { + subjectVisit = (await _repository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); + } + + if (subjectVisit!.CurrentActionUserId != _userInfo.Id) + { + throw new BusinessValidationFailedException("您不是该质控任务当前领取人,没有操作权限!"); + } + } + + + + public static async Task VerifyStudyImageDataAsync(IRepository _repository, Guid subjectId, Guid subjectVisitId, DateTime imageDate) + { + var visitList = await _repository.Where(t => t.SubjectId == subjectId).Select(t => new { t.VisitNum, t.EarliestScanDate, t.LatestScanDate, t.Id }).ToListAsync(); + + var currentVisitNum = await _repository.Where(t => t.Id == subjectVisitId).Select(t => t.VisitNum).FirstOrDefaultAsync(); + + + + //小于当前检查批次 最近的最晚拍片 + var before = visitList.Where(u => u.VisitNum < currentVisitNum).Max(k => k.LatestScanDate); + + if (before != null && before > imageDate) + { + throw new BusinessValidationFailedException($"当前检查批次检查时间{imageDate.ToString("yyyy-MM-dd")}不能早于前序检查批次检查时间{before?.ToString("yyyy-MM-dd")},请核对检查数据是否有误"); + } + + + //大于当前检查批次 最近的最早拍片日期 + var after = visitList.Where(u => u.VisitNum > currentVisitNum).Min(k => k.EarliestScanDate); + + if (after != null && after < imageDate) + { + throw new BusinessValidationFailedException($"当前检查批次检查时间{imageDate.ToString("yyyy-MM-dd")}不能晚于该检查批次之后的检查时间{after?.ToString("yyyy-MM-dd")},请核对检查数据是否有误"); + + } + } + + + + #region 处理方式多选过滤条件 + + public static Expression> GetSubjectVisitFilter(string[]? VisitPlanArray) + { + Expression> svExpression = x => true; + + bool isNeedVisitSearch = VisitPlanArray != null && VisitPlanArray?.Length > 0; + + if (isNeedVisitSearch) + { + var inPlanArray = VisitPlanArray!.Where(t => !t.Contains('.')).Select(t => decimal.Parse(t)).ToArray(); + var isSelectOutPlan = VisitPlanArray!.Any(t => t.Contains('.')); + + + if (inPlanArray.Length > 0) + { + svExpression = svExpression.And(t => inPlanArray.Contains(t.VisitNum)); + + if (isSelectOutPlan) + { + svExpression = svExpression.Or(t => t.InPlan == false); + + } + } + else if (isSelectOutPlan) + { + svExpression = t => t.InPlan == false; + } + + + + + } + + return svExpression; + } + + + public static Expression> GetQCChallengeFilter(string[]? VisitPlanArray) + { + Expression> svExpression = x => true; + + bool isNeedVisitSearch = VisitPlanArray != null && VisitPlanArray?.Length > 0; + + if (isNeedVisitSearch) + { + var inPlanArray = VisitPlanArray!.Where(t => !t.Contains('.')).Select(t => decimal.Parse(t)).ToArray(); + var isSelectOutPlan = VisitPlanArray!.Any(t => t.Contains('.')); + + + if (inPlanArray.Length > 0) + { + svExpression = svExpression.And(t => inPlanArray.Contains(t.SubjectVisit.VisitNum)); + + if (isSelectOutPlan) + { + svExpression = svExpression.Or(t => t.SubjectVisit.InPlan == false); + + } + } + else if (isSelectOutPlan) + { + svExpression = t => t.SubjectVisit.InPlan == false; + } + + + + } + + return svExpression; + } + + + public static Expression> GetDicomStudySubjectVisitFilter(string[]? VisitPlanArray) + { + Expression> svExpression = x => true; + + bool isNeedVisitSearch = VisitPlanArray != null && VisitPlanArray?.Length > 0; + + if (isNeedVisitSearch) + { + var inPlanArray = VisitPlanArray!.Where(t => !t.Contains('.')).Select(t => decimal.Parse(t)).ToArray(); + var isSelectOutPlan = VisitPlanArray!.Any(t => t.Contains('.')); + + + if (inPlanArray.Length > 0) + { + svExpression = svExpression.And(t => inPlanArray.Contains(t.SubjectVisit.VisitNum)); + + if (isSelectOutPlan) + { + svExpression = svExpression.Or(t => t.SubjectVisit.InPlan == false); + + } + } + else if (isSelectOutPlan) + { + svExpression = t => t.SubjectVisit.InPlan == false; + } + + + + + } + + return svExpression; + } + + + public static Expression> GetNoneDicomStudySubjectVisitFilter(string[]? VisitPlanArray) + { + Expression> svExpression = x => true; + + bool isNeedVisitSearch = VisitPlanArray != null && VisitPlanArray?.Length > 0; + + if (isNeedVisitSearch) + { + var inPlanArray = VisitPlanArray!.Where(t => !t.Contains('.')).Select(t => decimal.Parse(t)).ToArray(); + var isSelectOutPlan = VisitPlanArray!.Any(t => t.Contains('.')); + + + if (inPlanArray.Length > 0) + { + svExpression = svExpression.And(t => inPlanArray.Contains(t.SubjectVisit.VisitNum)); + + if (isSelectOutPlan) + { + svExpression = svExpression.Or(t => t.SubjectVisit.InPlan == false); + + } + } + else if (isSelectOutPlan) + { + svExpression = t => t.SubjectVisit.InPlan == false; + } + + + + + } + + return svExpression; + } + + public static Expression> GetStudyMonitorSubjectVisitFilter(string[]? VisitPlanArray) + { + Expression> svExpression = x => true; + + bool isNeedVisitSearch = VisitPlanArray != null && VisitPlanArray?.Length > 0; + + if (isNeedVisitSearch) + { + var inPlanArray = VisitPlanArray!.Where(t => !t.Contains('.')).Select(t => decimal.Parse(t)).ToArray(); + var isSelectOutPlan = VisitPlanArray!.Any(t => t.Contains('.')); + + + if (inPlanArray.Length > 0) + { + svExpression = svExpression.And(t => inPlanArray.Contains(t.SubjectVisit.VisitNum)); + + if (isSelectOutPlan) + { + svExpression = svExpression.Or(t => t.SubjectVisit.InPlan == false); + + } + } + + else if (isSelectOutPlan) + { + svExpression = t => t.SubjectVisit.InPlan == false; + } + + + + } + + return svExpression; + } + + #endregion + } +} diff --git a/IRaCIS.Core.Application/Service/QC/QCListService.cs b/IRaCIS.Core.Application/Service/QC/QCListService.cs new file mode 100644 index 0000000..e664985 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/QCListService.cs @@ -0,0 +1,885 @@ +using IRaCIS.Core.Application.Contracts; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Core.Application.Service; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Application.Service.Reading.Dto; + +namespace IRaCIS.Core.Application.Image.QA +{ + [ApiExplorerSettings(GroupName = "Image")] + public class QCListService : BaseService, IQCListService + { + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _clinicalDataTrialSet; + private readonly IRepository _trialQCQuestionAnswerRepository; + private readonly IRepository _trialQCQuestionRepository; + private readonly IRepository _consistencyCheckFileRepository; + + public QCListService( + IRepository subjectVisitRepository, + IRepository trialRepository, + IRepository clinicalDataTrialSet, + IRepository trialQCQuestionAnswerRepository, + IRepository trialQCQuestionRepository, + IRepository consistencyCheckFileRepository + ) + { + _subjectVisitRepository = subjectVisitRepository; + this._trialQCQuestionAnswerRepository = trialQCQuestionAnswerRepository; + this._trialQCQuestionRepository = trialQCQuestionRepository; + this._consistencyCheckFileRepository = consistencyCheckFileRepository; + _trialRepository = trialRepository; + this._clinicalDataTrialSet = clinicalDataTrialSet; + } + + + + + + #region IC上传、质疑页面 + /// + /// IC 检查批次上传列表 + /// + /// + /// + [HttpPost] + public async Task<(PageOutput, TrialSubjectAndSVConfig)> GetCRCVisitList(CRCVisitSearchDTO visitSearchDTO) + { + var svExpression = QCCommon.GetSubjectVisitFilter(visitSearchDTO.VisitPlanArray); + + var query = _subjectVisitRepository.Where(x => x.TrialId == visitSearchDTO.TrialId) + .Where(t => t.Subject.FinalSubjectVisitId != null ? t.VisitNum <= t.Subject.FinalSubjectVisit.VisitNum : true) + .WhereIf(visitSearchDTO.SiteId != null, t => t.SiteId == visitSearchDTO.SiteId) + .WhereIf(visitSearchDTO.SubjectId != null, t => t.Subject.Id == visitSearchDTO.SubjectId) + .WhereIf(!string.IsNullOrEmpty(visitSearchDTO.SubjectInfo), t => t.Subject.Code.Contains(visitSearchDTO.SubjectInfo)) + + .WhereIf(visitSearchDTO.VisitPlanArray != null && visitSearchDTO.VisitPlanArray?.Length > 0, svExpression) + //.WhereIf(!string.IsNullOrEmpty(visitSearchDTO.VisitPlanInfo), visitSearchDTO.VisitPlanInfo.Contains('.') ? t => t.InPlan == false : t => t.VisitNum == decimal.Parse(visitSearchDTO.VisitPlanInfo)) + .WhereIf(visitSearchDTO.AuditStateArray != null && visitSearchDTO.AuditStateArray?.Length > 0, t => visitSearchDTO.AuditStateArray!.Contains(t.AuditState)) + .WhereIf(visitSearchDTO.SubmitState != null, t => t.SubmitState == visitSearchDTO.SubmitState) + .WhereIf(visitSearchDTO.BeginSubmitTime != null, t => t.SubmitTime >= visitSearchDTO.BeginSubmitTime) + .WhereIf(visitSearchDTO.EndSubmitTime != null, t => t.SubmitTime <= visitSearchDTO.EndSubmitTime) + .WhereIf(visitSearchDTO.ChallengeState != null, t => t.ChallengeState == visitSearchDTO.ChallengeState) + .WhereIf(visitSearchDTO.IsUrgent != null, t => t.IsUrgent == visitSearchDTO.IsUrgent) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + .ProjectTo(_mapper.ConfigurationProvider); + + + var defalutSortArray = new string[] { nameof(SubjectVisit.IsUrgent) + " desc", nameof(SubjectVisit.SubjectId), nameof(SubjectVisit.VisitNum) }; + var pageList = await query.ToPagedListAsync(visitSearchDTO.PageIndex, visitSearchDTO.PageSize, visitSearchDTO.SortField, visitSearchDTO.Asc, string.IsNullOrWhiteSpace(visitSearchDTO.SortField), defalutSortArray); + + var config = await _repository.Where(t => t.Id == visitSearchDTO.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + config.IsHaveSubjectClinicalData = await _clinicalDataTrialSet.AnyAsync(x => x.TrialId == visitSearchDTO.TrialId && x.IsConfirm && x.ClinicalDataLevel == ClinicalLevel.Subject && x.UploadRole == UploadRole.CRC); + config.IsHaveVisitClinicalData = await _clinicalDataTrialSet.AnyAsync(x => x.TrialId == visitSearchDTO.TrialId && x.IsConfirm && x.ClinicalDataLevel == ClinicalLevel.SubjectVisit && x.UploadRole == UploadRole.CRC); + return (pageList, config); + + } + + + + /// + /// IC 质疑列表 + /// + /// + /// + [HttpPost] + public async Task<(PageOutput, TrialSubjectAndSVConfig)> GetCRCChallengeList(ChallengeQuery challengeQuery) + { + + #region IC 质疑列表 join连表方式 + //var query = from qcChanllenge in _qcChallengeRepository.Find(qcChallengeLambda) + // join subjectVisit in _subjectVisitRepository.Find() on qcChanllenge.SubjectVisitId equals subjectVisit.Id + // join trialSite in _trialSiteRepository.Find(t => t.TrialId == challengeQuery.TrialId) on subjectVisit.SiteId equals trialSite.SiteId + // join subject in _subjectRepository.Find() on subjectVisit.SubjectId equals subject.Id + // select new QCCRCChallengeViewModel() + // { + // Id = qcChanllenge.Id, + // SubjectVisitId = qcChanllenge.SubjectVisitId, + // ChallengeCode = qcChanllenge.ChallengeCode, + // ChallengeType = qcChanllenge.ChallengeType, + // Note = qcChanllenge.Note, + // Content = qcChanllenge.Content, + // UserTypeEnum = qcChanllenge.UserTypeEnum, + // CreateTime = qcChanllenge.CreateTime, + // CreateUser = qcChanllenge.CreateUser, + // CreateUserId = qcChanllenge.CreateUserId, + + // DeadlineTime = qcChanllenge.DeadlineTime, + // IsClosed = qcChanllenge.IsClosed, + // ClosedTime = qcChanllenge.ClosedTime, + // ClosedUser = qcChanllenge.ClosedUser, + // NeedReUpload = qcChanllenge.NeedReUpload, + + // VisitName = subjectVisit.VisitName, + // VisitNum = subjectVisit.VisitNum, + // SubjectCode = subject.Code, + // TrialSiteCode = trialSite.TrialSiteCode, + + // LatestMsgTime = qcChanllenge.LatestMsgTime, + // LatestReplyUser = qcChanllenge.LatestReplyUser, + // }; + #endregion + + var svExpression = QCCommon.GetQCChallengeFilter(challengeQuery.VisitPlanArray); + + var query2 = _repository.Where(x => x.TrialId == challengeQuery.TrialId) + //.WhereIf(challengeQuery.ChallengeState != null, t => t.SubjectVisit.ChallengeState == challengeQuery.ChallengeState) + .WhereIf(challengeQuery.ReuploadEnum != null, t => t.ReuploadEnum == challengeQuery.ReuploadEnum) + .WhereIf(challengeQuery.IsClosed != null, t => t.IsClosed == challengeQuery.IsClosed) + .WhereIf(challengeQuery.SiteId != null, t => t.SubjectVisit.SiteId == challengeQuery.SiteId) + .WhereIf(challengeQuery.SubjectId != null, t => t.SubjectVisit.SubjectId == challengeQuery.SubjectId) + .WhereIf(challengeQuery.CreateUserId != null, t => t.CreateUserId == challengeQuery.CreateUserId) + .WhereIf(challengeQuery.SubjectCode != null, t => t.SubjectVisit.Subject.Code.Contains(challengeQuery.SubjectCode!)) + .WhereIf(challengeQuery.VisitPlanArray != null && challengeQuery.VisitPlanArray?.Length > 0, svExpression) + //.WhereIf(!string.IsNullOrEmpty(challengeQuery.VisitPlanInfo), challengeQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(challengeQuery.VisitPlanInfo)) + .WhereIf(challengeQuery.IsOverTime != null && challengeQuery.IsOverTime == true, t => t.IsClosed ? t.ClosedTime > t.DeadlineTime : DateTime.Now > t.DeadlineTime) + .WhereIf(challengeQuery.IsOverTime != null && challengeQuery.IsOverTime == false, t => t.IsClosed ? t.ClosedTime < t.DeadlineTime : DateTime.Now < t.DeadlineTime) + .WhereIf(challengeQuery.IsUrgent != null, t => t.SubjectVisit.IsUrgent == challengeQuery.IsUrgent) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.SubjectVisit.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + .ProjectTo(_mapper.ConfigurationProvider); + + var pageList = await query2.ToPagedListAsync(challengeQuery.PageIndex, challengeQuery.PageSize, challengeQuery.SortField, challengeQuery.Asc, string.IsNullOrWhiteSpace(challengeQuery.SortField), new string[] { "IsUrgent desc", "IsClosed asc" }); + + var config = await _repository.Where(t => t.Id == challengeQuery.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + return (pageList, config); + + } + + #endregion + + + #region QC 质疑 质控页面 + + /// + /// QC 质疑列表 分页 + /// + /// + /// + [HttpPost] + public async Task<(PageOutput, TrialSubjectAndSVConfig)> GetQCChallengeList(ChallengeQuery challengeQuery) + { + #region linq方式 + Expression 拼接 + + //Expression> qcChallengeLambda = x => x.TrialId == challengeQuery.TrialId; + + //if (challengeQuery.ReuploadEnum != null) + //{ + // qcChallengeLambda = qcChallengeLambda.And(t => t.ReuploadEnum == challengeQuery.ReuploadEnum); + //} + //if (challengeQuery.IsClosed != null) + //{ + // qcChallengeLambda = qcChallengeLambda.And(t => t.IsClosed == challengeQuery.IsClosed); + //} + //if (challengeQuery.SiteId != null) + //{ + // qcChallengeLambda = qcChallengeLambda.And(t => t.SubjectVisit.SiteId == challengeQuery.SiteId.Value); + //} + //if (challengeQuery.SubjectId != null) + //{ + // qcChallengeLambda = qcChallengeLambda.And(t => t.SubjectVisit.Subject.Id == challengeQuery.SubjectId.Value); + //} + + //var query = from qcChanllenge in _qcChallengeRepository.Find(qcChallengeLambda) + // join subjectVisit in _subjectVisitRepository.Find(subjectVisitLambda) on qcChanllenge.SubjectVisitId equals subjectVisit.Id + // join trialSite in _trialSiteRepository.Find(t => t.TrialId == challengeQuery.TrialId) on subjectVisit.SiteId equals trialSite.SiteId + // join subject in _subjectRepository.Find() on subjectVisit.SubjectId equals subject.Id + // select new QCChallengeViewModel() + // { + // Id = qcChanllenge.Id, + // SubjectVisitId = qcChanllenge.SubjectVisitId, + // ChallengeCode = qcChanllenge.ChallengeCode.ToString(), + // ChallengeType = qcChanllenge.ChallengeType, + // Note = qcChanllenge.Note, + // UserTypeEnum = qcChanllenge.UserTypeEnum, + // Content = qcChanllenge.Content, + // CreateTime = qcChanllenge.CreateTime, + // CreateUserId = qcChanllenge.CreateUserId, + // CreateUser = qcChanllenge.CreateUser, + // SubjectCode = subject.Code, + // TrialSiteCode = trialSite.TrialSiteCode, + // DeadlineTime = qcChanllenge.DeadlineTime, + // IsClosed = qcChanllenge.IsClosed, + // ClosedTime = qcChanllenge.ClosedTime, + // ClosedUser = qcChanllenge.ClosedUser, + // NeedReUpload = qcChanllenge.NeedReUpload, + // VisitName = subjectVisit.VisitName, + // VisitNum = subjectVisit.VisitNum, + // LatestMsgTime = qcChanllenge.LatestMsgTime, + // LatestReplyUser = qcChanllenge.LatestReplyUser, + + // }; + #endregion + + var svExpression = QCCommon.GetQCChallengeFilter(challengeQuery.VisitPlanArray); + + var query = _repository.Where(x => x.TrialId == challengeQuery.TrialId) + //.WhereIf(challengeQuery.ChallengeState != null, t => t.SubjectVisit.ChallengeState == challengeQuery.ChallengeState) + .WhereIf(challengeQuery.ReuploadEnum != null, t => t.ReuploadEnum == challengeQuery.ReuploadEnum) + .WhereIf(challengeQuery.IsClosed != null, t => t.IsClosed == challengeQuery.IsClosed) + .WhereIf(challengeQuery.SiteId != null, t => t.SubjectVisit.SiteId == challengeQuery.SiteId) + .WhereIf(challengeQuery.SubjectId != null, t => t.SubjectVisit.SubjectId == challengeQuery.SubjectId) + .WhereIf(challengeQuery.CreateUserId != null, t => t.CreateUserId == challengeQuery.CreateUserId) + .WhereIf(!string.IsNullOrEmpty(challengeQuery.SubjectCode), t => t.SubjectVisit.Subject.Code.Contains(challengeQuery.SubjectCode)) + .WhereIf(challengeQuery.VisitPlanArray != null && challengeQuery.VisitPlanArray?.Length > 0, svExpression) + //.WhereIf(!string.IsNullOrEmpty(challengeQuery.VisitPlanInfo), challengeQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.InPlan == false : t => t.SubjectVisit.VisitNum == decimal.Parse(challengeQuery.VisitPlanInfo)) + .WhereIf(challengeQuery.IsUrgent != null, t => t.SubjectVisit.IsUrgent == challengeQuery.IsUrgent) + .WhereIf(challengeQuery.IsOverTime != null && challengeQuery.IsOverTime == true, t => t.IsClosed ? t.ClosedTime > t.DeadlineTime : DateTime.Now > t.DeadlineTime) + .WhereIf(challengeQuery.IsOverTime != null && challengeQuery.IsOverTime == false, t => t.IsClosed ? t.ClosedTime < t.DeadlineTime : DateTime.Now < t.DeadlineTime) + .ProjectTo(_mapper.ConfigurationProvider); + + var pageList = await query.ToPagedListAsync(challengeQuery.PageIndex, challengeQuery.PageSize, challengeQuery.SortField, challengeQuery.Asc, string.IsNullOrWhiteSpace(challengeQuery.SortField), new string[] { "IsUrgent desc", "IsClosed asc" }); + + var config = await _repository.Where(t => t.Id == challengeQuery.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + return (pageList, config); + } + + + /// + /// 获取下一个Qc信息 + /// + /// + /// + [HttpPost] + public async Task GetNextQCInfo(GetNextQCInfoInDto inDto) + { + var result= await GetQCVisitList(new QCVisitSearchDTO() + { + + TrialId = inDto.TrialId, + CurrentActionUserId=_userInfo.Id, + PageIndex=1, + PageSize=1, + }); + + return result.Item1.CurrentPageData.Count > 0 ? result.Item1.CurrentPageData[0] : null; + } + + /// + /// QC 检查批次列表 + /// + /// + /// + [HttpPost] + public async Task<(PageOutput, TrialSubjectAndSVConfig)> GetQCVisitList(QCVisitSearchDTO visitSearchDTO) + { + + #region 经典 linq + //Expression> subjectLambda = x => true; + + //Expression> subjectVisitLambda = x => x.TrialId == visitSearchDTO.TrialId; + //Expression> studyLambda = x => x.TrialId == visitSearchDTO.TrialId; + ////Expression> visitPlanLambda = x => x.TrialId == subjectVisitSearch.TrialId; + + //if (visitSearchDTO.SiteId != null && visitSearchDTO.SiteId != Guid.Empty) + //{ + // subjectVisitLambda = subjectVisitLambda.And(t => t.SiteId == visitSearchDTO.SiteId.Value); + // studyLambda = studyLambda.And(t => t.SiteId == visitSearchDTO.SiteId.Value); + //} + + //if (visitSearchDTO.SubjectId != null && visitSearchDTO.SubjectId != Guid.Empty) + //{ + // subjectLambda = subjectLambda.And(t => t.Id == visitSearchDTO.SubjectId.Value); + + // studyLambda = studyLambda.And(t => t.SubjectId == visitSearchDTO.SubjectId.Value); + //} + + //if (!string.IsNullOrEmpty(visitSearchDTO.SubjectInfo)) + //{ + // var search = visitSearchDTO.SubjectInfo.Trim(); + // subjectLambda = subjectLambda.And(t => t.FirstName.Contains(search) || t.LastName.Contains(search) || t.Code.Contains(search)); + //} + + //if (!string.IsNullOrWhiteSpace(visitSearchDTO.VisitPlanInfo)) + //{ + // var visitInfo = visitSearchDTO.VisitPlanInfo.Trim(); + + // if (visitInfo.Contains('.')) // 包含小数点的是计划外检查批次 + // { + // subjectVisitLambda = subjectVisitLambda.And(t => t.VisitNum.ToString().Contains(".")); + // } + // else + // { + // if (int.TryParse(visitInfo, out var visitNum)) + // { + // subjectVisitLambda = subjectVisitLambda.And(t => t.VisitNum == visitNum); + // } + // } + //} + + //var query = from subjectVisit in _subjectVisitRepository.Where(subjectVisitLambda) + // join subject in _subjectRepository.Where(subjectLambda) on subjectVisit.SubjectId equals subject.Id + // join trialSite in _trialSiteRepository.Where(t => t.TrialId == visitSearchDTO.TrialId) on subjectVisit.SiteId equals trialSite.SiteId + // join study in _studyRepository.Where(studyLambda).GroupBy(t => t.SubjectVisitId).Select(g => new { SubjectVisitId = g.Key, StudyCount = g.Count() }) on subjectVisit.Id equals study.SubjectVisitId into d + // from study in d.DefaultIfEmpty() + // join trial in _trialRepository.AsQueryable() on subjectVisit.TrialId equals trial.Id + + // select new QCVisitViewModel() + // { + // Id = subjectVisit.Id, + // //VisitPlanId = visitStage.Id, + // SubjectId = subjectVisit.SubjectId, + // TrialId = subjectVisit.TrialId, + // SiteId = subjectVisit.SiteId, + + // VisitDay = subjectVisit.VisitDay, + // VisitNum = subjectVisit.VisitNum, + // VisitName = subjectVisit.VisitName, + // SVENDTC = subjectVisit.SVENDTC, + // SVSTDTC = subjectVisit.SVSTDTC, + // SVUPDES = subjectVisit.SVUPDES, + // InPlan = subjectVisit.InPlan, + // VisitExecuted = subjectVisit.VisitExecuted, + + + // StudyCount = study.StudyCount, + // SubjectStatus = subject.Status, + + // SubjectCode = subject.Code, + + // IsUrgent = subjectVisit.IsUrgent, + + // TrialSiteCode = trialSite.TrialSiteCode, + + // EarliestScanDate = subjectVisit.EarliestScanDate, + // LatestScanDate = subjectVisit.LatestScanDate, + + // QCProcessEnum = trial.QCProcessEnum, + // CurrentActionUserId = subjectVisit.CurrentActionUserId, + // CurrentActionUser = subjectVisit.CurrentActionUser, + // CurrentActionUserExpireTime = subjectVisit.CurrentActionUserExpireTime, + // PreliminaryAuditTime = subjectVisit.PreliminaryAuditTime, + // PreliminaryAuitUser = subjectVisit.PreliminaryAuitUser, + // ReviewAuitTime = subjectVisit.ReviewAuitTime, + // ReviewAuitUser = subjectVisit.ReviewAuitUser, + + // AuditState = subjectVisit.AuditState, + // SubmitTime = subjectVisit.SubmitTime, + // IsTake = subjectVisit.IsTake, + + // }; + #endregion + + #region WhereIf + ling 统计 + //var query = from subjectVisit in _subjectVisitRepository.Where(x => x.TrialId == visitSearchDTO.TrialId) + // .WhereIf(visitSearchDTO.SiteId != null, t => t.SiteId == visitSearchDTO.SiteId) + // .WhereIf(visitSearchDTO.SubjectId != null, t => t.Subject.Id == visitSearchDTO.SubjectId) + // .WhereIf(!string.IsNullOrEmpty(subjectInfo), t => /*t.Subject.FirstName.Contains(subjectInfo) || t.Subject.LastName.Contains(subjectInfo) ||*/ t.Subject.Code.Contains(subjectInfo)) + // .WhereIf(!string.IsNullOrEmpty(visitInfo), visitInfo.Contains('.') ? t => t.VisitNum.ToString().Contains(".") : t => t.VisitNum.ToString() == visitInfo) + // .WhereIf(visitSearchDTO.AuditState != null, t => t.AuditState == visitSearchDTO.AuditState) + // .WhereIf(visitSearchDTO.HandleUser != null, t => t.PreliminaryAuitUser.Contains(visitSearchDTO.HandleUser) || t.ReviewAuitUser.Contains(visitSearchDTO.HandleUser)) + // .WhereIf(visitSearchDTO.IsUrgent != null, t => t.IsUrgent == visitSearchDTO.IsUrgent) + // //.WhereIf(visitSearchDTO.SubmitState != null, t => t.SubmitState == visitSearchDTO.SubmitState) + // //.WhereIf(visitSearchDTO.ChallengeState != null, t => t.ChallengeState == visitSearchDTO.ChallengeState) + // join subject in _subjectRepository.AsQueryable() on subjectVisit.SubjectId equals subject.Id + // join trialSite in _trialSiteRepository.Where(t => t.TrialId == visitSearchDTO.TrialId) on subjectVisit.SiteId equals trialSite.SiteId + // join study in _studyRepository.AsQueryable().GroupBy(t => t.SubjectVisitId).Select(g => new { SubjectVisitId = g.Key, StudyCount = g.Count() }) on subjectVisit.Id equals study.SubjectVisitId into d + // from study in d.DefaultIfEmpty() + // join trial in _trialRepository.AsQueryable() on subjectVisit.TrialId equals trial.Id + + // select new QCVisitViewModel() + // { + // Id = subjectVisit.Id, + // //VisitPlanId = visitStage.Id, + // SubjectId = subjectVisit.SubjectId, + // TrialId = subjectVisit.TrialId, + // SiteId = subjectVisit.SiteId, + + // VisitDay = subjectVisit.VisitDay, + // VisitNum = subjectVisit.VisitNum, + // VisitName = subjectVisit.VisitName, + // SVENDTC = subjectVisit.SVENDTC, + // SVSTDTC = subjectVisit.SVSTDTC, + // SVUPDES = subjectVisit.SVUPDES, + // InPlan = subjectVisit.InPlan, + // VisitExecuted = subjectVisit.VisitExecuted, + // IsUrgent = subjectVisit.IsUrgent, + // EarliestScanDate = subjectVisit.EarliestScanDate, + // LatestScanDate = subjectVisit.LatestScanDate, + + // CurrentActionUserId = subjectVisit.CurrentActionUserId, + // CurrentActionUser = subjectVisit.CurrentActionUser, + // CurrentActionUserExpireTime = subjectVisit.CurrentActionUserExpireTime, + // PreliminaryAuditTime = subjectVisit.PreliminaryAuditTime, + // PreliminaryAuitUser = subjectVisit.PreliminaryAuitUser, + // ReviewAuitTime = subjectVisit.ReviewAuitTime, + // ReviewAuitUser = subjectVisit.ReviewAuitUser, + + // AuditState = subjectVisit.AuditState, + // SubmitTime = subjectVisit.SubmitTime, + // IsTake = subjectVisit.IsTake, + + // QCProcessEnum = trial.QCProcessEnum, + + // TrialSiteCode = trialSite.TrialSiteCode, + + // SubjectStatus = subject.Status, + // SubjectCode = subject.Code, + + // StudyCount = study.StudyCount, + + // }; + + #endregion + + var svExpression = QCCommon.GetSubjectVisitFilter(visitSearchDTO.VisitPlanArray); + var query = _subjectVisitRepository.Where(x => x.TrialId == visitSearchDTO.TrialId) + .WhereIf(visitSearchDTO.CurrentActionUserId != null, t => t.CurrentActionUserId == visitSearchDTO.CurrentActionUserId) + .WhereIf(visitSearchDTO.ChallengeState != null, t => t.ChallengeState == visitSearchDTO.ChallengeState) + .WhereIf(visitSearchDTO.SiteId != null, t => t.SiteId == visitSearchDTO.SiteId) + .WhereIf(visitSearchDTO.SubjectId != null, t => t.Subject.Id == visitSearchDTO.SubjectId) + .WhereIf(!string.IsNullOrEmpty(visitSearchDTO.SubjectInfo), t => /*t.Subject.FirstName.Contains(subjectInfo) || t.Subject.LastName.Contains(subjectInfo) ||*/ t.Subject.Code.Contains(visitSearchDTO.SubjectInfo)) + .WhereIf(visitSearchDTO.VisitPlanArray != null && visitSearchDTO.VisitPlanArray?.Length > 0, svExpression) + //.WhereIf(!string.IsNullOrEmpty(visitSearchDTO.VisitPlanInfo), visitSearchDTO.VisitPlanInfo.Contains('.') ? t => t.VisitNum.ToString().Contains(".") : t => t.VisitNum == decimal.Parse(visitSearchDTO.VisitPlanInfo)) + //.WhereIf(visitSearchDTO.AuditState != null, t => t.AuditState == visitSearchDTO.AuditState) + .WhereIf(visitSearchDTO.AuditStateArray != null && visitSearchDTO.AuditStateArray?.Length > 0, t => visitSearchDTO.AuditStateArray!.Contains(t.AuditState)) + + .WhereIf(visitSearchDTO.HandleUserId != null, t => t.PreliminaryAuditUserId == visitSearchDTO.HandleUserId || t.CurrentActionUserId == visitSearchDTO.HandleUserId + || t.ReviewAuditUserId == visitSearchDTO.HandleUserId + || t.QCChallengeList.Any(t => t.CreateUserId == visitSearchDTO.HandleUserId) + || t.QCChallengeDialogList.Any(t => t.CreateUserId == visitSearchDTO.HandleUserId)) + .WhereIf(visitSearchDTO.IsUrgent != null, t => t.IsUrgent == visitSearchDTO.IsUrgent) + .Where(t => t.SubmitState != SubmitStateEnum.None) + //.WhereIf(visitSearchDTO.SubmitState != null, t => t.SubmitState == visitSearchDTO.SubmitState) + //.WhereIf(visitSearchDTO.ChallengeState != null, t => t.ChallengeState == visitSearchDTO.ChallengeState) + .ProjectTo(_mapper.ConfigurationProvider); + + var defalutSortArray = new string[] { nameof(SubjectVisit.IsUrgent) + " desc", nameof(SubjectVisit.SubjectId), nameof(SubjectVisit.VisitNum) }; + var pageList = await query.ToPagedListAsync(visitSearchDTO.PageIndex, visitSearchDTO.PageSize, visitSearchDTO.SortField, visitSearchDTO.Asc, string.IsNullOrWhiteSpace(visitSearchDTO.SortField), defalutSortArray); + + + + + var config = await _repository.Where(t => t.Id == visitSearchDTO.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + + return (pageList, config); + + } + + #endregion + + + #region 一致性核查 转发页面 + /// + /// 获取一致性核查列表 IC/PM 公用 + /// + /// + /// + [HttpPost] + public async Task<(PageOutput, TrialSubjectAndSVConfig)> GetConsistencyVerificationList(CheckQuery checkQuery) + { + #region linq 废弃 byzhouhang 2021 11 30 + //Expression> subjectLambda = x => true; + //Expression> subjectVisitLambda = x => x.TrialId == checkQuery.TrialId && x.AuditState == AuditStateEnum.QCPassed; + //Expression> studyLambda = x => x.TrialId == checkQuery.TrialId; + //if (checkQuery.SiteId != null && checkQuery.SiteId != Guid.Empty) + //{ + // subjectVisitLambda = subjectVisitLambda.And(t => t.SiteId == checkQuery.SiteId.Value); + //} + //if (checkQuery.SubjectId != null && checkQuery.SubjectId != Guid.Empty) + //{ + // subjectLambda = subjectLambda.And(t => t.Id == checkQuery.SubjectId.Value); + //} + + //if (_userInfo.UserTypeEnumInt == (int)UserType.ClinicalResearchCoordinator) + //{ + // var siteQuery = _userTrialSiteRepository.Where(t => t.UserId == _userInfo.Id && t.TrialId == checkQuery.TrialId).Select(t => t.SiteId); + + // subjectVisitLambda = subjectVisitLambda.And(t => siteQuery.Contains(t.SiteId)); + //} + + //var query = + // from subjectVisit in _subjectVisitRepository.Where(subjectVisitLambda) + // join trialSite in _trialSiteRepository.Where(t => t.TrialId == checkQuery.TrialId) on subjectVisit.SiteId equals trialSite.SiteId + // join subject in _subjectRepository.Where() on subjectVisit.SubjectId equals subject.Id + + // select new QCCheckViewModel() + // { + // Id = subjectVisit.Id, + // AuditState = subjectVisit.AuditState, + + // CheckTime = subjectVisit.CheckTime, + // CheckState = subjectVisit.CheckState, + + // CheckChallengeState = subjectVisit.CheckChallengeState, + + // CheckResult = subjectVisit.CheckResult, + + // VisitName = subjectVisit.VisitName, + // VisitNum = subjectVisit.VisitNum, + + // SubjectCode = subject.Code, + + // //Modality=study.Modalities, + // //StudyTime = subjectVisit.sv, + // TrialSiteCode = trialSite.TrialSiteCode, + // }; + #endregion + + var svExpression = QCCommon.GetSubjectVisitFilter(checkQuery.VisitPlanArray); + + var query = _subjectVisitRepository.Where(x => x.TrialId == checkQuery.TrialId) + .Where(x => x.AuditState == AuditStateEnum.QCPassed) //一致性核查中的,或者还没一致性核查的 + .WhereIf(checkQuery.CheckState != null, t => t.CheckState == checkQuery.CheckState) + .WhereIf(checkQuery.SiteId != null, t => t.SiteId == checkQuery.SiteId) + .WhereIf(!string.IsNullOrEmpty(checkQuery.SubjectInfo), t => t.Subject.Code.Contains(checkQuery.SubjectInfo)) + .WhereIf(checkQuery.VisitPlanArray != null && checkQuery.VisitPlanArray?.Length > 0, svExpression) + //.WhereIf(!string.IsNullOrEmpty(checkQuery.VisitPlanInfo), checkQuery.VisitPlanInfo.Contains('.') ? t => t.InPlan == false : t => t.VisitNum == decimal.Parse(checkQuery.VisitPlanInfo)) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id))//IC 过滤负责的site + .ProjectTo(_mapper.ConfigurationProvider); + + var pageList = await query.ToPagedListAsync(checkQuery.PageIndex, checkQuery.PageSize, checkQuery.SortField, checkQuery.Asc); + var config = await _repository.Where(t => t.Id == checkQuery.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + return (pageList, config); + } + + /// + /// 一致性核查 聊天记录列表 + /// + /// + /// + /// + + [HttpGet("{subjectVisitId:guid}")] + public async Task GetCheckChallengeDialogList(Guid subjectVisitId) + { + + var sv = (await _subjectVisitRepository.Where(t => t.Id == subjectVisitId) + .ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + sv.DialogList.ForEach(t => t.IsCurrentUser = _userInfo.Id == t.CreateUserId); + + return sv; + } + + /// + /// 转发列表 查询一致性核查通过的,针对不一致性核查的 要维护CV状态 + /// + /// + /// + [HttpPost] + public async Task> GetForwardList(ForwardQuery forwardQuery) + { + //var trialConfig = _trialReposioty.Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }).FirstOrDefault(t => t.TrialId == forwardQuery.TrialId); + + var svExpression = QCCommon.GetSubjectVisitFilter(forwardQuery.VisitPlanArray); + + var query = _subjectVisitRepository.Where(x => x.TrialId == forwardQuery.TrialId) + .Where(t => t.CheckState == CheckStateEnum.CVPassed) + .WhereIf(forwardQuery.ForwardState != null, t => t.ForwardState == forwardQuery.ForwardState) + .WhereIf(forwardQuery.SiteId != null, t => t.SiteId == forwardQuery.SiteId) + .WhereIf(!string.IsNullOrEmpty(forwardQuery.SubjectInfo), t => t.Subject.Code.Contains(forwardQuery.SubjectInfo)) + .WhereIf(forwardQuery.VisitPlanArray != null && forwardQuery.VisitPlanArray?.Length > 0, svExpression) + //.WhereIf(!string.IsNullOrEmpty(forwardQuery.VisitPlanInfo), forwardQuery.VisitPlanInfo.Contains('.') ? t => t.InPlan == false : t => t.VisitNum == decimal.Parse(forwardQuery.VisitPlanInfo)) + //.WhereIf(_userInfo.UserTypeEnumInt == (int)UserType.ClinicalResearchCoordinator, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id))//IC 过滤负责的site + .ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToPagedListAsync(forwardQuery.PageIndex, forwardQuery.PageSize, forwardQuery.SortField, forwardQuery.Asc); + + } + + #endregion + + + + #region QC 具体质控页面 各种列表 + /// + /// 获取某次检查批次 QA界面所有信息 单独每一项都有接口(往下看),这里是一个大接口,方便第一次获取完整的所有的数据 + /// + /// + /// 项目配置的针对检查批次检查是那种审核,0 不审,1 单审,2双审 + /// 当前 QC进入的是那种审核 1 单审,2复审 + + [HttpGet("{subjectVisitId:guid}/{trialQCProcess:int}/{currentQCType:int}")] + public async Task GetVisitQCInfo(Guid subjectVisitId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType) + { + var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); + + var temp = await GetVisitQCStudyAndSeriesList(subjectVisitId); + + var qacheckList = await GetQCQuestionAnswerList(subjectVisitId, sv.TrialId, trialQCProcess, currentQCType); + + return new TrialVisitQADTO + { + QCQuestionAnswerList = qacheckList, + StudyList = temp.StudyList, + SeriesList = temp.SeriesList, + RelationInfo = await GetVisitQCSubjectInfo(subjectVisitId), + NoneDicomStudyList = await _repository.Where(t => t.SubjectVisitId == subjectVisitId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(), + SubjectClinicalData = await _subjectVisitRepository.Where(t => t.Id == subjectVisitId) + .ProjectTo(_mapper.ConfigurationProvider, new { subjectVisitId = subjectVisitId, token = _userInfo.UserToken }).FirstOrDefaultAsync() + }; + } + + /// + /// 获取质控问题答案 + /// + /// + /// + [HttpPost] + public async Task> GetQCQuestionAnswer(GetQCQuestionAnswerInDto inDto) + { + var questionAnswerlist = await (from data in _trialQCQuestionRepository.Where(x => x.TrialId == inDto.TrialId && x.IsEnable) + join answer in _trialQCQuestionAnswerRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId && x.QCProcessEnum == inDto.QCProcessEnum && x.CurrentQCEnum == inDto.CurrentQCEnum).AsQueryable() on data.Id equals answer.TrialQCQuestionConfigureId into answertemp + from leftanswer in answertemp.DefaultIfEmpty() + select new QCQuestionAnswer() + { + Answer = leftanswer.Answer, + ShowOrder = data.ShowOrder, + QuestionName = data.QuestionName, + Id = data.Id, + IsRequired = data.IsRequired, + ParentId = data.ParentId, + ParentTriggerValue = data.ParentTriggerValue, + Type = data.Type, + TypeValue = data.TypeValue + }).ToListAsync(); + + var result = questionAnswerlist.Where(x => x.ParentId == null).ToList(); + result.ForEach(x => + { + GetQuestionChild(x, questionAnswerlist); + }); + + return result; + } + + + private void GetQuestionChild(QCQuestionAnswer parent, List dataList) + { + parent.Childrens = dataList.Where(x => x.ParentId == parent.Id).ToList(); + + if (parent.Childrens.Count != 0) + { + parent.Childrens.ForEach(x => + { + GetQuestionChild(x, dataList); + }); + } + + + } + + /// + /// 获取某次检查批次 QC 问题核对答案 列表 初始化进去的时候是模板项,QC填写了就是对应的内容 + /// + /// + /// + /// 项目配置的针对检查批次检查是那种审核,0 不审,1 单审,2双审 + /// 当前 QC进入的是那种审核 1 单审,2复审 + + [HttpGet("{trialId:guid}/{subjectVisitId:guid}/{trialQCProcess:int}/{currentQCType:int}")] + public async Task> GetQCQuestionAnswerList(Guid subjectVisitId, Guid trialId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType) + { + + //https://github.com/dotnet/efcore/issues/26833 ef core 导航属性不支持右连接 + + // 直接用ProjectTo的方式 多做一次查询 代码简洁很多 特别是连表多,字段多的情况 + if (await _repository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.QCProcessEnum == trialQCProcess && t.CurrentQCEnum == currentQCType)) + { + var list = await _repository.Where(t => t.SubjectVisitId == subjectVisitId && t.QCProcessEnum == trialQCProcess && t.CurrentQCEnum == currentQCType).OrderBy(t => t.TrialQCQuestionConfigure.ShowOrder) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + return list; + } + else + { + return await _repository.Where(t => t.IsEnable == true && t.TrialId == trialId).OrderBy(t => t.ShowOrder).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + } + + + /// + /// 获次QC 历史质疑列表 不分页 + /// + /// + /// 项目配置的针对检查批次检查是那种审核,0 不审,1 单审,2双审 + /// 当前 QC进入的是那种审核 1 单审,2复审 + + [HttpGet("{subjectVisitId:guid}/{trialQCProcess:int}/{currentQCType:int}")] + public async Task> GetHistoryChallengeList(Guid subjectVisitId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType) + { + var qaChallengeQuery = _repository.Where(t => t.SubjectVisitId == subjectVisitId && t.QCProcessEnum == trialQCProcess) + .WhereIf(currentQCType == CurrentQC.First, t => t.CurrentQCEnum == currentQCType)//复审的时候可以看到初审的质疑 + .ProjectTo(_mapper.ConfigurationProvider); + var qaChallenges = await qaChallengeQuery.ToListAsync(); + + return qaChallenges; + } + + + /// + /// 获取检查批次下的患者检查批次、患者、site信息 + /// + /// + /// + public async Task GetVisitQCSubjectInfo(Guid subjectVisitId) + { + + // automapper Explicit expansion ProjectTo + return await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + + } + + + + /// + /// 检查批次下的Study 和Series列表 + /// + /// + /// + [HttpGet("{subjectVisitId:guid}")] + public async Task GetVisitQCStudyAndSeriesList(Guid subjectVisitId) + { + var studyList = await _repository.Where(s => s.SubjectVisitId == subjectVisitId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var studyIds = studyList.Select(t => t.StudyId).ToList(); + + var seriesList = await _repository.Where(t => studyIds.Contains(t.StudyId)).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider) + .OrderBy(t => t.StudyCode).ThenBy(t => t.SeriesNumber).ToListAsync(); + + return new QAStudySeriesInfo { StudyList = studyList, SeriesList = seriesList }; + + } + + /// + /// 检查批次下的检查列表 + /// + /// + /// + [HttpGet("{subjectVisitId:guid}")] + public async Task<(List,object)> GetSubjectVisitUploadedStudyList(Guid subjectVisitId) + { + var list= await _repository.Where(s => s.SubjectVisitId == subjectVisitId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + var config = await _repository.Where(t => t.Id == subjectVisitId).Select(t=>t.Trial).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + + return (list, config); + } + + + /// + /// 展开 某一QC质疑 下得 聊天记录 + /// + /// + /// + [HttpGet("{qaChallengeId:guid}")] + public async Task> GetQCChallengeDialogList(Guid qaChallengeId) + { + + var list = await _repository.Where(t => t.Id == qaChallengeId) + .ProjectTo(_mapper.ConfigurationProvider, new { currentUserId = _userInfo.Id }).ToListAsync(); + + //利用automapper 运行时映射 + //list.ForEach(t => t.IsCurrentUser = _userInfo.Id == t.CreateUserId); + + return list; + } + + #endregion + + + /// + /// IC/PM 看到某次检查批次下的所有质疑和聊天内容 包括初审和复审的 。 + /// + /// + /// + /// + [HttpGet("{subjectVisitId:guid}/{trialQCProcess:int}")] + public async Task> GetCRCVisitChallengeAndDialog(Guid subjectVisitId, [FromRoute] TrialQCProcess trialQCProcess) + { + + var qaChallengeQuery = _repository.Where(t => t.SubjectVisitId == subjectVisitId && t.QCProcessEnum == trialQCProcess) + .ProjectTo(_mapper.ConfigurationProvider, new { currentUserId = _userInfo.Id }); + + var list = await qaChallengeQuery.ToListAsync(); + + //list.ForEach(t => t.DialogList.ToList().ForEach(u => u.IsCurrentUser = _userInfo.Id == u.CreateUserId)); + + return list; + } + + /// + /// 质询发起人 + /// + /// + /// + [HttpGet("{trialId:guid}")] + public async Task> GetQCChallengeCreatorList(Guid trialId) + { + + return await _repository.Where(t => t.TrialId == trialId && t.User.UserTypeEnum == UserTypeEnum.IQC).Select(t => new QCChanllengeCreatorDto() + { + CreatorRealName = t.User.FullName, + Creator = t.User.UserName, + CreateUserId = t.UserId + }).ToListAsync(); + + } + + /// + /// QC 质疑页面 处理用户 下拉框 + /// + /// + /// + [HttpGet("{trialId:guid}")] + public async Task> GetQCParticipantList(Guid trialId) + { + return await _repository.Where(t => t.TrialId == trialId && (t.User.UserTypeEnum == UserTypeEnum.IQC || t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator)).Select(t => new ParticipantDTO() + { + HandleUserRealName = t.User.FullName, + HandleUser = t.User.UserName, + HandleUserId = t.UserId + }).ToListAsync(); + } + + + + /// + /// 获取一致性核查文件上传记录 + /// + /// + [HttpPost] + public async Task> GetConsistencyCheckFile(GetConsistencyCheckFileInDto indto) + { + var query = _repository.Where(t => t.TrialId == indto.TrialId) + .ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToPagedListAsync(indto.PageIndex, indto.PageSize, "CreateTime", false); + } + + + /// + /// 添加计划外检查批次 下拉框 选择上一次检查批次 + /// + /// + /// + [HttpGet("{subjectId:guid}")] + public async Task> GetSubjectVisitSelectList(Guid subjectId) + { + return await _subjectVisitRepository.Where(t => t.SubjectId == subjectId).OrderBy(T => T.VisitNum).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + + + /// + /// 上传界面 患者 检查批次、site 基本信息 + /// + /// + /// + [HttpGet("{subjectVisitId:guid}/{trialQCProcess:int}")] + [Obsolete] + public async Task GetUploadInitInfo(Guid subjectVisitId) + { + return await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs new file mode 100644 index 0000000..1a7e8ba --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs @@ -0,0 +1,1915 @@ +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Domain.Share; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using System.Data; +using Microsoft.AspNetCore.Authorization; +using Newtonsoft.Json; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Application.Service.Inspection.DTO; +using Nito.AsyncEx; +using System.ComponentModel.DataAnnotations; +using IRaCIS.Core.Application.Auth; +using IRaCIS.Core.Application.Helper; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Application.Service; + +namespace IRaCIS.Core.Application.Image.QA +{ + [ApiExplorerSettings(GroupName = "Image")] + public class QCOperationService : BaseService, IQCOperationService + { + + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _qcChallengeRepository; + private readonly IRepository _dicomStudyRepository; + private readonly IRepository _dicomSeriesrepository; + private readonly IRepository _subjectRepository; + private readonly IRepository _readingClinicalDataRepository; + private readonly IRepository _qCChallengeDialogrepository; + private readonly IRepository _checkChallengeDialogrepository; + private readonly IRepository _trialRepository; + private readonly IRepository _visitTaskRepository; + private readonly IVisitTaskHelpeService _IVisitTaskHelpeService; + + private readonly AsyncLock _mutex = new AsyncLock(); + + public QCOperationService(IRepository subjectVisitRepository, + IRepository qcChallengeRepository, + IRepository trialRepository, + IRepository visitTaskRepository, + IRepository dicomStudyRepository, + IRepository dicomSeriesrepository, + IRepository subjectRepository, + IRepository readingClinicalDataRepository, + IRepository qCChallengeDialogrepository, + IRepository checkChallengeDialogrepository, + IVisitTaskHelpeService visitTaskHelpeService + ) + { + _subjectVisitRepository = subjectVisitRepository; + _qcChallengeRepository = qcChallengeRepository; + _dicomStudyRepository = dicomStudyRepository; + this._dicomSeriesrepository = dicomSeriesrepository; + this._subjectRepository = subjectRepository; + this._readingClinicalDataRepository = readingClinicalDataRepository; + this._qCChallengeDialogrepository = qCChallengeDialogrepository; + this._checkChallengeDialogrepository = checkChallengeDialogrepository; + _trialRepository = trialRepository; + this._visitTaskRepository = visitTaskRepository; + _IVisitTaskHelpeService = visitTaskHelpeService; + } + + #region QC质疑 以及回复 关闭 + + [HttpGet("{trialId:guid}/{subjectVisitId:guid}/{currentQCType:int}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + // [Authorize(Policy = IRaCISPolicy.IQC)] + public async Task VerifyQCCanAddChallenge(Guid subjectVisitId, [FromRoute] CurrentQC currentQCType) + { + await VerifyIsCanQCAsync(null, subjectVisitId); + + if (!await _repository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == currentQCType)) + { + return ResponseOutput.NotOk("请先核查图像,并保存审核问题,然后再发质疑。"); + } + return ResponseOutput.Ok(); + + } + + /// + /// 添加和更新质疑 + /// + /// + /// + /// + /// + /// + [HttpPost("{trialId:guid}/{trialQCProcess:int}/{currentQCType:int}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + // [Authorize(Policy = IRaCISPolicy.IQC)] + public async Task AddOrUpdateQCChallenge(QCChallengeCommand qaQuestionCommand, Guid trialId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType) + { + await VerifyIsCanQCAsync(null, qaQuestionCommand.SubjectVisitId); + + if (qaQuestionCommand.Id == null) + { + if (await _qcChallengeRepository.AnyAsync(t => t.IsClosed == false && t.SubjectVisitId == qaQuestionCommand.SubjectVisitId && t.ReuploadEnum == QCChanllengeReuploadEnum.QCAgreeUpload)) + { + throw new BusinessValidationFailedException("当前检查批次未关闭的质疑已设置了同意IC重传影像。请在IC完成影像重传后,先关闭原质疑,再添加新的质疑。"); + + } + + var trialConfig = (await _trialRepository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }).FirstOrDefaultAsync()).IfNullThrowException(); + + + + using (await _mutex.LockAsync()) + { + //获取编号 + var code = _qcChallengeRepository.Where(t => t.TrialId == trialId).Select(t => t.Code).DefaultIfEmpty().Max(); + + var qcChallenge = _mapper.Map(qaQuestionCommand); + + qcChallenge.QCProcessEnum = trialConfig.QCProcessEnum; + qcChallenge.CurrentQCEnum = currentQCType; + qcChallenge.TrialId = trialId; + qcChallenge.Code = code + 1; + qcChallenge.UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt; + + qcChallenge.ChallengeCode = AppSettings.GetCodeStr(qcChallenge.Code, nameof(QCChallenge)); + + qcChallenge = await _qcChallengeRepository.AddAsync(qcChallenge, true); + + return ResponseOutput.Ok(qcChallenge.Id); + + } + + } + else + { + + await _repository.UpdateFromDTOAsync(qaQuestionCommand, true); + + return ResponseOutput.Ok(); + + } + + + } + + + /// + /// 关闭质疑,什么情况下允许? + /// + /// + [HttpPut] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + // [Authorize(Policy = IRaCISPolicy.IQC)] + public async Task CloseQCChallenge(CloseQCChallengeInDto input) + { + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IQC) + { + await VerifyIsCanQCAsync(null, input.subjectVisitId); + } + + var dbQCChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == input.qcChallengeId)).IfNullThrowException(); + + + if (dbQCChallenge.ReuploadEnum == QCChanllengeReuploadEnum.CRCRequestReupload || dbQCChallenge.ReuploadEnum == QCChanllengeReuploadEnum.QCAgreeUpload) + { + throw new BusinessValidationFailedException("IC已申请重传或者QC同意重传,不允许关闭该质疑。请在QC拒绝重传申请或者IC设置重传影像后,再关闭质疑。"); + } + + + #region 之前 + + dbQCChallenge.CloseResonEnum = input.closeEnum; + + dbQCChallenge.IsClosed = true; + + dbQCChallenge.ClosedTime = DateTime.Now; + + + dbQCChallenge.DialogList.Add(new QCChallengeDialog() + { + SubjectVisitId = dbQCChallenge.SubjectVisitId, + UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, + QCChallengeId = dbQCChallenge.Id, + TalkContent = "关闭原因: " + input.closeReason + }); + + var success = await _qcChallengeRepository.SaveChangesAsync(); + + #endregion + + + return ResponseOutput.Result(success); + + } + + + + /// + /// 删除QC质疑记录 + /// + /// + [HttpDelete("{trialId:guid}/{subjectVisitId:guid}/{qcChallengeId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + // [Authorize(Policy = IRaCISPolicy.IQC)] + public async Task DeleteQCChallenge(Guid qcChallengeId) + { + + if (await _repository.AnyAsync(t => t.QCChallengeId == qcChallengeId)) + { + ResponseOutput.NotOk("当前QC质疑已经回复。"); + } + + var qaRecord = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qcChallengeId)).IfNullThrowException(); + + + await _qcChallengeRepository.DeleteAsync(qaRecord); + + + var success1 = await _repository.SaveChangesAsync(); + + return ResponseOutput.Result(success1 /*|| success2 || success3*/); + } + + /// + /// 针对 某条QC质疑 添加回复 + /// + /// + /// + [HttpPost("{trialId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.CRC_IQC)] + public async Task AddQCChallengeReply(QADialogCommand qaDialogCommand) + { + var qaReply = _mapper.Map(qaDialogCommand); + + await _qCChallengeDialogrepository.AddAsync(qaReply); + + qaReply.UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt; + + var dbQCChallenge = (await _repository.FirstOrDefaultAsync(t => t.Id == qaDialogCommand.QCChallengeId)).IfNullThrowException(); + + dbQCChallenge.LatestMsgTime = DateTime.Now; + + + dbQCChallenge.LatestReplyUserId = _userInfo.Id; + + + var success = await _repository.SaveChangesAsync(); + + + return ResponseOutput.Result(success, qaReply); + } + + #endregion + + + #region 一致性核查 + + /// + /// 一致性核查 质疑的添加/回复 + /// + /// + /// + [HttpPost("{trialId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddCheckChallengeReply(CheckChallengeDialogCommand checkDialogCommand) + { + + + //修改一致性核查 质疑状态 + var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == checkDialogCommand.SubjectVisitId)).IfNullThrowException(); + + var qaReply = _mapper.Map(checkDialogCommand); + + qaReply.UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt; + + await _repository.AddAsync(qaReply); + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator) + { + sv.CheckChallengeState = CheckChanllengeTypeEnum.CRCWaitPMReply; + + } + else if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt != (int)UserTypeEnum.APM) + { + sv.CheckChallengeState = CheckChanllengeTypeEnum.PMWaitCRCReply; + } + else + { + throw new BusinessValidationFailedException("您没有权限回复一致性核查对话。"); + } + + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Result(success, qaReply.Id); + } + + /// + /// 关闭 一致性核查质疑 + /// + /// + [HttpPut("{trialId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task CloseCheckChallenge(CloseCheckChallengeDto input) + { + + var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == input.subjectVisitId)).IfNullThrowException(); + + if (sv.RequestBackState == RequestBackStateEnum.CRC_RequestBack) + { + ResponseOutput.NotOk("当前检查批次处于申请回退状态, 不允许关闭质疑。"); + } + + + sv.CloseTheReason = input.CloseCheckChallenge; + + sv.CheckChallengeState = CheckChanllengeTypeEnum.Closed; + await _checkChallengeDialogrepository.AddAsync(new CheckChallengeDialog() + { + SubjectVisitId = input.subjectVisitId, + TalkContent = "原因:" + input.CloseCheckChallenge, + UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, + + }); + await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(sv); + } + + + /// + /// 手动设置一致性核查通过 + /// + /// + [HttpPut("{trialId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task SetCheckPass(SetCheckPassDt data) + { + //if (_userInfo.UserTypeEnumInt != (int)UserTypeEnum.ProjectManager && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.APM) + //{ + // ResponseOutput.NotOk("您不是PM/APM,不允许设置一致性核查通过"); + //} + + var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == data.Id)).IfNullThrowException(); + + + if (sv.RequestBackState == RequestBackStateEnum.PM_AgressBack) + { + ResponseOutput.NotOk("当前检查批次处于回退状态,不允许设置一致性核查通过"); + } + + if (sv.CheckChallengeState != CheckChanllengeTypeEnum.Closed && sv.AuditState == AuditStateEnum.QCPassed) + { + ResponseOutput.NotOk("请先关闭一致性核查质疑后,再设置一致性核查通过。"); + } + + sv.CheckUserId = _userInfo.Id; + sv.CheckState = CheckStateEnum.CVPassed; + + sv.ReadingStatus = ReadingStatusEnum.TaskAllocate; + + sv.ForwardState = ForwardStateEnum.ToForward; + sv.ManualPassReason = data.ManualPassReason; + sv.CheckPassedTime = DateTime.Now; + + await _subjectVisitRepository.SaveChangesAsync(); + + + return ResponseOutput.Result(true); + + } + + /// + /// IC 请求回退 + /// + /// + /// + [HttpPut("{trialId:guid}/{subjectVisitId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task CRCRequstCheckBack(Guid subjectVisitId) + { + var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); + + + if (sv.CheckState == CheckStateEnum.CVPassed) + { + return ResponseOutput.NotOk("当前检查批次已通过一致性核查,不允许申请回退"); + } + + var QCChallengeId = await _qcChallengeRepository.Where(x => x.SubjectVisitId == subjectVisitId).Select(x => x.Id).FirstOrDefaultAsync(); + + await _checkChallengeDialogrepository.AddAsync(new CheckChallengeDialog() + { + SubjectVisitId = subjectVisitId, + UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, + TalkContent = "IC申请回退" + }); + + + if (sv.RequestBackState == RequestBackStateEnum.NotRequest || sv.RequestBackState == RequestBackStateEnum.PM_NotAgree) + { + sv.RequestBackState = RequestBackStateEnum.CRC_RequestBack; + + await _subjectVisitRepository.SaveChangesAsync(); + } + else + { + return ResponseOutput.NotOk("其他IC已申请处理,请刷新页面"); + } + + + + return ResponseOutput.Ok(); + + } + + + + [HttpPut("{trialId:guid}/{subjectVisitId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task RejectCheckBack(Guid subjectVisitId) + { + //if (_userInfo.UserTypeEnumInt != (int)UserTypeEnum.ProjectManager && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.APM) + //{ + // return ResponseOutput.NotOk(" 只有PM/APM具有操作权限!"); + //} + + var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); + + if (sv.RequestBackState == RequestBackStateEnum.CRC_RequestBack) + { + sv.RequestBackState = RequestBackStateEnum.PM_NotAgree; + + await _repository.AddAsync(new CheckChallengeDialog() { SubjectVisitId = subjectVisitId, TalkContent = "PM/APM拒绝一致性核查回退", UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt }); + + await _subjectVisitRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + else + { + return ResponseOutput.NotOk("您不具备操作权限。"); + } + } + + /// + /// 一致性核查 回退 对话记录不清除 只允许PM回退 [签名 不需要对] + /// + /// + [HttpPut("{trialId:guid}/{subjectVisitId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task CheckBack(Guid subjectVisitId) + { + //if (_userInfo.UserTypeEnumInt != (int)UserTypeEnum.ProjectManager && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.APM) + //{ + // return ResponseOutput.NotOk("只有PM/APM具有操作权限!"); + //} + + var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); + + + if (sv.CheckState == CheckStateEnum.CVPassed || sv.CheckState == CheckStateEnum.ToCheck) + { + return ResponseOutput.NotOk("当前检查批次还未进行核查或者核查已通过,不允许设置回退。"); + } + + //sv.CheckChallengeState = (int)CheckChanllengeTypeEnum.None; + //sv.CheckState = CheckStateEnum.None; + //sv.ChallengeState = (int)ChallengeStateEnum.No; + + sv.AuditState = AuditStateEnum.None; + sv.SubmitState = SubmitStateEnum.ToSubmit; + sv.ReadingStatus = ReadingStatusEnum.ImageNotSubmit; + + //回退后,回退状态恢复 + sv.RequestBackState = RequestBackStateEnum.NotRequest; + sv.IsCheckBack = true; + sv.CheckBackTime = DateTime.Now; + sv.CheckState = CheckStateEnum.None; + sv.CheckChallengeState = CheckChanllengeTypeEnum.None; + + sv.SVENDTC = null; + sv.SVSTDTC = null; + + sv.PreliminaryAuditTime = null; + sv.SubmitTime = null; + sv.ReviewAuditTime = null; + sv.CurrentActionUserExpireTime = null; + + + sv.IsTake = false; + sv.CurrentActionUserId = null; + sv.PreliminaryAuditUserId = null; + sv.ReviewAuditUserId = null; + + //var success1 = _studyRepository.Delete(t => t.SubjectVisitId == subjectVisitId); + //var succeess2 = _instanceRepository.Delete(t => t.SubjectVisitId == subjectVisitId); + //var success3 = _seriesRepository.Delete(t => t.SubjectVisitId == subjectVisitId); + + //_qcChallengeRepository.Delete(t => t.SubjectVisitId == subjectVisitId); + //_qcChallengeDialogRepository.Delete(t => t.SubjectVisitId == subjectVisitId); + //_checkChallengeDialogRepository.Delete(t => t.SubjectVisitId == subjectVisitId); + await _repository.AddAsync(new CheckChallengeDialog() { SubjectVisitId = subjectVisitId, TalkContent = "PM/APM同意一致性核查回退。", UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt }); + + await _repository.BatchDeleteAsync(t => t.SubjectVisitId == subjectVisitId); + + await _repository.BatchDeleteAsync(t => t.DicomSerie.IsDeleted); + await _repository.BatchDeleteAsync(t => t.IsDeleted); + + var success = await _subjectVisitRepository.SaveChangesAsync(); + + + return ResponseOutput.Result(success); + + } + + #endregion + + + #region QC 核对问题 操作检查各种操作 + + /// + /// 添加或者更新 QC核对问题列表 两个人不能同时操作,就算意外进去了,提交数据,也不会覆盖前一个人数据, 后台已经做好判断 + /// + [HttpPost("{trialId:guid}/{subjectVisitId:guid}/{trialQCProcess:int}/{currentQCType:int}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddOrUpdateQCQuestionAnswerList(QCQuestionAnswerCommand[] qcQuestionAnswerCommands, Guid trialId, Guid subjectVisitId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType) + { + //验证是否能操作 + await VerifyIsCanQCAsync(null, subjectVisitId); + + //更新 + if (qcQuestionAnswerCommands.Any(t => t.Id != null)) + { + + #region 先删除再添加 + + //await _repository.DeleteFromQueryAsync(t => t.SubjectVisitId == subjectVisitId && t.QCProcessEnum == trialQCProcess && t.CurrentQCEnum == currentQCType); + + //var addlist = _mapper.Map>(qcQuestionAnswerCommands); + + //addlist.ForEach(t => { t.TrialId = trialId; t.SubjectVisitId = subjectVisitId; t.CurrentQCEnum = currentQCType; t.QCProcessEnum = trialQCProcess; }); + + //await _repository.AddRangeAsync(addlist); + + #endregion + + #region 先查询再更新 + + var questionAnswerList = await _repository.Where(t => t.SubjectVisitId == subjectVisitId && t.QCProcessEnum == trialQCProcess && t.CurrentQCEnum == currentQCType, true).ToListAsync(); + + qcQuestionAnswerCommands.ToList().ForEach(t => + { + var temp = questionAnswerList.FirstOrDefault(u => u.Id == t.Id); + + if (temp != null) + { + temp.Answer = t.Answer; + //temp.ChildAnswer = t.ChildAnswer; + } + + }); + + //Automapper 映射有问题 automapper 会把Guid? 类型的null 值转为 guid.Empty 导致映射错误 + //_mapper.Map(qcQuestionAnswerCommands, questionAnswerList); + #endregion + + + + return ResponseOutput.Ok(await _repository.SaveChangesAsync()); + } + else + { + var addlist = _mapper.Map>(qcQuestionAnswerCommands); + + addlist.ForEach(t => { t.TrialId = trialId; t.SubjectVisitId = subjectVisitId; t.CurrentQCEnum = currentQCType; t.QCProcessEnum = trialQCProcess; }); + + await _repository.AddRangeAsync(addlist); + + return ResponseOutput.Result(await _repository.SaveChangesAsync()); + } + + + } + + + + /// + /// 1、设置为不读片,2 设置为读片(取消 先前设置为不读片) 4 设置为删除(数据库记录软删除) 5 恢复为未删除 + /// + /// + /// + /// + /// + /// + [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{studyId:guid}/{seriesId:guid}/{state:int}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetSeriesState(Guid subjectVisitId, Guid studyId, Guid seriesId, int state) + { + + await VerifyIsCanQCAsync(null, subjectVisitId); + + var series = (await _repository.Where(t => t.Id == seriesId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); + + + if (state == 1) + { + series.IsReading = false; + } + else if (state == 2) + { + series.IsReading = true; + } + else if (state == 4) + { + series.IsDeleted = true; + + var study = (await _repository.Where(t => t.Id == studyId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); + + var instanceIdList = await _repository.Where(t => t.SeriesId == seriesId).Select(t => t.Id).ToListAsync(); + + //instanceIdList.ForEach(t => + //{ + // var path = _dicomFileStoreHelper.GetInstanceFilePath(study, seriesId, t.ToString()); + + // if (System.IO.File.Exists(path)) + // { + // File.Delete(path); + // } + + //}); + + study.InstanceCount = study.InstanceCount - instanceIdList.Count; + study.SeriesCount = study.SeriesCount - 1; + + study.IsDeleted = study.SeriesCount == 0; + + } + else if (state == 5) + { + series.IsDeleted = false; + + var study = (await _repository.Where(t => t.Id == studyId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); + + + var instanceIdList = await _repository.Where(t => t.SeriesId == seriesId).Select(t => t.Id).ToListAsync(); + + study.InstanceCount = study.InstanceCount + instanceIdList.Count; + + study.SeriesCount = study.SeriesCount + 1; + + study.IsDeleted = study.SeriesCount == 0; + } + + return ResponseOutput.Ok(await _repository.SaveChangesAsync()); + } + + /// + ///type 1 :study 2: series 3:非dicom QC修改检查部位和 拍片类型 + /// + /// + [HttpPost("{trialId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task UpdateModality(UpdateModalityCommand updateModalityCommand) + { + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IQC) + { + await VerifyIsCanQCAsync(null, updateModalityCommand.SubjectVisitId); + + } + else + { + if (await _subjectVisitRepository.AnyAsync(t => t.Id == updateModalityCommand.SubjectVisitId && t.SubmitState == SubmitStateEnum.Submitted)) + { + throw new BusinessValidationFailedException("提交之后,不允许修改!"); + } + } + + + DateTime time = DateTime.Now.AddMilliseconds(500); + if (updateModalityCommand.Type == 1) + { + + var studyId = updateModalityCommand.Id; + var study = (await _repository.FirstOrDefaultAsync(t => t.Id == studyId)).IfNullThrowException(); + + + study.BodyPartForEdit = updateModalityCommand.BodyPart; + //study.Modalities = updateModalityCommand.Modality; + study.ModalityForEdit = updateModalityCommand.Modality; + await _repository.BatchUpdateAsync(t => t.StudyId == studyId, r => new DicomSeries() { BodyPartForEdit = updateModalityCommand.BodyPart }); + } + else if (updateModalityCommand.Type == 2) + { + + var seriesId = updateModalityCommand.Id; + var series = (await _repository.FirstOrDefaultAsync(t => t.Id == seriesId)).IfNullThrowException(); + series.BodyPartForEdit = updateModalityCommand.BodyPart; + + } + else if (updateModalityCommand.Type == 3) + { + + } + + await _repository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } + + + /// + /// 验证是否质疑都关闭了 可以审核通过和不通过 + /// + /// + /// + [HttpGet("{trialId:guid}/{subjectVisitId:guid}")] + public async Task VerifyCanQCPassedOrFailed(Guid subjectVisitId) + { + await VerifyIsCanQCAsync(null, subjectVisitId); + + if (await _repository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.IsClosed == false)) + { + return ResponseOutput.NotOk("当前检查批次有质疑未关闭,不允许该操作"); + } + return ResponseOutput.Ok(); + } + + + + + /// + /// 删除检查列表 不能用delete方法 传递数组 + /// + /// + /// + /// + /// SeriesCount + [HttpPost, Route("{trialId:guid}/{subjectVisitId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task DeleteStudyList(Guid[] ids, Guid subjectVisitId, Guid trialId) + { + + + //提交了 但是IQC同意的时候 是可以删除的 | 普通提交后也不能删除 + if (await _subjectVisitRepository.AnyAsync(t => t.Id == subjectVisitId && t.SubmitState == SubmitStateEnum.Submitted && + (!t.QCChallengeList.Any(u => u.ReuploadEnum == QCChanllengeReuploadEnum.QCAgreeUpload)))) + { + return ResponseOutput.NotOk("IC已经提交,不允许删除。"); + } + var waitDeleteStudyList = await _dicomStudyRepository.Where(x => ids.Contains(x.Id)).ToListAsync(); + + foreach (var study in waitDeleteStudyList) + { + + var id = study.Id; + + await _dicomStudyRepository.DeleteAsync(study); + + + //var DicomSeriess = await _repository.GetQueryable().Where(t => t.StudyId == id).Select(x => new + //{ + // x.StudyId, + // x.SubjectId, + // x.SiteId, + // x.TrialId, + // x.Id, + // x.SubjectVisitId, + // x.SeriesTime, + // x.IsReading, + // x.InstanceCount, + // x.SeriesNumber, + // StudyCode = x.DicomStudy.StudyCode, + // Modalities = x.DicomStudy.Modalities, + + //}).ToListAsync(); + + var succeess2 = await _repository.BatchDeleteAsync(t => t.StudyId == id); + var success3 = await _dicomSeriesrepository.BatchDeleteNoTrackingAsync(t => t.StudyId == id); + + //var success3 = await _dicomSeriesrepository.DeleteFromQueryAsync(t => t.StudyId == id, true); + //var success4 = await _repository.BatchDeleteAsync(t => t.StudyId == id); + + //删除 物理文件 + + var instancePathList = await _repository.Where(t => t.StudyId == id) + .Select(t => t.Path).ToListAsync(); + + instancePathList.ForEach(path => + { + + var physicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, path); + + if (System.IO.File.Exists(physicalPath)) + { + File.Delete(physicalPath); + } + }); + + //var instanceIdList = await _repository.Where(t => t.StudyId == id) + // .Select(t => new { InstanceId = t.Id, t.SeriesId, t.StudyId, t.SubjectId, t.SiteId }).ToListAsync(); + + //instanceIdList.ForEach(t => + //{ + // var dicomStudy = new DicomStudy() { Id = t.StudyId, SubjectId = t.SubjectId, TrialId = trialId, SiteId = t.SiteId, SubjectVisitId = subjectVisitId }; + // var (physicalPath, relativePath) = + // FileStoreHelper.GetDicomInstanceFilePath(_hostEnvironment, dicomStudy.TrialId, dicomStudy.SiteId, dicomStudy.SubjectId, dicomStudy.SubjectVisitId, dicomStudy.Id, t.InstanceId); + + // if (System.IO.File.Exists(physicalPath)) + // { + // File.Delete(physicalPath); + // } + + //}); + } + + await _subjectVisitRepository.SaveChangesAsync(); + + + return ResponseOutput.Ok(); + } + + #endregion + + [HttpGet] + public async Task IsQCCanOpt([FromQuery, Required] Guid subjectVisitId) + { + await VerifyIsCanQCAsync(null, subjectVisitId); + + return ResponseOutput.Ok(); + } + + + private async Task VerifyIsCanQCAsync(SubjectVisit? subjectVisit, Guid? sujectVisitId = null) + { + if (sujectVisitId != null) + { + subjectVisit = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == sujectVisitId)).IfNullThrowException(); + } + + if (subjectVisit!.CurrentActionUserId != _userInfo.Id) + { + throw new BusinessValidationFailedException("您不是该质控任务当前领取人,没有操作权限!"); + } + } + + #region 临床数据签名 领取、 设置紧急、RequestToQC QC通过、不通过 + + + /// 替换当前领取人 + [HttpPut("{trialId:guid}/{subjectVisitId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ReplaceQCTaskActionUser(Guid trialId, Guid subjectVisitId) + { + var dbSubjectVisit = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); + + if (dbSubjectVisit.CurrentActionUserId == null && dbSubjectVisit.IsTake == false) + { + + return ResponseOutput.NotOk("当前检查批次的影像质控任务已被原领取人释放。您可以通过“领取”获得", ApiResponseCodeEnum.NeedTips); + } + + + dbSubjectVisit.IsTake = true; + + dbSubjectVisit.CurrentActionUserId = _userInfo.Id; + + dbSubjectVisit.CurrentActionUserExpireTime = DateTime.Now.AddHours(1); + + var success = await _subjectVisitRepository.SaveChangesAsync(); + + return ResponseOutput.Result(success); + } + + + /// + /// 手动领取 或者取消 QC任务 + /// + /// + /// + /// true 获取 false是取消领取 + /// + [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{obtaionOrCancel:bool}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ObtainOrCancelQCTask(Guid trialId, Guid subjectVisitId, bool obtaionOrCancel) + { + + + + var dbSubjectVisit = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); + + + var trialConfig = await _trialRepository + .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }) + .FirstOrDefaultAsync(t => t.TrialId == trialId) + .IfNullThrowException(); + + if (obtaionOrCancel) + { + if (dbSubjectVisit.CurrentActionUserId != null && dbSubjectVisit.IsTake) + { + //throw new BusinessValidationFailedException("当前检查批次已被领取,不允许领取"); + + return ResponseOutput.NotOk("当前检查批次的影像质控任务已被其他QC领取,不允许领取", ApiResponseCodeEnum.NeedTips); + + } + + if (await _subjectVisitRepository.AnyAsync(t => t.Trial.QCQuestionConfirmedUserId == null && t.Id == subjectVisitId)) + { + return ResponseOutput.NotOk("请先配置影像质控审核问题,再领取影像质控任务", ApiResponseCodeEnum.NeedTips); + } + + + //if (await _subjectVisitRepository.AnyAsync(t => t.IsTake && + // t.SubjectId != dbSubjectVisit.SubjectId && + // t.CurrentActionUserId == _userInfo.Id && + // t.TrialId == dbSubjectVisit.TrialId + // )) + //{ + + // return ResponseOutput.NotOk("您已经领取了其他患者,完成后才允许领取新的患者"); + //} + + #region 处理验证 + + if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) + { + return ResponseOutput.NotOk("项目配置为不审,没有领取QC Task"); + } + else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) + { + if (dbSubjectVisit.PreliminaryAuditUserId == _userInfo.Id) + { + return ResponseOutput.NotOk("初审已通过,不能继续领取"); + } + + if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.ToAudit) + { + dbSubjectVisit.AuditState = AuditStateEnum.InPrimaryQC; + } + if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) + { + //单审QC 释放后 也是可以领取的 + } + else + { + return ResponseOutput.NotOk("项目配置为单审,不满足SubmmitState:已提交 或者 AuditState:待审核/审核中, 不允许领取,请刷新界面"); + } + } + else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit) + { + if (dbSubjectVisit.PreliminaryAuditUserId == _userInfo.Id) + { + return ResponseOutput.NotOk("复审不能和初审是同一个人"); + } + + //提交 并且初审通过 那么领取后进入 复审中 + if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.PrimaryQCPassed) + { + dbSubjectVisit.AuditState = AuditStateEnum.InSecondaryQC; + + } + else if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.ToAudit) + { + dbSubjectVisit.AuditState = AuditStateEnum.InPrimaryQC; + } + else if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC || dbSubjectVisit.AuditState == AuditStateEnum.InSecondaryQC)) + { + //初审中 复审中 领取也是ok的 其他人接着做 + } + else + { + return ResponseOutput.NotOk("项目配置为复审,不满足提交状态:已提交 或者 审核状态:待审核/QC中, 不允许领取,请刷新界面"); + } + } + + #endregion + + dbSubjectVisit.IsTake = true; + + dbSubjectVisit.CurrentActionUserId = _userInfo.Id; + + dbSubjectVisit.CurrentActionUserExpireTime = DateTime.Now.AddHours(1); + + + //启动定时任务 1h后处理 + //BackgroundJob.Schedule(t => t.CancelQCObtaion(subjectVisitId, DateTime.Now), TimeSpan.FromHours(1)); + } + else + { + if (dbSubjectVisit!.CurrentActionUserId != _userInfo.Id) + { + return ResponseOutput.NotOk("您不是该质控任务当前领取人,没有操作权限!", ApiResponseCodeEnum.NeedTips); + } + + + if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) + { + return ResponseOutput.NotOk("项目配置影像质控为不审,不需要取消任务功能"); + } + else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) + { + + if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) + { + dbSubjectVisit.AuditState = AuditStateEnum.ToAudit; + } + + else + { + return ResponseOutput.NotOk("当前检查批次影像质控任务没有当前领取人,不能释放。"); + } + } + else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit) + { + + //提交 并且初审通过 那么领取后进入 复审中 + if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.InSecondaryQC) + { + dbSubjectVisit.AuditState = AuditStateEnum.PrimaryQCPassed; + + } + else if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) + { + dbSubjectVisit.AuditState = AuditStateEnum.ToAudit; + } + + else + { + return ResponseOutput.NotOk("当前检查批次影像质控任务没有当前领取人, 不能释放"); + } + } + + dbSubjectVisit.IsTake = false; + + dbSubjectVisit.CurrentActionUserId = null; + + dbSubjectVisit.CurrentActionUserExpireTime = null; + + } + var success = await _subjectVisitRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + + + + [HttpPost] + public async Task VerifyCRCRequestToQC(CRCRequestToQCCommand cRCRequestToQCCommand) + { + var dbSubjectVisitList = await _subjectVisitRepository.Where(t => cRCRequestToQCCommand.SubjectVisitIds.Contains(t.Id), true).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + //普通提交 + if (dbSubjectVisitList.Count() == 1) + { + var sv = dbSubjectVisitList[0]; + + var nameList = await _subjectVisitRepository.Where(t => t.SubjectId == sv.SubjectId && t.SubmitState != SubmitStateEnum.Submitted && t.VisitNum < sv.VisitNum && t.IsLostVisit == false).Select(t => t.VisitName).ToListAsync() ?? new List(); + + if (sv.PDState == PDStateEnum.PDProgress) + { + + if (nameList.Count() > 0) + { + return ResponseOutput.NotOk($"当前检查批次要求进行疾病进展确认。请在提交当前检查批次前,先处理未提交的前序检查批次:{string.Join('、', nameList)}。", 1, ApiResponseCodeEnum.NeedTips); + } + } + else + { + if (nameList.Count() > 0) + { + + return ResponseOutput.NotOk($"在提交当前检查批次后,请尽快处理尚未提交的前序检查批次:{string.Join('、', nameList)}。", 0, ApiResponseCodeEnum.NeedTips); + } + } + } + + //同时要根据项目有没有配置Subject 级别临床数据 + + + if (dbSubjectVisitList.Any(t => t.IsBaseLine && !t.IsHaveClinicalData) && await _repository.AnyAsync(t=>t.TrialId== cRCRequestToQCCommand.TrialId && t.ClinicalDataLevel==ClinicalLevel.Subject)) + { + return ResponseOutput.NotOk($"基线没有临床数据,确认提交?", 0, ApiResponseCodeEnum.NeedTips); + } + + return ResponseOutput.Ok(); + + + } + + /// + /// IC RequestToQC 批量提交 [需要签名 不需要对] + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task CRCRequestToQC(CRCRequestToQCCommand cRCRequestToQCCommand) + { + + var trialId = cRCRequestToQCCommand.TrialId; + + var trialConfig = (await _trialRepository + .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification, t.IsUrgent, t.IsHaveFirstGiveMedicineDate, t.ClinicalInformationTransmissionEnum }) + .FirstOrDefaultAsync(t => t.TrialId == cRCRequestToQCCommand.TrialId)).IfNullThrowException(); + + //找到符合配置的标准 确认的并且签名的、双重,有序,阅片期仲裁 + var trialReadingCriterionIdList = _repository.Where(t => t.TrialId == cRCRequestToQCCommand.TrialId && t.IsConfirm && t.ReadingInfoSignTime != null + //&& t.ReadingType == ReadingMethod.Double && t.ArbitrationRule == ArbitrationRule.Reading + && t.IsReadingTaskViewInOrder && t.IsReadingPeriod).Select(t => t.Id).ToList(); + + var trialInOrderCriterionIdList = _repository.Where(t => t.TrialId == cRCRequestToQCCommand.TrialId && t.IsConfirm && t.ReadingInfoSignTime != null + //&& t.ReadingType == ReadingMethod.Double && t.ArbitrationRule == ArbitrationRule.Reading + && t.IsReadingTaskViewInOrder).Select(t => t.Id).ToList(); + + + var dbSubjectVisitList = await _subjectVisitRepository.Where(t => cRCRequestToQCCommand.SubjectVisitIds.Contains(t.Id), true).Include(t => t.Subject).ToListAsync(); + + + if (dbSubjectVisitList.Any(t => t.SubmitState == SubmitStateEnum.None)) + { + return ResponseOutput.NotOk("有检查批次未上传任何Dicom/非Dicom影像数据,不允许提交"); + } + + //单个提交提示信息 + if (dbSubjectVisitList.Count() == 1 && dbSubjectVisitList.First().SubmitState == SubmitStateEnum.Submitted) + { + return ResponseOutput.NotOk("当前检查批次的影像数据,已经由其他IC提交。", 3, ApiResponseCodeEnum.NeedTips); + } + else if (dbSubjectVisitList.Any(t => t.SubmitState == SubmitStateEnum.Submitted)) + { + return ResponseOutput.NotOk("当前批量提交检查批次的影像数据,其中部分已由其他IC提交。", 3, ApiResponseCodeEnum.NeedTips); + } + + //获取确认的临床数据配置 + var clinicalDataConfirmList = _repository.Where(t => t.TrialId == trialId && t.IsConfirm).ToList(); + + // 别人未提交的 + foreach (var dbSubjectVisit in dbSubjectVisitList) + { + + //基线不验证 + if (trialConfig.IsHaveFirstGiveMedicineDate && !dbSubjectVisit.IsBaseLine && dbSubjectVisit.Subject.FirstGiveMedicineTime == null) + { + return ResponseOutput.NotOk("项目配置了需要填写检查批次基准日期。但是患者没有填写检查批次基准日期,不允许提交"); + } + + //基线 且配置了临床数据 + if (trialConfig.ClinicalInformationTransmissionEnum != 0 /*&& dbSubjectVisit.IsBaseLine*//*&&dbSubjectVisit.ClinicalDataSignUserId==null*/) + { + + + ////找到需要确认的PDF 临床数据 如果没有IC 没有上传 那么就添加一条没有文件的记录 + + //var crcNeedConfirmClinicalDataSetList = clinicalDataConfirmList.Where(t => t.UploadRole == UploadRole.IC && t.ClinicalUploadType == ClinicalUploadType.PDF).ToList(); + + //// 找到IC 已经自己添加的临床PDF数据文件 + + //var crcAddClinicalDataIdList = _readingClinicalDataRepository.Where(t => t.ReadingId == dbSubjectVisit.Id && t.ClinicalDataTrialSet.UploadRole == UploadRole.IC && t.ClinicalDataTrialSet.ClinicalUploadType == ClinicalUploadType.PDF) + // .Select(t => new { t.ClinicalDataTrialSetId }).ToList(); + + //foreach (var crcNeedConfirmClinicalDataSet in crcNeedConfirmClinicalDataSetList) + //{ + // if (!crcAddClinicalDataIdList.Any(t => t.ClinicalDataTrialSetId == crcNeedConfirmClinicalDataSet.Id)) + // { + // await _repository.AddAsync(new ReadingClinicalData() + // { + // TrialId = dbSubjectVisit.TrialId, + // SubjectId = dbSubjectVisit.SubjectId, + // ReadingId = dbSubjectVisit.Id, + // ClinicalDataTrialSetId = crcNeedConfirmClinicalDataSet.Id, + // IsVisit = true, + // IsSign = true, + // ReadingClinicalDataState = ReadingClinicalDataStatus.HaveSigned + + // }); + // } + //} + + //已确认临床数据完整性 + dbSubjectVisit.IsConfirmedClinicalData = true; + + + // IC 上传的基线数据签名 + + await _readingClinicalDataRepository.UpdatePartialFromQueryAsync(x => x.ClinicalDataTrialSet.UploadRole == UploadRole.CRC && x.ReadingId == dbSubjectVisit.Id && x.IsSign==false, x => new ReadingClinicalData() + { + IsSign = true, + ReadingClinicalDataState = ReadingClinicalDataStatus.HaveSigned + }); + + + + //var signSuccess = await _repository.UpdateFromQueryAsync(t => t.Id == cRCRequestToQCCommand.SignId, u => new TrialSign() { IsCompleted = true }); + + + ////现在修改为 提交时 设置签名信息 + //dbSubjectVisit.ClinicalDataSignUserId = _userInfo.Id; + //dbSubjectVisit.ClinicalDataSignTime = DateTime.Now; + + //那么没有录入 不允许提交 + //if (!await _repository.AnyAsync(t => t.PreviousHistoryList.Any() || t.PreviousOtherList.Any() || t.PreviousSurgeryList.Any())) + //{ + // return ResponseOutput.NotOk("没有临床数据,不允许提交"); + //} + + //return ResponseOutput.NotOk("没有签名临床数据,不允许提交"); + } + + + + + + var maxVisit = await _subjectVisitRepository.Where(t => t.SubjectId == dbSubjectVisit.SubjectId && t.SubmitState == SubmitStateEnum.Submitted) + .OrderByDescending(t => t.VisitNum).Select(t => new { t.Id, t.VisitNum }).FirstOrDefaultAsync(); + + //修改患者最新检查批次 + dbSubjectVisit.Subject.LatestSubjectVisitId = maxVisit == null ? dbSubjectVisit.Id : maxVisit.VisitNum < dbSubjectVisit.VisitNum ? dbSubjectVisit.Id : maxVisit.Id; + + //var maxVisitNum = maxVisit == null ? dbSubjectVisit.VisitNum : maxVisit.VisitNum < dbSubjectVisit.VisitNum ? dbSubjectVisit.VisitNum : maxVisit.VisitNum; + + ////判断是否有缺失影像 + //dbSubjectVisit.Subject.IsMissingImages = await _subjectVisitRepository.AnyAsync(t => (t.VisitNum < maxVisitNum && t.SubmitState != SubmitStateEnum.Submitted && t.IsLostVisit == false)); + + //项目或者Subject IsUrgent 提交时 检查批次也设置为紧急 + if (trialConfig.IsUrgent || dbSubjectVisit.Subject.IsUrgent || (dbSubjectVisit.PDState == PDStateEnum.PDProgress && !dbSubjectVisit.IsBaseLine) || (dbSubjectVisit.IsEnrollmentConfirm && dbSubjectVisit.IsBaseLine)) + { + if (dbSubjectVisit.PDState == PDStateEnum.PDProgress) + { + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(x => x.SubjectId == dbSubjectVisit.SubjectId && x.VisitNum <= dbSubjectVisit.VisitNum, x => new SubjectVisit() + { + IsUrgent = true + }); + + + + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => trialInOrderCriterionIdList.Contains(x.TrialReadingCriterionId)&& x.SubjectId == dbSubjectVisit.SubjectId&& + dbSubjectVisit.VisitNum<= x.VisitTaskNum && x.VisitTaskNum < Math.Ceiling(dbSubjectVisit.VisitNum+0.01m) // 当前的检查批次 全局 裁判 及之前 全都加急 + && x.ReadingTaskState != ReadingTaskState.HaveSigned && x.TaskState == TaskState.Effect, x => new VisitTask() + { + + IsUrgent = true, + TaskUrgentType = TaskUrgentType.PDProgress, + IsCanEditUrgentState=false, + }); + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => trialInOrderCriterionIdList.Contains(x.TrialReadingCriterionId) && x.SubjectId == dbSubjectVisit.SubjectId + && x.VisitTaskNum < dbSubjectVisit.VisitNum // 当前的检查批次 全局 裁判 及之前 全都加急 + && x.ReadingTaskState != ReadingTaskState.HaveSigned && x.TaskState == TaskState.Effect, x => new VisitTask() + { + + IsUrgent = true, + TaskUrgentType = TaskUrgentType.Other, + TaskUrgentRemake= "后续检查批次设为pd", + IsCanEditUrgentState = false, + }); + } + else if (dbSubjectVisit.IsEnrollmentConfirm) + { + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(x => x.Id == dbSubjectVisit.Id, x => new SubjectVisit() + { + IsUrgent = true + }); + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => trialInOrderCriterionIdList.Contains(x.TrialReadingCriterionId) && + x.VisitTaskNum>= dbSubjectVisit.VisitNum && x.VisitTaskNum < Math.Ceiling(dbSubjectVisit.VisitNum + 0.01m) // 当前的检查批次 全局 裁判 全都加急 + && x.ReadingTaskState != ReadingTaskState.HaveSigned && x.TaskState == TaskState.Effect, x => new VisitTask() + { + + IsUrgent = true, + TaskUrgentType = TaskUrgentType.EnrollmentConfirm, + IsCanEditUrgentState = false, + }); + } + else + { + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => trialInOrderCriterionIdList.Contains(x.TrialReadingCriterionId) && + x.VisitTaskNum >= dbSubjectVisit.VisitNum && x.VisitTaskNum < Math.Ceiling(dbSubjectVisit.VisitNum + 0.01m) // 当前的检查批次 全局 裁判 全都加急 + && x.ReadingTaskState != ReadingTaskState.HaveSigned && x.TaskState == TaskState.Effect, x => new VisitTask() + { + + IsUrgent = true, + TaskUrgentType = TaskUrgentType.VisitUrgent, + }); + } + + dbSubjectVisit.IsUrgent = true; + + + //PD确认的紧急会把前面所有未QC完成的检查批次均标记为紧急 + + var previosSVlist = await _subjectVisitRepository.Where(t => t.SubjectId == dbSubjectVisit.SubjectId && t.VisitNum < dbSubjectVisit.VisitNum && t.IsUrgent == false && t.SubmitState == SubmitStateEnum.Submitted, true).ToListAsync(); + + previosSVlist.ForEach(t => + { + t.IsUrgent = true; + }); + } + + + if (dbSubjectVisit.SubmitState == SubmitStateEnum.ToSubmit) + { + dbSubjectVisit.SubmitState = SubmitStateEnum.Submitted; + dbSubjectVisit.SubmitTime = DateTime.Now; + dbSubjectVisit.SubmitUserId = _userInfo.Id; + + //维护统一状态 + dbSubjectVisit.ReadingStatus = ReadingStatusEnum.ImageQuality; + + } + //不审 直接QC通过 可能一致性核查 也可能不一致性核查 + if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) + { + dbSubjectVisit.AuditState = AuditStateEnum.QCPassed; + + //维护统一状态 + dbSubjectVisit.ReadingStatus = ReadingStatusEnum.ConsistencyCheck; + + + + // 不一致性核查 就CVPassed ToForward 否则就是待核查 + dbSubjectVisit.CheckState = trialConfig.IsImageConsistencyVerification ? CheckStateEnum.ToCheck : CheckStateEnum.CVPassed; + + //维护统一状态 + dbSubjectVisit.ReadingStatus = trialConfig.IsImageConsistencyVerification ? ReadingStatusEnum.ConsistencyCheck : ReadingStatusEnum.TaskAllocate; + + + + if (dbSubjectVisit.CheckState == CheckStateEnum.CVPassed) + { + dbSubjectVisit.CheckPassedTime = DateTime.Now; + } + + dbSubjectVisit.ForwardState = trialConfig.IsImageConsistencyVerification ? ForwardStateEnum.None : ForwardStateEnum.ToForward; + + } + else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) + { + dbSubjectVisit.AuditState = AuditStateEnum.ToAudit; + + } + else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit) + { + dbSubjectVisit.AuditState = AuditStateEnum.ToAudit; + } + + //非基线设置为PD的话 或者设置为末次检查批次 根据配置自动生成阅片期 + if (!dbSubjectVisit.IsBaseLine && (dbSubjectVisit.PDState == PDStateEnum.PDProgress || dbSubjectVisit.IsFinalVisit) ) + { + + //该标准需要添加阅片期 + foreach (var trialReadingCriterionId in trialReadingCriterionIdList) + { + //不存在该阅片期 (重传、回退都会重新提交) + if (!await _repository.Where(t => t.SubjectVisitId == dbSubjectVisit.Id && t.ReadingSetType == ReadingSetType.ImageReading && t.TrialReadingCriterionId == trialReadingCriterionId).AnyAsync()) + { + + var newReadModule = await _repository.AddAsync(new ReadModule() + { + TrialReadingCriterionId = trialReadingCriterionId, + ReadingSetType = ReadingSetType.ImageReading, + SubjectVisitId = dbSubjectVisit.Id, + ModuleName = $"G-{dbSubjectVisit.VisitName}", + ModuleType = ModuleTypeEnum.Global, + IsUrgent = dbSubjectVisit.IsUrgent, + TrialId = dbSubjectVisit.TrialId, + SubjectId = dbSubjectVisit.SubjectId + }); + + } + + } + + + + } + + } + + + + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(success); + + + + } + + /// + /// 设置QC 通过或者不通过 7:QC failed 8:QC passed [签名 不需要对] + /// + /// + /// + /// + /// + [HttpPost("{trialId:guid}/{subjectVisitId:guid}/{auditState:int}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task QCPassedOrFailed(Guid trialId, Guid subjectVisitId, [FromRoute] AuditStateEnum auditState) + { + + if (!await _repository.AnyAsync(t => t.TrialId == trialId && t.UserId == _userInfo.Id)) + { + return ResponseOutput.NotOk("您已经被移出项目,没有操作权限。"); + } + + + if (auditState == AuditStateEnum.QCPassed) + { + //判断质疑是否都关闭了 + if (await _repository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.IsClosed == false)) + { + return ResponseOutput.NotOk("当前检查批次有影像质控质疑未关闭,不能进行此操作。"); + } + } + + + + var trialConfig = await _trialRepository + .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }) + .FirstOrDefaultAsync(t => t.TrialId == trialId) + .IfNullThrowException(); + + var dbSubjectVisit = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); + + await VerifyIsCanQCAsync(dbSubjectVisit); + + + //删除 软删除的物理文件 + var instancePathList = await _repository.Where(t => t.DicomSerie.IsDeleted && t.SubjectVisitId == subjectVisitId) + .Select(t => t.Path).ToListAsync(); + + //维护统一状态 + dbSubjectVisit.ReadingStatus = ReadingStatusEnum.ConsistencyCheck; + + //有人QC Passed + if (auditState == AuditStateEnum.QCPassed) + { + //判断 QC流程 不审 单审 双审 + + if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) + { + return ResponseOutput.NotOk("项目配置影像质控为不审,不需要设置为影像质控通过。"); + + } + else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) + { + if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) + { + if (!await _repository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == CurrentQC.First)) + { + return ResponseOutput.NotOk("影像质控审核问题没有保存,不能进行此操作。"); + } + + // 单审 + dbSubjectVisit.AuditState = AuditStateEnum.QCPassed; + dbSubjectVisit.CheckState = trialConfig.IsImageConsistencyVerification ? CheckStateEnum.ToCheck : CheckStateEnum.CVPassed; + dbSubjectVisit.ForwardState = trialConfig.IsImageConsistencyVerification ? ForwardStateEnum.None : ForwardStateEnum.ToForward; + dbSubjectVisit.PreliminaryAuditUserId = _userInfo.Id; + dbSubjectVisit.PreliminaryAuditTime = DateTime.Now; + + + //维护统一状态 + dbSubjectVisit.ReadingStatus = trialConfig.IsImageConsistencyVerification ? ReadingStatusEnum.ConsistencyCheck : ReadingStatusEnum.TaskAllocate; + + //删除影像 + instancePathList.ForEach(path => + { + + var physicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, path); + + //var dicomStudy = new DicomStudy() { Id = t.StudyId, SubjectId = t.SubjectId, TrialId = trialId, SiteId = t.SiteId, SubjectVisitId = subjectVisitId }; + //var (physicalPath, relativePath) = + + // FileStoreHelper.GetDicomInstanceFilePath(_hostEnvironment, dicomStudy.TrialId, dicomStudy.SiteId, dicomStudy.SubjectId, dicomStudy.SubjectVisitId, dicomStudy.Id, t.InstanceId); + + if (System.IO.File.Exists(physicalPath)) + { + File.Delete(physicalPath); + } + + }); + + } + else + { + return ResponseOutput.NotOk("项目配置影像质控为单审,当前检查批次影像质控任务不能从当前审核状态变更到 审核通过。"); + } + + + + } + else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit) + { + + // 双审 如果当前db 状态是 InPrimaryQC 当前操作为 QCPassed 那么设置为 PrimaryQCPassed + if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) + { + + if (!await _repository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == CurrentQC.First)) + { + return ResponseOutput.NotOk("影像质控审核问题没有保存,不能进行此操作。"); + } + + dbSubjectVisit.AuditState = AuditStateEnum.PrimaryQCPassed; + dbSubjectVisit.PreliminaryAuditUserId = _userInfo.Id; + dbSubjectVisit.PreliminaryAuditTime = DateTime.Now; + + + } + else if (dbSubjectVisit.AuditState == AuditStateEnum.InSecondaryQC) + { + + if (!await _repository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == CurrentQC.Second)) + { + return ResponseOutput.NotOk("影像质控审核问题没有保存,不能进行此操作。"); + } + + dbSubjectVisit.AuditState = AuditStateEnum.QCPassed; + + dbSubjectVisit.CheckState = trialConfig.IsImageConsistencyVerification ? CheckStateEnum.ToCheck : CheckStateEnum.CVPassed; + + dbSubjectVisit.ForwardState = trialConfig.IsImageConsistencyVerification ? ForwardStateEnum.None : ForwardStateEnum.ToForward; + + dbSubjectVisit.ReviewAuditUserId = _userInfo.Id; + + dbSubjectVisit.ReviewAuditTime = DateTime.Now; + + //维护统一状态 + dbSubjectVisit.ReadingStatus = trialConfig.IsImageConsistencyVerification ? ReadingStatusEnum.ConsistencyCheck : ReadingStatusEnum.TaskAllocate; + + //删除影像 + instancePathList.ForEach(path => + { + + var physicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, path); + + if (System.IO.File.Exists(physicalPath)) + { + File.Delete(physicalPath); + } + + }); + + } + else + { + return ResponseOutput.NotOk($"项目配置影像质控为双审。当前审核状态为 {dbSubjectVisit.AuditState},不能变更到 审核通过。"); + } + } + } + + else if (auditState == AuditStateEnum.QCFailed) + { + //判断 QC流程 不审 单审 双审 + + if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) + { + return ResponseOutput.NotOk("项目配置影像质控为不审,不允许设置影像质控终止。"); + } + else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) + { + // 单审 + if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) + { + dbSubjectVisit.AuditState = AuditStateEnum.QCFailed; + dbSubjectVisit.PreliminaryAuditUserId = _userInfo.Id; + + } + else + { + return ResponseOutput.NotOk("项目配置影像质控为单审,当前审核状态不为 InPrimaryQC,不能变更到 审核终止"); + } + } + else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit) + { + // 双审 + if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) + { + dbSubjectVisit.AuditState = AuditStateEnum.QCFailed; + dbSubjectVisit.PreliminaryAuditUserId = _userInfo.Id; + } + else if (dbSubjectVisit.AuditState == AuditStateEnum.InSecondaryQC) + { + dbSubjectVisit.AuditState = AuditStateEnum.QCFailed; + dbSubjectVisit.ReviewAuditUserId = _userInfo.Id; + } + else + { + return ResponseOutput.NotOk($"项目配置影像质控为双审,当前审核状态为 {dbSubjectVisit.AuditState},不能变更到 审核终止"); + } + + + } + + await _qcChallengeRepository.UpdatePartialFromQueryAsync(t => t.IsClosed == false&& t.SubjectVisitId==dbSubjectVisit.Id, u => new QCChallenge() { IsClosed = true, ClosedTime=DateTime.Now, CloseResonEnum = QCChallengeCloseEnum.Unresolvable }); + + + } + dbSubjectVisit.Auditor = _userInfo.Id; + dbSubjectVisit.IsTake = false; + dbSubjectVisit.CurrentActionUserId = null; + dbSubjectVisit.CurrentActionUserExpireTime = null; + + await _repository.SaveChangesAsync(); + + + return ResponseOutput.Result(true); + + + } + + + + + /// + /// 设置、取消 检查批次紧急 + /// + /// + /// + /// + /// + [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{setOrCancel:bool}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + // [Authorize(Policy = IRaCISPolicy.IQC)] + public async Task SetVisitUrgent(Guid trialId, Guid subjectVisitId, bool setOrCancel) + { + var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); + + sv.IsUrgent = setOrCancel; + var success = await _repository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } + + #endregion + + + #region 重传、重传完设置 + + /// 验证质疑对话页面 是否可以 跳转到重传页面 进行重传 + [HttpGet("{trialId:guid}/{qcChallengeId:guid}")] + public async Task VerifyReuploadIsCanJump(Guid trialId, Guid qcChallengeId) + { + var qcChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qcChallengeId)).IfNullThrowException(); + + if (qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.QCAgreeUpload) + { + throw new BusinessValidationFailedException("当前重传状态不为QC同意重传,不允许重传"); + } + return ResponseOutput.Ok(); + } + + + /// + /// QA设置 同意重传 + /// + /// + /// + /// + [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{qcChallengeId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetNeedReupload(Guid trialId, Guid qcChallengeId) + { + + //获取项目配置 + var trialConfig = await _repository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }) + .FirstOrDefaultAsync().IfNullThrowException(); + + if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) + { + return ResponseOutput.NotOk("不审操作,不会有需要重传的操作!"); + } + + var qcChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qcChallengeId)).IfNullThrowException(); + + var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == qcChallenge.SubjectVisitId)).IfNullThrowException(); + + await VerifyIsCanQCAsync(sv); + + + if (qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.CRCRequestReupload) + { + throw new BusinessValidationFailedException("当前重传状态不为IC申请重传,不允许设置同意重传"); + } + + + if (await _qcChallengeRepository.CountAsync(t => t.ReuploadEnum == QCChanllengeReuploadEnum.QCAgreeUpload && t.SubjectVisitId == qcChallenge.SubjectVisitId && t.IsClosed == false) >= 1) + { + return ResponseOutput.NotOk("当前检查批次,有一个未关闭的质疑 QC设置了同意重传,IC还未完成上传,当前不允许再次设置"); + } + + + + qcChallenge.ReuploadEnum = QCChanllengeReuploadEnum.QCAgreeUpload; + qcChallenge.LatestMsgTime = DateTime.Now; + qcChallenge.LatestReplyUserId = _userInfo.Id; + + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == qcChallenge.SubjectVisitId, c => new SubjectVisit() { IsQCConfirmedReupload = true }); + + qcChallenge.DialogList.Add(new QCChallengeDialog() + { + SubjectVisitId = qcChallenge.SubjectVisitId, + UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, + QCChallengeId = qcChallenge.Id, + TalkContent = "QC同意重传" + }); + + //双审 并且是2QC 那么需要回退到1QC 讲1QC数据清除 + if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit && await _repository.AnyAsync(t => t.Id == qcChallengeId && t.SubjectVisit.AuditState == AuditStateEnum.InSecondaryQC)) + { + + + + // //一致性核查质疑状态 + // sv.CheckChallengeState = CheckChanllengeTypeEnum.None; + //// 一致性核查状态 + // sv.CheckState = CheckStateEnum.None; + // 审核状态 + sv.AuditState = AuditStateEnum.InPrimaryQC; + + sv.CurrentActionUserExpireTime = DateTime.Now.AddHours(1); + sv.CurrentActionUserId = _userInfo.Id; + + //BackgroundJob.Schedule(t => t.CancelQCObtaion(qcChallenge.SubjectVisitId, DateTime.Now), TimeSpan.FromHours(1)); + sv.IsTake = true; + + sv.PreliminaryAuditUserId = null; + sv.ReviewAuditUserId = null; + + + //删除1QC 填写的问题答案 + + await _repository.BatchDeleteAsync(t => t.SubjectVisitId == qcChallenge.SubjectVisitId && t.CurrentQCEnum == CurrentQC.First); + + //2QC 数据变为1QC + await _repository.BatchUpdateAsync(t => t.SubjectVisitId == qcChallenge.SubjectVisitId && t.CurrentQCEnum == CurrentQC.Second, k => new QCChallenge() { CurrentQCEnum = CurrentQC.First }); + await _repository.BatchUpdateAsync(t => t.SubjectVisitId == qcChallenge.SubjectVisitId && t.CurrentQCEnum == CurrentQC.Second, k => new TrialQCQuestionAnswer() { CurrentQCEnum = CurrentQC.First }); + + + + } + + + var success = await _repository.SaveChangesAsync(); + + + return ResponseOutput.Result(success); + + } + + /// + /// IC 设置已经重传完成 [需要签名 不需要对] + /// + + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetReuploadFinished(CRCReuploadFinishedCommand cRCReuploadFinishedCommand) + { + + var qcChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == cRCReuploadFinishedCommand.QCChallengeId)).IfNullThrowException(); + + if (qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.QCAgreeUpload) + { + throw new BusinessValidationFailedException("当前重传状态不为QC同意重传,不允许设置重传完成"); + } + + + var subjectVisitId = qcChallenge.SubjectVisitId; + + if (await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).SelectMany(t => t.StudyList).CountAsync() == 0 && + await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).SelectMany(t => t.NoneDicomStudyList).SelectMany(u => u.NoneDicomFileList).CountAsync() == 0) + { + throw new BusinessValidationFailedException("当前没有影像,不允许设置重传完成"); + } + + var trialConfig = await _trialRepository + .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification, t.IsUrgent, t.IsHaveFirstGiveMedicineDate, t.ClinicalInformationTransmissionEnum }) + .Where(t => t.TrialId == cRCReuploadFinishedCommand.TrialId).FirstOrDefaultAsync(); + + + + qcChallenge.ReuploadEnum = QCChanllengeReuploadEnum.CRCReuploaded; + + qcChallenge.ReUploadedTime = DateTime.Now; + + qcChallenge.ReUploadUserId = _userInfo.Id; + + qcChallenge.ReUploader = _userInfo.RealName; + + qcChallenge.LatestMsgTime = DateTime.Now; + + qcChallenge.LatestReplyUserId = _userInfo.Id; + + var dbSubjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == qcChallenge.SubjectVisitId).IfNullThrowException(); + + + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == qcChallenge.SubjectVisitId, c => new SubjectVisit() { IsQCConfirmedReupload = false }); + + qcChallenge.DialogList.Add(new QCChallengeDialog() + { + SubjectVisitId = qcChallenge.SubjectVisitId, + + UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, + + QCChallengeId = qcChallenge.Id, + + TalkContent = "IC已重传完成" + }); + + + //基线 且配置了临床数据 + if (trialConfig.ClinicalInformationTransmissionEnum != 0 && dbSubjectVisit.IsBaseLine) + { + //已确认临床数据完整性 + dbSubjectVisit.IsConfirmedClinicalData = true; + + //var signSuccess = await _repository.BatchUpdateAsync(t => t.Id == cRCReuploadFinishedCommand.SignId, u => new TrialSign() { IsCompleted = true }); + } + + var success = await _subjectVisitRepository.SaveChangesAsync(); + + + return ResponseOutput.Result(success); + + } + + + + [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{qcChallengeId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task CRCRequestReUpload(Guid qcChallengeId) + { + var qcChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qcChallengeId)).IfNullThrowException(); + + + if (qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.None && qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.CRCReuploaded) + { + throw new BusinessValidationFailedException("当前质疑重传状态不为初始状态|IC重传完成状态,不允许申请重传"); + } + + + if (qcChallenge.ReuploadEnum == QCChanllengeReuploadEnum.CRCReuploaded) + { + qcChallenge.ReUploadedTime = null; + } + + qcChallenge.LatestMsgTime = DateTime.Now; + qcChallenge.LatestReplyUserId = _userInfo.Id; + qcChallenge.ReuploadEnum = QCChanllengeReuploadEnum.CRCRequestReupload; + + qcChallenge.DialogList.Add(new QCChallengeDialog() + { + SubjectVisitId = qcChallenge.SubjectVisitId, + UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, + QCChallengeId = qcChallenge.Id, + TalkContent = "IC申请重传/上传影像" + }); + + var isSuccess = await _qcChallengeRepository.SaveChangesAsync(); + + return ResponseOutput.Result(isSuccess); + } + + #endregion + + + /// + /// 上传界面 更新患者检查批次基准日期 是否入组确认,以及检查批次 是否PD进展 + /// + /// + /// + + [HttpPut("{trialId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task UpdateSubjectAndSVInfo(UploadSubjectAndVisitCommand command) + { + var dbSubjectVisit = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == command.SubjectVisitId)).IfNullThrowException(); + + dbSubjectVisit.PDState = command.PDState; + + if (command.IsEnrollmentConfirm != null) + { + if (await _subjectVisitRepository.Where(t => t.Id == command.SubjectVisitId) + .AnyAsync(t => t.SubmitState == SubmitStateEnum.Submitted && t.IsEnrollmentConfirm != command.IsEnrollmentConfirm)) + { + return ResponseOutput.NotOk("该检查批次已提交,不能修改入组确认状态"); + } + + if (await _subjectVisitRepository.Where(t => t.Id == command.SubjectVisitId) + .AnyAsync(t => t.IsEnrollmentConfirm != command.IsEnrollmentConfirm && t.RequestBackState == RequestBackStateEnum.PM_AgressBack)) + { + return ResponseOutput.NotOk("该检查批次为回退检查批次,不允许修改PD确认状态"); + } + + dbSubjectVisit.IsEnrollmentConfirm = command.IsEnrollmentConfirm.Value; + } + + if (command.SubjectFirstGiveMedicineTime != null) + { + await _subjectRepository.UpdatePartialFromQueryAsync(command.SubjectId, u => new Subject() { FirstGiveMedicineTime = command.SubjectFirstGiveMedicineTime, }, true); + } + + await _subjectVisitRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/QC/QCQuestionService.cs b/IRaCIS.Core.Application/Service/QC/QCQuestionService.cs new file mode 100644 index 0000000..32c51fa --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/QCQuestionService.cs @@ -0,0 +1,181 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-11 11:04:54 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Infrastructure; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// 系统QC 问题管理 + /// + [ApiExplorerSettings(GroupName = "Image")] + public class QCQuestionConfigureService : BaseService, IQCQuestionService + { + private readonly IRepository _qcQuestionRepository; + private readonly IRepository _trialQCQuestionRepository; + + public QCQuestionConfigureService(IRepository qcQuestionRepository, + IRepository trialQCQuestionRepository + + ) + { + _qcQuestionRepository = qcQuestionRepository; + this._trialQCQuestionRepository = trialQCQuestionRepository; + } + + + /// + /// 父问题 下拉框选项 需要排除自己 、把自己设置为父亲 (互为父亲) 、是自己孙辈的(明明是自己子孙,却设置为自己父亲) + /// + /// + /// + [HttpPost] + public async Task> GetQCQuestionSelectList(QCQuestionFilterSelect trialQCQuestionFilterSelect) + { + + //设置父亲的时候,不允许设置为自己的孙子 这种会形成环 + + var initList = await _qcQuestionRepository + .WhereIf(trialQCQuestionFilterSelect.TypeArray.Count() > 0, t => trialQCQuestionFilterSelect.TypeArray.Contains(t.Type)) + .WhereIf(trialQCQuestionFilterSelect.Id != null, t => t.Id != trialQCQuestionFilterSelect.Id && t.ParentId != trialQCQuestionFilterSelect.Id) + .OrderBy(t => t.ShowOrder).Select(x => new TrialQCQuestionSelect() { + ShowOrder = x.ShowOrder, + Id = x.Id, + ParentId = x.ParentId, + QuestionName = x.QuestionName, + TypeValue = x.TypeValue, + + }).ToListAsync(); + + //父亲的序号肯定要比自己小 + if (trialQCQuestionFilterSelect.Id != null) + { + var selectItem = initList.FirstOrDefault(t => t.Id == trialQCQuestionFilterSelect.Id); + + initList = initList.WhereIf(selectItem != null, t => t.Id != selectItem!.Id && t.ShowOrder < selectItem.ShowOrder).ToList(); + } + + + var exceptList = GetChildId(trialQCQuestionFilterSelect.Id ?? Guid.Empty, initList); + + + return initList.Where(t => !exceptList.Contains(t.Id)).ToList(); + } + + private List GetChildId(Guid parentId, List list) + { + + var ids = new List(); + + var childIds = list.Where(t => t.ParentId == parentId).Select(t => t.Id).ToList(); + + foreach (var childId in childIds) + { + ids.AddRange(childId); + + var childs = GetChildId(childId, list); + + ids.AddRange(childs); + + } + + return ids; + } + + + [HttpPost] + public async Task> GetQCQuestionConfigureList(QCQuestionQuery queryQCQuestionConfigure) + { + + var QCQuestionQueryable = _qcQuestionRepository + .WhereIf(queryQCQuestionConfigure.IsEnable != null,x=>x.IsEnable== queryQCQuestionConfigure.IsEnable) + .WhereIf(!string.IsNullOrWhiteSpace(queryQCQuestionConfigure.QuestionName), t => t.QuestionName.Contains(queryQCQuestionConfigure.QuestionName)) + .WhereIf(!string.IsNullOrWhiteSpace(queryQCQuestionConfigure.Type), t => t.Type.Contains(queryQCQuestionConfigure.Type)) + .WhereIf(queryQCQuestionConfigure.IsDefeaultViewParent==true,t=>t.ParentId==null) + .OrderBy(t=>t.ShowOrder) + .ProjectTo(_mapper.ConfigurationProvider); + + return await QCQuestionQueryable.ToListAsync(); + } + + public async Task AddOrUpdateQCQuestionConfigure(QCQuestionAddOrEdit addOrEditQCQuestionConfigure) + { + + if (await _qcQuestionRepository.AnyAsync(x => x.Id != addOrEditQCQuestionConfigure.Id && x.ShowOrder == addOrEditQCQuestionConfigure.ShowOrder)) + { + throw new BusinessValidationFailedException("序号重复,操作失败"); + + } + + var entity = await _qcQuestionRepository.InsertOrUpdateAsync(addOrEditQCQuestionConfigure, true); + return ResponseOutput.Ok(entity.Id.ToString()); + } + + + [HttpDelete("{qCQuestionConfigureId:guid}")] + public async Task DeleteQCQuestionConfigure(Guid qCQuestionConfigureId) + { + if (await _qcQuestionRepository.AnyAsync(x => x.ParentId == qCQuestionConfigureId)) + { + throw new BusinessValidationFailedException("当前任务存在子问题,删除失败"); + } + await _qcQuestionRepository.DeleteFromQueryAsync(t => t.Id == qCQuestionConfigureId,true); + return ResponseOutput.Ok(); + } + + + /// + /// 获取问题预览 + /// + /// + /// + /// 传TrialId为获取项目的 + /// 不传为获取系统的 + /// + /// + [HttpPost] + public async Task> GetQuestionView(QCQuestionViewInDto inDto) + { + var question = new List(); + if (inDto.TrialId!= null) + { + question = await _trialQCQuestionRepository.Where(x => x.TrialId == inDto.TrialId&&x.IsEnable).OrderBy(x => x.ShowOrder).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + else + { + question=await _qcQuestionRepository.Where(x=>x.IsEnable).OrderBy(x => x.ShowOrder).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + + var result = question.Where(x => x.ParentId == null).ToList(); + result.ForEach(x => { + GetQuestionChild(x, question); + }); + + return result; + } + + + + private void GetQuestionChild(QCQuestionView parent, List dataList) + { + parent.Childrens = dataList.Where(x => x.ParentId == parent.Id).ToList(); + + if (parent.Childrens.Count != 0) + { + parent.Childrens.ForEach(x => + { + GetQuestionChild(x, dataList); + }); + } + + + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/QC/TrialQCQuestionService.cs b/IRaCIS.Core.Application/Service/QC/TrialQCQuestionService.cs new file mode 100644 index 0000000..be1fe45 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/TrialQCQuestionService.cs @@ -0,0 +1,293 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-11 11:04:54 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.Auth; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Infrastructure; +using MassTransit; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// 项目QC 问题 管理 + /// + [ApiExplorerSettings(GroupName = "Image")] + public class TrialQCQuestionConfigureService : BaseService, ITrialQCQuestionConfigureService + { + private readonly IRepository _trialQcQuestionRepository; + private readonly IRepository _qCQuestionRepository; + + public TrialQCQuestionConfigureService( + IRepository trialQcQuestionRepository, + IRepository qCQuestionRepository + ) + { + _trialQcQuestionRepository = trialQcQuestionRepository; + this._qCQuestionRepository = qCQuestionRepository; + } + + + [HttpPost] + public async Task<(List, object)> GetTrialQCQuestionConfigureList(TrialQCQuestionQuery queryTrialQCQuestionConfigure) + { + + + var trialQCQuestionQueryable = _trialQcQuestionRepository.Where(t => t.TrialId == queryTrialQCQuestionConfigure.TrialId) + .WhereIf(!string.IsNullOrWhiteSpace(queryTrialQCQuestionConfigure.QuestionName), t => t.QuestionName.Contains(queryTrialQCQuestionConfigure.QuestionName)) + .WhereIf(!string.IsNullOrWhiteSpace(queryTrialQCQuestionConfigure.Type), t => t.Type.Contains(queryTrialQCQuestionConfigure.Type)) + .WhereIf(queryTrialQCQuestionConfigure.IsEnable != null, t => t.IsEnable == queryTrialQCQuestionConfigure.IsEnable) + .WhereIf(queryTrialQCQuestionConfigure.IsRequired != null, t => t.IsRequired == queryTrialQCQuestionConfigure.IsRequired) + + + .ProjectTo(_mapper.ConfigurationProvider); + + var list = await trialQCQuestionQueryable.OrderBy(t => t.ShowOrder).ToListAsync(); + + var isHaveQCQuestion = _repository.Where(t => t.TrialId == queryTrialQCQuestionConfigure.TrialId).Any(); + + + var signInfo = await _repository.Where(t => t.Id == queryTrialQCQuestionConfigure.TrialId) + .Select(trial => new + { + trial.QCProcessEnum, + trial.QCQuestionConfirmedTime, + trial.QCQuestionConfirmedUserId, + RealName = trial.QCQuestionConfirmedUser.FullName, + trial.QCQuestionConfirmedUser.UserName, + IsHaveQCQuestion = isHaveQCQuestion + }).FirstOrDefaultAsync(); + + return (list, signInfo!); + } + + + /// + /// 父问题 下拉框选项 需要排除自己 、把自己设置为父亲 (互为父亲) 、是自己孙辈的(明明是自己子孙,却设置为自己父亲) + /// + /// + /// + [HttpPost] + public async Task> GetTrialQCQuestionSelectList(TrialQCQuestionFilterSelect trialQCQuestionFilterSelect) + { + + //设置父亲的时候,不允许设置为自己的孙子 这种会形成环 + + var initList = await _trialQcQuestionRepository.Where(t => t.TrialId == trialQCQuestionFilterSelect.TrialId) + .WhereIf(trialQCQuestionFilterSelect.TypeArray.Count() > 0, t => trialQCQuestionFilterSelect.TypeArray.Contains(t.Type)) + .WhereIf(trialQCQuestionFilterSelect.Id != null, t => t.Id != trialQCQuestionFilterSelect.Id && t.ParentId != trialQCQuestionFilterSelect.Id) + .OrderBy(t => t.ShowOrder).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + //父亲的序号肯定要比自己小 + if (trialQCQuestionFilterSelect.Id != null) + { + var selectItem = initList.FirstOrDefault(t => t.Id == trialQCQuestionFilterSelect.Id); + + initList = initList.WhereIf(selectItem != null, t => t.Id != selectItem!.Id && t.ShowOrder < selectItem.ShowOrder).ToList(); + } + + + var exceptList = GetChildId(trialQCQuestionFilterSelect.Id ?? Guid.Empty, initList); + + + return initList.Where(t => !exceptList.Contains(t.Id)).ToList(); + } + + + private List GetChildId(Guid parentId, List list) + { + + var ids = new List(); + + var childIds = list.Where(t => t.ParentId == parentId).Select(t => t.Id).ToList(); + + foreach (var childId in childIds) + { + ids.AddRange(childId); + + var childs = GetChildId(childId, list); + + ids.AddRange(childs); + + } + + return ids; + } + + + public async Task VerifyIsQCConfirmedAsync(Guid trialId) + { + if (!await _repository.AnyAsync(t => t.Id == trialId && t.QCQuestionConfirmedUserId == null)) + { + throw new BusinessValidationFailedException("影像质控审核问题模板已经确认,不允许操作。"); + } + } + + /// + /// 批量添加 QC 问题 + /// + /// + /// + /// + [HttpPost("{trialId:guid}")] + // [Authorize(Policy = IRaCISPolicy.IQC)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task BatchAddTrialQCQuestionConfigure(List batchList, Guid trialId) + { + + await VerifyIsQCConfirmedAsync(trialId); + + var maxShowOrder = await _trialQcQuestionRepository.Where(x => x.TrialId == trialId).OrderByDescending(x => x.ShowOrder).Select(x => x.ShowOrder).FirstOrDefaultAsync(); + maxShowOrder++; + + #region OLd + //batchList.ForEach(x => + //{ + // maxShowOrder++; + // x.ShowOrder = maxShowOrder; + // x.SystemQuestionId = x.Id.Value; + // x.Id = NewId.NextGuid(); + + //}); + + //foreach (var item in batchList.Where(x => x.ParentId != null)) + //{ + // var parent = batchList.Where(x => x.SystemQuestionId == item.ParentId).FirstOrDefault(); + // if (parent == null) + // { + // item.ParentId = null; + // item.ParentTriggerValue = String.Empty; + // } + // else + // { + // item.ParentId = parent.Id; + // } + //} + + #endregion + + #region New + //查询所有的子问题 + var list = await _qCQuestionRepository.Where(t => t.ParentId != null).ToListAsync(); + + var mappedList = _mapper.Map>(list); + + var childList = new List(); + + //遍历父层级的问题 + var batchConfigList = _mapper.Map>(batchList.Where(t => t.ParentId == null)); + + foreach (var item in batchConfigList.Where(t => t.ParentId == null).OrderBy(t => t.ShowOrder)) + { + var oldParentId = item.Id; + item.Id = NewId.NextGuid(); + item.TrialId = trialId; + item.ShowOrder = maxShowOrder++; + + var findChildList = GetChildList(oldParentId, item.Id, mappedList); + + + foreach (var findChild in findChildList) + { + + findChild.TrialId = trialId; + + findChild.ShowOrder = maxShowOrder++; + } + + childList.AddRange(findChildList); + } + + + + #endregion + + + await _trialQcQuestionRepository.AddRangeAsync(batchConfigList); + + await _trialQcQuestionRepository.AddRangeAsync(childList); + + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Result(success); + } + + + private List GetChildList(Guid parentId, Guid newParentId, List list) + { + var childList = new List(); + + var findlist = list.Where(t => t.ParentId == parentId).ToList(); + + foreach (var child in findlist) + { + var oldParentId = child.Id; + + child.Id = NewId.NextGuid(); + child.ParentId = newParentId; + + childList.Add(child); + + var findList = GetChildList(oldParentId, child.Id, list); + + childList.AddRange(findList); + + } + + return childList; + } + + // [Authorize(Policy = IRaCISPolicy.IQC)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddOrUpdateTrialQCQuestionConfigure(TrialQCQuestionAddOrEdit addOrEditTrialQCQuestionConfigure) + { + + + if (await _trialQcQuestionRepository.AnyAsync(x => x.TrialId == addOrEditTrialQCQuestionConfigure.TrialId && x.Id != addOrEditTrialQCQuestionConfigure.Id && x.ShowOrder == addOrEditTrialQCQuestionConfigure.ShowOrder)) + { + throw new BusinessValidationFailedException("序号重复,操作失败"); + + } + + await VerifyIsQCConfirmedAsync(addOrEditTrialQCQuestionConfigure.TrialId); + + var entity = await _trialQcQuestionRepository.InsertOrUpdateAsync(addOrEditTrialQCQuestionConfigure, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + } + + + + + + [HttpDelete("{trialId:guid}/{trialQCQuestionConfigureId:guid}")] + // [Authorize(Policy = IRaCISPolicy.IQC)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task DeleteTrialQCQuestionConfigure(Guid trialQCQuestionConfigureId, Guid trialId) + { + await VerifyIsQCConfirmedAsync(trialId); + + if (await _trialQcQuestionRepository.AnyAsync(t => t.ParentId == trialQCQuestionConfigureId)) + { + return ResponseOutput.NotOk("请在删除父问题前,请先删除引用该父问题的子问题。"); + } + + if (await _repository.AnyAsync(t => t.TrialQCQuestionConfigureId == trialQCQuestionConfigureId)) + { + return ResponseOutput.NotOk("该审核问题已被影像质控过程引用,不允许删除。"); + } + + await _trialQcQuestionRepository.DeleteFromQueryAsync(t => t.Id == trialQCQuestionConfigureId, true); + return ResponseOutput.Result(true); + } + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/QC/_MapConfig.cs b/IRaCIS.Core.Application/Service/QC/_MapConfig.cs new file mode 100644 index 0000000..c9db9f8 --- /dev/null +++ b/IRaCIS.Core.Application/Service/QC/_MapConfig.cs @@ -0,0 +1,561 @@ +using AutoMapper; +using AutoMapper.EquivalencyExpression; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Core.Application.MediatR.CommandAndQueries; +using IRaCIS.Core.Domain.Share; +using static IRaCIS.Core.Application.Contracts.SubjectProgressDto; + +namespace IRaCIS.Core.Application.Service +{ + public class QCConfig : Profile + { + public QCConfig() + { + + #region 导出列表 + + + var isEn_Us = false; + + CreateMap(); + CreateMap() + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) + .ForMember(d => d.SubmitUserName, u => u.MapFrom(s => s.SubmitUser.FullName)) + + .ForMember(d => d.IsHaveClinicalData, u => u.MapFrom(t => t.IsBaseLine ? t.PreviousHistoryList.Any() || t.PreviousOtherList.Any() + || t.ReadingClinicalDataList.Any(x => x.ClinicalDataTrialSet.UploadRole == Domain.Share.UploadRole.CRC && x.ReadingClinicalDataPDFList.Count() > 0) + || t.PreviousSurgeryList.Any() : false)); + + + + CreateMap() + + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.SubjectVisit.Subject.Code)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.SubjectVisit.TrialSite.TrialSiteCode)) + .ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)) + + .ForMember(d => d.CreateUserName, u => u.MapFrom(s => s.CreateUser.UserName)) + .ForMember(d => d.LatestReplyUserName, u => u.MapFrom(t => t.LatestReplyUser.UserName)) + + .ForMember(d => d.DialogStr, u => u.MapFrom(t => string.Join(" | ", t.DialogList.OrderBy(t => t.CreateTime).Select(c => c.CreateUser.UserName + " " + c.CreateTime.ToString("yyyy-mm-dd hh:mm:ss") + " :" + c.TalkContent)))) + + .ForMember(d => d.SubjectState, u => u.MapFrom(s => s.SubjectVisit.Subject.Status)); + + + CreateMap() + .ForMember(d => d.SiteName, u => u.MapFrom(s => s.TrialSite.Site.SiteName)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + .ForMember(d => d.LatestBlindName, u => u.MapFrom(s => s.LatestSubjectVisit.BlindName)) + .ForMember(d => d.LatestVisitName, u => u.MapFrom(s => s.LatestSubjectVisit.VisitName)) + .ForMember(d => d.FinalSubjectVisitName, u => u.MapFrom(s => s.FinalSubjectVisit.VisitName)) + .ForMember(d => d.InPlanVisitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.InPlan))) + .ForMember(d => d.OutPlanVisitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.InPlan == false))) + //执行不一定上传了 可能是失访 实际执行过了 + .ForMember(d => d.MissingSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.VisitNum < s.LatestSubjectVisit.VisitNum && t.SubmitState != SubmitStateEnum.Submitted && t.IsLostVisit == false))) + .ForMember(d => d.InPlanVisitSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.SubmitState == SubmitStateEnum.Submitted && t.InPlan == true))) + .ForMember(d => d.LostVisitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.IsLostVisit))) + .ForMember(d => d.OutPlanVisitSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.SubmitState == SubmitStateEnum.Submitted && t.InPlan == false))) + + .ForMember(d => d.IsPDProgress, u => u.MapFrom(s => s.SubjectVisitList.Any(t => t.IsEnrollmentConfirm == true))) + .ForMember(d => d.IsEnrollmentConfirmed, u => u.MapFrom(s => s.SubjectVisitList.Any(t => t.PDState == PDStateEnum.PDProgress))) + + .ForMember(d => d.RadiologyClinicalDataCount, u => u.MapFrom(s => s.ClinicalDataList.Count(t => t.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.ImageRead))) + .ForMember(d => d.OncologyClinicalDataCount, u => u.MapFrom(s => s.ClinicalDataList.Count(t => t.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.OncologyRead))) + + .ForMember(d => d.ChallengeWaitReplyCount, u => u.MapFrom(s => s.SubjectVisitList.SelectMany(c => c.QCChallengeList).Count(t => t.IsClosed == false && !t.DialogList.Any(t => t.CreateUser.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator)))) + .ForMember(d => d.CheckWaitReplyCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.CheckState == CheckStateEnum.CVIng && t.CheckChallengeState != CheckChanllengeTypeEnum.Closed && !t.CheckChallengeDialogList.Any(t => t.CreateUser.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator)))) + ; + + CreateMap() + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Code)) + .ForMember(d => d.SubjectStatus, u => u.MapFrom(s => s.Status)) + .ForMember(d => d.VisitList, u => u.MapFrom(s => s.SubjectVisitList.Where(t => t.VisitExecuted != VisitExecutedEnum.Unavailable) + )); + + //CreateMap () + // .ForMember(d => d.TaskName, u => u.MapFrom(s => s.VisitName)) + // .ForMember(d => d.VisitTaskNum, u => u.MapFrom(s => s.VisitNum)) + // .ForMember(d => d.Inplan, u => u.MapFrom(s => s.InPlan)) + // .ForMember(d => d.ReadingStatus, u => u.MapFrom(s => s.VisitTaskList + // .Count(t=>t.TaskState==TaskState.Effect && t.IsAnalysisCreate==false && t.ReadingCategory==ReadingCategory.Visit && t.ReadingTaskState==ReadingTaskState.HaveSigned)==s. s.ReadingStatus)) + + + + + + + CreateMap() + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.Subject.TrialSite.TrialSiteCode)) + .ForMember(d => d.DeadlineVisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)) + .ForMember(d => d.EarliestScanDate, u => u.MapFrom(s => s.SubjectVisit.EarliestScanDate)) + .ForMember(d => d.LatestScanDate, u => u.MapFrom(s => s.SubjectVisit.LatestScanDate)) + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) + .ForMember(d => d.SubjectStatus, u => u.MapFrom(s => s.Subject.Status)) + .ForMember(d => d.FirstGiveMedicineTime, u => u.MapFrom(s => s.Subject.FirstGiveMedicineTime)) + .ForMember(d => d.TrialReadingCriterionName, u => u.MapFrom(s => s.TrialReadingCriterion.CriterionName)); + + + CreateMap() + .ForMember(d => d.TalkContent, u => u.MapFrom(s => s.CheckChallengeDialogList.OrderByDescending(y => y.CreateTime).Select(x => x.TalkContent).FirstOrDefault())) + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + + .ForMember(d => d.CheckDialogStr, u => u.MapFrom(t => string.Join(" | ", t.CheckChallengeDialogList.OrderBy(t => t.CreateTime).Select(c => c.CreateUser.UserName + " " + c.CreateTime.ToString("yyyy-mm-dd hh:mm:ss") + " :" + c.TalkContent)))) + .ForMember(d => d.ModalityList, c => c.MapFrom(s => + (s.NoneDicomStudyList.Select(t => t.Modality) + .Union(s.StudyList.Select(k => k.ModalityForEdit))).Distinct())) + ; + + CreateMap() + .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName)) + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.IsSelfAnalysis == true ? u.BlindTrialSiteCode : u.Subject.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.IsSelfAnalysis == true ? u.BlindSubjectCode : u.Subject.Code)) + .ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode)) + .ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)) + .ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName)) + .ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName)) + .ForMember(o => o.SubjectStatus, t => t.MapFrom(u => u.Subject.Status)); + + + + CreateMap().IncludeMembers(t => t.OriginalReReadingTask) + .ForMember(o => o.ReReadingNewTaskCode, t => t.MapFrom(u => u.NewReReadingTask.TaskCode)) + /*.ForMember(o => o.ApplyTask, t => t.MapFrom(u => u.OriginalReReadingTask))*/; + + CreateMap() + .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName)) + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.IsSelfAnalysis == true ? u.BlindTrialSiteCode : u.Subject.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.IsSelfAnalysis == true ? u.BlindSubjectCode : u.Subject.Code)) + .ForMember(o => o.UserCode, t => t.MapFrom(u => u.DoctorUser.UserCode)) + .ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)) + .ForMember(o => o.FullName, t => t.MapFrom(u => u.DoctorUser.FullName)) + .ForMember(o => o.UserTypeShortName, t => t.MapFrom(u => u.DoctorUser.UserTypeRole.UserTypeShortName)) + .ForMember(o => o.SubjectStatus, t => t.MapFrom(u => u.Subject.Status)); + + CreateMap().IncludeMembers(t => t.VisitTask) + + .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.VisitTask.TrialReadingCriterion.CriterionName)) + .ForMember(o => o.MedicalNo, t => t.MapFrom(u => u.VisitTask.Subject.MedicalNo)) + //.ForMember(o => o.DoctorUserName, t => t.MapFrom(u => u.VisitTask.DoctorUser.UserName)) + .ForMember(o => o.MedicalManagerUserName, t => t.MapFrom(u => u.MedicalManagerUser.UserName)); + + CreateMap() + .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName)) + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.IsSelfAnalysis == true ? u.BlindTrialSiteCode : u.Subject.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.IsSelfAnalysis == true ? u.BlindSubjectCode : u.Subject.Code)); + + + CreateMap() + .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName)) + .ForMember(o => o.CriterionType, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionType)) + + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.Subject.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.Subject.Code)) + .ForMember(o => o.IsBaseline, t => t.MapFrom(u => u.SourceSubjectVisit.IsBaseLine)) + .ForMember(o => o.EvaluateResult, t => t.MapFrom(u => + u.SourceSubjectVisit.IsBaseLine? u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.ExistDisease).FirstOrDefault().Answer + : u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.Tumor).FirstOrDefault().Answer )) + + .ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)); + + CreateMap().IncludeBase(); + CreateMap().IncludeBase(); + + + + CriterionType? criterionType = null; + CreateMap() + // .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName)) + .ForMember(o => o.IsBaseline, t => t.MapFrom(u => u.SourceSubjectVisit.IsBaseLine)) + + // .ForMember(o => o.OverallTumorEvaluationResult, t => t.MapFrom(u => + //criterionType == CriterionType.RECIST1Pointt1 ?( u.SourceSubjectVisit.IsBaseLine==true ? u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.ExistDisease).FirstOrDefault().Answer: + //u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.Tumor).FirstOrDefault().Answer) + // : criterionType == CriterionType.PCWG3 ? u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.SiteVisitForTumorEvaluation).FirstOrDefault().Answer : String.Empty + // )) + + .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.Subject.TrialSite.TrialSiteCode)) + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.Subject.Code)) + .ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)); + + CreateMap().IncludeBase() + //.ForMember(o => o.TargetlesionEvaluationResult, t => t.MapFrom(u => u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.TargetLesion).FirstOrDefault().Answer)) + //.ForMember(o => o.NoneTargetlesionEvaluationResult, t => t.MapFrom(u => u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.NoTargetLesion).FirstOrDefault().Answer)) + //.ForMember(o => o.IsExistNewlesionEvaluationResult, t => t.MapFrom(u => u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.NewLesions).FirstOrDefault().Answer)) + .ForMember(o => o.EvaluationSummary, t => t.MapFrom(u => u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.AdjustReason).FirstOrDefault().Answer)) + + //.ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.Subject.TrialSite.TrialSiteCode)) + //.ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.Subject.Code)) + //.ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)) + ; + + + CreateMap().IncludeBase() + .ForMember(o => o.LesionList, t => t.MapFrom(u => u.LesionList)); + + CreateMap() + .ForMember(o => o.LessionCode, t => t.MapFrom(u => u.RowMark)) + .ForMember(o => o.LessionType, t => t.MapFrom(u =>(int?) u.ReadingQuestionTrial.LesionType)) + .ForMember(o => o.IsLymph, t => t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.IsLymph).FirstOrDefault().Answer)) + + //位置可能是自己填写的 + .ForMember(o => o.LessionLocation, t => + //t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.Location).FirstOrDefault().Answer) + t.MapFrom(u => u.OrganInfo.IsCanEditPosition? + u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.Location).FirstOrDefault().Answer: isEn_Us ? u.OrganInfo.TULATEN : u.OrganInfo.TULAT) + ) + .ForMember(o => o.LessionOrgan, t => + //t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.Organ).FirstOrDefault().Answer) + t.MapFrom(u => isEn_Us ? u.OrganInfo.TULOCEN : u.OrganInfo.TULOC) + ) + .ForMember(o => o.BodyPartDescription, t => + //t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.Part).FirstOrDefault().Answer) + t.MapFrom(u => isEn_Us? u.OrganInfo.PartEN : u.OrganInfo.Part) + + ) + + //.ForMember(o => o.MeasurementResult, t => t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.Location).FirstOrDefault().Answer)) + .ForMember(o => o.LongDiameter, t => t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.MajorAxis).FirstOrDefault().Answer)) + .ForMember(o => o.ShortDiameter, t => t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.ShortAxis).FirstOrDefault().Answer)) + .ForMember(o => o.LessionState, t => t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State).FirstOrDefault().Answer) + ); + + + CreateMap().IncludeBase() + .ForMember(o => o.LesionList, t => t.MapFrom(u => u.LesionList)); + + + CreateMap() + .ForMember(o => o.LessionCode, t => t.MapFrom(u => u.RowMark)) + .ForMember(o => o.LessionType, t => t.MapFrom(u => (int?)u.ReadingQuestionTrial.LesionType)) + + //位置可能是自己填写的 + .ForMember(o => o.LessionLocation, t => + //t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.Location).FirstOrDefault().Answer) + t.MapFrom(u => u.OrganInfo.IsCanEditPosition ? + u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.Location).FirstOrDefault().Answer : isEn_Us ? u.OrganInfo.TULATEN : u.OrganInfo.TULAT) + ) + + .ForMember(o => o.LessionOrgan, t => + t.MapFrom(u => isEn_Us ? u.OrganInfo.TULOCEN : u.OrganInfo.TULOC) + ) + + .ForMember(o => o.BodyPartDescription, t => + //t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.Part).FirstOrDefault().Answer) + t.MapFrom(u => isEn_Us ? u.OrganInfo.PartEN : u.OrganInfo.Part) + ) + .ForMember(o => o.LessionState, t => t.MapFrom(u => u.LesionAnswerList.Where(c => c.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State).FirstOrDefault().Answer)); + + #endregion + + + + string token = string.Empty; + //一致性核查 + CreateMap(); + + CreateMap(); + CreateMap(); + + + #region QA 废弃 + //CreateMap(); + + //CreateMap(); + + + //CreateMap(); + + + //CreateMap(); + + + //CreateMap(); + + //CreateMap(); + //CreateMap(); + + + //CreateMap(); + + //CreateMap(); + + //CreateMap(); + //CreateMap(); + + //CreateMap(); + + + + + + + #endregion + CreateMap(); + + CreateMap(); + + CreateMap(); + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + CreateMap(); + + + CreateMap() + .ForMember(d => d.ParentShowOrder, u => u.MapFrom(s => s.ParentQuestion.ShowOrder)) + .ForMember(d => d.ParentQuestionName, u => u.MapFrom(s => s.ParentQuestion.QuestionName)); + + CreateMap(); + + CreateMap(); + + CreateMap() + .ForMember(d => d.ParentShowOrder, u => u.MapFrom(s => s.ParentQCQuestion.ShowOrder)); + + CreateMap(); + + CreateMap(); + + CreateMap().ReverseMap(); + //患者临床数据 添加编辑 + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + + // 患者临床数据 视图映射 + Guid subjectVisitId = Guid.Empty; + CreateMap() + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) + .ForMember(d => d.SubjectVisitId, u => u.MapFrom(s => s.Id)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + .ForMember(d => d.PreviousHistoryList, u => u.MapFrom(s => s.PreviousHistoryList.Where(t => t.SubjectVisitId == subjectVisitId || t.IsSubjectLevel))) + .ForMember(d => d.PreviousOtherList, u => u.MapFrom(s => s.PreviousOtherList.Where(t => t.SubjectVisitId == subjectVisitId || t.IsSubjectLevel))) + .ForMember(d => d.PreviousSurgeryList, u => u.MapFrom(s => s.PreviousSurgeryList.Where(t => t.SubjectVisitId == subjectVisitId || t.IsSubjectLevel))); + + CreateMap(); + CreateMap() + .ForMember(d => d.IsUploadedImage, u => u.MapFrom(s => s.StudyList.Count() > 0 || s.NoneDicomStudyList.SelectMany(u => u.NoneDicomFileList).Count() > 0)); + + CreateMap() + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path + "?access_token=" + token)); + + + //影像质控 + CreateMap() + .ForMember(d => d.ChallengeCount, u => u.MapFrom(s => s.QCChallengeList.Count())) + + //.ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) + //.ForMember(d => d.IsEnrollmentConfirm, u => u.MapFrom(s => s.Subject.IsEnrollmentConfirm)) + //.ForMember(d => d.FistGiveMedicineTime, u => u.MapFrom(s => s.Subject.FistGiveMedicineTime)) + //.ForMember(d => d.SubjectVisitId, u => u.MapFrom(s => s.Id)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + .ForMember(d => d.QCProcessEnum, u => u.MapFrom(s => s.Trial.QCProcessEnum)) + .ForMember(d => d.SubjectStatus, u => u.MapFrom(s => s.Subject.Status)) + .ForMember(d => d.StudyCount, u => u.MapFrom(s => s.StudyList.Count())) + .ForMember(d => d.CurrentActionUserName, u => u.MapFrom(s => s.CurrentActionUser.UserName)) + .ForMember(d => d.PreliminaryAuditUserName, u => u.MapFrom(s => s.PreliminaryAuditUser.UserName)) + .ForMember(d => d.ReviewAuditUserName, u => u.MapFrom(s => s.ReviewAuditUser.UserName)) + .ForMember(d => d.IsHaveClinicalData, u => u.MapFrom(t => t.IsBaseLine ? t.PreviousHistoryList.Any() || t.PreviousOtherList.Any() + || t.ReadingClinicalDataList.Any(x => x.ClinicalDataTrialSet.UploadRole == Domain.Share.UploadRole.CRC && x.ReadingClinicalDataPDFList.Count() > 0) + || t.PreviousSurgeryList.Any() : false)) + .ForMember(d => d.DicomStudyCount, u => u.MapFrom(t => t.StudyList.Count())) + .ForMember(d => d.NoneDicomStudyCount, u => u.MapFrom(t => t.NoneDicomStudyList.Count(t => t.NoneDicomFileList.Any()))); + + //IC 上传列表 + CreateMap()/*.IncludeMembers(t=>t.Subject)*/ + //.ForMember(d => d.SubjectStatus, u => u.MapFrom(s => s.Subject.Status)) + //.ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) + // .ForMember(d => d.SubejctIsEnrollmentConfirm, u => u.MapFrom(t => t.Subject.IsEnrollmentConfirm)) + // .ForMember(d => d.SubejctFistGiveMedicineTime, u => u.MapFrom(t => t.Subject.FistGiveMedicineTime)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + .ForMember(d => d.QCProcessEnum, u => u.MapFrom(s => s.Trial.QCProcessEnum)) + .ForMember(d => d.SubjectId, u => u.MapFrom(t => t.Subject.Id)) + .ForMember(d => d.MedicalNo, u => u.MapFrom(s => s.Subject.MedicalNo)) + .ForMember(d => d.Sex, u => u.MapFrom(s => s.Subject.Sex)) + .ForMember(d => d.Age, u => u.MapFrom(t => t.Subject.Age)) + .ForMember(d => d.IsHaveClinicalData, u => u.MapFrom(t => t.IsBaseLine ? t.PreviousHistoryList.Any() || t.PreviousOtherList.Any() + || t.ReadingClinicalDataList.Any(x => x.ClinicalDataTrialSet.UploadRole == Domain.Share.UploadRole.CRC && x.ReadingClinicalDataPDFList.Count() > 0) + || t.PreviousSurgeryList.Any() : false)) + + //.ForMember(d => d.VisitName, u => u.MapFrom(t =>t.InPlan? t.VisitStage.VisitName : t.VisitName)) + //.ForMember(d => d.VisitNum, u => u.MapFrom(t => t.InPlan ? t.VisitStage.VisitNum : t.VisitNum)) + //.ForMember(d => d.VisitDay, u => u.MapFrom(t => t.InPlan ? t.VisitStage.VisitDay : t.VisitDay)) + .ForMember(d => d.DicomStudyCount, u => u.MapFrom(t => t.StudyList.Count())) + .ForMember(d => d.NoneDicomStudyCount, u => u.MapFrom(t => t.NoneDicomStudyList.Count(t => t.NoneDicomFileList.Any()))); + //.ForMember(d => d.StudyCount, u => u.MapFrom(s => s.StudyList.Count())); + CreateMap(); + + //一致性核查 + CreateMap() + .ForMember(d => d.TalkContent, u => u.MapFrom(s => s.CheckChallengeDialogList.OrderByDescending(y => y.CreateTime).Select(x => x.TalkContent).FirstOrDefault())) + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)); + + CreateMap().IncludeBase() + .ForMember(d => d.ModalityList, c => c.MapFrom(s => + (s.NoneDicomStudyList.Select(t => t.Modality) + .Union(s.StudyList.Select(k => k.ModalityForEdit))).Distinct())); + + + //一致性核查 质疑对话 + CreateMap() + .ForMember(d => d.CreateUserName, u => u.MapFrom(t => t.CreateUser.UserName)) + .ForMember(d => d.CreateUserFullName, u => u.MapFrom(t => t.CreateUser.FullName)); + + CreateMap() + .ForMember(d => d.SubjectVisitCheck, u => u.MapFrom(t => t)) + .ForMember(d => d.DialogList, u => u.MapFrom(t => t.CheckChallengeDialogList.OrderBy(t => t.CreateTime))); + + + CreateMap() + .ForMember(d => d.ForwardUserName, u => u.MapFrom(s => s.ForwardUser.UserName)) + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)); + + //QC 界面 患者 site 基本信息 展平的属性 比如 SubjectAge => Subject.Age + CreateMap().IncludeMembers(t => t.Subject) + .ForMember(d => d.SubjectVisitId, u => u.MapFrom(s => s.Id)) + .ForMember(d => d.SubjectName, u => u.MapFrom(s => s.Subject.ShortName)) + .ForMember(d => d.IsHaveFirstGiveMedicineDate, u => u.MapFrom(s => s.Trial.IsHaveFirstGiveMedicineDate)) + //.ForMember(d => d.ChangeDefalutDays, u => u.MapFrom(s => s.Trial.ChangeDefalutDays)) + .ForMember(d => d.SubjectFirstGiveMedicineTime, u => u.MapFrom(s => s.Subject.FirstGiveMedicineTime)) + .ForMember(d => d.SiteName, u => u.MapFrom(s => s.Site.SiteName)) + .ForMember(d => d.TotalChallengeCount, u => u.MapFrom(s => s.QCChallengeList.Count())) + .ForMember(d => d.NotClosedChallengeCount, u => u.MapFrom(s => s.QCChallengeList.Count(c => c.IsClosed == false))); + + + CreateMap(MemberList.None); + + + + // 临床数据上传 路径拼接返回 + + CreateMap() + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path + "?access_token=" + token)); + CreateMap() + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path + "?access_token=" + token)); + CreateMap() + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path + "?access_token=" + token)); + + + + + + //QC 质疑对话 + var currentUserId = Guid.Empty; + CreateMap() + .ForMember(d => d.CreateUserName, u => u.MapFrom(t => t.CreateUser.UserName)) + .ForMember(d => d.CreateUserFullName, u => u.MapFrom(t => t.CreateUser.FullName)) + + .ForMember(d => d.IsCurrentUser, u => u.MapFrom(s => s.CreateUserId == currentUserId)); + //质疑编号 + CreateMap() + .ForMember(d => d.LatestReplyUserName, u => u.MapFrom(t => t.LatestReplyUser.UserName)) + .ForMember(d => d.CreateUserName, u => u.MapFrom(t => t.CreateUser.UserName)) + .ForMember(d => d.CurrentActionUserId, u => u.MapFrom(t => t.SubjectVisit.CurrentActionUserId)) + .ForMember(d => d.CurrentActionUserName, u => u.MapFrom(t => t.SubjectVisit.CurrentActionUser.UserName)) + + .ForMember(d => d.SubjectId, u => u.MapFrom(t => t.SubjectVisit.SubjectId)) + .ForMember(d => d.ChallengeCode, u => u.MapFrom(s => s.ChallengeCode)) + + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.SubjectVisit.Subject.Code)) + .ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)) + .ForMember(d => d.BlindName, u => u.MapFrom(s => s.SubjectVisit.BlindName)); + + + CreateMap() + .ForMember(d => d.LatestReplyUserName, u => u.MapFrom(t => t.LatestReplyUser.UserName)) + .ForMember(d => d.CreateUserName, u => u.MapFrom(t => t.CreateUser.UserName)) + .ForMember(d => d.CurrentActionUserId, u => u.MapFrom(t => t.SubjectVisit.CurrentActionUserId)) + .ForMember(d => d.CurrentActionUserName, u => u.MapFrom(t => t.SubjectVisit.CurrentActionUser.UserName)) + + .ForMember(d => d.SubjectId, u => u.MapFrom(t => t.SubjectVisit.SubjectId)) + .ForMember(d => d.DialogList, u => u.MapFrom(t => t.DialogList.OrderBy(t => t.CreateTime))) + .ForMember(d => d.ChallengeCode, u => u.MapFrom(s => s.ChallengeCode)) + + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.SubjectVisit.Subject.Code)) + .ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)) + .ForMember(d => d.BlindName, u => u.MapFrom(s => s.SubjectVisit.BlindName)) + + ; + + // 一致性核查文件 + CreateMap() + .ForMember(d => d.CreateUserName, u => u.MapFrom(t => t.User.FirstName + "/" + t.User.LastName)); + + + //IC 质疑列表 + CreateMap() + .ForMember(d => d.PreliminaryAuditUserName, u => u.MapFrom(s => s.SubjectVisit.PreliminaryAuditUser.UserName)) + .ForMember(d => d.PreliminaryAuditUserId, u => u.MapFrom(s => s.SubjectVisit.PreliminaryAuditUserId)) + .ForMember(d => d.CurrentActionUserId, u => u.MapFrom(s => s.SubjectVisit.CurrentActionUserId)) + .ForMember(d => d.CurrentActionUserName, u => u.MapFrom(s => s.SubjectVisit.CurrentActionUser.UserName)) + .ForMember(d => d.SubmitState, u => u.MapFrom(s => s.SubjectVisit.SubmitState)) + + + .ForMember(d => d.SiteId, u => u.MapFrom(s => s.SubjectVisit.SiteId)) + .ForMember(d => d.AuditState, u => u.MapFrom(s => s.SubjectVisit.AuditState)) + .ForMember(d => d.IsUrgent, u => u.MapFrom(s => s.SubjectVisit.IsUrgent)) + .ForMember(d => d.IsBaseLine, u => u.MapFrom(s => s.SubjectVisit.IsBaseLine)) + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.SubjectVisit.Subject.Code)) + .ForMember(d => d.SubjectId, u => u.MapFrom(t => t.SubjectVisit.SubjectId)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.SubjectVisit.TrialSite.TrialSiteCode)) + .ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)) + .ForMember(d => d.RequestBackState, u => u.MapFrom(s => s.SubjectVisit.RequestBackState)) + .ForMember(d => d.VisitNum, u => u.MapFrom(s => s.SubjectVisit.VisitNum)) + .ForMember(d => d.BlindName, u => u.MapFrom(s => s.SubjectVisit.BlindName)) + //.ForMember(d => d.ClosedUserUserName, u => u.MapFrom(s => s.ClosedUser.UserName)) + .ForMember(d => d.IsQCConfirmedReupload, u => u.MapFrom(s => s.SubjectVisit.IsQCConfirmedReupload)) + .ForMember(d => d.CreateUserName, u => u.MapFrom(s => s.CreateUser.UserName)) + .ForMember(d => d.LatestReplyUserName, u => u.MapFrom(t => t.LatestReplyUser.UserName)) + .ForMember(d => d.ChallengeCode, u => u.MapFrom(s => s.ChallengeCode)); //排序的时候有坑 把这个带到sql 中去了 + //.AfterMap((src, dest) => dest.ChallengeCode = "Q" + src.ChallengeCode.ToString("D5"));//实测没有效果 + + + + + //质疑问题答案 + CreateMap().EqualityComparison((odto, o) => odto.Id == o.Id) + .ForAllMembers(opt => opt.Condition((src, dest, srcMember) => srcMember != null)); + //.ForMember(d => d.TrialQCQuestionConfigureId, opt => opt.Ignore())//前端更新的时候不会传递这个参数,但是添加的时候会传递 + //.ForMember(d => d.SubjectVisitId, opt => opt.Ignore()); + //更新的时候,因为前端没有传递TrialQCQuestionConfigureId 导致映射后的数据变为 Guid.Empty,明明配置了 如果source 为null 就不映射 但是没生效 临时解决 + //.BeforeMap((src, dest) => src.TrialQCQuestionConfigureId = dest.TrialQCQuestionConfigureId == System.Guid.Empty ? src.TrialQCQuestionConfigureId : dest.TrialQCQuestionConfigureId); + + + CreateMap().IncludeMembers(t => t.TrialQCQuestionConfigure) + .ForMember(d => d.ParentShowOrder, u => u.MapFrom(s => s.TrialQCQuestionConfigure.ParentQCQuestion.ShowOrder)); + + CreateMap() + .ForMember(d => d.Id, u => u.Ignore()) + .ForMember(d => d.ParentShowOrder, u => u.MapFrom(s => s.ParentQCQuestion.ShowOrder)) + .ForMember(d => d.TrialQCQuestionConfigureId, u => u.MapFrom(s => s.Id)); + + + CreateMap().ReverseMap(); + + + CreateMap() + .ForMember(d => d.FullFilePath, u => u.MapFrom(s => s.Path)); + + CreateMap() + //.ForMember(d => d.FileCount, u => u.MapFrom(s => s.NoneDicomFileList.Count)) + .ForMember(d => d.NoneDicomStudyFileList, u => u.MapFrom(s => s.NoneDicomFileList)) + .ForMember(d => d.CodeView, u => u.MapFrom(s => s.StudyCode)); + + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/Reading/ClinicalData/ClinicalDataSetService.cs b/IRaCIS.Core.Application/Service/Reading/ClinicalData/ClinicalDataSetService.cs new file mode 100644 index 0000000..9b2dd9d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ClinicalData/ClinicalDataSetService.cs @@ -0,0 +1,377 @@ +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using MassTransit; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Application.Services +{ + /// + /// 临床数据配置 + /// + [ApiExplorerSettings(GroupName = "Reading")] + public class ClinicalDataSetService : BaseService + { + + public IRepository _subjectVisitRepository; + + private readonly IRepository _clinicalDataTrialSetRepository; + private readonly IRepository _clinicalDataSystemSetRepository; + private readonly IRepository _previousPDFRepository; + private readonly IRepository _trialRepository; + + + public ClinicalDataSetService(IRepository subjectVisitRepository, + + IRepository ClinicalDataTrialSetRepository, + IRepository ClinicalDataSystemSetRepository, + IRepository previousPDFRepository, + IRepository trialRepository + + + ) + { + + _subjectVisitRepository = subjectVisitRepository; + + _clinicalDataTrialSetRepository = ClinicalDataTrialSetRepository; + _clinicalDataSystemSetRepository = ClinicalDataSystemSetRepository; + this._previousPDFRepository = previousPDFRepository; + this._trialRepository = trialRepository; + } + + + + #region 系统 + /// + /// 新增或者修改(系统) + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateClinicalDataSystemSet(ClinicalDataSystemSetAddOrEdit indto) + { + + var existsQuery = _clinicalDataSystemSetRepository + .WhereIf(indto.Id != null, x => x.Id != indto.Id) + .Where(x => x.ClinicalDataSetName == indto.ClinicalDataSetName); + + if (await existsQuery.AnyAsync()) + { + return ResponseOutput.NotOk("存在同类型的临床数据,操作失败"); + } + + indto.CriterionEnumListStr= $"|{String.Join('|', indto.CriterionEnumList)}|"; + + var entity = await _clinicalDataSystemSetRepository.InsertOrUpdateAsync(indto); + + //entity.SystemClinicalDataCriterionList = indto.SystemCriterionIdList.Select(t => new SystemClinicalDataCriterion() + //{ + // SystemClinicalDataSetId = entity.Id, + // SystemReadingCriterionId = t + //}).ToList(); + + //if (indto.Id != null) + //{ + // await _systemClinicalDataCriterionRepository.BatchDeleteNoTrackingAsync(t => t.SystemClinicalDataSetId == entity.Id); + + // await _systemClinicalDataCriterionRepository.AddRangeAsync(entity.SystemClinicalDataCriterionList); + //} + + await _clinicalDataSystemSetRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(entity.Id.ToString()); + } + + /// + /// 获取系统临床数据(系统) + /// + /// + [HttpPost] + public async Task> GetSystemClinicalDataSystemSetList(GetTrialClinicalDataSystemIndto inDto) + { + return await _clinicalDataSystemSetRepository.AsQueryable() + .WhereIf(inDto.ClinicalDataLevel != null, x => x.ClinicalDataLevel == inDto.ClinicalDataLevel) + .WhereIf(inDto.ClinicalUploadType != null, x => x.ClinicalUploadType == inDto.ClinicalUploadType) + .WhereIf(inDto.ClinicalDataSetName != String.Empty, x => x.ClinicalDataSetName.Contains(inDto.ClinicalDataSetName)) + + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + /// + /// 删除(系统) + /// + /// + /// + [HttpDelete("{id:guid}")] + public async Task DeleteClinicalSystemSetData(Guid id) + { + await _clinicalDataSystemSetRepository.DeleteFromQueryAsync(x => x.Id == id, true); + return ResponseOutput.Result(true); + } + + #endregion + + + + #region 项目 + /// + /// 新增或者修改(项目) + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateClinicalDataTrialSet(ClinicalDataTrialSetAddOrEdit indto) + { + var existsQuery = _clinicalDataTrialSetRepository + .WhereIf(indto.Id != null, x => x.Id != indto.Id) + .Where(x => x.ClinicalDataSetName == indto.ClinicalDataSetName && x.TrialId == indto.TrialId); + + if (await existsQuery.AnyAsync()) + { + return ResponseOutput.NotOk("存在同类型的临床数据,操作失败"); + } + + //indto.CriterionEnumListStr = $"|{String.Join('|', indto.CriterionEnumList)}|"; + + var entity = await _clinicalDataTrialSetRepository.InsertOrUpdateAsync(indto, true); + + entity.TrialClinicalDataSetCriteriaList = indto.TrialCriterionIdList.Select(t => new TrialClinicalDataSetCriterion() + { + TrialClinicalDataSetId = entity.Id, + TrialReadingCriterionId = t + }).ToList(); + + if (indto.Id != null) + { + await _repository.BatchDeleteAsync(t => t.TrialClinicalDataSetId == entity.Id); + + await _repository.AddRangeAsync(entity.TrialClinicalDataSetCriteriaList); + } + + + + await _clinicalDataTrialSetRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id.ToString()); + } + + + + + + /// + /// 获取项目的临床数据 + /// + /// + /// + [HttpPost] + [UnitOfWork] + public async Task> GetTrialClinicalDataTrialSetList(GetTrialClinicalDataTrialIndto inDto) + { + await this.AddTrialClinicalDataTrialSet(inDto.TrialId); + + var trialClinicalDataList = await _clinicalDataTrialSetRepository.AsQueryable() + .Where(x => x.TrialId == inDto.TrialId) + .WhereIf(inDto.ClinicalDataLevel != null, x => x.ClinicalDataLevel == inDto.ClinicalDataLevel) + .WhereIf(inDto.ClinicalUploadType != null, x => x.ClinicalUploadType == inDto.ClinicalUploadType) + .WhereIf(inDto.ClinicalDataSetName != String.Empty, x => x.ClinicalDataSetName.Contains(inDto.ClinicalDataSetName)) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + return trialClinicalDataList; + } + + + + + /// + /// 删除(项目) + /// + /// + /// + [HttpDelete("{id:guid}")] + public async Task DeleteClinicalTrialSetData(Guid id) + { + await _clinicalDataTrialSetRepository.DeleteFromQueryAsync(x => x.Id == id, true); + return ResponseOutput.Result(true); + } + + #endregion + + + #region 系统和项目标准下拉 界面上配置和临床数据关联 废弃 + + //[HttpPost] + //public async Task> GetSystemCriterionSelectList(SystemCriterionSelectQuery inQuery) + //{ + // return await _repository.Where() + // .WhereIf(!string.IsNullOrEmpty(inQuery.CriterionName), t => t.CriterionName.Contains(inQuery.CriterionName)) + // .WhereIf(inQuery.IsEnable != null, t => t.IsEnable == inQuery.IsEnable) + // .WhereIf(inQuery.IsCompleteConfig != null, t => t.IsCompleteConfig == inQuery.IsCompleteConfig) + // .Select(t => new SystemCriterionSelectDto() + // { + // Id = t.Id, + // CriterionName = t.CriterionName, + // IsCompleteConfig = t.IsCompleteConfig, + // IsEnable = t.IsEnable + // }).ToListAsync(); + //} + + + //[HttpPost] + //public async Task> GetTrialCriterionSelectList(TrialCriterionSelectQuery inQuery) + //{ + // return await _repository.Where(t => t.TrialId == inQuery.TrialId) + // .WhereIf(!string.IsNullOrEmpty(inQuery.CriterionName), t => t.CriterionName.Contains(inQuery.CriterionName)) + // .WhereIf(inQuery.IsEnable != null, t => t.IsEnable == inQuery.IsEnable) + // .WhereIf(inQuery.IsCompleteConfig != null, t => t.IsCompleteConfig == inQuery.IsCompleteConfig) + // .Select(t => new TrialCriterionSelectDto() + // { + // Id = t.Id, + // CriterionName = t.CriterionName, + // IsCompleteConfig = t.IsCompleteConfig, + // IsEnable = t.IsEnable + // }).ToListAsync(); + //} + + + #endregion + + + + #region 将系统配置添加到项目配置 + + /// + /// 将系统配置添加到项目配置 + /// + /// + /// + + private async Task AddTrialClinicalDataTrialSet(Guid trialId) + { + + #region MyRegion + + //不存在的时候,就将系统数据同步到项目临床数据配置 + if (!await _clinicalDataTrialSetRepository.AnyAsync(x => x.TrialId == trialId && x.SystemClinicalDataSetId != null)) + { + var systemClinicalDataList = await _clinicalDataSystemSetRepository.AsQueryable().ToListAsync(); + var systemIds = systemClinicalDataList.Select(x => x.Id).ToList(); + + var trialSystemClinicalDataSetIds = await _clinicalDataTrialSetRepository.Where(x => x.TrialId == trialId && x.SystemClinicalDataSetId != null).Select(x => x.SystemClinicalDataSetId.Value).ToListAsync(); + + var needAddids = systemIds.Except(trialSystemClinicalDataSetIds).ToList(); + + var systemDataList = systemClinicalDataList.Where(x => needAddids.Contains(x.Id)).ToList(); + + var readingCriterionList = _repository.Where(t => t.TrialId == trialId).Where(t => t.ReadingQuestionCriterionSystemId != null) + .Select(t => new { t.ReadingQuestionCriterionSystemId, TrialReadingCriterionId = t.Id ,t.CriterionType}).ToList(); + + + List dataSets = systemDataList.Select(x => new ClinicalDataTrialSet() + { + Id = NewId.NextGuid(), + SystemClinicalDataSetId = x.Id, + ClinicalDataSetName = x.ClinicalDataSetName, + ClinicalDataLevel = x.ClinicalDataLevel, + ClinicalUploadType = x.ClinicalUploadType, + UploadRole = x.UploadRole, + FileName = x.FileName, + Path = x.Path, + TrialId = trialId, + + //项目不采用 标准枚举字符串的方式 + //CriterionEnumListStr=x.CriterionEnumListStr + + TrialClinicalDataSetCriteriaList = readingCriterionList.Where(t => x.CriterionEnumList.Contains( (int )t.CriterionType)).Select(c => + new TrialClinicalDataSetCriterion() { TrialReadingCriterionId = c.TrialReadingCriterionId }).ToList() + }).ToList(); + + + + await _clinicalDataTrialSetRepository.AddRangeAsync(dataSets); + + var result = await _clinicalDataTrialSetRepository.SaveChangesAsync(); + + } + + #endregion + + + #region OLd + //var syncClinicalDataTime = await _trialRepository.Where(x => x.Id == trialId).Select(x => x.SyncClinicalDataTime).FirstOrDefaultAsync(); + //if (syncClinicalDataTime == null) + //{ + // var systemClinicalDataList = await _clinicalDataSystemSetRepository.AsQueryable().Include(t => t.SystemClinicalDataCriterionList).ToListAsync(); + // var systemIds = systemClinicalDataList.Select(x => x.Id).ToList(); + + // var trialSystemClinicalDataSetIds = await _clinicalDataTrialSetRepository.Where(x => x.TrialId == trialId && x.SystemClinicalDataSetId != null).Select(x => x.SystemClinicalDataSetId.Value).ToListAsync(); + + // var needAddids = systemIds.Except(trialSystemClinicalDataSetIds).ToList(); + + // var systemDataList = systemClinicalDataList.Where(x => needAddids.Contains(x.Id)).ToList(); + + // var readingCriterionList = _repository.Where(t => t.TrialId == trialId).Where(t => t.ReadingQuestionCriterionSystemId != null) + // .Select(t => new { t.ReadingQuestionCriterionSystemId, TrialReadingCriterionId = t.Id }).ToList(); + + + // List dataSets = systemDataList.Select(x => new ClinicalDataTrialSet() + // { + // Id = NewId.NextGuid(), + // SystemClinicalDataSetId = x.Id, + // ClinicalDataSetName = x.ClinicalDataSetName, + // ClinicalDataLevel = x.ClinicalDataLevel, + // ClinicalUploadType = x.ClinicalUploadType, + // UploadRole = x.UploadRole, + // FileName = x.FileName, + // Path = x.Path, + // TrialId = trialId, + + // TrialClinicalDataCriterionList = readingCriterionList.Where(t => x.SystemClinicalDataCriterionList.Select(c => (Guid?)c.SystemReadingCriterionId).ToList().Contains(t.ReadingQuestionCriterionSystemId)).Select(c => + // new TrialClinicalDataCriterion() { TrialReadingCriterionId = c.TrialReadingCriterionId }).ToList() + // }).ToList(); + + + // #region 临床数据标准 + + + // // dataSets.ForEach(x => + // //x.TrialClinicalDataCriterionList = systemClinicalDataList.Where(t => t.Id == x.SystemClinicalDataSetId) + // // .Select(t => new TrialClinicalDataCriterion() { TrialReadingCriterionId = x.cl }).ToList() + // // ); + + // #endregion + + + + // await _clinicalDataTrialSetRepository.AddRangeAsync(dataSets); + + // //var needUpdateitemList = await _clinicalDataTrialSetRepository.Where(x => x.TrialId == trialId && needUpdateIds.Contains(x.SystemClinicalDataSetId.Value)).ToListAsync(); + // //foreach (var item in needUpdateitemList) + // //{ + // // var systemData = systemClinicalDataList.FirstOrDefault(x => x.Id == item.SystemClinicalDataSetId); + // // await _clinicalDataTrialSetRepository.UpdatePartialNoQueryAsync(item.Id,x=>new ClinicalDataTrialSet() { + + // // ClinicalDataSetName = systemData.ClinicalDataSetName, + // // ClinicalDataLevel = systemData.ClinicalDataLevel, + // // ClinicalUploadType = systemData.ClinicalUploadType, + // // UploadRole = systemData.UploadRole, + // // FileName = systemData.FileName, + // // Path = systemData.Path, + // // }); + // //} + + // await _trialRepository.BatchUpdateNoTrackingAsync(x => x.Id == trialId, x => new Trial() + // { + // SyncClinicalDataTime = DateTime.Now, + // }); + + + // var result = await _clinicalDataTrialSetRepository.SaveChangesAsync(); + + //} + + #endregion + + + } + #endregion + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ClinicalData/ReadingClinicalDataService.cs b/IRaCIS.Core.Application/Service/Reading/ClinicalData/ReadingClinicalDataService.cs new file mode 100644 index 0000000..aa30e8e --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ClinicalData/ReadingClinicalDataService.cs @@ -0,0 +1,780 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Infra.EFCore.Common; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Service.Inspection.DTO; +using Panda.DynamicWebApi.Attributes; +using Microsoft.Extensions.DependencyInjection; +using System.Linq.Expressions; +using IRaCIS.Core.Infrastructure; + +namespace IRaCIS.Application.Services +{ + /// + /// 阅片临床数据 + /// + [ApiExplorerSettings(GroupName = "Reading")] + public class ReadingClinicalDataService : BaseService, IReadingClinicalDataService + { + private readonly IRepository _readingClinicalDataRepository; + private readonly IRepository _clinicalDataTrialSetRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _previousPDFRepository; + + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _previousHistoryRepository; + private readonly IRepository _previousOtherRepository; + private readonly IRepository _previousSurgeryRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IServiceProvider iServiceProvider; + private readonly IRepository _subjectRepository; + private readonly IRepository _readModuleRepository; + private readonly IRepository _readingClinicalDataPDFRepository; + + public ReadingClinicalDataService(IRepository readingClinicalDataRepository, + IRepository clinicalDataTrialSetRepository, + IRepository previousPDFRepository, + IRepository subjectVisitRepository, + IRepository previousHistoryRepository, + + IRepository previousOtherRepository, + IRepository previousSurgeryRepository, + IRepository readingQuestionCriterionTrialRepository, + IServiceProvider IServiceProvider, + IRepository subjectRepository, + + IRepository readModuleRepository, + IRepository readingClinicalDataPDFRepository, + IRepository visitTaskRepository) + { + this._readingClinicalDataRepository = readingClinicalDataRepository; + this._clinicalDataTrialSetRepository = clinicalDataTrialSetRepository; + this._previousPDFRepository = previousPDFRepository; + this._subjectVisitRepository = subjectVisitRepository; + this._previousHistoryRepository = previousHistoryRepository; + this._previousOtherRepository = previousOtherRepository; + this._previousSurgeryRepository = previousSurgeryRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this.iServiceProvider = IServiceProvider; + this._subjectRepository = subjectRepository; + this._readModuleRepository = readModuleRepository; + this._readingClinicalDataPDFRepository = readingClinicalDataPDFRepository; + this._visitTaskRepository = visitTaskRepository; + } + + + #region 临床数据基本增删改 + + /// + /// 新增或者修改 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateReadingClinicalData(AddOrUpdateReadingClinicalDataDto indto) + { + var existsQuery = _readingClinicalDataRepository + .WhereIf(indto.Id != null, x => x.Id != indto.Id) + .Where(x => x.ClinicalDataTrialSetId == indto.ClinicalDataTrialSetId && x.ReadingId == indto.ReadingId); + + + + + if (await existsQuery.AnyAsync()) + { + return ResponseOutput.NotOk("存在同类型的临床数据"); + } + var clinicalDataTrialSet = (await _clinicalDataTrialSetRepository.Where(x => x.Id == indto.ClinicalDataTrialSetId).FirstOrDefaultAsync()).IfNullThrowException(); + + //subject 或者检查批次级别的 都是在检查批次传 + indto.IsVisit = clinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Subject || clinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.SubjectVisit; + + if (indto.Id == null) + { + var entity = _mapper.Map(indto); + entity.ReadingClinicalDataPDFList = indto.AddFileList.Select(x => new ReadingClinicalDataPDF() + { + + FileName = x.FileName, + Path = x.Path, + + }).ToList(); + + entity.ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded; + entity.IsBlind = null; + entity.IsComplete = null; + entity.FileCount = entity.ReadingClinicalDataPDFList.Count(); + await _readingClinicalDataRepository.AddAsync(entity, true); + var success = await _readingClinicalDataRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id); + } + else + { + var entity = (await _readingClinicalDataRepository.Where(t => t.Id == indto.Id, true).FirstOrDefaultAsync()).IfNullThrowException(); + await _readingClinicalDataPDFRepository.BatchDeleteNoTrackingAsync(x => indto.DeleteFileIds.Contains(x.Id)); + + var addFileList = indto.AddFileList.Select(x => new ReadingClinicalDataPDF() + { + + FileName = x.FileName, + Path = x.Path, + + ReadingClinicalDataId = entity.Id, + }).ToList(); + + + + _mapper.Map(indto, entity); + + //上传 或者删除了文件 核查状态需要重新确认 + + if (indto.AddFileList.Count > 0 || indto.AddFileList.Count > 0) + { + entity.IsComplete = null; + entity.IsBlind = null; + } + + await _readingClinicalDataPDFRepository.AddRangeAsync(addFileList); + + await _readingClinicalDataPDFRepository.SaveChangesAsync(); + + var fileCount = await _readingClinicalDataPDFRepository.Where(t => t.ReadingClinicalDataId == indto.Id).CountAsync(); + entity.ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded; + entity.FileCount = fileCount; + var success = await _readingClinicalDataRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id); + + } + + } + + + /// + /// 删除 + /// + /// + /// + [HttpDelete("{id:guid}")] + public async Task DeleteReadingClinicalData(Guid id) + { + + await _readingClinicalDataRepository.DeleteFromQueryAsync(x => x.Id == id, true); + await _readingClinicalDataPDFRepository.DeleteFromQueryAsync(x => x.ReadingClinicalDataId == id, true); + return ResponseOutput.Result(true); + } + + + #endregion + + + #region 临床数据IC 相关 + + /// + /// 获取IC上传的文件 + /// + /// + /// + [HttpPost] + public async Task> GetCRCClinicalData(GetCRCClinicalDataInDto inDto) + { + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator) + { + await this.AddCRCClinicalData(inDto); + } + + + List cRCClinicalDataList = await _readingClinicalDataRepository.Where(x => x.ReadingId == inDto.SubjectVisitId) + .WhereIf(inDto.IsBaseline, x => x.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Subject || x.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.SubjectVisit) + .WhereIf(!inDto.IsBaseline, x => x.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.SubjectVisit) + .Where(x => x.ClinicalDataTrialSet.TrialId == inDto.TrialId && x.ClinicalDataTrialSet.UploadRole == UploadRole.CRC) + .Select(x => new GetCRCClinicalDataOutDto() + { + Id = x.Id, + ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName, + ClinicalUploadType = x.ClinicalDataTrialSet.ClinicalUploadType, + ClinicalDataTrialSetId = x.ClinicalDataTrialSet.Id, + FileName = x.ClinicalDataTrialSet.FileName, + UploadRole = x.ClinicalDataTrialSet.UploadRole, + Path = x.ClinicalDataTrialSet.Path, + IsBlind = x.IsBlind, + IsComplete = x.IsComplete, + PDFFileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() + { + Id = y.Id, + FileName = y.FileName, + Path = y.Path, + CreateTime = y.CreateTime, + }).ToList() + }).ToListAsync(); + + List clinicalData = (await this.GetReadingClinicalDataList(new GetReadingClinicalDataListIndto() + { + SubjectId = inDto.SubjectId, + ReadingId = inDto.SubjectVisitId, + TrialId = inDto.TrialId, + })).Item1; + + var previousHistoryList = await _previousHistoryRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var previousOtherList = await _previousOtherRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var previousSurgeryList = await _previousSurgeryRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + foreach (var item in cRCClinicalDataList) + { + //item.PDFFileList = clinicalData.Where(y => y.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).SelectMany(x => x.FileList).ToList(); + item.ClinicalTableData = new ClinicalDataTable() + { + PreviousHistoryList = previousHistoryList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + PreviousOtherList = previousOtherList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + PreviousSurgeryList = previousSurgeryList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + }; + + } + return cRCClinicalDataList; + } + + + /// + /// 添加IC数据类型 + /// + /// + private async Task AddCRCClinicalData(GetCRCClinicalDataInDto inDto) + { + var cRCClinicalDataIds = await _clinicalDataTrialSetRepository.Where(x => x.TrialId == inDto.TrialId && x.UploadRole == UploadRole.CRC && x.IsConfirm) + .WhereIf(inDto.IsBaseline, x => x.ClinicalDataLevel == ClinicalLevel.Subject || x.ClinicalDataLevel == ClinicalLevel.SubjectVisit) + .WhereIf(!inDto.IsBaseline, x => x.ClinicalDataLevel == ClinicalLevel.SubjectVisit) + .Select(x => x.Id).ToListAsync(); + + var needAddIds = cRCClinicalDataIds.Where(x => _readingClinicalDataRepository.Where(y => y.ReadingId == inDto.SubjectVisitId && x == y.ClinicalDataTrialSetId).Count() == 0).ToList(); + + List readingClinicals = needAddIds.Select(x => new ReadingClinicalData() + { + ClinicalDataTrialSetId = x, + IsVisit = true, + SubjectId = inDto.SubjectId, + ReadingId = inDto.SubjectVisitId, + TrialId = inDto.TrialId + }).ToList(); + + await _readingClinicalDataRepository.AddRangeAsync(readingClinicals); + + await _readingClinicalDataRepository.SaveChangesAsync(); + + } + + #endregion + + + #region 临床数据签名 和确认 + + /// + /// 影像阅片临床数据签名 + /// + // [HttpPost] + + public async Task ReadClinicalDataSign(ReadingClinicalDataSignIndto inDto) + { + + + var data = await _readingClinicalDataRepository.FirstOrDefaultAsync(t => t.Id == inDto.ReadingClinicalDataId); + + if (data.ReadingClinicalDataState != ReadingClinicalDataStatus.HaveChecked) + { + throw new BusinessValidationFailedException("当前临床数据状态不是已核查状态,不允许签名!"); + } + data.IsSign = true; + data.ReadingClinicalDataState = ReadingClinicalDataStatus.HaveSigned; + + //await _readingClinicalDataRepository.UpdatePartialFromQueryAsync(x => x.Id == inDto.ReadingClinicalDataId, x => new ReadingClinicalData() + //{ + // IsSign = true, + // ReadingClinicalDataState = ReadingClinicalDataStatus.HaveSigned + //}); + + var result = await _readingClinicalDataRepository.SaveChangesAsync(); + + + var readingId = await _readingClinicalDataRepository.Where(x => x.Id == inDto.ReadingClinicalDataId).Select(x => x.ReadingId).FirstOrDefaultAsync(); + + await this.iServiceProvider.GetService().AddOncologyTask(readingId); + + await DealVisiTaskClinicalDataSignedAsync(data.TrialId, data.SubjectId, data.ReadingId, data.IsVisit, inDto.TrialReadingCriterionId); + + return ResponseOutput.Result(result); + } + + //处理 任务 临床数据是否签名 + private async Task DealVisiTaskClinicalDataSignedAsync(Guid trialId, Guid subjectId, Guid readingId, bool isVisit, Guid trialReadingCritrialId) + { + //获取确认的临床数据配置 + var trialClinicalDataSetList = _clinicalDataTrialSetRepository.Where(t => t.TrialId == trialId && t.IsConfirm).Include(t => t.TrialClinicalDataSetCriteriaList).ToList(); + + //var criterionType = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == trialReadingCritrialId).Select(t => t.CriterionType).FirstOrDefaultAsync(); + + // 获取确认的项目标准 废弃 签名的时候 传递标准Id + //var confirmedCtritrialList = _repository.Where(t => t.TrialId == trialId && t.ReadingInfoSignTime != null).Select(t => new { t.Id, t.CriterionType }).ToList(); + + var needSignCount = 0; + var haveSignedCount = _readingClinicalDataRepository.Where(t => t.TrialId == trialId && t.IsSign && t.ReadingClinicalDataState == ReadingClinicalDataStatus.HaveSigned && t.ReadingId == readingId && t.ClinicalDataTrialSet.UploadRole == UploadRole.PM).Count(); + + + if (isVisit) + { + var isBaseLine = await _subjectVisitRepository.Where(t => t.Id == readingId).Select(t => t.IsBaseLine).FirstOrDefaultAsync(); + + //判断是否基线 + if (isBaseLine) + { + //IC 的自动签名 不用管 只用处理PM 的就好 + needSignCount = trialClinicalDataSetList.Where(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCritrialId) && (t.ClinicalDataLevel == ClinicalLevel.Subject || t.ClinicalDataLevel == ClinicalLevel.SubjectVisit) && t.UploadRole == UploadRole.PM).Count(); + } + else + { + needSignCount = trialClinicalDataSetList.Where(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCritrialId) && t.ClinicalDataLevel == ClinicalLevel.SubjectVisit && t.UploadRole == UploadRole.PM).Count(); + } + + //可能仅仅IC 基线 没有PM + if (needSignCount == haveSignedCount /*&& needSignCount != 0*/) + { + + //将该标准 该subject 该检查批次 任务临床数据状态变更 + await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trialId && t.SubjectId == subjectId && t.TrialReadingCriterionId == trialReadingCritrialId && t.SourceSubjectVisitId == readingId, u => new VisitTask() + { + IsClinicalDataSign = true + }); + + } + + + } + else + { + //判断是影像学 还是肿瘤学阅片 + var readingSetType = await _readModuleRepository.Where(t => t.Id == readingId).Select(t => t.ReadingSetType).FirstOrDefaultAsync(); + + //影像学 + if (readingSetType == ReadingSetType.ImageReading) + { + needSignCount = trialClinicalDataSetList.Where(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCritrialId) && t.ClinicalDataLevel == ClinicalLevel.ImageRead).Count(); + } + //肿瘤学 + else + { + needSignCount = trialClinicalDataSetList.Where(t => t.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == trialReadingCritrialId) && t.ClinicalDataLevel == ClinicalLevel.OncologyRead).Count(); + } + + //可能仅仅IC 基线 没有PM + if (needSignCount == haveSignedCount /*&& needSignCount != 0*/) + { + //将该标准 该subject 该阅片期|肿瘤学 任务临床数据状态变更 + await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trialId && t.SubjectId == subjectId && t.TrialReadingCriterionId == trialReadingCritrialId && t.SouceReadModuleId == readingId, u => new VisitTask() + { + IsClinicalDataSign = true + }); + + + } + + } + + Expression> visitTaskLambda = x => x.TrialId == trialId && x.SubjectId == subjectId && x.TrialReadingCriterionId == trialReadingCritrialId; + + + + + //维护 IsFrontTaskNeedSignButNotSign 在添加任务哪里用触发器也维护了 + var visitTaskIdQueryable = _visitTaskRepository.Where(visitTaskLambda) //该Subject 该标准的任务 + //小于自己任务号的任务 存在需要签名 但是没签名 + .Where(t => t.Subject.SubjectVisitTaskList.AsQueryable().Where(visitTaskLambda).Any(c => c.IsNeedClinicalDataSign == true && c.IsClinicalDataSign == false && c.VisitTaskNum < t.VisitTaskNum) + && t.IsFrontTaskNeedSignButNotSign == false ) + .Select(t => t.Id); + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => visitTaskIdQueryable.Contains(t.Id), u => new VisitTask() + { + IsFrontTaskNeedSignButNotSign = true + }); + + + + var visitTaskIdQueryable2 = _visitTaskRepository.Where(visitTaskLambda) //该Subject 该标准的任务 + //小于自己任务号的任务 存在需要签名 但是没签名 + .Where(t => !t.Subject.SubjectVisitTaskList.AsQueryable().Where(visitTaskLambda).Any(c => c.IsNeedClinicalDataSign == true && c.IsClinicalDataSign == false && c.VisitTaskNum < t.VisitTaskNum) + && t.IsFrontTaskNeedSignButNotSign == true ) + .Select(t => t.Id); + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => visitTaskIdQueryable2.Contains(t.Id), u => new VisitTask() + { + IsFrontTaskNeedSignButNotSign = false + }); + + } + + + + + [HttpPut] + public async Task PMClinicalDataConfirm(PMClinicalDataConfirmCommand command) + { + await _readingClinicalDataRepository.UpdatePartialFromQueryAsync(t => t.Id == command.Id, u => new ReadingClinicalData() + { + IsBlind = command.IsBlind, + IsComplete = command.IsComplete, + ReadingClinicalDataState = ReadingClinicalDataStatus.HaveChecked + }); + await _readingClinicalDataRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + #endregion + + + #region 临床数据相关查询 + /// + /// 获取下拉菜单 + /// + /// + /// + [HttpPost] + public async Task> GetTrialClinicalDataSelect(GetTrialClinicalDataSelectIndto inDto) + { + var userTypes = new List() { + (int)UserTypeEnum.ProjectManager, + (int)UserTypeEnum.SPM, + (int)UserTypeEnum.CPM, + (int)UserTypeEnum.IndependentReviewer, + }; + + + + if (userTypes.Contains(_userInfo.UserTypeEnumInt)) + { + inDto.UploadRole = UploadRole.PM; + } + + ReadModule readModule = null; + if (inDto.IsVisit == false) + { + readModule = await _readModuleRepository.Where(x => x.Id == inDto.ReadingId).FirstOrDefaultAsync(); + } + + Dictionary keyValuePairs = new Dictionary(); + keyValuePairs.Add(ModuleTypeEnum.Global, ClinicalLevel.ImageRead); + keyValuePairs.Add(ModuleTypeEnum.Oncology, ClinicalLevel.OncologyRead); + + + + + + var usedIdsQuery = _readingClinicalDataRepository.Where(x => x.ReadingId == inDto.ReadingId && x.Id != inDto.ReadingClinicalDataId).Select(x => x.ClinicalDataTrialSetId); + + List clinicalList = await _clinicalDataTrialSetRepository.Where(x => x.TrialId == inDto.TrialId && x.IsConfirm) + .WhereIf(inDto.UploadRole != null, x => x.UploadRole == inDto.UploadRole) + .Where(x => !usedIdsQuery.Contains(x.Id)) + + + + .WhereIf(inDto.IsVisit && inDto.IsBaseLine, x => x.ClinicalDataLevel == ClinicalLevel.Subject || x.ClinicalDataLevel == ClinicalLevel.SubjectVisit) + .WhereIf(inDto.IsVisit && !inDto.IsBaseLine, x => x.ClinicalDataLevel == ClinicalLevel.SubjectVisit) + .WhereIf(!inDto.IsVisit, x => x.ClinicalDataLevel == ClinicalLevel.ImageRead || x.ClinicalDataLevel == ClinicalLevel.OncologyRead) + .WhereIf(readModule != null, x => x.ClinicalDataLevel == keyValuePairs[readModule.ModuleType]) + .WhereIf(inDto.TrialReadingCriterionId!=null,x=>x.TrialClinicalDataSetCriteriaList.Any(y=>y.TrialReadingCriterionId== inDto.TrialReadingCriterionId)) + //.WhereIf(criterion!=null,x=>x.CriterionEnumListStr.Contains($"|{(int)criterion.CriterionType}|")) + .Select(x => new GetTrialClinicalDataSelectOutDto() + { + ClinicalDataLevel = x.ClinicalDataLevel, + ClinicalDataSetName = x.ClinicalDataSetName, + ClinicalUploadType = x.ClinicalUploadType, + FileName = x.FileName, + Path = x.Path, + Id = x.Id, + CriterionEnumList = x.CriterionEnumList, + }).ToListAsync(); + + return clinicalList; + } + + + /// + /// 获取检查批次 阅片或任务临床数据 + /// + /// + /// + [HttpPost] + public async Task<(List, object)> GetReadingOrTaskClinicalDataList(GetReadingOrTaskClinicalDataListInDto inDto) + { + var readingNameOrTaskBlindName = string.Empty; + if (inDto.ReadingId == null) + { + var visitTask = await _visitTaskRepository.FirstOrDefaultAsync(x => x.Id == inDto.VisitTaskId); + inDto.ReadingId = visitTask.SouceReadModuleId ?? visitTask.SourceSubjectVisitId; + readingNameOrTaskBlindName = visitTask.TaskBlindName; + } + + if (readingNameOrTaskBlindName.IsNullOrEmpty()) + { + readingNameOrTaskBlindName = await _subjectVisitRepository.Where(x => x.Id == inDto.ReadingId).Select(x => x.VisitName).FirstOrDefaultAsync(); + if (readingNameOrTaskBlindName.IsNullOrEmpty()) + { + readingNameOrTaskBlindName = await _readModuleRepository.Where(x => x.Id == inDto.ReadingId).Select(x => x.ModuleName).FirstOrDefaultAsync(); + + } + } + inDto.SelectIsSign = false; + var result = await GetClinicalDataList(inDto); + + var readingIds = result.Select(x => x.ReadingId).ToList(); + + var previousHistoryList = await _previousHistoryRepository.Where(x => readingIds.Contains(x.SubjectVisitId)).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var previousOtherList = await _previousOtherRepository.Where(x => readingIds.Contains(x.SubjectVisitId)).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var previousSurgeryList = await _previousSurgeryRepository.Where(x => readingIds.Contains(x.SubjectVisitId)).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + foreach (var item in result) + { + item.ClinicalTableData = new ClinicalDataTable() + { + PreviousHistoryList = previousHistoryList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + PreviousOtherList = previousOtherList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + PreviousSurgeryList = previousSurgeryList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + }; + + } + + Dictionary keys = new Dictionary() { + {ClinicalLevel.SubjectVisit,0 }, + {ClinicalLevel.ImageRead,2 }, + {ClinicalLevel.OncologyRead,3 }, + {ClinicalLevel.Subject,4 }, + }; + + result = result.OrderBy(x => keys[x.ClinicalDataLevel]).ToList(); + + return (result, new + { + SubjectCode = await _subjectRepository.Where(x => x.Id == inDto.SubjectId).Select(x => x.Code).FirstOrDefaultAsync(), + ReadingNameOrTaskBlindName = readingNameOrTaskBlindName, + }); + } + + + /// + /// 获取阅片临床数据列表 + /// + /// + /// + [HttpPost] + public async Task<(List, object)> GetReadingClinicalDataList(GetReadingClinicalDataListIndto inDto) + { + var getTrialClinicalDataSelectIndto = new GetTrialClinicalDataSelectIndto() + { + ReadingId = inDto.ReadingId, + TrialId = inDto.TrialId, + IsBaseLine = inDto.IsBaseLine, + SubjectId = inDto.SubjectId, + IsVisit = inDto.IsVisit, + TrialReadingCriterionId = inDto.TrialReadingCriterionId + }; + + var clinicalDataList = await this.GetTrialClinicalDataSelect(getTrialClinicalDataSelectIndto); + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.IQC) + { + inDto.UploadRole = UploadRole.CRC; + } + var isBaseLine = await _subjectVisitRepository.AnyAsync(x => x.Id == inDto.ReadingId && x.IsBaseLine); + + + var result = await this.GetReadingClinicalList(inDto); + var readingIds = result.Select(x => x.ReadingId).ToList(); + + var previousHistoryList = await _previousHistoryRepository.Where(x => readingIds.Contains(x.SubjectVisitId)).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var previousOtherList = await _previousOtherRepository.Where(x => readingIds.Contains(x.SubjectVisitId)).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var previousSurgeryList = await _previousSurgeryRepository.Where(x => readingIds.Contains(x.SubjectVisitId)).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + foreach (var item in result) + { + item.ClinicalTableData = new ClinicalDataTable() + { + PreviousHistoryList = previousHistoryList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + PreviousOtherList = previousOtherList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + PreviousSurgeryList = previousSurgeryList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + }; + + } + + return (result, new + { + IsCanAddClinicalData = clinicalDataList.Count() > 0, + }); + } + + [NonDynamicMethod] + public async Task> GetClinicalDataList(GetReadingOrTaskClinicalDataListInDto inDto) + { + + if (inDto.ReadingId == null) + { + var visitTask = await _visitTaskRepository.FirstOrDefaultAsync(x => x.Id == inDto.VisitTaskId); + inDto.ReadingId = visitTask.SouceReadModuleId ?? visitTask.SourceSubjectVisitId; + } + + + var resultQuery = _readingClinicalDataRepository.Where(x => x.SubjectId == inDto.SubjectId) + .WhereIf(inDto.ReadingClinicalDataId != null, x => x.Id == inDto.ReadingClinicalDataId) + .WhereIf(inDto.SelectIsSign, x => x.IsSign == true) + .Where(x => x.ReadingId == inDto.ReadingId || (x.SubjectId == inDto.SubjectId && x.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Subject)) + .Select(x => new GetReadingClinicalDataListOutDto() + { + ClinicalDataLevel = x.ClinicalDataTrialSet.ClinicalDataLevel, + SubjectId = x.SubjectId, + ReadingId = x.ReadingId, + ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName, + ClinicalDataTrialSetId = x.ClinicalDataTrialSetId, + IsSign = x.IsSign, + ClinicalUploadType = x.ClinicalDataTrialSet.ClinicalUploadType, + CriterionEnumList = x.ClinicalDataTrialSet.CriterionEnumList, + TrialClinicalDataSetCriteriaList=x.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList, + Id = x.Id, + UploadRole = x.ClinicalDataTrialSet.UploadRole, + IsCRCUpload = x.ClinicalDataTrialSet.UploadRole == UploadRole.CRC, + FileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() + { + Id = y.Id, + FileName = y.FileName, + Path = y.Path, + CreateTime = y.CreateTime, + }).ToList() + }); + + + var result = await resultQuery.ToListAsync(); + // 根据标准 + if (inDto.VisitTaskId != null) + { + var visitTaskInfo = await _visitTaskRepository.FirstOrDefaultAsync(x => x.Id == inDto.VisitTaskId); + + result = result.Where(x => x.TrialClinicalDataSetCriteriaList.Any(z=>z.TrialReadingCriterionId==visitTaskInfo.TrialReadingCriterionId)).ToList(); + } + + + result = result.Where(x => !(x.ClinicalUploadType == ClinicalUploadType.PDF && x.FileList.Count() == 0)).ToList(); + // 需要排除表格为空的数据 + + + var readingIds = result.Select(x => x.ReadingId).ToList(); + + var tablecount = (await _previousHistoryRepository.Where(x => readingIds.Contains(x.SubjectVisitId)).CountAsync()) + + (await _previousOtherRepository.Where(x => readingIds.Contains(x.SubjectVisitId)).CountAsync()) + + (await _previousSurgeryRepository.Where(x => readingIds.Contains(x.SubjectVisitId)).CountAsync()); + + if (tablecount == 0) + { + result = result.Where(x => x.ClinicalUploadType != ClinicalUploadType.Table).ToList(); + } + + return result; + } + + + /// + /// 获取临床数据集合 + /// + /// + [NonDynamicMethod] + public async Task> GetReadingClinicalList(GetReadingClinicalDataListIndto inDto) + { + + //var criterionType = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).Select(x => x.CriterionType).FirstNotNullAsync(); + + + var resultQuery = _readingClinicalDataRepository.Where(x => x.SubjectId == inDto.SubjectId) + .Where(x => x.ReadingId == inDto.ReadingId || (x.SubjectId == inDto.SubjectId && x.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Subject)) + //.WhereIf(inDto.UploadRole == UploadRole.IC, x => x.ClinicalDataTrialSet.UploadRole == UploadRole.IC) + + .Where(x => x.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList.Any(t=>t.TrialReadingCriterionId==inDto.TrialReadingCriterionId)) + .Select(x => new GetReadingClinicalDataListOutDto() + { + ClinicalDataLevel = x.ClinicalDataTrialSet.ClinicalDataLevel, + SubjectId = x.SubjectId, + ReadingId = x.ReadingId, + ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName, + ClinicalDataTrialSetId = x.ClinicalDataTrialSetId, + IsSign = x.IsSign, + ClinicalUploadType = x.ClinicalDataTrialSet.ClinicalUploadType, + Id = x.Id, + UploadRole = x.ClinicalDataTrialSet.UploadRole, + IsCRCUpload = x.ClinicalDataTrialSet.UploadRole == UploadRole.CRC, + IsBlind = x.IsBlind, + IsComplete = x.IsComplete, + FileCount = x.FileCount, + + ReadingClinicalDataState = x.ReadingClinicalDataState, + + FileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() + { + Id = y.Id, + FileName = y.FileName, + Path = y.Path, + CreateTime = y.CreateTime, + }).ToList() + }); + + var result = await resultQuery.ToListAsync(); + //result = result.Where(x => !(x.UploadRole == UploadRole.IC && x.ClinicalUploadType == ClinicalUploadType.PDF && x.FileList.Count() == 0)).ToList(); + return result; + } + + #endregion + + + #region 阅片临床数据PDF + + /// + /// 获取单个阅片临床数据的所有文件 + /// + /// + /// + [HttpPost] + public async Task> GetReadingClinicalDataPDFList(GetReadingClinicalDataPDFListIndto inDto) + { + + var result = await _readingClinicalDataPDFRepository.Where(x => x.ReadingClinicalDataId == inDto.ReadingClinicalDataId).ProjectTo(_mapper.ConfigurationProvider) + .ToPagedListAsync(inDto.PageIndex, inDto.PageSize, inDto.SortField == null ? nameof(GetReadingClinicalDataPDFListOutDto.FileName) : inDto.SortField, + inDto.Asc); + return result; + } + + /// + /// 删除PDF单个文件 + /// + /// + /// + [HttpDelete("{id:guid}")] + public async Task DeleteReadingClinicalDataPDF(Guid id) + { + var pdf = await _readingClinicalDataPDFRepository.FirstOrDefaultAsync(t => t.Id == id); + + await _readingClinicalDataPDFRepository.DeleteAsync(pdf, true); + + var fileCount = await _readingClinicalDataPDFRepository.Where(t => t.ReadingClinicalDataId == pdf.ReadingClinicalDataId).CountAsync(); + + await _readingClinicalDataRepository.UpdatePartialFromQueryAsync(pdf.ReadingClinicalDataId, c => new ReadingClinicalData() { FileCount = fileCount }, true); + + return ResponseOutput.Result(true); + } + + #endregion + + + ///// + ///// 设置临床数据是否盲化 + ///// + ///// + //[HttpPost] + //public async Task SetReadingClinicalDataIsBlind(SetReadingClinicalDataIsBlind inDto) + //{ + // await _readingClinicalDataRepository.UpdatePartialFromQueryAsync(inDto.Id, x=>new ReadingClinicalData() { + // IsBlind=inDto.IsBlind, + // }); + // return ResponseOutput.Ok(await _readingClinicalDataRepository.SaveChangesAsync()); + //} + + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ClinicalDataServiceViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ClinicalDataServiceViewModel.cs new file mode 100644 index 0000000..d864d83 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ClinicalDataServiceViewModel.cs @@ -0,0 +1,335 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.Reading.Dto +{ + + + public class ClinicalDataTrialSetAddOrEdit + { + public Guid? Id { get; set; } + + + /// + /// 名称 + /// + public string ClinicalDataSetName { get; set; } + + /// + /// 临床级别 + /// + public ClinicalLevel ClinicalDataLevel { get; set; } + + + /// + /// 上传方式 + /// + public ClinicalUploadType ClinicalUploadType { get; set; } + + /// + /// 上传角色 + /// + public UploadRole UploadRole { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 项目ID + /// + public Guid? TrialId { get; set; } + + /// + /// 是否勾选 + /// + public bool IsConfirm { get; set; } + + /// + /// 模板文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件路径 + /// + public string Path { get; set; } + + + public List TrialCriterionIdList { get; set; } + + //public string CriterionEnumListStr { get; set; } = String.Empty; + + //public List CriterionEnumList { get; set; } + } + + public class ClinicalDataSystemSetAddOrEdit + { + public Guid? Id { get; set; } + + + /// + /// 枚举 + /// + public int ClinicalDataSetEnum { get; set; } + + /// + /// 名称 + /// + public string ClinicalDataSetName { get; set; } + + /// + /// 临床级别 + /// + public ClinicalLevel ClinicalDataLevel { get; set; } + + /// + /// 上传角色 + /// + public UploadRole UploadRole { get; set; } + + + /// + /// 上传方式 + /// + public ClinicalUploadType ClinicalUploadType { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 模板文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件路径 + /// + public string Path { get; set; } + + + //public List SystemCriterionIdList { get; set; } + + + public string CriterionEnumListStr { get; set; } = String.Empty; + + public List CriterionEnumList { get; set; } + + + } + + + + public class ClinicalDataTrialSetView: ClinicalDataTrialSetAddOrEdit + { + public List TrialCriterionNameList { get; set; } + + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + + + /// + /// 系统的ClinicalDataSetId + /// + public Guid? SystemClinicalDataSetId { get; set; } + + + + /// + /// 是否来自于系统数据 + /// + public bool IsFromSystemData + { + get + { + return this.SystemClinicalDataSetId == null ? false : true; + } + } + + /// + /// 是否使用 + /// + public bool IsUsed { get; set; } = false; + + } + + + public class ClinicalDataSystemSetView : ClinicalDataSystemSetAddOrEdit + { + + //public List SystemCriterionNameList { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + } + + public class ClinicalDataInDto + { + /// + /// 项目Id + /// + public Guid TrialId { get; set; } + + /// + /// 患者ID + /// + public Guid SubjectId { get; set; } + + /// + /// 检查批次或者阅片ID + /// + public Guid VisitOrReadId { get; set; } + } + public class ClinicalDataQuery : PageInput + { + /// + /// 项目ID + /// + public Guid? TrialId { get; set; } + + /// + /// 名称 + /// + public string ClinicalDataSetName { get; set; } = string.Empty; + + } + public class GetTrialClinicalDataSystemIndto + { + /// + /// 名称 + /// + public string ClinicalDataSetName { get; set; } = String.Empty; + + /// + /// 临床级别 + /// + public ClinicalLevel? ClinicalDataLevel { get; set; } + + + /// + /// 上传方式 + /// + public ClinicalUploadType? ClinicalUploadType { get; set; } + } + + + public class SetClinicalDataCheckdIndto + { + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + + + public List ClinicalDataTrialIds { get; set; } + } + + + public class GetTrialClinicalDataTrialIndto + { + public Guid TrialId { get; set; } + + /// + /// 名称 + /// + public string ClinicalDataSetName { get; set; } = String.Empty; + + /// + /// 临床级别 + /// + public ClinicalLevel? ClinicalDataLevel { get; set; } + + + /// + /// 上传方式 + /// + public ClinicalUploadType? ClinicalUploadType { get; set; } + } + + + + public class CriterionSelectDto + { + public Guid Id { get; set; } + + public string CriterionName { get; set; } + } + + public class SystemCriterionSelectDto + { + public Guid Id { get; set; } + + public string CriterionName { get; set; } + + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + public bool IsCompleteConfig { get; set; } + } + + public class TrialCriterionSelectDto + { + public Guid Id { get; set; } + + public string CriterionName { get; set; } + + public bool IsEnable { get; set; } + + public bool IsCompleteConfig { get; set; } + + public bool IsConfirm { get; set; } + } + + + + + + public class SystemCriterionSelectQuery + { + public string? CriterionName { get; set; } + + public bool? IsEnable { get; set; } + + public bool? IsCompleteConfig { get; set; } + } + + public class TrialCriterionSelectQuery + { + public Guid TrialId { get; set; } + public string? CriterionName { get; set; } + + public bool? IsEnable { get; set; } + + public bool? IsCompleteConfig { get; set; } + + public bool? IsConfirm { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/DefaultShortcutKeyViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/DefaultShortcutKeyViewModel.cs new file mode 100644 index 0000000..1157a2b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/DefaultShortcutKeyViewModel.cs @@ -0,0 +1,82 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2023-02-13 10:38:12 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.ViewModel +{ + /// DefaultShortcutKeyView 列表视图模型 + public class DefaultShortcutKeyView + { + public Guid Id { get; set; } + public string Keyboardkey { get; set; } + public int ShortcutKeyEnum { get; set; } + public int ImageToolType { get; set; } + public bool AltKey { get; set; } + + public bool CtrlKey { get; set; } + + public bool ShiftKey { get; set; } + + public bool MetaKey { get; set; } + + public string Text { get; set; } + + public string Code { get; set; } = string.Empty; + } + + public class RestoreDefaultShortcutKeyInDto + { + public int ImageToolType { get; set; } + } + + public class SetDefaultShortcutKey + { + public int ImageToolType { get; set; } + + public List ShortcutKeyList { get; set; } + } + + public class DefaultShortcutKeyItem + { + public string Keyboardkey { get; set; } + public int ShortcutKeyEnum { get; set; } + + public bool AltKey { get; set; } + + + public string Text { get; set; } + + public bool CtrlKey { get; set; } + + public bool ShiftKey { get; set; } + + public bool MetaKey { get; set; } + public string Code { get; set; } = string.Empty; + } + + + ///DefaultShortcutKeyQuery 列表查询参数模型 + public class DefaultShortcutKeyQuery + { + public int ImageToolType { get; set; } + + public int? ShortcutKeyEnum { get; set; } + } + + /// DefaultShortcutKeyAddOrEdit 列表查询参数模型 + public class DefaultShortcutKeyAddOrEdit + { + public Guid Id { get; set; } + public string Keyboardkey { get; set; } + public int ShortcutKeyEnum { get; set; } + public int ImageToolType { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/GetReadModuleDto.cs b/IRaCIS.Core.Application/Service/Reading/Dto/GetReadModuleDto.cs new file mode 100644 index 0000000..fb390eb --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/GetReadModuleDto.cs @@ -0,0 +1,338 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.Reading.Dto +{ + public class GetReadModuleDto : PageInput + { + public Guid? TrialId { get; set; } + + public Guid? SubjectId { get; set; } + + /// + /// 项目中心Code + /// + public string? TrialSiteCode { get; set; } + + /// + /// 患者Code + /// + public string? SubjectCode { get; set; } + + /// + /// 模块类型 + /// + public ModuleTypeEnum? ModuleType { get; set; } + + /// + /// 状态 + /// + public ReadingStatusEnum? ReadingStatus { get; set; } + + /// + /// 任务名称 + /// + public string? Name { get; set; } + + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + } + + public class GetSubjectReadVisitsOutDto + { + public Guid SubjectVisitId { get; set; } + + public decimal VisitNum { get; set; } + + public string VisitName { get; set; } + } + + + public class GetSubjectReadVisitsInDto + { + /// + /// 阅片配置的类型 + /// + public ReadingSetType ReadingSetType { get; set; } + + /// + /// 患者ID + /// + public Guid SubjectId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + public class ReadModuleAddDto + { + public Guid TrialId { get; set; } + + /// + /// 截止日期 + /// + public DateTime? ExpirationDate { get; set; } + + /// + /// 截止检查批次 + /// + public decimal? ExpirationVisitNum { get; set; } + + /// + /// 检查批次计划ID + /// + public Guid? VisitStageId { get; set; } + + /// + /// 患者ID + /// + public Guid SubjectId { get; set; } + + /// + /// 名称 + /// + public string Name { get; set; } + + /// + /// 阅片配置的类型 + /// + public ReadingSetType ReadingSetType { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + + public class ReadModuleAddOrEdit : ReadModule + { + public new Guid? Id { get; set; } + + } + + public class VisitFinishReadingAddReadModuleInDto + { + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 患者Id + /// + public Guid SubjectId { get; set; } + + /// + /// 检查批次Id + /// + public Guid SubjectVisitId { get; set; } + + + } + + public class GetReadModuleSingleIndto + { + public Guid Id { get; set; } + + public Guid SubjectId { get; set; } + + + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + + } + + + public class GetReadModuleSingleOutdto + { + public Guid Id { get; set; } + + /// + /// 模块类型 + /// + public ModuleTypeEnum ModuleType { get; set; } + + /// + /// 检查批次ID + /// + public Guid? SubjectVisitId { get; set; } + + + /// + /// 检查批次名称 + /// + public string SubjectVisitName { get; set; } + + /// + /// 干系人Id + /// + public List StakeholderIds { get; set; } + + /// + /// 干系人姓名 + /// + public List StakeholderNames { get; set; } + + /// + /// 状态 + /// + public ReadingStatusEnum Status { get; set; } + } + + + + + public class GetReadModuleResultDto + { + /// + /// 当前页索引 + /// + public int PageIndex { get; set; } + + /// + /// 每页的记录条数 + /// + public int PageSize { get; set; } + + /// + /// 数据总数 + /// + public long TotalCount { get; set; } = 0; + + /// + /// 最大长度 + /// + public int MaxLength { get; set; } + + /// + /// 数据 + /// + public List CurrentPageData { get; set; } + } + + + //public class GetReadModuleDtoOut + + public class GetReadModuleDtoOut + { + public string SubjectCode { get; set; } + + public string TrialSiteCode { get; set; } + + public string SiteCode { get; set; } + + public Guid SiteId { get; set; } + + public Guid SubjectId { get; set; } + + public List Data { get; set; } + + } + + + + public class ReadPlanView + { + public string SubjectCode { get; set; } + + public string TrialSiteCode { get; set; } + + public string SiteCode { get; set; } + + public Guid SiteId { get; set; } + + public Guid SubjectId { get; set; } + + + public List SubjectVisitPlanList { get; set; } + + public List ReadMoudlePlanList { get; set; } + } + + public class ReadMoudleView + { + public Guid Id { get; set; } + + public string Name { get; set; } + + public Guid SubjectId { get; set; } + + public ModuleTypeEnum ModuleType { get; set; } + + + public bool? IsUrgent { get; set; } + + + public Guid SubjectVisitId { get; set; } + + public string SubjectVisitName { get; set; } + + + public Guid? CutOffVisitId { get; set; } + + + public string? CutOffVisitName { get; set; } + + + public Guid? ReadModuleId { get; set; } + + + public string? ReadModuleName { get; set; } + + public DateTime CreateTime { get; set; } + + public bool IsVisit { get; set; } + + } + + public class SubjectVisitPlanView + { + /// + /// Id(阅片期Id 或者 检查批次ID) + /// + public Guid Id { get; set; } + + public string Name { get; set; } + + public Guid SubjectId { get; set; } + + public ModuleTypeEnum ModuleType { get; set; } + + + public bool? IsUrgent { get; set; } + + public Guid SubjectVisitId { get; set; } + + public string SubjectVisitName { get; set; } + + + public DateTime CreateTime { get; set; } + + + public bool IsFinalVisit { get; set; } = false; + + public Guid? OutPlanPreviousVisitId { get; set; } + + public string OutPlanPreviousVisitName { get; set; } + + public Guid? SiteId { get; set; } + + public bool IsVisit { get; set; } = true; + + public decimal? VisitNum { get; set; } + + + public PDStateEnum PDState { get; set; } + + public bool IsEnrollmentConfirm { get; set; } = false; + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/OrganInfoViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/OrganInfoViewModel.cs new file mode 100644 index 0000000..52fb841 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/OrganInfoViewModel.cs @@ -0,0 +1,481 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-12 14:07:12 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Core.Application.ViewModel +{ + + public class CriterionNidusView + { + public Guid? Id { get; set; } + public Guid CriterionId { get; set; } + /// + /// 器官类型 + /// + public OrganType OrganType { get; set; } + + public LesionType LesionType { get; set; } + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + } + + public class OrganDictionary + { + + public string ChildGroup { get; set; } + + public int ChildCodeEnum { get; set; } + + + public DicDataTypeEnum DataTypeEnum { get; set; } + + + + public string Value { get; set; } = string.Empty; + + + public string ValueCN { get; set; } = string.Empty; + + + + public string Description { get; set; } = string.Empty; + + public int ShowOrder { get; set; } + + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + + + public string Code { get; set; } + + public Guid? ParentId { get; set; } + + public bool IsEnable { get; set; } + + public Guid? ConfigTypeId { get; set; } + + } + + public class GetCriterionLesionTypeInDto + { + public Guid CriterionId { get; set; } + } + + ///CriterionNidusQuery 列表查询参数模型 + public class CriterionNidusQuery + { + + public Guid CriterionId { get; set; } + } + + /// CriterionNidusAddOrEdit 列表查询参数模型 + public class CriterionNidusAddOrEdit + { + public Guid? Id { get; set; } + public Guid CriterionId { get; set; } + /// + /// 病灶类型 + /// + public LesionType LesionType { get; set; } + /// + /// 器官类型 + /// + public OrganType OrganType { get; set; } + + /// + /// 是否是系统标准 + /// + public bool IsSystemCriterion { get; set; } = true; + + public bool CriterionNidus { get; set; } + } + public class BatchAddTrialOrganInDto + { + public Guid TrialId { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + + public List OrganIds { get; set; } + } + + + /// OrganInfoView 列表视图模型 + public class OrganInfoView + { + + + public Guid Id { get; set; } + + /// + /// 部位 + /// + public string Part { get; set; } + + /// + /// TULOC + /// + public string TULOC { get; set; } + + /// + /// 位置 + /// + public string TULAT { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + + /// + /// 是否是淋巴结 + /// + public IsLymph IsLymphNodes { get; set; } + + + /// + /// 是否可编辑位置 + /// + public bool IsCanEditPosition { get; set; } + + /// + /// 器官类型 + /// + public OrganType OrganType { get; set; } + + + /// + /// 部位 英文 + /// + public string PartEN { get; set; } = string.Empty; + + + + /// + /// TULOC 英文 + /// + public string TULOCEN { get; set; } = string.Empty; + + + + + /// + /// 位置 英文 + /// + public string TULATEN { get; set; } = string.Empty; + + + /// + /// 分类 + /// + public string Classification { get; set; } = string.Empty; + + /// + /// 分类 英文 + /// + public string ClassificationEN { get; set; } = string.Empty; + + + /// + /// 序号 + /// + public int ShowOrder { get; set; } = 0; + } + + public class GetTrialSelectOrganListInDto + { + public Guid TrialId { get; set; } + + } + + public class CriterionNidusData : CriterionNidus + { + public Guid OriginalId { get; set; } + } + + + public class SynchronizeSystemOrganInDto + { + [NotDefault] + public Guid FromCriterionId { get; set; } + [NotDefault] + public Guid ToCriterionId { get; set; } + } + + + public class SynchronizeSystemOrganToTrialInDto + { + + public Guid TrialReadingCriterionId { get; set; } + + + + public Guid? SystemCriterionId { get; set; } + } + public class GetReadingOrganListOutDto + { + /// + /// 病灶类型 + /// + public LesionType LesionType { get; set; } + + public List OrganList { get; set; } + } + + public class GetReadingOrganListInDto + { + [NotDefault] + public Guid VisitTaskId { get; set; } + } + + public class SetOrganIsEnableInDto + { + public bool IsEnable { get; set; } + + public List Ids { get; set; } + } + public class GetTrialOrganListInDto + { + public Guid? TrialReadingCriterionId { get; set; } + + public Guid? VisitTaskId { get; set; } + + + public OrganType? OrganType { get; set; } + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + public bool? IsEnable { get; set; } + + public IsLymph? IsLymphNodes { get; set; } + + + public string Part { get; set; } = string.Empty; + public string TULOC { get; set; } = string.Empty; + public string TULAT { get; set; } = string.Empty; + + + + + + + + + + + /// + /// 部位 英文 + /// + public string PartEN { get; set; } = string.Empty; + + + /// + /// TULOC 英文 + /// + public string TULOCEN { get; set; } = string.Empty; + + + + /// + /// 位置 英文 + /// + public string TULATEN { get; set; } = string.Empty; + } + + public class GetTrialCheckOrganList : GetTrialOrganListOutDto + { + public bool IsCheckd { get; set; } + } + + public class ReadingOrganDto : GetTrialOrganListOutDto + { + + public Guid OrganInfoId { get; set; } + + /// + /// 病灶类型 + /// + public LesionType LesionType { get; set; } + } + public class GetTrialOrganListOutDto + { + public Guid Id { get; set; } + public string? Part { get; set; } + public string? TULOC { get; set; } + public string? TULAT { get; set; } + + /// + /// 分类 + /// + public string Classification { get; set; } = string.Empty; + + /// + /// 分类 英文 + /// + public string ClassificationEN { get; set; } = string.Empty; + + + /// + /// 序号 + /// + public int ShowOrder { get; set; } = 0; + + + /// + /// 是否可编辑位置 + /// + public bool IsCanEditPosition { get; set; } + + public bool IsEnable { get; set; } + + /// + /// 器官类型 + /// + public OrganType OrganType { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + + /// + /// 是否是淋巴结 + /// + public IsLymph IsLymphNodes { get; set; } + + + /// + /// 部位 英文 + /// + public string PartEN { get; set; } = string.Empty; + + + /// + /// TULOC 英文 + /// + public string TULOCEN { get; set; } = string.Empty; + + + + /// + /// 位置 英文 + /// + public string TULATEN { get; set; } = string.Empty; + + } + + ///OrganInfoQuery 列表查询参数模型 + public class OrganInfoQuery:PageInput + { + + /// + /// 病灶类型 + /// + public OrganType? OrganType { get; set; } + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + public Guid SystemCriterionId { get; set; } + + } + + + + + + /// OrganInfoAddOrEdit 列表查询参数模型 + public class OrganInfoAddOrEdit + { + public Guid? Id { get; set; } + public string Part { get; set; } + + /// + /// 分类 + /// + public string Classification { get; set; } = string.Empty; + + /// + /// 分类 英文 + /// + public string ClassificationEN { get; set; } = string.Empty; + + + /// + /// 序号 + /// + public int ShowOrder { get; set; } = 0; + + public string TULOC { get; set; } + public string TULAT { get; set; } + + + /// + /// 是否可编辑位置 + /// + public bool IsCanEditPosition { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 是否是淋巴结 + /// + public IsLymph IsLymphNodes { get; set; } + + /// + /// 病灶类型 + /// + public OrganType OrganType { get; set; } + + /// + /// 标准 病灶类型 + /// + public Guid SystemCriterionId { get; set; } + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + + + /// + /// 部位 英文 + /// + public string PartEN { get; set; } = string.Empty; + + + + /// + /// TULOC 英文 + /// + public string TULOCEN { get; set; } = string.Empty; + + + + + /// + /// 位置 英文 + /// + public string TULATEN { get; set; } = string.Empty; + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/PreviousPDFViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/PreviousPDFViewModel.cs new file mode 100644 index 0000000..5704da1 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/PreviousPDFViewModel.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.Reading.Dto +{ + + + public class PreviousPDFInDto + { + /// + /// 项目Id + /// + public Guid TrialId { get; set; } + + /// + /// 患者ID + /// + public Guid SubjectId { get; set; } + + /// + /// 检查批次或者阅片ID + /// + public Guid VisitOrReadId { get; set; } + } + public class PreviousPDFQuery + { + /// + /// 项目ID + /// + public Guid? TrialId { get; set; } + + + + /// + /// 页码 + /// + public int PageIndex { get; set; } = 1; + + /// + /// 每页大小 + /// + public int PageSize { get; set; } = 10; + + + /// + /// 排序字段 + /// + public string? SortField { get; set; } + + /// + /// 排序字段 + /// + public bool SortAsc { get; set; } = true; + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingCalculateViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingCalculateViewModel.cs new file mode 100644 index 0000000..7bab26a --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingCalculateViewModel.cs @@ -0,0 +1,297 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-22 09:36:37 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.Service.Reading.Dto +{ + + public class CalculateTaskInDto + { + public Guid VisitTaskId { get; set; } + + + public bool IsChangeOtherTask { get; set; } + } + + public class TargetLesionCalculateDto + { + /// + /// 最低SOD + /// + public decimal LowSod { get; set; } + + /// + /// 当前SOD + /// + public decimal PresentSod { get; set; } + + /// + /// 是否存在靶病灶 (当前检查批次 至少一个靶病灶的状态为存在) + /// + public bool ExistsTargetLesion { get; set; } + + /// + /// 非淋巴结靶病灶长径之和 decimal + /// + public decimal SumOfDiameter { get; set; } + + /// + /// SOD变化百分比 + /// + public decimal SODPercent { get; set; } + + /// + /// 所有淋巴结靶病灶的短径小于10mm + /// + public bool DiameterLessThan10 { get; set; } + + /// + /// SOD 百分比与基线期SOD相比减小≥30% + /// + public bool SODPercentBigger30 { get; set; } + + /// + /// SOD 百分比 与基线期SOD相比减小<30% + /// + public bool SODPercentLess30 { get; set; } + + /// + /// SOD 百分比 整体检查批次期间SOD最低点SOD相比增加<20% + /// + public bool LowSODPercentLess20 { get; set; } + + /// + /// SOD 百分比 比整体检查批次期间最低点SOD增加≥20% + /// + public bool LowSODPercentBigger20 { get; set; } + + /// + /// SOD 变化值 比整体检查批次期间最低点SOD绝对增加值<5 mm + /// + public bool LowSODChangeLess5 { get; set; } + + /// + /// 比整体检查批次期间最低点SOD绝对增加值≥5 mm + /// + public bool LowSODChangeBigger5 { get; set; } + + /// + /// 被评估为NE的单个靶病灶 是否存在状态为不可评估的靶病灶 + /// + public bool ExixtsNETargetLesion { get; set; } + + /// + /// 上次检查批次点整体肿瘤评估 + /// + public string LastTargetLesionEvaluate { get; set; } + + /// + /// 当前检查批次点非淋巴结病灶至少一个非淋巴结靶病灶的长径>0 + /// + public bool CurrentMajoreBigger0 { get; set; } + + /// + /// 至少一个淋巴结靶病灶短径≥10 mm + /// + public bool CurrenShortBigger10 { get; set; } + + /// + /// 淋巴结非靶病灶状态全部为消失 + /// + public bool NonTargetStateIsLoss { get; set; } + + /// + /// 该淋巴结靶病灶短径绝对增加值≥5 mm + /// + public bool IsAddFive { get; set; } + + /// + /// 靶病灶短径增加值有5mm的Index + /// + public List AddFiveIndexs { get; set; } + + /// + /// 短径有10mm的Index + /// + public List ShortBigger10Indexs { get; set; } + } + + /// + /// 阅片计算Dto + /// + public class ReadingCalculateDto + { + public Guid SubjectId { get; set; } + + public string TaskBlindName { get; set; } = string.Empty; + + public bool IsAnalysisCreate { get; set; } + + public bool? IsSelfAnalysis { get; set; } + + public bool IsBaseLine { get; set; } + + /// + /// TrialReadingCriterionId + /// + public Guid TrialReadingCriterionId { get; set; } + + public string VisitName { get; set; } + + public string BlindName { get; set; } + + public decimal VisitTaskNum { get; set; } + + public Guid VisitTaskId { get; set; } + + public Guid BaseLineTaskId { get; set; } + + public Guid CriterionId { get; set; } + + public Guid TrialId { get; set; } + + public Guid? DoctorUserId { get; set; } + + public Guid SubjectVisitId { get; set; } + + public Arm ArmEnum { get; set; } + + /// + /// 是否修改其他任务 + /// + public bool IsChangeOtherTask { get; set; } = false; + + /// + /// 是否只改其他任务 + /// + public bool IsOnlyChangeAllTask { get; set; } = false; + + /// + /// 修约小数点 + /// + public int DigitPlaces { get; set; } = 2; + + public List QuestionInfo { get; set; } = new List(); + + + } + + + + public class QuestionInfo + { + public Guid QuestionId { get; set; } + + /// + /// 答案 + /// + public string Answer { get; set; } + + /// + /// 问题名称 + /// + public string QuesionName { get; set; } + + public ValueOfType? ValueType { get; set; } + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + public QuestionType? QuestionType { get; set; } + + public List TableRowInfoList = new List(); + + } + + + public class TableRowInfo + { + public decimal RowIndex { get; set; } + + public decimal FristAddTaskNum { get; set; } + + + public string MeasureData { get; set; } + + public List TableQuestionList { get; set; } = new List(); + } + + public class TableQuestionInfo + { + public Guid RowId { get; set; } + + public Guid AnswerId { get; set; } + + /// + /// 答案 + /// + public string Answer { get; set; } + + /// + /// 问题Id + /// + public Guid TableQuestionId { get; set; } + + public Guid QuestionId { get; set; } + + public decimal RowIndex { get; set; } + + /// + /// 问题标识 + /// + public QuestionMark? QuestionMark { get; set; } + + public QuestionType? QuestionType { get; set; } + + } + + + + public class VisitTaskAnswerInfo + { + public Guid VisitTaskId { get; set; } + public Guid QuestionId { get; set; } + + public string VisitName { get; set; } + + public string BlindName { get; set; } + + public decimal SOD { get; set; } + + public decimal VisitTaskNum { get; set; } + + public decimal NewLesionsCount { get; set; } + } + + public class ChangeAllTaskDto + { + public ReadingCalculateDto calculateDto { get; set; } + + public bool IsAnalysisCreate { get; set; } + + public Guid QuestionId { get; set; } + } + + public class ReadingCalculateData + { + public QuestionType QuestionType { get; set; } + + public Func> GetDecimalFun { get; set; } + + public Func> GetIntFun { get; set; } + + public Func> GetDecimalNullFun { get; set; } + + public Func> GetStringFun { get; set; } + + public Func ChangeAllTaskFun { get; set; } + } +} + + diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingClinicalDataDto.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingClinicalDataDto.cs new file mode 100644 index 0000000..e6eb05d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingClinicalDataDto.cs @@ -0,0 +1,512 @@ +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.Reading.Dto +{ + + public class AddOrUpdateReadingClinicalDataDto + { + + public Guid? Id { get; set; } + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 患者ID + /// + public Guid SubjectId { get; set; } + + /// + /// 阅片ID + /// + public Guid ReadingId { get; set; } + + /// + /// 临床数据类型Id + /// + public Guid ClinicalDataTrialSetId { get; set; } + + /// + /// 是否是检查批次 + /// + public bool IsVisit { get; set; } + + + ///// + ///// 是否盲化 + ///// + //public bool IsBlind { get; set; } + + ///// + ///// 是否完整 + ///// + //public bool IsComplete { get; set; } + + /// + /// 要删除的对象 + /// + + public List DeleteFileIds { get; set; } = new List(); + + + public List AddFileList { get; set; } = new List(); + } + + public class GetCRCClinicalDataOutDto + { + + public Guid Id { get; set; } + + /// + /// 名称 + /// + public Guid ClinicalDataTrialSetId { get; set; } + + /// + /// 名称 + /// + public string ClinicalDataSetName { get; set; } + + + /// + /// 上传方式 + /// + public ClinicalUploadType ClinicalUploadType { get; set; } + + + /// + /// 上传角色 + /// + public UploadRole UploadRole { get; set; } + + /// + /// 模板文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件路径 + /// + public string Path { get; set; } + + /// + /// 是否盲化 + /// + public bool? IsBlind { get; set; } + + /// + /// 是否完整 + /// + public bool? IsComplete { get; set; } + + //public List ReadingClinicalDataList { get; set; } + + public List PDFFileList { get; set; } = new List(); + + public ClinicalDataTable ClinicalTableData { get; set; } + } + + + public class ClinicalDataTable + { + public List PreviousHistoryList { get; set; } + + public List PreviousOtherList { get; set; } + + public List PreviousSurgeryList{ get; set; } + + + } + + + + public class GetCRCClinicalDataInDto + { + [NotDefault] + public Guid SubjectVisitId { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid SubjectId { get; set; } + + public bool IsBaseline { get; set; } + } + + /// + /// 是否盲化 + /// + public class SetReadingClinicalDataIsBlind + { + public Guid Id { get; set; } + + public bool IsBlind { get; set; } + } + + public class FileDto + { + /// + /// 文件名称 + /// + public string FileName { get; set; } + + /// + /// 路径 + /// + public string Path { get; set; } + } + + + public class GetReadingOrTaskClinicalDataListInDto + { + [NotDefault] + public Guid SubjectId { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + + public Guid? ReadingId { get; set; } + + public Guid? VisitTaskId { get; set; } + + public Guid? ReadingClinicalDataId { get; set; } + + /// + /// 只查询已经签名的临床数据 + /// + public bool SelectIsSign { get; set; } = true; + } + + /// + /// 获取检查批次列表 + /// + public class GetReadingClinicalDataListIndto + { + [NotDefault] + public Guid SubjectId { get; set; } + [NotDefault] + public Guid ReadingId { get; set; } + + public UploadRole? UploadRole { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + public Guid TrialId { get; set; } + + /// + /// 是否是检查批次 + /// + public bool IsVisit { get; set; } + + /// + /// 是否是基线 + /// + public bool IsBaseLine { get; set; } + } + + public class GetReadingClinicalDataPDFListIndto:PageInput + { + public Guid ReadingClinicalDataId { get; set; } + } + + public class GetReadingClinicalDataPDFListOutDto + { + public Guid? Id { get; set; } + + + /// + /// 阅片临床数据ID + /// + public Guid ReadingClinicalDataId { get; set; } + + /// + /// Path + /// + public string Path { get; set; } + + /// + /// FileName + /// + public string FileName { get; set; } + + /// + /// 是否为检查批次 + /// + public bool IsVisit { get; set; } + + + } + + public class AddOrUpdateReadingClinicalDataPDFDto + { + + public Guid? Id { get; set; } + + + + /// + /// 阅片临床数据ID + /// + public Guid ReadingClinicalDataId { get; set; } + + /// + /// Path + /// + public string Path { get; set; } + + /// + /// FileName + /// + public string FileName { get; set; } + + /// + /// 是否为检查批次 + /// + public bool IsVisit { get; set; } + } + + + public class PMClinicalDataConfirmCommand + { + public Guid Id { get; set; } + + /// + /// 是否盲化 + /// + public bool? IsBlind { get; set; } + + /// + /// 是否完整 + /// + public bool? IsComplete { get; set; } + + } + + + public class GetReadingClinicalDataListOutDto + { + + public Guid Id { get; set; } + + + /// + /// 检查批次Id 或者模块Id + /// + public Guid ReadingId { get; set; } + + + /// + /// 项目配置临床类型Name + /// + public string ClinicalDataSetName { get; set; } + + /// + /// 下拉ID + /// + public Guid ClinicalDataTrialSetId { get; set; } + + /// + /// 临床级别 + /// + public ClinicalLevel ClinicalDataLevel { get; set; } + + /// + /// 上传角色 + /// + public UploadRole UploadRole { get; set; } + + public Guid SubjectId { get; set; } + + /// + /// 上传方式 + /// + public ClinicalUploadType ClinicalUploadType { get; set; } + + /// + /// 标准枚举 + /// + public List CriterionEnumList { get; set; } + + /// + /// 临床级别名称 + /// + public string ClinicalDataLevelName { get; set; } + + /// + /// 上传方式名称 + /// + public string ClinicalUploadTypeName { get; set; } + + /// + /// 是否为IC上传 + /// + public bool IsCRCUpload { get; set; } + + + /// + /// 是否签名 + /// + public bool IsSign { get; set; } + + + /// + /// 是否盲化 + /// + public bool? IsBlind { get; set; } + + /// + /// 是否完整 + /// + public bool? IsComplete { get; set; } + + + //临床数据状态 + public ReadingClinicalDataStatus ReadingClinicalDataState { get; set; } + + /// + /// 文件数量 + /// + public int FileCount { get; set; } + //{ + // get + // { + // return this.FileList.Count(); + // } + //} + + public List FileList { get; set; } = new List(); + + + public ClinicalDataTable ClinicalTableData { get; set; } + + public List TrialClinicalDataSetCriteriaList { get; set; } + } + + + public class GetFileDto + { + + /// + /// Id + /// + + public Guid Id { get; set; } + + /// + /// Path + /// + + public string Path { get; set; } + + /// + /// FileName + /// + + public string FileName { get; set; } + + /// + /// 上传时间 + /// + public DateTime CreateTime { get; set; } + } + + + public class GetTrialClinicalDataSelectIndto + { + /// + /// 项目ID + /// + [NotDefault] + public Guid TrialId { get; set; } + + /// + /// 阅片期临床数据ID + /// + public Guid? ReadingClinicalDataId { get; set; } + + /// + /// 对象ID + /// + public Guid? ReadingId { get; set; } + + public Guid? SubjectId { get; set; } + + /// + /// 是否是检查批次 + /// + public bool IsVisit { get; set; } + + /// + /// 是否是基线 + /// + public bool IsBaseLine { get; set; } + + + /// + /// 上传角色 + /// + public UploadRole? UploadRole { get; set; } + + + public Guid? TrialReadingCriterionId { get; set; } + } + + public class GetTrialClinicalDataSelectOutDto + { + + public Guid Id { get; set; } + + /// + /// 名称 + /// + public string ClinicalDataSetName { get; set; } + + /// + /// 临床级别 + /// + public ClinicalLevel ClinicalDataLevel { get; set; } + + + /// + /// 上传方式 + /// + public ClinicalUploadType ClinicalUploadType { get; set; } + + /// + /// 临床级别名称 + /// + public string ClinicalDataLevelName { get; set; } + + /// + /// 上传方式名称 + /// + public string ClinicalUploadTypeName { get; set; } + + + /// + /// 模板文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件路径 + /// + public string Path { get; set; } + + public List CriterionEnumList { get; set; } + + + } + +} diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs new file mode 100644 index 0000000..8a21c17 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs @@ -0,0 +1,2303 @@ +using IRaCIS.Core.Domain.Share; +using MassTransit; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.Reading.Dto +{ + #region 阅片问题 + + + public class ReadingReportDto + { + public Guid Id + { + get + { + return Guid.NewGuid(); + } + } + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + public Guid QuestionId { get; set; } + + public Guid TableQuestionId { get; set; } + + public Guid? GroupId { get; set; } + + public int ShowOrder { get; set; } + + + /// + /// 是否显示在Dicom阅片中 + /// + public bool IsShowInDicom { get; set; } = false; + + public decimal RowIndex { get; set; } + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// 问题类型 + /// + public TableQuestionType? TableQuestionType { get; set; } + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } + + /// + /// 问题类型 + /// + public QuestionType? QuestionType { get; set; } + + /// + /// 数据来源 + /// + public DataSources DataSource { get; set; } = DataSources.ManualEntry; + + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + public string GroupName { get; set; } + + public string QuestionName { get; set; } + + public bool IsCanEditPosition { get; set; } = false; + + public string BlindName { get; set; } = string.Empty; + + public Guid? RowId { get; set; } + + public string SplitOrMergeLesionName { get; set; } + + public SplitOrMergeType? SplitOrMergeType { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 问题标识 + /// + public QuestionMark? QuestionMark { get; set; } + + + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + /// + /// 序号标记 + /// + public string OrderMark { get; set; } = string.Empty; + + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 自定义单位 + /// + public string CustomUnit { get; set; } = string.Empty; + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + public decimal FristAddTaskNum { get; set; } + + public ReportLayType ReportLayType { get; set; } = ReportLayType.Group; + + + /// + /// 问题英文分组 + /// + public string GroupEnName { get; set; } = string.Empty; + + public List Answer { get; set; } = new List(); + + public List Childrens { get; set; } = new List(); + } + + + public class TaskQuestionAnswer + { + public string TaskName { get; set; } + + public Guid VisitTaskId { get; set; } + + public string Answer { get; set; } + + public bool IsGlobalChange { get; set; } = false; + + /// + /// 全局阅片修改的答案 + /// + public string GlobalChangeAnswer { get; set; } = string.Empty; + + + } + + + public class GetReadingReportEvaluationOutDto + { + + public object ReportCalculateResult{ get; set; } + + public object CalculateResult { get; set; } + public ReadingTaskState ReadingTaskState { get; set; } + + public List VisitTaskList { get; set; } + + public List TaskQuestions { get; set; } + } + + public class VisitTaskGroupAnswerDto + { + public Guid VisitTaskId { get; set; } + + public List Questions { get; set; } + + } + + public class VisitTaskGroupInfo + { + public Guid QuestionId { get; set; } + + public string QuestionName { get; set; } + + public string GroupName { get; set; } + + public int ShowOrder { get; set; } + + + + public List QuestionList = new List(); + } + + + public class VisitTaskQuestionInfo + { + public Guid QuestionId { get; set; } + + + public string QuestionName { get; set; } + + public string GroupName { get; set; } + + public int ShowOrder { get; set; } + + public string Answer { get; set; } = string.Empty; + + public List TableRowQuestionList = new List(); + } + + + public class VistTaskTableQuestionRowInfo + { + public decimal RowIndex { get; set; } + + /// + /// 序号标记 + /// + public string OrderMark { get; set; } = string.Empty; + + + public List TableQuestionList = new List(); + } + + + public class VistTaskTableQuestionInfo + { + public Guid QuestionId { get; set; } + + public Guid TableQuestionId { get; set; } + + public string QuestionName { get; set; } + + + + public int ShowOrder { get; set; } + + public string Answer { get; set; } = string.Empty; + } + + + + public class VisitTaskInfo + { + public Guid VisitTaskId { get; set; } + + public string BlindName { get; set; } + + public bool IsBaseLine { get; set; } + + public DateTime? LatestScanDate { get; set; } + + public string TaskName { get; set; } + + public decimal VisitTaskNum { get; set; } + + public bool IsCurrentTask { get; set; } + } + + public class MergeLesionInDto + { + public Guid VisitTaskId { get; set; } + + public Guid QuestionId { get; set; } + + public Guid MainRowId { get; set; } + + public Guid MergeRowId { get; set; } + + + } + + public class SplitLesionInDto + { + public Guid VisitTaskId { get; set; } + + public Guid QuestionId { get; set; } + + public Guid RowId { get; set; } + } + + public class ChangeCalculationAnswerInDto + { + public Guid VisitTaskId { get; set; } + + public List QuestionAnswer { get; set; } = new List(); + + public List TableQuestionAnswer { get; set; } = new List(); + } + + public class ChangeTableQuestion + { + public Guid RowId { get; set; } + + public Guid QuestionId { get; set; } + + public Guid TableQuestionId { get; set; } + + public string Answer { get; set; } + } + + public class ChangeQuestion + { + public Guid QuestionId { get; set; } + + public string Answer { get; set; } + } + + + + public class ReadClinicalDataInDto + { + [NotDefault] + public Guid VisitTaskId { get; set; } + } + + public class GetReadingToolInDto + { + public Guid VisitTaskId { get; set; } + + } + + public class GetReadingToolOutDto + { + public ReadingTool? ReadingTool { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + + public CriterionType CriterionType { get; set; } + + } + public class ChangeDicomReadingQuestionAnswerInDto + { + public Guid VisitTaskId { get; set; } + + public List Answers { get; set; } + } + + + public class ChangeDicomReadingQuestionAnswerDto + { + public Guid Id { get; set; } + + public string Answer { get; set; } + } + public class DicomReadingQuestionAnswer : ReadingQuestionTrial + { + public string Answer { get; set; } + + public List Childrens = new List(); + } + + public class DeleteReadingRowAnswerInDto + { + public Guid VisitTaskId { get; set; } + + public Guid QuestionId { get; set; } + + public Guid RowId { get; set; } + } + + public class GetDicomReadingQuestionAnswerInDto + { + public Guid VisitTaskId { get; set; } + + public Guid TrialId { get; set; } + } + + public class ReadingTableAnswerRowInfoBase + { + public Guid Id { get; set; } + + /// + /// 第一层的Question + /// + public Guid QuestionId { get; set; } + + /// + /// 器官Id + /// + public Guid? OrganInfoId { get; set; } + + /// + /// VisitTaskId + /// + public Guid VisitTaskId { get; set; } + + /// + /// TrialId + /// + public Guid TrialId { get; set; } + + /// + /// 首次添加任务ID + /// + public Guid FristAddTaskId { get; set; } + + /// + /// 窗宽WW + /// + public decimal? WW { get; set; } + + /// + /// 窗位WL + /// + public decimal? WL { get; set; } + + + /// + /// InstanceId + /// + public Guid? InstanceId { get; set; } + + /// + /// SeriesId + /// + public Guid? SeriesId { get; set; } + + + /// + /// StudyId + /// + public Guid? StudyId { get; set; } + + public bool IsCanEditPosition { get; set; } = false; + + + /// + /// 是Dicom阅片 + /// + public bool IsDicomReading { get; set; } = true; + + /// + /// RowIndex + /// + public decimal RowIndex { get; set; } + + /// + /// MeasureData + /// + public string MeasureData { get; set; } = string.Empty; + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + /// + /// 是否是当前任务添加 + /// + public bool IsCurrentTaskAdd { get; set; } = false; + + + /// + /// SplitRowId + /// + public Guid? SplitRowId { get; set; } + + + /// + /// MergeRowId + /// + public Guid? MergeRowId { get; set; } + + public string BlindName { get; set; } = string.Empty; + + public string OrderMark { get; set; } = string.Empty; + + /// + /// 截图地址 + /// + public string PicturePath { get; set; } = string.Empty; + + /// + /// 第一次添加的任务ID + /// + public decimal FristAddTaskNum { get; set; } = 0; + + + public SplitOrMergeType? SplitOrMergeType { get; set; } + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + + public int? NumberOfFrames { get; set; } + + public Guid UpdateUserId { get; set; } + //string UpdateUserName { get; set; } + public DateTime UpdateTime { get; set; } + + + + + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + + + + public string RowMark { get; set; } = string.Empty; + } + + public class CopyTableAnswerRowInfo : ReadingTableAnswerRowInfoBase + { + public Guid OriginalId { get; set; } + } + public class CopyTableAnswerDto + { + public decimal RowIndex { get; set; } + + /// + /// 问题标识 + /// + public QuestionMark? QuestionMark { get; set; } + + public bool IsCopy { get; set; } + + + /// + /// 问题Id + /// + + public Guid QuestionId { get; set; } + + + public Guid RowId { get; set; } + + /// + /// 表格问题Id + /// + public Guid TableQuestionId { get; set; } + + /// + /// 项目Id + /// + public Guid TrialId { get; set; } + + public string Answer { get; set; } + } + public class GetReadingReportEvaluationInDto + { + public Guid VisitTaskId { get; set; } + + public Guid TrialId { get; set; } + + public bool IsCalculate { get; set; } = true; + } + + public class GetReadingQuestionAndAnswerOutDto: GetReadingTableQuestionOutDto + { + + public bool IsBaseLineTask { get; set; } + + public decimal TaskNum { get; set; } + + /// + /// 任务盲态名称 和检查批次盲态一样 + /// + public string BlindName { get; set; } + + public ReadingTaskState ReadingTaskState { get; set; } + + + } + + public class GetCustomTableQuestionAnswerInDto + { + [NotDefault] + public Guid VisitTaskId { get; set; } + } + + public class GetReadingTableQuestionOutDto + { + public List SinglePage { get; set; } + + public List MultiPage { get; set; } + + public List PublicPage { get; set; } + } + + + public class TrialReadQuestionData : ReadingQuestionTrial + { + + public bool IsPage { get; set; } = false; + + public string Answer { get; set; } + + /// + /// 分页名称 + /// + public string PageName { get; set; } + + /// + /// 是否公共分页 + /// + public bool? IsPublicPage { get; set; } = false; + + public int? PageShowOrder { get; set; } + + + + public List Childrens { get; set; } + + public TrialReadTableQuestion TableQuestions { get; set; } + + + } + + /// + /// 病灶Index + /// + public class lesionsIndexDto + { + + + public Guid QuestionId { get; set; } + + public List Rowindexs { get; set; } + } + + public class TableAnsweRowInfo : ReadingTableAnswerRowInfo + { + public string SplitName { get; set; } + + public string MergeName { get; set; } + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + } + + public class GetReadingTableQuestionOrAnswerInDto + { + public Guid TrialReadingCriterionId { get; set; } + public Guid? TaskId { get; set; } + public List TableAnswers { get; set; } = new List(); + + public List TableAnsweRowInfos { get; set; } = new List(); + + public List OrganInfos { get; set; } = new List(); + + public bool IsGetallQuestion { get; set; } = false; + + /// + /// 是否获取预览 + /// + public bool IsGetPreview { get; set; } = false; + } + public class ReadingTableQuestionAnswerInfo : ReadingTableQuestionAnswer + { + public int ShowOrder { get; set; } + + /// + /// 问题标识 + /// + public QuestionMark? QuestionMark { get; set; } + } + + public class TrialReadTableQuestion + { + public List Questions { get; set; } + + public List> Answers { get; set; } + } + + + public class TableQuestionTrial:ReadingTableQuestionTrial + { + public List RelationQuestions { get; set; } = new List(); + } + + + public class GetTableAnswerRowInfoOutDto + { + public Guid Id { get; set; } + + /// + /// 序号标记 + /// + public string OrderMark { get; set; } = string.Empty; + + + public string OrderMarkName { get; set; } = string.Empty; + + + /// + /// 截图地址 + /// + public string PicturePath { get; set; } = string.Empty; + + public int? NumberOfFrames { get; set; } + public Guid RowId { get; set; } + + /// + /// QuestionId + /// + public Guid QuestionId { get; set; } + + /// + /// VisitTaskId + /// + public Guid VisitTaskId { get; set; } + + /// + /// TrialId + /// + public Guid TrialId { get; set; } + + + /// + /// InstanceId + /// + public Guid? InstanceId { get; set; } + + /// + /// SeriesId + /// + public Guid? SeriesId { get; set; } + + + /// + /// StudyId + /// + public Guid? StudyId { get; set; } + + public bool IsCanEditPosition { get; set; } + + /// + /// RowIndex + /// + public string RowIndex { get; set; } + + /// + /// RowIndex + /// + public decimal RowIndexNum { get; set; } + + + /// + /// 是Dicom阅片 + /// + public bool IsDicomReading { get; set; } = true; + + public string BlindName { get; set; } = string.Empty; + + + /// + /// 首次添加任务ID + /// + public Guid FristAddTaskId { get; set; } + + /// + /// 窗宽WW + /// + public decimal? WW { get; set; } + + /// + /// 窗位WL + /// + public decimal? WL { get; set; } + + /// + /// MeasureData + /// + public string MeasureData { get; set; } + + public int ShowOrder { get; set; } + + } + + public class GetTableAnswerRowInfoInDto + { + public Guid VisitTaskId { get; set; } + + public Guid? QuestionId { get; set; } + } + public class GetReadingQuestionAndAnswerInDto + { + + public Guid TrialId { get; set; } + + public Guid VisitTaskId { get; set; } + } + + #endregion + public class GetHistoryGlobalInfoOutDto + { + public Guid VisitTaskId { get; set; } + + public string TaskName { get; set; } + + public ReadingTaskState ReadingTaskState { get; set; } + + public decimal VisitTaskNum { get; set; } + } + + public class GetHistoryGlobalInfoInDto + { + public Guid VisitTaskId { get; set; } + } + + + public class SubmitOncologyReadingInfoInDto + { + public Guid OncologyTaskId { get; set; } + } + + public class SubmitGlobalReadingInfoInDto + { + public Guid GlobalTaskId { get; set; } + } + + public class SetOncologyQuestion + { + public Guid VisitTaskId { get; set; } + + /// + /// 评估结果 + /// + public string EvaluationResult { get; set; } = string.Empty; + + /// + /// 评估原因 + /// + public string EvaluationReason { get; set; } = string.Empty; + } + + public class SetOncologyReadingInfoInDto + { + public Guid OncologyTaskId { get; set; } + + public List OncologyQuestionList { get; set; } + } + + public class BatchSubmitGlobalReadingInfo + { + public Guid GlobalTaskId { get; set; } + + public Guid SubjectId { get; set; } + + public Guid TrialId { get; set; } + + public List VisitTaskAnswerList { get; set; } + + } + + + public class SubmitGlobalReading + { + public Guid VisitTaskId { get; set; } + + public List AnswerList { get; set; } + } + + public class GlobalAnswer + { + public Guid? QuestionId { get; set; } + + + + public GlobalAnswerType GlobalAnswerType { get; set; } + + public string Answer { get; set; } + } + + public class SaveGlobalReadingInfoInDto + { + public Guid GlobalTaskId { get; set; } + + public Guid SubjectId { get; set; } + + public Guid TrialId { get; set; } + + public List QuestionList { get; set; } + + } + + + public class SaveGlobalReadingQuestion + { + public Guid? QuestionId { get; set; } + + public Guid VisitTaskId { get; set; } + + public GlobalAnswerType GlobalAnswerType { get; set; } + + public string Answer { get; set; } + } + + + public class OncologyQuestion + { + public Guid QuestionId { get; set; } + + public string QuestionName { get; set; } + + public string Answer { get; set; } + + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } + + } + + public class OncologyVisitTaskInfo + { + public Guid VisitTaskId { get; set; } + + public string VisitName { get; set; } + + public bool IsHaveChange { get; set; } + + public string VisitRemark { get; set; } = string.Empty; + + /// + /// 评估结果 + /// + public string EvaluationResult { get; set; } = string.Empty; + + /// + /// 评估原因 + /// + public string EvaluationReason { get; set; } = string.Empty; + + + public List QuestionList { get; set; } = new List(); + + } + + public class GetOncologyReadingInfoOutDto + { + public Guid OncologyTaskId { get; set; } + + ///// + ///// 评估结果 + ///// + //public string TrialEvaluationResult { get; set; } = string.Empty; + + /// + /// 评估原因 + /// + public string TrialEvaluationReason { get; set; } = string.Empty; + + + /// + /// 是否显示详情 + /// + public bool IsShowDetail { get; set; } + + //任务阅片状态 + public ReadingTaskState ReadingTaskState { get; set; } + + public Guid GlobalTaskId { get; set; } + + + public Guid SubjectId { get; set; } + + public List AssessTypeList { get; set; } + + public List OncologyVisits { get; set; } = new List(); + } + + public class GetRelatedVisitTaskOutDto + { + public Guid VisitTaskId { get; set; } + public string TaskName { get; set; } + + public bool IsBaseLineTask { get; set; } + + public string TaskBlindName { get; set; } + + public ReadingTaskState ReadingTaskState { get; set; } + + public decimal VisitTaskNum { get; set; } + + public Guid? VisitId { get; set; } + + public bool IsCurrentTask { get; set; } + } + + public class GetVisitReadingQuestionOutDto + { + public Guid QuestionId { get; set; } + + public Guid QuestionName { get; set; } + + //public + } + + public class GetVisitReadingQuestionInDto + { + public Guid TrialId { get; set; } + + public Guid VisitTaskId { get; set; } + } + + public class GetRelatedVisitTaskInDto + { + [NotDefault] + public Guid VisitTaskId { get; set; } + } + + public class GetOncologyReadingInfoInDto + { + public Guid VisitTaskId { get; set; } + } + + public class GetGlobalReadingInfoInDto + { + public Guid VisitTaskId { get; set; } + + /// + /// 当新答案为空的时候 是否是有原数据 + /// + public bool UsingOriginalData { get; set; } = false; + } + + public class GetGlobalReadingInfoOutDto + { + public Guid GlobalTaskId { get; set; } + + public Guid? OtherGlobalTaskId { get; set; } + + public string SubjectCode { get; set; } = string.Empty; + + public string TaskBlindName { get; set; } = string.Empty; + + public Guid? JudgeTaskId { get; set; } + + public string? JudgeTaskName { get; set; } + + public ReadingTaskState ReadingTaskState { get; set; } + + public string GlobalUpdateType { get; set; } + + public List AssessTypeList { get; set; } + + public List TaskList { get; set; } + } + + public class GlobalVisitInfo + { + public string VisitName { get; set; } + + public string BlindName { get; set; } + + public Guid VisitId { get; set; } + + public Guid VisitTaskId { get; set; } + + public decimal VisitNum { get; set; } + + public bool IsBaseLine { get; set; } + + public Arm ArmEnum { get; set; } + + public string AgreeOrNotAnswer + { + get + { + + return this.AgreeOrNot.Select(x => x.Answer).FirstOrDefault()??string.Empty; + } + } + + public List BeforeQuestionList { get; set; } + + public List AgreeOrNot { get; set; } + + public List AfterQuestionList { get; set; } + } + + public class GetGlobalQuestionType + { + public GlobalAnswerType GlobalAnswerType { get; set; } + + public string QuestionName { get; set; } + } + public class GlobalQuestionInfo + { + public Guid? QuestionId { get; set; } + + public bool IsHaveChange { get; set; } = false; + + public string VisitAnswer { get; set; } = string.Empty; + + public string QuestionName { get; set; } + + public string Answer { get; set; } + + public bool IsGlobalAnswer { get; set; } = false; + + /// + /// 问题类型 + /// + public QuestionType? QuestionType { get; set; } + + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + /// + /// 全局阅片显示类型 + /// + public GlobalReadingShowType GlobalReadingShowType { get; set; } = GlobalReadingShowType.NotShow; + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + + + + /// + /// 答案分组 + /// + public string AnswerGroup { get; set; } = string.Empty; + + /// + /// 答案组合 + /// + public string AnswerCombination { get; set; } = string.Empty; + + /// + /// 裁判类型 + /// + public JudgeTypeEnum JudgeType { get; set; } + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } + + + public GlobalAnswerType GlobalAnswerType { get; set; } + + /// + /// 是否是裁判问题 + /// + public bool IsJudgeQuestion { get; set; } = true; + } + + public class GetReadingImgOutDto + { + public string Path { get; set; } + + public string FileName { get; set; } + } + + public class SubjectTask + { + public Guid SubjectId { get; set; } + + public int UnReadTaskCount { get; set; } + + public int Index { get; set; } + } + + + public class GetNextTaskInDto + { + + public string SubjectCode { get; set; }=string.Empty; + + public Guid TrialId { get; set; } + + public Guid? SubjectId { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + public Guid? VisitTaskId { get; set; } + } + + public class GetReadingTaskDto + { + public Guid VisitTaskId { get; set; } + public Guid VisistId { get; set; } + + public Arm ArmEnum { get; set; } + + public bool IsExistsClinicalData { get; set; } + + public bool IsNeedReadClinicalData { get; set; } + + public bool IsReadClinicalData { get; set; } + + /// + /// 修约小数点 + /// + public int? DigitPlaces { get; set; } = 2; + + + /// + /// 标准类型 + /// + public CriterionType CriterionType { get; set; } + + /// + /// eCRF报告是否显示在图像页面 + /// + public bool IseCRFShowInDicomReading { get; set; } = false; + + public bool IsExistsNoDicomFile { get; set; } = false; + + public string TaskBlindName { get; set; } + + public Guid SubjectId { get; set; } + + public string SubjectCode { get; set; }=String.Empty; + + public ReadingCategory ReadingCategory { get; set; } + + public decimal VisitNum { get; set; } + + public bool IsReadingShowSubjectInfo { get; set; } = false; + + public bool IsReadingShowPreviousResults { get; set; } = false; + + /// + /// 任务展示检查批次 读片任务显示是否顺序 + /// + public bool IsReadingTaskViewInOrder { get; set; } = true; + + public Guid TrialReadingCriterionId { get; set; } + + } + + public class GetReadingImgInDto + { + + public Guid? SubjectId { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + + public Guid? VisistTaskId { get; set; } + } + + public class GetConfirmCriterionInDto + { + [NotDefault] + public Guid TrialId { get; set; } + + + public Guid? VisitTaskId { get; set; } + } + + + + /// + /// 返回对象 + /// + public class GetTrialConfirmCriterionListOutDto + { + public Guid ReadingQuestionCriterionTrialId { get; set; } + + /// + /// 标准 + /// + public string ReadingQuestionCriterionTrialName { get; set; } + } + + + public class SetTrialCriterionJudgeQuestionAnswerGroupInDto + { + [NotDefault] + public Guid ReadingQuestionTrialId { get; set; } + + + + public List AnswerGroup { get; set; } + + + public List AnswerCombination { get; set; } + + + + public JudgeTypeEnum JudgeType { get; set; } + } + + public class GetTrialCriterionJudgeQuestionListOutDto + { + public Guid ReadingQuestionTrialId { get; set; } + + public string QuestionName { get; set; } + + + public string PageName { get; set; } + + + public string TypeValue { get; set; } + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + /// + /// 裁判类别 + /// + public JudgeTypeEnum JudgeType { get; set; } + + /// + /// 答案分组 + /// + public List AnswerGroup { get; set; } + + /// + /// 答案组合 + /// + public List AnswerCombination { get; set; } + + + } + + public class GetTrialCriterionJudgeQuestionListInDto + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + public class VisitTaskArm + { + public Arm ArmEnum { get; set; } + + + public Guid VisitTaskId { get; set; } + } + + public class GetJudgeReadingInfoOutDto + { + public Guid? JudgeResultTaskId { get; set; } + + public List VisitInfoList { get; set; } + + public List VisitTaskArmList { get; set; } + + + public ReadingTaskState ReadingTaskState { get; set; } + + /// + /// 裁判结果的备注 + /// + public string JudgeResultRemark { get; set; } + + public List JudgeResultImagePathList { get; set; } = new List(); + + //public string JudgeResultImagePath { get; set; } = string.Empty; + } + + public class GetReadingPastResultListOutDto + { + public Guid VisitTaskId { get; set; } + public string TaskName { get; set; } + + public string TaskBlindName { get; set; } + + public decimal VisitTaskNum { get; set; } + + + public Arm? JudgeResultArm { get; set; } + } + + public class GetJudgeReadingPastResultListInDto + { + [NotDefault] + public Guid VisitTaskId { get; set; } + } + + public class GetReadingPastResultListInDto + { + [NotDefault] + public Guid VisitTaskId { get; set; } + } + + public class SaveJudgeVisitTaskResult + { + public Guid VisitTaskId { get; set; } + + public Guid JudgeResultTaskId { get; set; } + + public string JudgeResultRemark { get; set; } = string.Empty; + + //public string JudgeResultImagePath { get; set; } = string.Empty; + + public List JudgeResultImagePathList { get; set; } = new List(); + } + + public class GetReadingSubjectInfoOutDto + { + public Guid VisitTaskId { get; set; } + + public Guid SubjectId { get; set; } + + public string SubjectCode { get; set; } = string.Empty; + + public ReadingCategory ReadingCategory { get; set; } + + public string TaskBlindName { get; set; } = string.Empty; + + public bool IsReadingShowPreviousResults { get; set; } = false; + + public bool IsReadingShowSubjectInfo { get; set; } = false; + } + + public class GetReadingSubjectInfoInDto + { + public Guid VisitTaskId { get; set; } + } + public class GetJudgeReadingInfo + { + public Guid VisitTaskId { get; set; } + } + + public class JudgeReadingInfoDto + { + public string VisitName { get; set; } + + public Guid VisitId { get; set; } + + + + public List VisitTaskInfoList { get; set; } + + + } + + + public class JudgeReadingQuestion + { + public Arm ArmEnum { get; set; } + + + public Guid GlobalVisitTaskId { get; set; } + + public Guid VisitTaskId { get; set; } + + public List JudgeQuestionList { get; set; } + } + + public class GlobalVisitJudgeQuestion : JudgeQuestion + { + public Guid VisitTaskId { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + } + + public class JudgeQuestion + { + public Guid QuestionId { get; set; } + + + public string QuestionName { get; set; } + + + + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + public dynamic Answer { get; set; } + + public JudgeReadingQuestionType QuestionType { get; set; } = JudgeReadingQuestionType.Question; + + } + + public class GetSystemReadingQuestionInDto + { + [NotDefault] + public Guid Id { get; set; } + + + } + + public class VerifyVisitTaskDto + { + public QuestionType QuestionType { get; set; } + + public Func> Fun { get; set; } + } + + public class GetReportVerifyInDto + { + public Guid VisitTaskId { get; set; } + } + + + + public class VerifyVisitTaskQuestionsInDto + { + /// + /// 任务Id + /// + public Guid VisitTaskId { get; set; } + } + + public class TableQuestionData + { + public Guid TableQuestionId { get; set; } + + public int Count { get; set; } + + } + + public class SubmitTableQuestionOutDto + { + public Guid RowId { get; set; } + } + public class SubmitTableQuestionInDto + { + public Guid QuestionId { get; set; } + + public Guid? OrganInfoId { get; set; } + + public int? NumberOfFrames { get; set; } + + public decimal RowIndex { get; set; } + + /// + /// 截图地址 + /// + public string PicturePath { get; set; } = string.Empty; + + /// + /// 任务Id + /// + public Guid VisitTaskId { get; set; } + + /// + /// 项目Id + /// + public Guid TrialId { get; set; } + + public string MeasureData { get; set; } = string.Empty; + + public Guid? SeriesId { get; set; } + + public Guid? InstanceId { get; set; } + + public Guid? StudyId { get; set; } + + public Guid? RowId { get; set; } + + public bool IsCanEditPosition { get; set; } = false; + + public decimal FristAddTaskNum { get; set; } = 0; + + public decimal? WW { get; set; } + + public decimal? WL { get; set; } + + public string BlindName { get; set; } = string.Empty; + + public bool IsDicomReading { get; set; } = true; + + public List AnswerList { get; set; } + + } + + public class SubmitTableQuestionInfo + { + public Guid TableQuestionId { get; set; } + + public string Answer { get; set; }=string.Empty; + } + + public class GetTrialReadingQuestionInDto + { + [NotDefault] + public Guid ReadingQuestionCriterionTrialId { get; set; } + + public Guid? VisitTaskId { get; set; } + + + public FormType? FormType { get; set; } + } + + public class SubmitDicomVisitTaskInDto + { + public Guid VisitTaskId { get; set; } + } + + /// + /// + /// + public class SubmitVisitTaskQuestionsInDto + { + [NotDefault] + public Guid TrialId { get; set; } + [NotDefault] + public Guid VisitTaskId { get; set; } + [NotDefault] + public Guid ReadingQuestionCriterionTrialId { get; set; } + + public List AnswerList { get; set; } = new List(); + + + } + + public class SaveJudgeTaskDto + { + public List VisitTaskIds { get; set; } + } + + public class QuestionAnswer + { + public Guid ReadingQuestionTrialId { get; set; } + + public string Answer { get; set; } + } + + + //public class GetTrialReadingQuestionDto + //{ + // public Guid Id { get; set; } + + // public string GroupName { get; set; } + + // List Questions { get; set; } + //} + + public class GroupTaskAnswerDto + { + public Guid QuestionId { get; set; } + + public string AnswerGroup { get; set; } + + + public string AnswerCombination { get; set; } + + public JudgeTypeEnum JudgeType { get; set; } + + public List TaskAnswerList { get; set; } + } + + public class TaskGroupAnswer + { + public string Answer { get; set; } + } + + public class AnswerGroup + { + public Guid GroupId { get; set; } + + public string GroupValue { get; set; } + } + + + public class AnswerCombinationDto + { + public List AnswerGroupA { get; set; } + + public List AnswerGroupB { get; set; } + } + + public class TaskAnswerDto + { + public Guid VisitTaskId { get; set; } + + public Guid QuestionId { get; set; } + + public decimal VisitTaskNum { get; set; } + public string Answer { get; set; } + + public string AnswerGroup { get; set; } + + public string AnswerCombination { get; set; } + + public JudgeTypeEnum JudgeType { get; set; } + } + + + public class GetSystemReadingQuestionPageDto + { + public List SinglePage { get; set; } + + public List MultiPage { get; set; } + + public List PublicPage { get; set; } + } + + public class GetTrialReadingQuestionPageDto + { + public List SinglePage { get; set; } + + public List MultiPage { get; set; } + + public List PublicPage{ get; set; } + } + + public class TableQuestionDataInfo + { + /// + /// + /// + public Guid Id { get; set; } + + /// + /// 问题ID + /// + public Guid ReadingQuestionId { get; set; } + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// Type + /// + public string Type { get; set; } + + /// + /// ParentId + /// + public Guid? ParentId { get; set; } + + /// + ///父问题触发值 + /// + public string ParentTriggerValue { get; set; } + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// IsRequired + /// + public IsRequired IsRequired { get; set; } + + /// + /// 排序号 + /// + public int ShowOrder { get; set; } + + /// + /// 值 + /// + public string TypeValue { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建用户 + /// + public Guid CreateUserId { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 显示父问题 + /// + public Guid? RelevanceId { get; set; } + + /// + /// 显示父问题的值 + /// + public string RelevanceValue { get; set; } + + /// + /// 是否显示 + /// + public int ShowQuestion { get; set; } + + /// + /// 最大问题数 + /// + public int? MaxRowCount { get; set; } + + /// + /// 数据表名称 + /// + public string DataTableName { get; set; } + + /// + /// 数据列 + /// + public string DataTableColumn { get; set; } + + + /// + /// 关联父问题 + /// + public Guid? DependParentId { get; set; } + + /// + /// 是否关联 + /// + public IsDepend IsDepend { get; set; } + + /// + /// 表格问题类型 + /// + public TableQuestionType? TableQuestionType { get; set; } + + + /// + /// 问题标识 + /// + public QuestionMark? QuestionMark { get; set; } + } + + + public class GetSystemReadingQuestionOutDto + { + + public Guid Id { get; set; } + + /// + /// 系统标准Id + /// + public Guid ReadingQuestionCriterionSystemId { get; set; } + + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } + + /// + /// 默认值 + /// + public string DefaultValue { get; set; } = string.Empty; + + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发 + /// + public string ParentTriggerValue { get; set; } + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 是否是必须 + /// + public IsRequired IsRequired { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 父问题ID + /// + public Guid? ParentId { get; set; } + + /// + /// 数据列 + /// + public string DataTableColumn { get; set; } = string.Empty; + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 是否是裁判问题 + /// + public bool IsJudgeQuestion { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 关联ID + /// + public Guid? RelevanceId { get; set; } + + /// + /// 关联Value + /// + public string RelevanceValue { get; set; } = string.Empty; + + /// + /// 分组 + /// + public string GroupName { get; set; } + + /// + /// 图片数量 + /// + public int ImageCount { get; set; } = 1; + + /// + /// 是否显示 + /// + public ShowQuestion ShowQuestion { get; set; } = ShowQuestion.Show; + + /// + /// 最大问题数 + /// + public int? MaxQuestionCount { get; set; } + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + /// + /// 问题类型 + /// + public QuestionType? QuestionType { get; set; } + + /// + /// 是否显示在Dicom阅片中 + /// + public bool IsShowInDicom { get; set; } + + + + + /// + /// 序号标记 + /// + public string OrderMark { get; set; } = string.Empty; + + + /// + /// 关联父问题 + /// + public Guid? DependParentId { get; set; } + + /// + /// 是否关联 + /// + public IsDepend? IsDepend { get; set; } + + /// + /// 表格问题类型 + /// + public TableQuestionType? TableQuestionType { get; set; } + + + /// + /// 问题标识 + /// + public QuestionMark? QuestionMark { get; set; } + + public List RelationQuestions { get; set; } + + + + //public List TableQuestions { get; set; } + + public List Childrens { get; set; } + } + + public class GetTrialReadingQuestionOutDto + { + + + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } + + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// 项目标准Id + /// + public Guid ReadingQuestionCriterionTrialId { get; set; } + + /// + /// 项目Id + /// + public Guid TrialId { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发 + /// + public string ParentTriggerValue { get; set; } + + public string GroupName { get; set; } + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 是否是必须 + /// + public IsRequired IsRequired { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + + public int? PageShowOrder { get; set; } + + /// + /// 父问题ID + /// + public Guid? ParentId { get; set; } + + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + /// + /// 答案 + /// + public string Answer { get; set; } + + /// + /// 分页名称 + /// + public string? PageName { get; set; } + + + public bool? IsPublicPage { get; set; } + + public bool IsPage { get; set; } = false; + + public string DefaultValue { get; set; } = string.Empty; + + /// + /// 标准分页Id + /// + public Guid? ReadingCriterionPageId { get; set; } + + /// + /// 关联ID + /// + public Guid? RelevanceId { get; set; } + + /// + /// 关联Value + /// + public string RelevanceValue { get; set; } = string.Empty; + + /// + /// 图片数量 + /// + public int ImageCount { get; set; } + + + /// + /// 是否显示 + /// + public ShowQuestion ShowQuestion { get; set; } + + /// + /// 最大问题数 + /// + public int? MaxQuestionCount { get; set; } + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + /// + /// 问题类型 + /// + public QuestionType? QuestionType { get; set; } + + /// + /// 是否显示在Dicom阅片中 + /// + public bool IsShowInDicom { get; set; } = false; + + //public List TableQuestions { get; set; } + + + public List RelationQuestions { get; set; } + + + public List Childrens { get; set; } + + + public Guid Id { get; set; } + + + public Guid? GroupId { get; set; } + + + /// + /// 数据列 + /// + public string DataTableColumn { get; set; } = string.Empty; + + + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 是否是裁判问题 + /// + public bool IsJudgeQuestion { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + + /// + /// 序号标记 + /// + public string OrderMark { get; set; } = string.Empty; + + + /// + /// 关联父问题 + /// + public Guid? DependParentId { get; set; } + + /// + /// 是否关联 + /// + public IsDepend? IsDepend { get; set; } + + /// + /// 表格问题类型 + /// + public TableQuestionType? TableQuestionType { get; set; } + + + /// + /// 问题标识 + /// + public QuestionMark? QuestionMark { get; set; } + + + + + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + + + + + + } + + ///// + ///// 表格问题 + ///// + //public class GetTrialReadingTableQuestion + //{ + // public List<> class TrialReadingTableQuestion + //} + + + //public class TrialReadingTableQuestion + //{ + + //} +} diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingMedicalReviewDto.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingMedicalReviewDto.cs new file mode 100644 index 0000000..82596f6 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingMedicalReviewDto.cs @@ -0,0 +1,681 @@ +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ImageInfo = IRaCIS.Core.Domain.Models.ImageInfo; + +namespace IRaCIS.Core.Application.Service.Reading.Dto +{ + + public class IRConfirmMedicalReviewInDto + { + public Guid TaskMedicalReviewId { get; set; } + + /// + /// 阅片人是否认同 + /// + public MedicalReviewDoctorUserIdea DoctorUserIdeaEnum { get; set; } + + + /// + /// 不同意重阅原因 + /// + public string DisagreeReason { get; set; } = string.Empty; + + + /// + /// 是否申请重阅 + /// + public bool IsApplyHeavyReading { get; set; } = false; + } + + public class GetIRMedicalFeedbackListInDto:PageInput + { + /// + /// 项目Id + /// + [NotDefault] + public Guid TrialId { get; set; } + + + public bool? IsUrgent { get; set; } + + public MedicalReviewAuditState? AuditState { get; set; } + + public string SubjectCode { get; set; } = string.Empty; + + public string TaskBlindName { get; set; } = string.Empty; + + public ReadingCategory? ReadingCategory { get; set; } + + /// + /// 审核建议 + /// + public AuditAdvice? AuditAdviceEnum { get; set; } + /// + /// 阅片人是否认同 + /// + public MedicalReviewDoctorUserIdea? DoctorUserIdeaEnum { get; set; } + /// + /// 是否关闭对话 + /// + public bool? IsClosedDialog { get; set; } + /// + /// 是否有问题 + /// + public bool? IsHaveQuestion { get; set; } + /// + /// 医学审核对话关闭原因 + /// + public MedicalDialogClose? MedicalDialogCloseEnum { get; set; } + + /// + /// 无效的 为True无效 + /// + public bool? IsInvalid { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + } + + public class GetIRMedicalFeedbackListOutDto + { + public Guid Id { get; set; } + + public bool IsUrgent { get; set; } + + /// + /// 医学审核对话关闭原因 + /// + public MedicalDialogClose MedicalDialogCloseEnum { get; set; } + + /// + /// 审核 质询状态 + /// + public MedicalReviewAuditState AuditState { get; set; } + + public string SubjectCode { get; set; } + + public string TaskBlindName { get; set; } + + /// + /// 阅片类别 + /// + public ReadingCategory ReadingCategory { get; set; } + + public DateTime? FirstReplyTime { get; set; } + + public DateTime? LastReplyTime { get; set; } + + /// + /// 审核建议 + /// + public AuditAdvice AuditAdviceEnum { get; set; } + + /// + /// 阅片人是否认同 + /// + public MedicalReviewDoctorUserIdea DoctorUserIdeaEnum { get; set; } + + + /// + /// 保存结论时间 + /// + public DateTime? SaveConclusionTime { get; set; } + + /// + /// 是否关闭对话 + /// + public bool IsClosedDialog { get; set; } + + /// + /// 是否有问题 + /// + public bool IsHaveQuestion { get; set; } = false; + + + public Guid VisitTaskId { get; set; } + + /// + /// 无效的 为True无效 + /// + public bool IsInvalid { get; set; } + + + public TaskState TaskState { get; set; } + + + public Guid SubjectId { get; set; } + public Guid TrialReadingCriterionId { get; set; } + public string TrialReadingCriterionName { get; set; } + + /// + /// 阅片工具 + /// + public ReadingTool? ReadingTool { get; set; } + + /// + /// 任务展示检查批次 读片任务显示是否顺序 + /// + public bool IsReadingTaskViewInOrder { get; set; } = true; + + + /// + /// 阅片是否显示患者信息 + /// + public bool IsReadingShowSubjectInfo { get; set; } = false; + + /// + /// IR阅片页面是否可以查看既往任务结果 + /// + public bool IsReadingShowPreviousResults { get; set; } = false; + + public int? DigitPlaces { get; set; } = 2; + + public bool IseCRFShowInDicomReading { get; set; } = false; + + public CriterionType CriterionType { get; set; } + } + + public class FinishMedicalReviewInDto + { + public Guid TaskMedicalReviewId { get; set; } + } + + + public class GetMedicalReviewDialogOutDto + { + /// + /// 对话内容 + /// + public string Content { get; set; } + + /// + /// 用户角色 + /// + public string UserTypeShortName { get; set; } + + /// + /// 医学审核对话关闭原因 + /// + public MedicalDialogClose? MedicalDialogCloseEnum { get; set; } + + /// + /// 用户角色枚举 + /// + public int UserTypeEnumInt { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 阅片人是否认同 + /// + public MedicalReviewDoctorUserIdea DoctorUserIdeaEnum { get; set; } = MedicalReviewDoctorUserIdea.defalut; + + /// + /// 是否有问题 + /// + public bool IsHaveQuestion { get; set; } = false; + + /// + /// 质询问题 + /// + public string Questioning { get; set; } = string.Empty; + + /// + /// 审核建议 + /// + public AuditAdvice AuditAdviceEnum { get; set; } = AuditAdvice.None; + + /// + /// 不同意重阅原因 + /// + public string DisagreeReason { get; set; } = string.Empty; + + /// + /// 是否申请重阅 + /// + public bool? IsApplyHeavyReading { get; set; } + + + /// + /// 是否是当前用户 + /// + public bool IsCurrentUser { get; set; } + + + public string CreateUserName { get; set; } + + /// + /// 图片路径 + /// + public string ImagePath { get; set; } = string.Empty; + + + public List FileList { get; set; } = new List(); + + + } + + + + public class GetMedicalReviewDialogInDto + { + [NotDefault] + public Guid TaskMedicalReviewId { get; set; } + } + + public class IRSendMedicalReviewDialogInDto + { + + public Guid TrialId { get; set; } + + + + public string RequestReReadingReason { get; set; } = string.Empty; + + + public Guid TaskMedicalReviewId { get; set; } + + public string Content { get; set; }=string.Empty; + + /// + /// 阅片人是否认同 + /// + public MedicalReviewDoctorUserIdea DoctorUserIdeaEnum { get; set; } = MedicalReviewDoctorUserIdea.defalut; + + + /// + /// 不同意重阅原因 + /// + public string DisagreeReason { get; set; } = string.Empty; + + public bool? IsCopyOrigenalForms { get; set; } = false; + public bool? IsCopyFollowForms { get; set; } = false; + + /// + /// 是否申请重阅 + /// + public bool? IsApplyHeavyReading { get; set; } + + + + public List FileList { get; set; } = new List(); + + + + + public string FileName { get; set; } = string.Empty; + + } + + public class SendMedicalReviewDialogInDto + { + public Guid TaskMedicalReviewId { get; set; } + + public string Content { get; set; } = string.Empty; + + + /// + /// 是否有问题 + /// + public bool IsHaveQuestion { get; set; } = false; + + /// + /// 质询问题 + /// + public string Questioning { get; set; } = string.Empty; + + /// + /// 审核建议 + /// + public AuditAdvice AuditAdviceEnum { get; set; } = AuditAdvice.None; + + } + + public class SaveMedicineQuestionInDto + { + public Guid TaskMedicalReviewId { get; set; } + public Guid VisitTaskId { get; set; } + + + + public List QuestionAnswerList { get; set; } + } + + public class MedicineQuestionAnswer + { + public Guid Id { get; set; } + + + public string Answer { get; set; } + } + + public class GetMedicalReviewReadingTaskInDto + { + public Guid TrialId { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + + public Guid TaskMedicalReviewId { get; set; } + } + + + public class GetMedicalReviewReadingTaskOutDto + { + //阅片类型 + public ReadingMethod ReadingType { get; set; } + + public string SubjectCode { get; set; } + + + public string TaskBlindName { get; set; } + + + public string ReadingUser { get; set; } + + + public bool IsReadingTaskViewInOrder { get; set; } + + public Guid VisitTaskId { get; set; } + + public Arm ArmEnum { get; set; } + + + public MedicalReviewAuditState AuditState { get; set; } + + public bool IsSendMessage { get; set; } + + + public GetOncologyReadingInfoOutDto OncologyInfo { get; set; } + + + public GetGlobalReadingInfoOutDto GlobalInfo { get; set; } + + + public GetJudgeReadingInfoOutDto JudgeInfo { get; set; } + + /// + /// 是否关闭 + /// + public bool IsClosedDialog { get; set; } + + + + public List TaskList { get; set; } = new List(); + + + public List QuestionAnswerList { get; set; } = new List(); + + + public MedicalReviewInfo MedicalReviewInfo { get; set; } + + } + + public class ClosedMedicalReviewDialogInDto + { + public Guid TaskMedicalReviewId { get; set; } + + /// + /// 是否关闭 + /// + public bool IsClosedDialog { get; set; } + + /// + /// 医学审核对话关闭原因 + /// + public MedicalDialogClose MedicalDialogCloseEnum { get; set; } + + /// + /// 对话关闭原因 + /// + public string DialogCloseReason { get; set; } = string.Empty; + } + + + + public class SaveMedicalReviewInfoInDto + { + public bool IsSendDialog { get; set; } + + public Guid TaskMedicalReviewId { get; set; } + + + public List FileList { get; set; } = new List(); + + + /// + /// 是否有问题 + /// + public bool IsHaveQuestion { get; set; } = false; + + /// + /// 质询问题 + /// + public string Questioning { get; set; } = string.Empty; + + ///// + ///// 图片路径 + ///// + //public string ImagePath { get; set; } = string.Empty; + + + ///// + ///// 文件名称 + ///// + //public string FileName { get; set; } = string.Empty; + + /// + /// 审核建议 + /// + public AuditAdvice AuditAdviceEnum { get; set; } + } + + public class MedicalReviewInfo + { + public Guid TaskMedicalReviewId { get; set; } + + public Guid VisitTaskId { get; set; } + + /// + /// 是否有问题 + /// + public bool IsHaveQuestion { get; set; } = false; + + /// + /// 质询问题 + /// + public string Questioning { get; set; } = string.Empty; + + + public List FileList { get; set; } = new List(); + + /// + /// 审核建议 + /// + public AuditAdvice AuditAdviceEnum { get; set; } + + public MedicalReviewAuditState AuditState { get; set; } + + /// + /// 是否关闭 + /// + public bool IsClosedDialog { get; set; } + + + public bool IsSendMessage { get; set; } + } + + public class ReadingMedicineQuestion + { + /// + /// 答案 + /// + public string Answer { get; set; } + + public Guid Id { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发值 + /// + public string ParentTriggerValue { get; set; } = string.Empty; + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 是否必须 + /// + public bool IsRequired { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 父问题 + /// + public Guid? ParentId { get; set; } + + /// + /// 是否确认 + /// + public bool? IsConfirm { get; set; } + + + public List Childrens { get; set; } + } + + public class TaskInfo + { + public Guid? TaskId { get; set; } + + + public decimal VisitTaskNum { get; set; } + /// + /// 是否是当前任务 + /// + public bool IsCurrentTask { get; set; } = false; + + /// + /// 任务名称 + /// + + public string TaskName { get; set; } + + /// + /// 是否有全局更新 + /// + public bool IsGlobalChange { get; set; } = false; + + /// + /// 盲态名称 + /// + public string TaskBlindName { get; set; } + + public Arm ArmEnum { get; set; } + + + + + /// + /// 评估结果 + /// + public string EvaluationResult { get; set; } + + public ReadingCategory ReadingCategory { get; set; } + + + public Guid? SourceSubjectVisitId { get; set; } + public Guid? SouceReadModuleId { get; set; } + + public Guid? OtherTaskId { get; set; } + + public Guid? GlobalTaskId { get; set; } + public string? GlobalTaskName { get; set; } + + public Guid? JudgeTaskId { get; set; } + + public string? JudgeTaskName { get; set; } + + public Arm OtherArmEnum { get; set; } + + //public Guid? JudgeVisitTaskId { get; set; } + + //public Guid? JudgeResultTaskId { get; set; } + + + public Arm? JudgeResultArm { get; set; } + + public Guid SubjectId { get; set; } + + /// + /// 肿瘤学结果 + /// + public string OncologyEvaluationResult { get; set; } = string.Empty; + + /// + /// 肿瘤学原因 + /// + public string OncologyEvaluationReason { get; set; } = string.Empty; + + public List JudgeQuestionAnswerInfoList { get; set; } + + } + + public class JudgeQuestionAnswerInfo + { + + public Guid VisitTaskId { get; set; } + + public string QuestionName { get; set; } + + public string Answer { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + } + +} diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingMedicineQuestionViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingMedicineQuestionViewModel.cs new file mode 100644 index 0000000..da6b07d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingMedicineQuestionViewModel.cs @@ -0,0 +1,350 @@ +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Core.Application.ViewModel +{ + + public class ReadingMedicineSystemQuestionView: ReadingMedicineSystemQuestionAddOrEdit + { + + + /// + /// 排序 + /// + public int? ParentShowOrder { get; set; } + } + + ///ReadingMedicineSystemQuestionQuery 列表查询参数模型 + public class ReadingMedicineSystemQuestionQuery:PageInput + { + public string Type { get; set; } = string.Empty; + public string ParentTriggerValue { get; set; } = string.Empty; + public string QuestionName { get; set; } = string.Empty; + public string TypeValue { get; set; } = string.Empty; + + + public CriterionType? CurrentCriterionType { get; set; } + + public CriterionType? CriterionTypeEnum { get; set; } + + public bool? IsGeneral { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + /// + /// 任务类型 + /// + public ReadingCategory? ReadingCategory { get; set; } + + + + + } + + + public class GetReadingMedicineTrialOtherQuestionOutDto + { + public Guid Id { get; set; } + + public string QuestionName { get; set; } + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + /// + /// 任务类型 + /// + public ReadingCategory ReadingCategory { get; set; } + } + + public class DeleteReadingMedicineTrialQuestion + { + public Guid TrialId { get; set; } + + public Guid Id { get; set; } + } + + public class TrialDataFromSystem : ReadingMedicineTrialQuestion + { + public Guid SystemQuestionId { get; set; } + } + public class AddTrialDataFromSystemInDto + { + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + public List SystemQuestionIds { get; set; } + } + + public class ConfirmReadingMedicineQuestionInDto + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + public class TrialQuestion + { + public Guid Id { get; set; } + + public int ShowOrder { get; set; } + + + public int? ParentShowOrder { get; set; } + + /// + /// 任务类型 + /// + public ReadingCategory ReadingCategory { get; set; } + } + + public class GetReadingMedicineSystemOtherQuestionInDto + { + public Guid? Id { get; set; } + + + + public int? ShowOrder { get; set; } + + /// + /// 任务类型 + /// + public ReadingCategory? ReadingCategory { get; set; } + + } + + /// + /// 获取项目的其他问题 + /// + public class GetReadingMedicineTrialOtherQuestionInDto + { + public Guid TrialId { get; set; } + + public Guid? Id { get; set; } + + public int? ShowOrder { get; set; } + + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + /// + /// 任务类型 + /// + public ReadingCategory? ReadingCategory { get; set; } + } + + + /// ReadingMedicineSystemQuestionAddOrEdit 列表查询参数模型 + public class ReadingMedicineSystemQuestionAddOrEdit + { + public Guid? Id { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发 + /// + public string ParentTriggerValue { get; set; } = string.Empty; + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 是否是必须 + /// + public bool IsRequired { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 父问题ID + /// + public Guid? ParentId { get; set; } + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 任务类型 + /// + public ReadingCategory ReadingCategory { get; set; } + + + public CriterionType? CriterionTypeEnum { get; set; } + + public bool IsGeneral { get; set; } + } + + public class ReadingMedicineTrialQuestionView : ReadingMedicineSystemQuestionAddOrEdit + { + public DateTime CreateTime { get; set; } + + public Guid UpdateUserId { get; set; } + public Guid CreateUserId { get; set; } + public Guid TrialId { get; set; } + public DateTime UpdateTime { get; set; } + + public bool? IsConfirm { get; set; } + + public int? ParentShowOrder { get; set; } + + } + + + public class GetMedicineQuestionPreviewInDto + { + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + /// + /// 任务类型 + /// + public ReadingCategory? ReadingCategory { get; set; } + } + + + public class GetMedicineQuestionPreviewOutDto + { + public Guid Id { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发值 + /// + public string ParentTriggerValue { get; set; } = string.Empty; + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 是否必须 + /// + public bool IsRequired { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 父问题 + /// + public Guid? ParentId { get; set; } + + /// + /// 是否确认 + /// + public bool? IsConfirm { get; set; } + + public List Childrens { get; set; } + } + + ///ReadingMedicineTrialQuestionQuery 列表查询参数模型 + public class ReadingMedicineTrialQuestionQuery:PageInput + { + /// + /// 项目Id + /// + public Guid TrialId { get; set; } + + + public Guid TrialReadingCriterionId { get; set; } + + public string Type { get; set; } = string.Empty; + + public string ParentTriggerValue { get; set; } = string.Empty; + + public string QuestionName { get; set; } = string.Empty; + + public string TypeValue { get; set; } = string.Empty; + + /// + /// 任务类型 + /// + public ReadingCategory? ReadingCategory { get; set; } + + } + + /// ReadingMedicineTrialQuestionAddOrEdit 列表查询参数模型 + public class ReadingMedicineTrialQuestionAddOrEdit + { + public Guid? Id { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + public string Type { get; set; } + public string? ParentTriggerValue { get; set; } = string.Empty; + public string QuestionName { get; set; } + public string TypeValue { get; set; } + public Guid TrialId { get; set; } + + public bool IsEnable { get; set; } + public bool IsRequired { get; set; } + public int ShowOrder { get; set; } + public Guid? ParentId { get; set; } + public bool? IsConfirm { get; set; } + + /// + /// 任务类型 + /// + public ReadingCategory ReadingCategory { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingPeriodSetViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingPeriodSetViewModel.cs new file mode 100644 index 0000000..2e86e3c --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingPeriodSetViewModel.cs @@ -0,0 +1,342 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.Reading.Dto +{ + public class ReadingPeriodSetAddOrEdit + { + public new Guid? Id { get; set; } + + /// + /// 项目ID + /// + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + /// + /// 阅片期名称 + /// + public string ReadingPeriodName { get; set; } + + + /// + /// 阅片范围 + /// + public ReadingScopeEnum? ReadingScope { get; set; } + + /// + /// 截止日期 + /// + public DateTime? ExpirationDate { get; set; } + + /// + /// 截止检查批次 + /// + public decimal? ExpirationVisitNum { get; set; } + + /// + /// 检查批次计划ID + /// + public Guid? VisitStageId { get; set; } + + /// + /// 是否生效 + /// + public ReadingPeriodStatus IsTakeEffect { get; set; } = ReadingPeriodStatus.NotTakeEffect; + + public ReadingSetType ReadingSetType { get; set; } = ReadingSetType.ImageReading; + + public List SiteIds { get; set; } = new List(); + + public List SubjectVisitIds { get; set; } = new List(); + + } + + + public class ReadingToGenerateInDto + { + /// + /// 阅片期ID + /// + + public Guid ReadingPeriodSetId { get; set; } + + /// + /// 检查批次ID + /// + public List SubjectVisitIds { get; set; } + } + + public class PreviewTheReadingListDto : PageInput + { + /// + /// 阅片期ID + /// + + public Guid ReadingPeriodSetId { get; set; } + } + + public class PreviewTheReadingListInDto : PageInput + { + /// + /// 阅片范围 + /// + public ReadingScopeEnum ReadingScope { get; set; } + + /// + /// 中心ID + /// + public List SiteIds { get; set; } = new List(); + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 截止日期 + /// + public DateTime? ExpirationDate { get; set; } + + /// + /// 截止检查批次 + /// + public decimal? ExpirationVisitNum { get; set; } + + /// + /// 检查批次计划ID + /// + public Guid? VisitStageId { get; set; } + + /// + /// 阅片期名称 + /// + public string ReadingPeriodName { get; set; } + + /// + /// 类型 + /// + public ReadingSetType ReadingSetType { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + + + } + + /// + /// 获取影像阅片预览的Dto + /// + public class PreviewTheReadingListOutDto + { + public Guid Id { get; set; } + + /// + /// 项目中心Code + /// + public string TrialSiteCode { get; set; } + + /// + /// 检查批次Id + /// + public Guid SubjectVisitId { get; set; } + + /// + /// 患者Id + /// + public Guid SubjectId { get; set; } + + /// + /// 患者名称 + /// + public string SubjectCode { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 最晚拍片日期 + /// + public DateTime? LatestScanDate { get; set; } + + /// + /// 截止日期 + /// + public DateTime? ExpirationDate { get; set; } + + /// + /// 阅片期ID + /// + + public Guid? ReadingPeriodSetId { get; set; } + + /// + /// 阅片期名称 + /// + public string ReadingPeriodName { get; set; } + + public string SubjectVisitName { get; set; } + + public DateTime? EffectOfTime { get; set; } + + + } + + public class ReadingPeriodSetView + { + /// + /// id + /// + + public Guid Id { get; set; } + + /// + /// 项目ID + /// + public Guid? TrialId { get; set; } + + /// + /// 阅片期名称 + /// + public string ReadingPeriodName { get; set; } + + /// + /// 阅片范围 + /// + public ReadingScopeEnum? ReadingScope { get; set; } + + /// + /// 截止日期 + /// + public DateTime? ExpirationDate { get; set; } + + /// + /// 截止检查批次 + /// + public decimal? ExpirationVisitNum { get; set; } + + /// + /// 检查批次计划ID + /// + public Guid? VisitStageId { get; set; } + + /// + /// 是否生效 + /// + public int? IsTakeEffect { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 生效时间 + /// + public DateTime? EffectOfTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + public string? SubjectVisitName { get; set; } + + /// + /// 阅片配置的类型 + /// + public ReadingSetType ReadingSetType { get; set; } + + /// + /// 中心Id + /// + public List SiteIds { get; set; } + + /// + /// 中心name + /// + public List SiteCodes { get; set; } + + /// + /// 计划条数 + /// + public int PlanCount { get; set; } + + public bool IsGlobal { get; set; } + + } + + public class GetReadingVisitListOutDto + { + public Guid VisitStageId { get; set; } + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + } + + public class GetReadingVisitListInDto + { + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 配置ID + /// + public Guid? ReadingPeriodSetId { get; set; } + + + /// + /// 阅片配置的类型 + /// + public ReadingSetType ReadingSetType { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + } + + + public class SetReadingPeriodSetEffect + { + /// + /// 项目ID + /// + public Guid Id { get; set; } + + /// + /// 设置阅片是否生效 + /// + public ReadingPeriodStatus IsTakeEffect { get; set; } + } + + public class ReadingPeriodSetQuery:PageInput + { + + /// + /// 截止检查批次 + /// + public decimal? ExpirationVisitNum { get; set; } + + /// + /// 项目ID + /// + [NotDefault] + public Guid TrialId { get; set; } + + /// + /// 阅片期名称 + /// + public string? ReadingPeriodName { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingQuestionViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingQuestionViewModel.cs new file mode 100644 index 0000000..2ca6c12 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingQuestionViewModel.cs @@ -0,0 +1,2046 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.Reading.Dto +{ + + + public class TumorAssessmentView: AddOrUpdateTumorAssessmentInDto + { + + } + + public class AddOrUpdateTumorAssessmentInDto + { + + public Guid? Id { get; set; } + + /// + /// 标准ID + /// + public Guid CriterionId { get; set; } + + /// + /// 靶病灶 + /// + public TargetAssessment TargetLesion { get; set; } + + /// + /// 非靶病灶 + /// + public NoTargetAssessment NonTargetLesions { get; set; } + + /// + /// 新病灶 + /// + public NewLesionAssessment NewLesion { get; set; } + + /// + /// 整体疗效 + /// + public OverallAssessment OverallEfficacy { get; set; } + + } + + public class GetTumorAssessmentListInDto:PageInput + { + public Guid CriterionId { get; set; } + + /// + /// 靶病灶 + /// + public TargetAssessment? TargetLesion { get; set; } + + /// + /// 非靶病灶 + /// + public NoTargetAssessment? NonTargetLesions { get; set; } + + /// + /// 新病灶 + /// + public NewLesionAssessment? NewLesion { get; set; } + + /// + /// 整体疗效 + /// + public OverallAssessment? OverallEfficacy { get; set; } + } + public class CopySystemCriterionDataInDto + { + public Guid SourceSystemCriterionId { get; set; } + + public Guid NewSystemCriterionId { get; set; } + + public bool IsCopyQuestion { get; set; } + } + + public class SetCriterionDictionaryInDto + { + [NotDefault] + public Guid CriterionId { get; set; } + + public string ParentCode { get; set; } + + public List DictionaryIds { get; set; } + } + public class SetAssessTypeInDto + { + + [NotDefault] + public Guid CriterionId { get; set; } + + public string ParentCode { get; set; } + + public List DictionaryList { get; set; } + + } + + public class SetCriterionDictionaryDto + { + public Guid DictionaryId { get; set; } + + /// + /// IsBaseLineUse + /// + public bool IsBaseLineUse { get; set; } = false; + + /// + /// IsBaseUse + /// + public bool IsFollowVisitUse { get; set; } = false; + } + + public class SetDictionaryFollowVisitUseInDto + { + public Guid Id { get; set; } + + public bool IsFollowVisitUse { get; set; } + + } + + public class SetDictionaryBaseLineUseInDto + { + public Guid Id { get; set; } + + public bool IsBaseLineUse { get; set; } + + } + + public class GetAssessTypeInDto + { + [NotDefault] + public Guid CriterionId { get; set; } + + public string ParentCode { get; set; } = string.Empty; + } + + + public class CriterionDictionaryInfo + { + + public Guid Id { get; set; } + + public Guid DictionaryId { get; set; } + + public int ShowOrder { get; set; } + + public string Description { get; set; } = string.Empty; + + public string Code { get; set; } + + public string ParentCode { get; set; } + + public string ChildGroup { get; set; } + + /// + /// IsBaseUse + /// + public bool IsBaseLineUse { get; set; } = false; + + + /// + /// IsBaseUse + /// + public bool IsFollowVisitUse { get; set; } = false; + + public string Value { get; set; } = string.Empty; + + public string ValueCN { get; set; } = string.Empty; + } + + + public class GetSystemGlobalInfoOutDto + { + /// + /// 是否必须全局阅片 + /// + public bool IsMustGlobalReading { get; set; } = false; + + public List DictionaryList { get; set; } + } + public class GetSystemOncologyInfoOutDto + { + public bool IsOncologyReading { get; set; } + + public List DictionaryList { get; set; } + } + + public class DeleteSystemCriterionDictionaryIndto + { + public Guid Id { get; set; } + + } + + public class AddSystemCriterionDictionaryCodeInDto + { + public Guid SystemCriterionId { get; set; } + + [NotDefault] + public List CodeList { get; set; } + } + + + public class SetSystemGlobalInfoInDto + { + [NotDefault] + public Guid SystemCriterionId { get; set; } + + public bool IsMustGlobalReading { get; set; } + + public List DictionaryList { get; set; } + } + + public class SystemGlobalInfo + { + /// + /// DictionaryId + /// + public Guid DictionaryId { get; set; } + + + /// + /// IsBaseLineUse + /// + public bool IsBaseLineUse { get; set; } = false; + + /// + /// IsBaseUse + /// + public bool IsFollowVisitUse { get; set; } = false; + + } + + public class SetSystemOncologyInfoInDto + { + [NotDefault] + public Guid SystemCriterionId { get; set; } + + public bool IsOncologyReading { get; set; } + + public List DictionaryIds { get; set; } + } + + public class GetSystemOncologyInfoInDto + { + [NotDefault] + public Guid SystemCriterionId { get; set; } + } + + public class GetSystemCriterionListOutDto + { + public Guid CriterionId { get; set; } + + public string CriterionName { get; set; } + } + + public class ReadingTableQuestionTrialView:ReadingTableQuestionTrial + { + public int? DependShowOrder { get; set; } + } + + public class ReadingTableQuestionSystemView : ReadingTableQuestionSystem + { + public int? DependShowOrder { get; set; } + + } + + + public class ReadingTableQuestionSystemQuery + { + + + public Guid ReadingQuestionId { get; set; } + + /// + /// 表格问题类型 + /// + public TableQuestionType? TableQuestionType { get; set; } + + } + + + public class ReadingTableQuestionTrialAddOrEdit + { + + /// + /// 自定义计算标记 + /// + public CustomCalculateMark? CustomCalculateMark { get; set; } + + + /// + /// IsDepend + /// + public IsDepend IsDepend { get; set; } + + /// + /// 自定义计算标记 + /// + public string CalculateQuestions { get; set; } = "[]"; + + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + + /// + /// 自定义单位 + /// + public string CustomUnit { get; set; } = string.Empty; + + /// + /// 数据来源 + /// + public DataSources? DataSource { get; set; } = DataSources.ManualEntry; + public Guid? Id { get; set; } + + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + public Guid ReadingQuestionId { get; set; } + public string Type { get; set; } = string.Empty; + public Guid? ParentId { get; set; } + public string ParentTriggerValue { get; set; } = string.Empty; + public string QuestionName { get; set; } = string.Empty; + public IsRequired IsRequired { get; set; } + + /// + /// 复制病灶的时候 是否复制这个问题 + /// + public bool IsCopy { get; set; } = false; + + public int ShowOrder { get; set; } + public string TypeValue { get; set; } = string.Empty; + public bool IsEnable { get; set; } + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public string Remark { get; set; } + public Guid? RelevanceId { get; set; } + public string RelevanceValue { get; set; } = string.Empty; + public int ShowQuestion { get; set; } + public int? MaxRowCount { get; set; } + public string DataTableName { get; set; } = string.Empty; + public string DataTableColumn { get; set; } = string.Empty; + + /// + /// 表格问题类型 + /// + public TableQuestionType? TableQuestionType { get; set; } + + /// + /// 依赖父问题 + /// + public Guid? DependParentId { get; set; } + + + + /// + /// 项目标准Id + /// + public Guid TrialCriterionId { get; set; } + + /// + /// 问题标识 + /// + public QuestionMark? QuestionMark { get; set; } + + /// + /// 问题英文名称 + /// + public string QuestionEnName { get; set; } = string.Empty; + + + + public List ParentTriggerValueList { get; set; } + public List RelevanceValueList { get; set; } + + } + + /// ReadingTableQuestionSystemAddOrEdit 列表查询参数模型 + public class ReadingTableQuestionSystemAddOrEdit + { + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 数据来源 + /// + public DataSources? DataSource { get; set; } = DataSources.ManualEntry; + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + public Guid? Id { get; set; } + public Guid ReadingQuestionId { get; set; } + public string Type { get; set; } = string.Empty; + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + public Guid? ParentId { get; set; } + public string ParentTriggerValue { get; set; } = string.Empty; + public string QuestionName { get; set; } = string.Empty; + public IsRequired IsRequired { get; set; } + public int ShowOrder { get; set; } + public string TypeValue { get; set; } = string.Empty; + public bool IsEnable { get; set; } + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public string Remark { get; set; } + public Guid? RelevanceId { get; set; } + public string RelevanceValue { get; set; } = string.Empty; + public int ShowQuestion { get; set; } + public int? MaxRowCount { get; set; } + public string DataTableName { get; set; } = string.Empty; + public string DataTableColumn { get; set; } = string.Empty; + + /// + /// 表格问题类型 + /// + public TableQuestionType? TableQuestionType { get; set; } + + /// + /// 依赖父问题 + /// + public Guid? DependParentId { get; set; } + + + /// + /// 系统标准Id + /// + public Guid SystemCriterionId { get; set; } + + /// + /// 问题标识 + /// + public QuestionMark? QuestionMark { get; set; } + + /// + /// 问题英文名称 + /// + public string QuestionEnName { get; set; } = string.Empty; + + + + + + } + + public class ReadingCriterionPageView + { + public Guid Id { get; set; } + public Guid TrialId { get; set; } + public string PageName { get; set; } + public bool IsEnable { get; set; } + + } + + + public class ReadingCriterionPageQuery + { + /// PageName + public string PageName { get; set; } + + } + + public class SetTrialJudgyInfoInDto : GetTrialJudgyInfoOutDto + { + public Guid TrialReadingCriterionId { get; set; } + } + + public class GetTrialJudgyInfoOutDto + { + public Guid TrialId { get; set; } + + public bool IsReadingTaskViewInOrder { get; set; } + + /// + /// 仲裁阅片 + /// + public bool? IsArbitrationReading { get; set; } + + /// + /// 仲裁规则 + /// + public ArbitrationRule ArbitrationRule { get; set; } + } + + public enum NeedSynchronize + { + /// + /// 需要 + /// + Need=0, + + /// + /// 不需要 + /// + NotNeed = 1, + + /// + /// 裁判不相等 + /// + JudgeNotEqual=2, + + } + + public class ReadingQuestionSystemData : ReadingQuestionSystem + { + public Guid OriginalId { get; set; } + } + + public class ReadingTableQuestionSystemData : ReadingTableQuestionSystem + { + public Guid OriginalId { get; set; } + } + + public class ReadingTrialTableQuestionData : ReadingTableQuestionTrial + { + public Guid OriginalId { get; set; } + } + + + public class SynchronizeSystemCriterionInDto + { + public Guid FromSystemCriterionId { get; set; } + public Guid ToSystemCriterionId { get; set; } + } + + public class SynchronizeCriterionInDto + { + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + public class VerifyeCriterionNeedSynchronizeInDto + { + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + public class GetTrialJudgyInfoInDto + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + + public class ReadingCriterionPageAddOrEdit + { + public Guid? Id { get; set; } + public Guid TrialId { get; set; } + public string PageName { get; set; } + public bool IsEnable { get; set; } + + public int ShowOrder { get; set; } = 0; + public Guid ReadingQuestionCriterionTrialId{ get; set; } + + /// + /// 是否公共分页 + /// + public bool IsPublicPage { get; set; } + + } + + + public class AddOrUpdateReadingQuestionCriterionTrialInDto + { + public Guid? Id { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + + /// + /// 描述 + /// + public string Description { get; set; } = string.Empty; + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 标准 + /// + public string CriterionName { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 表单类型 + /// + + public FormType FormType { get; set; } = FormType.SinglePage; + + + /// + /// 修约小数点 + /// + public int? DigitPlaces { get; set; } = 2; + + } + + + public class GetPreviewTheQuestionInDto + { + [NotDefault] + public Guid Id { get; set; } + } + + public class AddOrUpdateReadingQuestionCriterionSystemInDto + { + public Guid? Id { get; set; } + + /// + /// eCRF报告是否显示在图像页面 + /// + public bool IseCRFShowInDicomReading { get; set; } = false; + + /// + /// 标准 + /// + public string CriterionName { get; set; } + + /// + /// 描述 + /// + public string Description { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 标准类型 + /// + public CriterionType CriterionType { get; set; } + + /// + /// 是否完成配置 + /// + public bool IsCompleteConfig { get; set; } = false; + + + /// + /// 表单类型 + /// + + public FormType FormType { get; set; } = FormType.SinglePage; + } + + /// + /// + /// + public class ReadingQuestionCriterionSystemViewInDto:PageInput + { + public string CriterionName { get; set; }=String.Empty; + } + + public class CriterionList + { + public Guid Id { get; set; } + + public string Value { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + } + + public class SetSystemReadingQuestionCriterionIsIsEnable + { + [NotDefault] + public Guid Id { get; set; } + + + public bool IsEnable { get; set; } + } + + public class VerifySystemQuestionIsSetJudgeAnswerInDto + { + [NotDefault] + public Guid Id { get; set; } + } + public class SetSystemReadingQuestionCriterionIsIsCompleteConfig + { + [NotDefault] + public Guid Id { get; set; } + + + public bool IsCompleteConfig { get; set; } + } + + /// + /// + /// + public class ReadingQuestionCriterionTrialViewInDto : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public string CriterionName { get; set; } = string.Empty; + + } + + + public class ReadingQuestionTrialView + { + + public Guid Id { get; set; } + + /// + /// 分组 + /// + public string GroupName { get; set; } + + + + public Guid? GroupId { get; set; } + + /// + /// 全局阅片显示类型 + /// + public GlobalReadingShowType GlobalReadingShowType { get; set; } = GlobalReadingShowType.NotShow; + + /// + /// 默认值 + /// + public string DefaultValue { get; set; } = string.Empty; + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// Parent问题类型 + /// + public TableQuestionType? ParentQuestionGenre { get; set; } + + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + /// + /// Parent字典code + /// + public string ParentDictionaryCode { get; set; } = string.Empty; + + + /// + /// 系统标准Id + /// + public Guid ReadingQuestionCriterionTrialId { get; set; } + + /// + /// 是否复制病灶 + /// + public bool IsCopyLesions { get; set; } = false; + + public Guid TrialId { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发 + /// + public string ParentTriggerValue { get; set; } + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 父问题ID + /// + public Guid? ParentId { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + public bool IsEnable { get; set; } + + public string ParentQuestionName { get; set; } + + public int? ParentQuestionShowOrder { get; set; } + + /// + /// 是否是裁判问题 + /// + public bool IsJudgeQuestion { get; set; } + + /// + /// 标准分页Id + /// + public Guid? ReadingCriterionPageId { get; set; } + + + /// + /// 关联ID + /// + public Guid? RelevanceId { get; set; } + + /// + /// 关联Value + /// + public string RelevanceValue { get; set; } = string.Empty; + + public int? RelevanceShowOrder { get; set; } + + /// + /// Relevance问题类型 + /// + public TableQuestionType? RelevanceQuestionGenre { get; set; } + + /// + /// Relevance字典code + /// + public string RelevanceDictionaryCode { get; set; } = string.Empty; + + /// + /// 图片数量 + /// + public int ImageCount { get; set; } = 0; + + + /// + /// 是否是必须 + /// + public IsRequired IsRequired { get; set; } + + /// + /// 是否显示 + /// + public ShowQuestion ShowQuestion { get; set; } + + /// + /// 最大问题数 + /// + public int? MaxQuestionCount { get; set; } + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + + /// + /// 问题类型 + /// + public QuestionType? QuestionType { get; set; } + + /// + /// 是否显示在Dicom阅片中 + /// + public bool IsShowInDicom { get; set; } + + /// + /// 序号标记 + /// + public string OrderMark { get; set; } = string.Empty; + + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + + /// + /// 自定义单位 + /// + public string CustomUnit { get; set; } = string.Empty; + + /// + /// 自定义计算标记 + /// + public CustomCalculateMark? CustomCalculateMark { get; set; } + + /// + /// 自定义计算标记 + /// + public string CalculateQuestions { get; set; } + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + + /// + /// 数据来源 + /// + public DataSources? DataSource { get; set; } = DataSources.ManualEntry; + + /// + /// 问题英文名称 + /// + public string QuestionEnName { get; set; } = string.Empty; + + /// + /// 问题英文分组 + /// + public string GroupEnName { get; set; } = string.Empty; + + public List ParentTriggerValueList { get; set; } + public List RelevanceValueList { get; set; } + public List CalculateQuestionList { get; set; } + + } + + public class ReadingQuestionSystemView + { + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 数据来源 + /// + public DataSources? DataSource { get; set; } = DataSources.ManualEntry; + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + + /// + /// Id + /// + public Guid Id { get; set; } + + /// + /// 分组 + /// + public string GroupName { get; set; } + + public Guid? GroupId { get; set; } + + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// Parent问题类型 + /// + public TableQuestionType? ParentQuestionGenre { get; set; } + + + /// + /// 全局阅片显示类型 + /// + public GlobalReadingShowType GlobalReadingShowType { get; set; } = GlobalReadingShowType.NotShow; + + + /// + /// 默认值 + /// + public string DefaultValue { get; set; } = string.Empty; + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + + /// + /// Parent字典code + /// + public string ParentDictionaryCode { get; set; } = string.Empty; + + + /// + /// 关联ID + /// + public Guid? RelevanceId { get; set; } + + + /// + /// 关联Value + /// + public string RelevanceValue { get; set; } = string.Empty; + + + /// + /// 系统标准Id + /// + public Guid ReadingQuestionCriterionSystemId { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发 + /// + public string ParentTriggerValue { get; set; } + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 父问题ID + /// + public Guid? ParentId { get; set; } + + public string ParentQuestionName { get; set; } + + public int? ParentQuestionShowOrder { get; set; } + + public int? RelevanceShowOrder { get; set; } + + + /// + /// Relevance问题类型 + /// + public TableQuestionType? RelevanceQuestionGenre { get; set; } + + /// + /// Relevance字典code + /// + public string RelevanceDictionaryCode { get; set; } = string.Empty; + + + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + //public bool IsEnable { get; set; } + + /// + /// 是否是裁判问题 + /// + public bool IsJudgeQuestion { get; set; } + + /// + /// 图片数量 + /// + public int ImageCount { get; set; } + + + /// + /// 是否是必须 + /// + public IsRequired IsRequired { get; set; } + + /// + /// 是否显示 + /// + public ShowQuestion ShowQuestion { get; set; } + + + /// + /// 最大问题数 + /// + public int? MaxQuestionCount { get; set; } + + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + + /// + /// 问题类型 + /// + public QuestionType? QuestionType { get; set; } + + /// + /// 是否显示在Dicom阅片中 + /// + public bool IsShowInDicom { get; set; } + + /// + /// 序号标记 + /// + public string OrderMark { get; set; } = string.Empty; + + /// + /// 问题英文名称 + /// + public string QuestionEnName { get; set; } = string.Empty; + + /// + /// 问题英文分组 + /// + public string GroupEnName { get; set; } = string.Empty; + } + + public class GetQuestionCalculateRelationInDto + { + public Guid? TrialReadingCriterionId { get; set; } + + public Guid? ReadingQuestionId { get; set; } + + public bool IsGetAll { get; set; } = false; + + } + + + //public class GetQuestionCalculateRelationOutDto + //{ + // public List CalculateRelationList { get; set; } + //} + + + public class CalculateRelationDto + { + public Guid QuestionId { get; set; } + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 自定义计算标记 + /// + public CustomCalculateMark? CustomCalculateMark { get; set; } + + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + /// + /// 自定义单位 + /// + public string CustomUnit { get; set; } = string.Empty; + + public List CalculateQuestionList { get; set; } + } + + public class ReadingQuestionTrialViewInDto + { + /// + /// 系统标准Id + /// + [NotDefault] + public Guid ReadingQuestionCriterionTrialId { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } = string.Empty; + + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } = string.Empty; + + + + public Guid? ReadingCriterionPageId { get; set; } + } + + /// + /// 获取其他问题返回的Dto + /// + public class CriterionOtherQuestionOutDto + { + public Guid QuestionId { get; set; } + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + public string GroupName { get; set; } + + public Guid? GroupId { get; set; } + } + + public class SetTrialQuestionIsIsJudgeQuestionInDto + { + [NotDefault] + public Guid Id { get; set; } + + /// + /// 是否是裁判问题 + /// + public bool IsJudgeQuestion { get; set; } + } + + public class GetCalculateQuestionsOutDto + { + public Guid QuestionId { get; set; } + + public string QuestionName{ get; set; } + + public List TableQuestions { get; set; } + } + + + public class CalculateQuestion + { + public Guid QuestionId { get; set; } + + public string QuestionName { get; set; } + } + + + public class GetCalculateTableQuestionsOutDto + { + public Guid QuestionId { get; set; } + + public string QuestionName { get; set; } + } + + + public class GetCalculateTableQuestionsInDto + { + [NotDefault] + public Guid QuestionId { get; set; } + + public string Type { get; set; } + + } + + public class GetCustomQuestionPreviewInDto + { + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + public class GetCalculateQuestionsInDto + { + [NotDefault] + public Guid TrialCriterionId { get; set; } + + + public string Type { get; set; } + + } + + public class GetTrialGroupNameOutDto + { + public Guid GroupId { get; set; } + + public string GroupName { get; set; } = string.Empty; + + + } + public class GetTrialGroupNameListInDto + { + /// + /// 标准ID + /// + public Guid CriterionId { get; set; } + + + public Guid? ReadingCriterionPageId { get; set; } + } + + + public class GetReadingTableOtherQuestionSystemInDto + { + /// + /// 表格父问题的ID + /// + [NotDefault] + public Guid ReadingQuestionId { get; set; } + + /// + /// 当前ID + /// + public Guid? Id { get; set; } + } + + public class GetTrialCriterionOtherQuestionInDto + { + [NotDefault] + public Guid ReadingQuestionCriterionTrialId { get; set; } + + /// + /// 当前ID + /// + public Guid? Id { get; set; } + + public Guid? ReadingCriterionPageId { get; set; } + } + + public class GetSystemCriterionOtherQuestionInDto + { + [NotDefault] + public Guid ReadingQuestionCriterionSystemId { get; set; } + + /// + /// 当前ID + /// + public Guid? Id { get; set; } + } + + public class ReadingQuestionSystemViewInDto : PageInput + { + /// + /// 系统标准Id + /// + [NotDefault] + public Guid ReadingQuestionCriterionSystemId { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } = string.Empty; + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } = string.Empty; + } + + public class TrialQuestion + { + public Guid Id { get; set; } + + /// + /// 父问题ID + /// + public Guid? ParentId { get; set; } + + /// + /// 项目标准Id + /// + public Guid ReadingQuestionCriterionTrialId { get; set; } + + /// + /// 系统问题ID + /// + public Guid? ReadingQuestionSystemId { get; set; } + + public Guid? ReadingCriterionPageId { get; set; } + + /// + /// 系统标准的ParentId + /// + public Guid? SystemParentId { get; set; } + + /// + /// 答案分组 + /// + public string AnswerGroup { get; set; } = string.Empty; + + /// + /// 答案组合 + /// + public string AnswerCombination { get; set; } = string.Empty; + + /// + /// 裁判类型 + /// + public JudgeTypeEnum JudgeType { get; set; } + + + /// + /// 关联ID + /// + public Guid? RelevanceId { get; set; } + + /// + /// 关联Value + /// + public string RelevanceValue { get; set; } = string.Empty; + /// + /// 图片数量 + /// + public int ImageCount { get; set; } + } + public class AddOrUpdateReadingQuestionSystemInDto + { + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + + /// + /// 数据来源 + /// + public DataSources? DataSource { get; set; } = DataSources.ManualEntry; + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + + /// + /// 全局阅片显示类型 + /// + public GlobalReadingShowType GlobalReadingShowType { get; set; } = GlobalReadingShowType.NotShow; + + /// + /// 默认值 + /// + public string DefaultValue { get; set; } = string.Empty; + + /// + /// 分组 + /// + public string GroupName { get; set; } + + public Guid? GroupId { get; set; } + + /// + /// Id + /// + public Guid? Id { get; set; } + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// 系统标准Id + /// + public Guid ReadingQuestionCriterionSystemId { get; set; } + + /// + /// 关联ID + /// + public Guid? RelevanceId { get; set; } + + /// + /// 关联Value + /// + public string RelevanceValue { get; set; } = string.Empty; + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发 + /// + public string ParentTriggerValue { get; set; } = string.Empty; + + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } = 0; + + /// + /// 父问题ID + /// + public Guid? ParentId { get; set; } + + /// + /// 是否是裁判问题 + /// + public bool IsJudgeQuestion { get; set; } = false; + + + /// + /// 备注 + /// + public string Remark { get; set; } = string.Empty; + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + public bool IsEnable { get; set; } = false; + + /// + /// 是否是必须 + /// + public IsRequired IsRequired { get; set; } + + /// + /// 是否显示 + /// + public ShowQuestion ShowQuestion { get; set; } + + + /// + /// 图片数量 + /// + public int ImageCount { get; set; } = 1; + + /// + /// 最大问题数 + /// + public int? MaxQuestionCount { get; set; } + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + + + + /// + /// 问题类型 + /// + public QuestionType? QuestionType { get; set; } + + /// + /// 是否显示在Dicom阅片中 + /// + public bool IsShowInDicom { get; set; } = false; + + /// + /// 序号标记 + /// + public string OrderMark { get; set; } = string.Empty; + + /// + /// 问题英文名称 + /// + public string QuestionEnName { get; set; } = string.Empty; + + /// + /// 问题英文分组 + /// + public string GroupEnName { get; set; } = string.Empty; + } + + + + public class AddOrUpdateReadingQuestionTrialInDto + { + /// + /// Id + /// + public Guid? Id { get; set; } + + + public Guid TrialId { get; set; } + + /// + /// 是否复制病灶 + /// + public bool IsCopyLesions { get; set; } = false; + + /// + /// 全局阅片显示类型 + /// + public GlobalReadingShowType GlobalReadingShowType { get; set; } = GlobalReadingShowType.NotShow; + + /// + /// 默认值 + /// + public string DefaultValue { get; set; } =string.Empty; + + /// + /// 系统标准Id + /// + public Guid ReadingQuestionCriterionTrialId { get; set; } + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + + /// + /// 是否是裁判问题 + /// + public bool IsJudgeQuestion { get; set; } = false; + + /// + /// 备注 + /// + public string Remark { get; set; } = string.Empty; + + + /// + /// 分组 + /// + public string GroupName { get; set; } + + public Guid? GroupId { get; set; } + + + /// + /// 答案分组 + /// + public string AnswerGroup { get; set; }=String.Empty; + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发 + /// + public string ParentTriggerValue { get; set; } = string.Empty; + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } = 0; + + /// + /// 父问题ID + /// + public Guid? ParentId { get; set; } + + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + public bool IsEnable { get; set; } = false; + + /// + /// 标准分页Id + /// + public Guid? ReadingCriterionPageId { get; set; } + + /// + /// 关联ID + /// + public Guid? RelevanceId { get; set; } + + /// + /// 关联Value + /// + public string RelevanceValue { get; set; } = string.Empty; + + + /// + /// 图片数量 + /// + public int ImageCount { get; set; } = 0; + + /// + /// 是否是必须 + /// + public IsRequired IsRequired { get; set; } + + /// + /// 是否显示 + /// + public ShowQuestion ShowQuestion { get; set; } + + /// + /// 最大问题数 + /// + public int? MaxQuestionCount { get; set; } + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + + /// + /// 问题类型 + /// + public QuestionType? QuestionType { get; set; } + + /// + /// 是否显示在Dicom阅片中 + /// + public bool IsShowInDicom { get; set; } = false; + + /// + /// 序号标记 + /// + public string OrderMark { get; set; } = string.Empty; + + + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 数据来源 + /// + public DataSources? DataSource { get; set; } = DataSources.ManualEntry; + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + + /// + /// 自定义单位 + /// + public string CustomUnit { get; set; } = string.Empty; + + /// + /// 自定义计算标记 + /// + public CustomCalculateMark? CustomCalculateMark { get; set; } + + /// + /// 自定义计算标记 + /// + public string CalculateQuestions { get; set; } = "[]"; + + /// + /// 问题英文名称 + /// + public string QuestionEnName { get; set; } = string.Empty; + + /// + /// 问题英文分组 + /// + public string GroupEnName { get; set; } = string.Empty; + + public List ParentTriggerValueList { get; set; } + public List RelevanceValueList { get; set; } + + } + + public class GetSystemCriterionSelectDto + { + /// + /// 返回的对象 + /// + public Guid Id { get; set; } + + /// + /// 标准 + /// + public string CriterionName { get; set; } + + } + + public class ReadingQuestionCriterionSystemView + { + /// + /// 返回的对象 + /// + public Guid Id { get; set; } + + /// + /// 标准ID + /// + public Guid CriterionId { get; set; } + + /// + /// 标准 + /// + public string CriterionName { get; set; } + + public int ShowOrder { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// eCRF报告是否显示在图像页面 + /// + public bool IseCRFShowInDicomReading { get; set; } = false; + + + /// + /// 标准类型 + /// + public CriterionType CriterionType { get; set; } + + /// + /// 是否完成配置 + /// + public bool IsCompleteConfig { get; set; } + + public int QuestionCount { get; set; } + + + /// + /// 描述 + /// + public string Description { get; set; } + public bool IsBeUsed { get; set; } = false; + } + + + public class ReadingQuestionCriterionTrialView + { + /// + /// 返回的对象 + /// + public Guid Id { get; set; } + + /// + /// 系统标准ID + /// + public Guid? ReadingQuestionCriterionSystemId { get; set; } + + /// + /// 标准 + /// + public string CriterionName { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 是否完成配置 + /// + public bool IsCompleteConfig { get; set; } + + public int QuestionCount { get; set; } + + public bool IsBeUsed { get; set; } = false; + + /// + /// 表单类型 + /// + + public FormType FormType { get; set; } + + + /// + /// 描述 + /// + public string Description { get; set; } + + + /// + /// 修约小数点 + /// + public int? DigitPlaces { get; set; } = 2; + + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + + + + + /// + /// 是否是系统数据 + /// + public bool IsSystemData + { + get + { + return this.ReadingQuestionCriterionSystemId != null; + } + } + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/Interface/IOrganInfoService.cs b/IRaCIS.Core.Application/Service/Reading/Interface/IOrganInfoService.cs new file mode 100644 index 0000000..dc0d219 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Interface/IOrganInfoService.cs @@ -0,0 +1,30 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-12 14:07:43 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// IOrganInfoService + /// + public interface IOrganInfoService + { + + + + + Task> GetOrganInfoList(OrganInfoQuery inQuery); + + Task AddOrUpdateOrganInfo(OrganInfoAddOrEdit addOrEditOrganInfo); + + Task DeleteOrganInfo(Guid organInfoId); + + Task SynchronizeSystemOrganToTrial(SynchronizeSystemOrganToTrialInDto inDto); + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/Interface/IReadModuleService.cs b/IRaCIS.Core.Application/Service/Reading/Interface/IReadModuleService.cs new file mode 100644 index 0000000..8630bf0 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Interface/IReadModuleService.cs @@ -0,0 +1,16 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:20:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + + +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IReadModuleService + { + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Reading/Interface/IReadingClinicalDataService.cs b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingClinicalDataService.cs new file mode 100644 index 0000000..1d1f3f6 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingClinicalDataService.cs @@ -0,0 +1,27 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:20:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + + +using IRaCIS.Core.Application.Service.Inspection.DTO; +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IReadingClinicalDataService + { + Task AddOrUpdateReadingClinicalData(AddOrUpdateReadingClinicalDataDto indto); + + Task ReadClinicalDataSign(ReadingClinicalDataSignIndto inDto); + + Task> GetClinicalDataList(GetReadingOrTaskClinicalDataListInDto inDto); + + + // Task<(List, object)> GetReadingClinicalDataList(GetReadingClinicalDataListIndto inDto); + + Task> GetReadingClinicalList(GetReadingClinicalDataListIndto inDto); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Reading/Interface/IReadingImageTaskService.cs b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingImageTaskService.cs new file mode 100644 index 0000000..79dfc06 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingImageTaskService.cs @@ -0,0 +1,37 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:20:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + + +using IRaCIS.Core.Application.Service.Reading.Dto; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IReadingImageTaskService + { + Task SubmitVisitTaskQuestions(SubmitVisitTaskQuestionsInDto inDto); + + Task SubmitJudgeVisitTaskResult(SaveJudgeVisitTaskResult inDto); + + Task GetOncologyReadingInfo(GetOncologyReadingInfoInDto inDto); + + Task GetJudgeReadingInfo(GetJudgeReadingInfo inDto); + + Task GetGlobalReadingInfo(GetGlobalReadingInfoInDto inDto); + + + Task> GetReadingQuestion(Guid trialReadingCriterionId, Guid? visitTaskId); + + Task GetReadingTableQuestion(GetReadingTableQuestionOrAnswerInDto inDto); + + Task SubmitGlobalReadingInfo(SubmitGlobalReadingInfoInDto inDto); + + Task SubmitDicomVisitTask(SubmitDicomVisitTaskInDto inDto); + + Task SubmitOncologyReadingInfo(SubmitOncologyReadingInfoInDto inDto); + + Task AddOncologyTask(Guid oncologModuleId); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Reading/Interface/IReadingMedicalReviewService.cs b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingMedicalReviewService.cs new file mode 100644 index 0000000..732026a --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingMedicalReviewService.cs @@ -0,0 +1,13 @@ + + + +using IRaCIS.Core.Application.Service.Reading.Dto; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IReadingMedicalReviewService + { + Task FinishMedicalReview(FinishMedicalReviewInDto inDto); + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Reading/Interface/IReadingMedicineQuestionService.cs b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingMedicineQuestionService.cs new file mode 100644 index 0000000..5015eae --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingMedicineQuestionService.cs @@ -0,0 +1,17 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:20:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + + +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IReadingMedicineQuestionService + { + Task ConfirmReadingMedicineQuestion(ConfirmReadingMedicineQuestionInDto inDto); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Reading/Interface/IReadingQuestionService.cs b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingQuestionService.cs new file mode 100644 index 0000000..ef83838 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingQuestionService.cs @@ -0,0 +1,21 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:20:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + + +using IRaCIS.Core.Application.Service.Reading.Dto; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IReadingQuestionService + { + //Task SetSystemCriterionDisable(Guid dictionaryId, Guid? parentId); + + Task SynchronizeCriterion(SynchronizeCriterionInDto inDto); + + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Reading/MedicalAudit/ReadingMedicalReviewService.cs b/IRaCIS.Core.Application/Service/Reading/MedicalAudit/ReadingMedicalReviewService.cs new file mode 100644 index 0000000..20f2d12 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/MedicalAudit/ReadingMedicalReviewService.cs @@ -0,0 +1,820 @@ + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Infra.EFCore.Common; +using MassTransit; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Domain.Share; +using Panda.DynamicWebApi.Attributes; +using IRaCIS.Core.Application.Contracts; +using Newtonsoft.Json; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Core.Application.Service +{ + /// + /// 阅片医学审核 + /// + [ ApiExplorerSettings(GroupName = "Reading")] + public class ReadingMedicalReviewService : BaseService, IReadingMedicalReviewService + { + + private readonly IRepository _readingMedicineTrialQuestionRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _readingTaskQuestionAnswerRepository; + private readonly IRepository _readingGlobalTaskInfoRepository; + private readonly IRepository _readingOncologyTaskInfoRepository; + private readonly IRepository _readingCriterionDictionaryRepository; + + private readonly IReadingImageTaskService _iReadingImageTaskService; + private readonly IRepository _userTaskRepository; + private readonly IVisitTaskService _visitTaskService; + private readonly IRepository _taskMedicalReviewRepository; + private readonly IRepository _readingMedicalReviewDialogRepository; + private readonly IRepository _readingQuestionCriterionTrial; + private readonly IRepository _readingMedicineQuestionAnswerRepository; + private readonly IRepository _readingMedicineSystemQuestionRepository; + + private readonly ITrialEmailNoticeConfigService _trialEmailNoticeConfigService; + + public ReadingMedicalReviewService( + IRepository readingMedicineTrialQuestionRepository, + IRepository trialRepository, + IRepository visitTaskRepository, + IReadingImageTaskService readingImageTaskService, + IRepository readingTaskQuestionAnswerRepository, + IRepository readingGlobalTaskInfoRepository, + IRepository readingOncologyTaskInfoRepository, + IRepository readingCriterionDictionaryRepository, + IRepository userTaskRepository, + IVisitTaskService visitTaskService, + IRepository taskMedicalReviewRepository, + IRepository readingMedicalReviewDialogRepository, + IRepository readingQuestionCriterionTrial, + IRepository readingMedicineQuestionAnswerRepository, + IRepository readingMedicineSystemQuestionRepository, + ITrialEmailNoticeConfigService trialEmailNoticeConfigService + + ) + { + this._readingMedicineTrialQuestionRepository = readingMedicineTrialQuestionRepository; + this._trialRepository = trialRepository; + this._iReadingImageTaskService = readingImageTaskService; + this._visitTaskRepository = visitTaskRepository; + this._readingTaskQuestionAnswerRepository = readingTaskQuestionAnswerRepository; + this._readingGlobalTaskInfoRepository = readingGlobalTaskInfoRepository; + this._readingOncologyTaskInfoRepository = readingOncologyTaskInfoRepository; + this._readingCriterionDictionaryRepository = readingCriterionDictionaryRepository; + this._userTaskRepository = userTaskRepository; + this._visitTaskService = visitTaskService; + this._taskMedicalReviewRepository = taskMedicalReviewRepository; + this._readingMedicalReviewDialogRepository = readingMedicalReviewDialogRepository; + this._readingQuestionCriterionTrial = readingQuestionCriterionTrial; + this._readingMedicineQuestionAnswerRepository = readingMedicineQuestionAnswerRepository; + this._readingMedicineSystemQuestionRepository = readingMedicineSystemQuestionRepository; + _trialEmailNoticeConfigService = trialEmailNoticeConfigService; + } + + /// + /// 获取医学审核任务信息 + /// + /// + /// + [HttpPost] + public async Task GetMedicalReviewReadingTask(GetMedicalReviewReadingTaskInDto inDto) + { + MedicalReviewInfo reviewInfo= await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId) + .ProjectTo(_mapper.ConfigurationProvider).FirstNotNullAsync(); + + var taskInfo = await _visitTaskRepository.Where(x => x.Id == reviewInfo.VisitTaskId).Include(x=>x.DoctorUser).Include(x => x.Subject).FirstNotNullAsync(); + + //if (taskInfo.TaskState != TaskState.Effect) + //{ + // throw new BusinessValidationFailedException("当前医学审核已失效!"); + //} + inDto.TrialReadingCriterionId = taskInfo.TrialReadingCriterionId; + var medicalReviewInfo = await _readingQuestionCriterionTrial.Where(x => x.Id == taskInfo.TrialReadingCriterionId).Select(x => new GetMedicalReviewReadingTaskOutDto() + { + ReadingType=x.ReadingType, + IsReadingTaskViewInOrder=x.IsReadingTaskViewInOrder, + + }).FirstNotNullAsync(); + + medicalReviewInfo.MedicalReviewInfo = reviewInfo; + + + + medicalReviewInfo.VisitTaskId = taskInfo.Id; + medicalReviewInfo.ArmEnum = taskInfo.ArmEnum; + medicalReviewInfo.SubjectCode = taskInfo.IsAnalysisCreate?taskInfo.BlindSubjectCode: taskInfo.Subject.Code; + medicalReviewInfo.TaskBlindName = taskInfo.TaskBlindName; + medicalReviewInfo.ReadingUser = taskInfo.DoctorUser.FirstName + taskInfo.DoctorUser.LastName; + + medicalReviewInfo.IsClosedDialog = medicalReviewInfo.MedicalReviewInfo.IsClosedDialog; + medicalReviewInfo.AuditState = medicalReviewInfo.MedicalReviewInfo.AuditState; + medicalReviewInfo.IsSendMessage = medicalReviewInfo.MedicalReviewInfo.IsSendMessage; + + + medicalReviewInfo.QuestionAnswerList = await this.GetMedicalQuestionAnswer(inDto); + + + + var result = await _readingMedicineQuestionAnswerRepository.SaveChangesAsync(); + + #region 取任务 + + + switch (taskInfo.ReadingCategory) + { + case ReadingCategory.Oncology: + medicalReviewInfo.OncologyInfo = await _iReadingImageTaskService.GetOncologyReadingInfo(new GetOncologyReadingInfoInDto() + { + VisitTaskId= taskInfo.Id + }); + break; + case ReadingCategory.Judge: + medicalReviewInfo.JudgeInfo = await _iReadingImageTaskService.GetJudgeReadingInfo(new GetJudgeReadingInfo() + { + VisitTaskId = taskInfo.Id + }); + break; + + case ReadingCategory.Global: + medicalReviewInfo.GlobalInfo = await _iReadingImageTaskService.GetGlobalReadingInfo(new GetGlobalReadingInfoInDto() + { + VisitTaskId = taskInfo.Id + }); + break; + case ReadingCategory.Visit: + // 有序 + if (medicalReviewInfo.IsReadingTaskViewInOrder) + { + medicalReviewInfo.TaskList = await _visitTaskRepository + + .Where(x => x.IsAnalysisCreate == taskInfo.IsAnalysisCreate) + .Where(x => x.TaskState == TaskState.Effect || x.Id == taskInfo.Id) + .Where(x => x.SubjectId == taskInfo.SubjectId + && x.ArmEnum == taskInfo.ArmEnum + &&x.ReadingCategory== ReadingCategory.Visit + && x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId + + && x.DoctorUserId == taskInfo.DoctorUserId && + x.ReadingTaskState == ReadingTaskState.HaveSigned && + x.ReReadingApplyState != ReReadingApplyState.Agree + ).OrderBy(x => x.VisitTaskNum).Select(x => new TaskInfo() + { + TaskId = x.Id, + VisitTaskNum=x.VisitTaskNum, + //JudgeTaskId=x.JudgeVisitTaskId, // 在下面赋值 要去已完成的 + IsCurrentTask = x.Id == taskInfo.Id, + ReadingCategory = x.ReadingCategory, + TaskBlindName = x.TaskBlindName, + ArmEnum = x.ArmEnum, + TaskName = x.TaskName, + SouceReadModuleId = x.SouceReadModuleId, + SourceSubjectVisitId = x.SourceSubjectVisitId, + //JudgeVisitTaskId = x.JudgeVisitTaskId, + JudgeResultArm = x.JudgeResultTask.ArmEnum, + SubjectId = x.SubjectId, + }).ToListAsync(); + + + List otherTask = await _visitTaskRepository.Where(x => + x.SubjectId == taskInfo.SubjectId + && x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId + && x.TaskState == TaskState.Effect + &&x.ReadingCategory == ReadingCategory.Visit + && x.IsAnalysisCreate == taskInfo.IsAnalysisCreate + && x.IsSelfAnalysis == taskInfo.IsSelfAnalysis + && x.DoctorUserId != taskInfo.DoctorUserId + && x.ReadingTaskState == ReadingTaskState.HaveSigned + && x.ReReadingApplyState != ReReadingApplyState.Agree) + .OrderBy(x => x.VisitTaskNum).Select(x => new TaskInfo() + { + TaskId = x.Id, + + IsCurrentTask = x.Id == taskInfo.Id, + ReadingCategory = x.ReadingCategory, + TaskBlindName = x.TaskBlindName, + ArmEnum = x.ArmEnum, + TaskName = x.TaskName, + SouceReadModuleId = x.SouceReadModuleId, + SourceSubjectVisitId = x.SourceSubjectVisitId, + }).ToListAsync(); + + medicalReviewInfo.TaskList.ForEach(x => + { + + var otherTaskInfo = otherTask.Where(y => y.SouceReadModuleId == x.SouceReadModuleId && y.SourceSubjectVisitId == x.SourceSubjectVisitId).FirstOrDefault(); + if (otherTaskInfo != null) + { + x.OtherTaskId = otherTaskInfo.TaskId; + x.OtherArmEnum = otherTaskInfo.ArmEnum; + } + + }); + } + // 无序 + else + { + medicalReviewInfo.TaskList = await _visitTaskRepository.Where(x => x.Id == taskInfo.Id).Select(x => new TaskInfo() + { + TaskId = x.Id, + VisitTaskNum = x.VisitTaskNum, + //JudgeTaskId = x.JudgeVisitTaskId, // 在下面赋值 要去已完成的 + IsCurrentTask = x.Id == taskInfo.Id, + ReadingCategory = x.ReadingCategory, + TaskBlindName = x.TaskBlindName, + ArmEnum = x.ArmEnum, + TaskName = x.TaskName, + SouceReadModuleId = x.SouceReadModuleId, + SourceSubjectVisitId = x.SourceSubjectVisitId, + SubjectId = x.SubjectId, + }).ToListAsync(); + + List otherTask = await _visitTaskRepository.Where(x => + x.SouceReadModuleId == taskInfo.SouceReadModuleId + && x.SourceSubjectVisitId == taskInfo.SourceSubjectVisitId + && x.DoctorUserId != taskInfo.DoctorUserId + && x.IsAnalysisCreate == taskInfo.IsAnalysisCreate + && x.IsSelfAnalysis == taskInfo.IsSelfAnalysis + && x.ReadingTaskState == ReadingTaskState.HaveSigned + && x.ReReadingApplyState != ReReadingApplyState.Agree + && x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId + && x.TaskState == TaskState.Effect + ) + + .OrderBy(x => x.VisitTaskNum).Select(x => new TaskInfo() + { + TaskId = x.Id, + IsCurrentTask = x.Id == taskInfo.Id, + ReadingCategory = x.ReadingCategory, + TaskBlindName = x.TaskBlindName, + ArmEnum = x.ArmEnum, + TaskName = x.TaskName, + SouceReadModuleId = x.SouceReadModuleId, + SourceSubjectVisitId = x.SourceSubjectVisitId, + }).ToListAsync(); + + medicalReviewInfo.TaskList.ForEach(x => + { + var otherTaskInfo = otherTask.Where(y => y.SouceReadModuleId == x.SouceReadModuleId && y.SourceSubjectVisitId == x.SourceSubjectVisitId).FirstOrDefault(); + if (otherTaskInfo != null) + { + x.OtherTaskId = otherTaskInfo.TaskId; + x.OtherArmEnum = otherTaskInfo.ArmEnum; + } + + }); + } + + var taskIds = medicalReviewInfo.TaskList.Select(x => x.TaskId).ToList(); + + // 找检查批次 + var visitTaskAnswer = await _readingTaskQuestionAnswerRepository.Where(x => taskIds.Contains(x.VisitTaskId) && x.ReadingQuestionTrial.IsJudgeQuestion).Select(x => new JudgeQuestionAnswerInfo + { + Answer = x.Answer, + VisitTaskId = x.VisitTaskId, + DictionaryCode = x.ReadingQuestionTrial.DictionaryCode, + ShowOrder = x.ReadingQuestionTrial.ShowOrder, + QuestionGenre = x.ReadingQuestionTrial.QuestionGenre, + QuestionName = x.ReadingQuestionTrial.QuestionName.LanguageName(x.ReadingQuestionTrial.QuestionEnName,_userInfo.IsEn_Us) + }).ToListAsync(); + var globalChangeAnswer = await _readingGlobalTaskInfoRepository.Where(x => taskIds.Contains(x.GlobalTaskId) && x.Answer != string.Empty && x.GlobalAnswerType == GlobalAnswerType.Question && x.GlobalVisitTask.TaskState == TaskState.Effect).ToListAsync(); + // 找全局阅片 和裁判 + var globalTask = await _visitTaskRepository.Where(x => + x.SubjectId == taskInfo.SubjectId + && x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId + && x.TaskState == TaskState.Effect + && x.ArmEnum== taskInfo.ArmEnum + && (x.ReadingCategory == ReadingCategory.Global||x.ReadingCategory==ReadingCategory.Judge) + && x.IsAnalysisCreate == taskInfo.IsAnalysisCreate + && x.IsSelfAnalysis == taskInfo.IsSelfAnalysis + && x.DoctorUserId == taskInfo.DoctorUserId + && x.ReadingTaskState == ReadingTaskState.HaveSigned + && x.ReReadingApplyState != ReReadingApplyState.Agree) + .Select(x => new + { + x.VisitTaskNum, + x.TaskBlindName, + x.Id + }).ToListAsync(); + + + medicalReviewInfo.TaskList.ForEach(x => + { + + var visitGlobalInfo = globalTask.Where(y => y.VisitTaskNum == x.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]).FirstOrDefault(); + if (visitGlobalInfo != null) + { + x.GlobalTaskId = visitGlobalInfo.Id; + x.GlobalTaskName = visitGlobalInfo.TaskBlindName; + } + + var visitJudgeInfo = globalTask.Where(y => y.VisitTaskNum == x.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Judge]).FirstOrDefault(); + if (visitJudgeInfo != null) + { + x.JudgeTaskId = visitJudgeInfo.Id; + x.JudgeTaskName = visitJudgeInfo.TaskBlindName; + } + + + x.IsGlobalChange = globalChangeAnswer.Any(y => y.TaskId == x.TaskId); + x.JudgeQuestionAnswerInfoList = visitTaskAnswer.Where(y => y.VisitTaskId == x.TaskId).OrderBy(y => y.ShowOrder).ToList(); + }); + + medicalReviewInfo.TaskList.ForEach(x => + { + + x.IsCurrentTask = x.TaskId == taskInfo.Id; + }); + break; + } + + + + + #endregion + return medicalReviewInfo; + + } + + #region 问题 + + private async Task> GetMedicalQuestionAnswer(GetMedicalReviewReadingTaskInDto inDto) + { + var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).FirstNotNullAsync(); + var taskInfo = await _visitTaskRepository.Where(x => x.Id == medicalReview.VisitTaskId).FirstNotNullAsync(); + + var questionQuery = from data in _readingMedicineTrialQuestionRepository.Where(x => x.TrialId == inDto.TrialId && x.IsConfirm && x.IsEnable&&x.ReadingCategory==taskInfo.ReadingCategory&&x.TrialReadingCriterionId==inDto.TrialReadingCriterionId) + join questionAnswer in _readingMedicineQuestionAnswerRepository.Where(x => x.TaskMedicalReviewId == inDto.TaskMedicalReviewId) on data.Id equals questionAnswer.ReadingMedicineQuestionId into questionAnswerTemp + from leftquestionAnswer in questionAnswerTemp.DefaultIfEmpty() + select new ReadingMedicineQuestion() + { + Id = data.Id, + Type = data.Type, + ParentTriggerValue = data.ParentTriggerValue, + IsEnable = data.IsEnable, + IsConfirm = data.IsConfirm, + QuestionName = data.QuestionName, + IsRequired = data.IsRequired, + ShowOrder = data.ShowOrder, + ParentId = data.ParentId, + TypeValue = data.TypeValue, + Answer = leftquestionAnswer.Answer + }; + + var questionList = await questionQuery.OrderBy(x=>x.ShowOrder).ToListAsync(); + + List medicineQuestionList = questionList.Where(x => x.ParentId == null).ToList(); + medicineQuestionList.ForEach(x => + { + FindChildQuestion(x, questionList); + }); + + return medicineQuestionList; + } + + + private void FindChildQuestion(ReadingMedicineQuestion medicineQuestionList, List questionlists) + { + medicineQuestionList.Childrens = questionlists.Where(x => x.ParentId == medicineQuestionList.Id).ToList(); + if (medicineQuestionList.Childrens != null && medicineQuestionList.Childrens.Count != 0) + { + medicineQuestionList.Childrens.ForEach(x => + { + this.FindChildQuestion(x, questionlists); + }); + } + } + + #endregion + + + + /// + /// 保存医学审核问题 答案 + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SaveMedicineQuestion(SaveMedicineQuestionInDto inDto) + { + var medicalReviewInfo = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).FirstNotNullAsync(); + if (medicalReviewInfo.IsInvalid) + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_invalid"]); + } + await _readingMedicineQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => x.TaskMedicalReviewId == inDto.TaskMedicalReviewId); + + List questionAnswerList = inDto.QuestionAnswerList.Select(x => new ReadingMedicineQuestionAnswer() + { + Answer = x.Answer, + ReadingMedicineQuestionId = x.Id, + TaskMedicalReviewId = inDto.TaskMedicalReviewId, + VisitTaskId = medicalReviewInfo.VisitTaskId, + + }).ToList(); + + await _readingMedicineQuestionAnswerRepository.AddRangeAsync(questionAnswerList); + + await _taskMedicalReviewRepository.UpdatePartialFromQueryAsync(inDto.TaskMedicalReviewId, x => new TaskMedicalReview() + { + + SaveQuestionTime = DateTime.Now, + AuditState = MedicalReviewAuditState.Auditing, + + }); + + + + + + var result = await _readingMedicineQuestionAnswerRepository.SaveChangesAsync(); + + return ResponseOutput.Result(result); + + } + + /// + /// 保存医学审核 结论 + /// + /// + /// + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SaveMedicalReviewInfo(SaveMedicalReviewInfoInDto inDto) + { + var medicalReviewInfo = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).FirstNotNullAsync(); + if (medicalReviewInfo.IsInvalid) + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_invalid"]); + } + if ((await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).Select(x => x.SaveQuestionTime).FirstOrDefaultAsync()) == null) + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_SaveQuestion"]); + } + await _taskMedicalReviewRepository.UpdatePartialFromQueryAsync(inDto.TaskMedicalReviewId, x => new TaskMedicalReview() + { + IsHaveQuestion = inDto.IsHaveQuestion, + Questioning = inDto.Questioning, + IsSendMessage = inDto.IsSendDialog && inDto.IsHaveQuestion, + ImagePath = JsonConvert.SerializeObject(inDto.FileList), + + AuditAdviceEnum = inDto.AuditAdviceEnum, + SaveConclusionTime = DateTime.Now, + + }); + + var medicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).FirstNotNullAsync(); + if (inDto.IsSendDialog&& !medicalReview.IsSendMessage && inDto.IsHaveQuestion) + { + + await _readingMedicalReviewDialogRepository.AddAsync(new ReadingMedicalReviewDialog() + { + AuditAdviceEnum = inDto.AuditAdviceEnum, + TaskMedicalReviewId = inDto.TaskMedicalReviewId, + UserTypeShortName = _userInfo.UserTypeShortName, + IsHaveQuestion=inDto.IsHaveQuestion, + Questioning=inDto.Questioning, + VisitTaskId= medicalReview.VisitTaskId, + + UserTypeEnumInt = _userInfo.UserTypeEnumInt, + ImagePath = JsonConvert.SerializeObject(inDto.FileList), + }); + } + + var result = await _taskMedicalReviewRepository.SaveChangesAsync(); + return ResponseOutput.Result(result); + } + + + /// + /// 关闭医学审核对话 + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ClosedMedicalReviewDialog(ClosedMedicalReviewDialogInDto inDto) + { + await _taskMedicalReviewRepository.UpdatePartialFromQueryAsync(inDto.TaskMedicalReviewId, x => new TaskMedicalReview() + { + IsClosedDialog = inDto.IsClosedDialog, + MedicalDialogCloseEnum=inDto.MedicalDialogCloseEnum, + DialogCloseReason=inDto.DialogCloseReason, + }); + + var taskMedical = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).FirstNotNullAsync(); + + ReadingMedicalReviewDialog dialog = new ReadingMedicalReviewDialog() + { + TaskMedicalReviewId = inDto.TaskMedicalReviewId, + VisitTaskId= taskMedical.VisitTaskId, + UserTypeShortName = _userInfo.UserTypeShortName, + MedicalDialogCloseEnum= inDto.MedicalDialogCloseEnum, + Content = inDto.DialogCloseReason, + UserTypeEnumInt = _userInfo.UserTypeEnumInt, + }; + + await _readingMedicalReviewDialogRepository.AddAsync(dialog); + var result = await _taskMedicalReviewRepository.SaveChangesAsync(); + return ResponseOutput.Result(result); + } + + /// + /// 发送对话消息 + /// + /// + /// + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SendMedicalReviewDialog(SendMedicalReviewDialogInDto inDto) + { + var medicalReviewInfo = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).FirstNotNullAsync(); + if (medicalReviewInfo.IsInvalid) + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_invalid"]); + } + + var visitTaskId = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).Select(x => x.VisitTaskId).FirstOrDefaultAsync(); + ReadingMedicalReviewDialog dialog = new ReadingMedicalReviewDialog() + { + TaskMedicalReviewId = inDto.TaskMedicalReviewId, + UserTypeShortName = _userInfo.UserTypeShortName, + Content = inDto.Content, + UserTypeEnumInt = _userInfo.UserTypeEnumInt, + VisitTaskId = visitTaskId, + AuditAdviceEnum=inDto.AuditAdviceEnum, + + IsHaveQuestion=inDto.IsHaveQuestion, + Questioning=inDto.Questioning, + }; + + await _readingMedicalReviewDialogRepository.AddAsync(dialog); + + var result=await _readingMedicalReviewDialogRepository.SaveChangesAsync(); + return ResponseOutput.Result(result); + } + + /// + /// IR发送消息 + /// + /// + /// + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task IRSendMedicalReviewDialog(IRSendMedicalReviewDialogInDto inDto) + { + var medicalReviewInfo = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).FirstNotNullAsync(); + if (medicalReviewInfo.IsInvalid) + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_invalid"]); + } + var visitTaskId = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).Select(x => x.VisitTaskId).FirstOrDefaultAsync(); + if (inDto.IsApplyHeavyReading??false) + { + await _visitTaskService.ApplyReReading(new ApplyReReadingCommand() + { + IsCopyOrigenalForms = inDto.IsCopyOrigenalForms??false, + IsCopyFollowForms=inDto.IsCopyFollowForms ?? false, + RequestReReadingReason = inDto.RequestReReadingReason, + RequestReReadingType = RequestReReadingType.DocotorApply, + TrialId = inDto.TrialId, + TaskIdList = new List() + { + visitTaskId + + } + }); + await _taskMedicalReviewRepository.UpdatePartialFromQueryAsync(x => !x.IsClosedDialog && x.Id == inDto.TaskMedicalReviewId, x => new TaskMedicalReview() + { + IsClosedDialog = true, + MedicalDialogCloseEnum = MedicalDialogClose.IRApplyReReading, + + + IsApplyHeavyReading = true, + FileName = inDto.FileName, + ImagePath = JsonConvert.SerializeObject(inDto.FileList), + DisagreeReason = inDto.DisagreeReason, + + + }); + } + + await _taskMedicalReviewRepository.UpdatePartialFromQueryAsync(inDto.TaskMedicalReviewId, x => new TaskMedicalReview() + { + DoctorUserIdeaEnum = inDto.DoctorUserIdeaEnum, + }); + ReadingMedicalReviewDialog dialog = new ReadingMedicalReviewDialog() + { + TaskMedicalReviewId = inDto.TaskMedicalReviewId, + UserTypeShortName = _userInfo.UserTypeShortName, + Content = inDto.Content, + ImagePath=JsonConvert.SerializeObject(inDto.FileList), + UserTypeEnumInt = _userInfo.UserTypeEnumInt, + VisitTaskId = visitTaskId, + FileName=inDto.FileName, + DisagreeReason = inDto.DisagreeReason, + DoctorUserIdeaEnum = inDto.DoctorUserIdeaEnum, + IsApplyHeavyReading = inDto.IsApplyHeavyReading, + }; + + await _readingMedicalReviewDialogRepository.AddAsync(dialog); + var result = await _readingMedicalReviewDialogRepository.SaveChangesAsync(); + return ResponseOutput.Result(result); + } + + + /// + /// 获取医学审核对话 + /// + /// + /// + [HttpPost] + public async Task<(List,object)> GetMedicalReviewDialog(GetMedicalReviewDialogInDto inDto) + { + var taskMedicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).FirstNotNullAsync(); + List result = await _readingMedicalReviewDialogRepository.Where(x => x.TaskMedicalReviewId == inDto.TaskMedicalReviewId) + .ProjectTo(_mapper.ConfigurationProvider) + .OrderBy(x => x.CreateTime).ToListAsync(); + result.ForEach(x => { + x.IsCurrentUser = x.CreateUserId == _userInfo.Id; + }); + return (result,new { + + IsIRReply= result.Any(x=>x.UserTypeEnumInt== (int)UserTypeEnum.IndependentReviewer), + taskMedicalReview.IsClosedDialog, + taskMedicalReview.IsHaveQuestion, + }); + } + + /// + /// 完成医学审核 + /// + /// + /// + [NonDynamicMethod] + public async Task FinishMedicalReview(FinishMedicalReviewInDto inDto) + { + var medicalReviewInfo = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).FirstNotNullAsync(); + if (medicalReviewInfo.IsInvalid) + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_invalid"]); + } + + var taskmedicalReview = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).Select(x => new + { + x.SaveConclusionTime, + x.SaveQuestionTime, + x.IsClosedDialog, + x.IsHaveQuestion + + }).FirstNotNullAsync(); + + if (taskmedicalReview.SaveQuestionTime == null || taskmedicalReview.SaveConclusionTime==null) + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_NeedSave"]); + + } + + if (taskmedicalReview.IsHaveQuestion&&!taskmedicalReview.IsClosedDialog) + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_NotClosed"]); + + } + + await _taskMedicalReviewRepository.UpdatePartialFromQueryAsync(inDto.TaskMedicalReviewId, x => new TaskMedicalReview() + { + AuditState = MedicalReviewAuditState.HaveSigned, + AuditSignTime=DateTime.Now, + }); + + var result = await _taskMedicalReviewRepository.SaveChangesAsync(); + + //自动发送邮件逻辑 + if (medicalReviewInfo.IsAutoGenerate) + { + + var allMedicalAuditList= await _taskMedicalReviewRepository.Where(x => medicalReviewInfo.PDRelationTaskIdList.Contains(x.VisitTaskId) && x.IsAutoGenerate && x.IsInvalid==false).ToListAsync(); + + if (allMedicalAuditList.All(t => t.IsApplyHeavyReading == false && t.AuditSignTime!=null && t.AuditState == MedicalReviewAuditState.HaveSigned)) + { + //自动发送 + await _trialEmailNoticeConfigService.BaseBusinessScenarioSendEmailAsync(medicalReviewInfo.VisitTaskId, false); + } + + + } + + + + + return ResponseOutput.Result(result); + } + + /// + /// IR回复确认医学审核 + /// + /// + /// + [HttpPost] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task IRConfirmMedicalReview(IRConfirmMedicalReviewInDto inDto) + { + var medicalReviewInfo = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).FirstNotNullAsync(); + if (medicalReviewInfo.IsInvalid) + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_invalid"]); + } + + await _taskMedicalReviewRepository.UpdatePartialFromQueryAsync(inDto.TaskMedicalReviewId, x => new TaskMedicalReview() + { + DoctorUserIdeaEnum = inDto.DoctorUserIdeaEnum, + DisagreeReason = inDto.DisagreeReason, + IsApplyHeavyReading=inDto.IsApplyHeavyReading, + }); + + var visitTaskId = await _taskMedicalReviewRepository.Where(x => x.Id == inDto.TaskMedicalReviewId).Select(x => x.VisitTaskId).FirstOrDefaultAsync(); + await _readingMedicalReviewDialogRepository.AddAsync(new ReadingMedicalReviewDialog() + { + + TaskMedicalReviewId = inDto.TaskMedicalReviewId, + UserTypeShortName = _userInfo.UserTypeShortName, + DoctorUserIdeaEnum = inDto.DoctorUserIdeaEnum, + DisagreeReason = inDto.DisagreeReason, + IsApplyHeavyReading = inDto.IsApplyHeavyReading, + VisitTaskId = visitTaskId, + UserTypeEnumInt = _userInfo.UserTypeEnumInt, + }); + + var result = await _taskMedicalReviewRepository.SaveChangesAsync(); + return ResponseOutput.Result(result); + } + + + /// + /// 获取IR医学审核信息 + /// + /// + /// + [HttpPost] + public async Task> GetIRMedicalFeedbackList(GetIRMedicalFeedbackListInDto inDto) + { + var taskMedicalReviewquery = _taskMedicalReviewRepository.Where(x => x.TrialId == inDto.TrialId).Include(x => x.VisitTask) + .Where(x => x.VisitTask.DoctorUserId == _userInfo.Id) + .Where(x => x.IsHaveQuestion) + .WhereIf(!inDto.TaskBlindName.IsNullOrEmpty(), x => x.VisitTask.TaskBlindName == inDto.TaskBlindName) + .WhereIf(inDto.IsUrgent != null, x => x.VisitTask.IsUrgent == inDto.IsUrgent!) + .WhereIf(inDto.AuditState != null, x => x.AuditState == inDto.AuditState!) + .WhereIf(inDto.ReadingCategory != null, x => x.VisitTask.ReadingCategory == inDto.ReadingCategory!) + .WhereIf(inDto.AuditAdviceEnum != null, x => x.AuditAdviceEnum == inDto.AuditAdviceEnum!) + .WhereIf(inDto.DoctorUserIdeaEnum != null, x => x.DoctorUserIdeaEnum == inDto.DoctorUserIdeaEnum!) + .WhereIf(inDto.IsClosedDialog != null, x => x.IsClosedDialog == inDto.IsClosedDialog!) + .WhereIf(inDto.IsHaveQuestion != null, x => x.IsHaveQuestion == inDto.IsHaveQuestion!) + .WhereIf(inDto.MedicalDialogCloseEnum != null, x => x.MedicalDialogCloseEnum == inDto.MedicalDialogCloseEnum!) + .WhereIf(inDto.IsInvalid != null, x => x.IsInvalid == inDto.IsInvalid!) + .WhereIf(inDto.TrialReadingCriterionId != null, x => x.VisitTask.TrialReadingCriterionId == inDto.TrialReadingCriterionId!) + .Select(x => new GetIRMedicalFeedbackListOutDto + { + TaskState=x.VisitTask.TaskState, + Id = x.Id, + VisitTaskId=x.VisitTaskId, + IsUrgent = x.VisitTask.IsUrgent, + AuditState = x.AuditState, + SubjectCode = x.VisitTask.BlindSubjectCode == string.Empty ? x.VisitTask.Subject.Code : x.VisitTask.BlindSubjectCode, + TaskBlindName = x.VisitTask.TaskBlindName, + ReadingCategory = x.VisitTask.ReadingCategory, + FirstReplyTime = x.ReadingMedicalReviewDialogList.Min(x => x.CreateTime), + LastReplyTime = x.ReadingMedicalReviewDialogList.Max(x => x.CreateTime), + AuditAdviceEnum = x.AuditAdviceEnum, + DoctorUserIdeaEnum = x.DoctorUserIdeaEnum, + SaveConclusionTime=x.SaveConclusionTime, + IsClosedDialog=x.IsClosedDialog, + IsHaveQuestion=x.IsHaveQuestion, + MedicalDialogCloseEnum=x.MedicalDialogCloseEnum, + IsInvalid=x.IsInvalid, + + SubjectId=x.VisitTask.SubjectId, + TrialReadingCriterionId = x.VisitTask.TrialReadingCriterion.Id, + TrialReadingCriterionName =x.VisitTask.TrialReadingCriterion.CriterionName, + ReadingTool= x.VisitTask.TrialReadingCriterion.ReadingTool, + + IsReadingTaskViewInOrder = x.VisitTask.TrialReadingCriterion.IsReadingTaskViewInOrder, + IsReadingShowSubjectInfo = x.VisitTask.TrialReadingCriterion.IsReadingShowSubjectInfo, + IsReadingShowPreviousResults = x.VisitTask.TrialReadingCriterion.IsReadingShowPreviousResults, + DigitPlaces = x.VisitTask.TrialReadingCriterion.DigitPlaces, + IseCRFShowInDicomReading = x.VisitTask.TrialReadingCriterion.IseCRFShowInDicomReading, + CriterionType = x.VisitTask.TrialReadingCriterion.CriterionType, + + }).WhereIf(!inDto.SubjectCode.IsNullOrEmpty(), x => x.SubjectCode == inDto.SubjectCode); + + var result=await taskMedicalReviewquery.ToPagedListAsync(inDto.PageIndex, inDto.PageSize, inDto.SortField.IsNullOrEmpty() ? nameof(GetIRMedicalFeedbackListOutDto.AuditState) : inDto.SortField, + inDto.Asc); + + return result; + } + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/MedicalAudit/ReadingMedicineQuestionService.cs b/IRaCIS.Core.Application/Service/Reading/MedicalAudit/ReadingMedicineQuestionService.cs new file mode 100644 index 0000000..5f52e81 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/MedicalAudit/ReadingMedicineQuestionService.cs @@ -0,0 +1,488 @@ +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Infra.EFCore.Common; +using MassTransit; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Core.Application.Service +{ + /// + /// 医学审核问题 + /// + [ ApiExplorerSettings(GroupName = "Reading")] + public class ReadingMedicineQuestionService: BaseService, IReadingMedicineQuestionService + { + + private readonly IRepository _readingMedicineTrialQuestionRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _readingMedicineSystemQuestionRepository; + + public ReadingMedicineQuestionService( + IRepository readingMedicineTrialQuestionRepository, + IRepository readingQuestionCriterionTrial, + IRepository trialRepository, + IRepository readingMedicineSystemQuestionRepository + ) + { + this._readingMedicineTrialQuestionRepository = readingMedicineTrialQuestionRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrial; + this._trialRepository = trialRepository; + this._readingMedicineSystemQuestionRepository = readingMedicineSystemQuestionRepository; + } + + + + #region 系统 + + /// + /// 获取系统的医学审核问题 + /// + /// + /// + [HttpPost] + public async Task> GetReadingMedicineSystemQuestionList(ReadingMedicineSystemQuestionQuery inDto) + { + //避免前端遍历 + var criterionEnum = inDto.TrialReadingCriterionId != null ? _readingQuestionCriterionTrialRepository.Where(t => t.Id == inDto.TrialReadingCriterionId).Select(t => t.CriterionType).FirstOrDefault(): CriterionType.NoCriterion; + + var query = _readingMedicineSystemQuestionRepository.AsQueryable() + .WhereIf(!inDto.TypeValue.IsNullOrEmpty(), x => x.TypeValue.Contains(inDto.TypeValue)) + .WhereIf(!inDto.ParentTriggerValue.IsNullOrEmpty(), x => x.ParentTriggerValue.Contains(inDto.ParentTriggerValue)) + .WhereIf(!inDto.QuestionName.IsNullOrEmpty(), x => x.QuestionName.Contains(inDto.QuestionName)) + .WhereIf(!inDto.Type.IsNullOrEmpty(), x => x.Type.Contains(inDto.Type)) + .WhereIf(inDto.ReadingCategory != null, x => x.ReadingCategory == inDto.ReadingCategory) + .WhereIf(inDto.CurrentCriterionType!=null,x=>x.CriterionTypeEnum==null||x.CriterionTypeEnum==inDto.CurrentCriterionType) + .WhereIf(inDto.CriterionTypeEnum != null, x => x.CriterionTypeEnum==inDto.CriterionTypeEnum) + .WhereIf(inDto.TrialReadingCriterionId != null, x => x.CriterionTypeEnum== criterionEnum || x.IsGeneral==true) + .WhereIf(inDto.IsGeneral != null, x => x.IsGeneral == inDto.IsGeneral) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(x => x.ShowOrder); + + return await query.ToPagedListAsync(inDto.PageIndex, inDto.PageSize,inDto.SortField,inDto.Asc); + } + + /// + /// 获取系统的其他医学审核问题 + /// + /// + /// + [HttpPost] + public async Task> GetReadingMedicineSystemOtherQuestion(GetReadingMedicineSystemOtherQuestionInDto inDto) + { + var types = new List() + { + "select","radio" + }; + + var questionList = await _readingMedicineSystemQuestionRepository + .Where(x => types.Contains(x.Type)) + .WhereIf(inDto.Id != null, x => x.Id != inDto.Id && x.ParentId != inDto.Id) + .WhereIf(inDto.ShowOrder != null, x => x.ShowOrder < inDto.ShowOrder) + .WhereIf(inDto.ReadingCategory != null, x => x.ReadingCategory == inDto.ReadingCategory) + .Select(x => new GetReadingMedicineTrialOtherQuestionOutDto() + { + Id = x.Id, + QuestionName = x.QuestionName, + TypeValue = x.TypeValue, + ReadingCategory = x.ReadingCategory, + }).ToListAsync(); + + return questionList; + } + + /// + /// 新增或修改系统医学审核问题 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateReadingMedicineSystemQuestion(ReadingMedicineSystemQuestionAddOrEdit inDto) + { + var existsQuery = _readingMedicineSystemQuestionRepository + .WhereIf(inDto.Id != null, x => x.Id != inDto.Id) + .Where(x => x.ShowOrder == inDto.ShowOrder); + + if (await existsQuery.AnyAsync()) + { + return ResponseOutput.NotOk("当前问题序号存在重复"); + } + + //inDto.CriterionEnumStr = $"|{String.Join('|', inDto.CriterionEnumList)}|"; + + var entity = await _readingMedicineSystemQuestionRepository.InsertOrUpdateAsync(inDto); + await _readingMedicineSystemQuestionRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id.ToString()); + } + + /// + /// 删除系统的医学审核问题 + /// + /// + /// + [HttpDelete("{id:guid}")] + public async Task DeleteReadingMedicineSystemQuestion(Guid id) + { + if (await _readingMedicineSystemQuestionRepository.AnyAsync(x => x.ParentId == id)) + { + return ResponseOutput.NotOk("此问题存在子问题,请先删除子问题"); + } + var success = await _readingMedicineSystemQuestionRepository.DeleteFromQueryAsync(t => t.Id == id); + var result = await _readingMedicineSystemQuestionRepository.SaveChangesAsync(); + return ResponseOutput.Result(result); + } + + #endregion + + + #region 项目 + + /// 获取项目的医学审核问题 + /// + /// + /// + [HttpPost] + public async Task<(PageOutput, object)> GetReadingMedicineTrialQuestionList(ReadingMedicineTrialQuestionQuery inDto) + { + var query = _readingMedicineTrialQuestionRepository.AsQueryable() + .Where(x => x.TrialId == inDto.TrialId && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId) + .WhereIf(!inDto.TypeValue.IsNullOrEmpty(), x => x.TypeValue.Contains(inDto.TypeValue)) + .WhereIf(!inDto.ParentTriggerValue.IsNullOrEmpty(), x => x.ParentTriggerValue.Contains(inDto.ParentTriggerValue)) + .WhereIf(!inDto.QuestionName.IsNullOrEmpty(), x => x.QuestionName.Contains(inDto.QuestionName)) + .WhereIf(!inDto.Type.IsNullOrEmpty(), x => x.Type.Contains(inDto.Type)) + .WhereIf(inDto.ReadingCategory != null, x => x.ReadingCategory == inDto.ReadingCategory) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(x => x.ShowOrder); + + + var isConfirmMedicineQuestion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).Select(x => x.IsConfirmMedicineQuestion).FirstOrDefaultAsync(); + var questionList = await query.ToPagedListAsync(inDto.PageIndex, inDto.PageSize, inDto.SortField, inDto.Asc); + return (questionList, new + { + IsConfirmMedicineQuestion = isConfirmMedicineQuestion, + //QuestionCount = questionList.Count(), + }); + } + + + + /// + /// 获取项目的其他医学审核问题 + /// + /// + /// + [HttpPost] + public async Task> GetReadingMedicineTrialOtherQuestion(GetReadingMedicineTrialOtherQuestionInDto inDto) + { + var types = new List() + { + "select","radio" + }; + + var questionList = await _readingMedicineTrialQuestionRepository.Where(x => x.TrialId == inDto.TrialId && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId) + .Where(x => types.Contains(x.Type)) + .WhereIf(inDto.Id != null, x => x.Id != inDto.Id && x.ParentId != inDto.Id) + .WhereIf(inDto.ShowOrder != null, x => x.ShowOrder < inDto.ShowOrder) + .WhereIf(inDto.ReadingCategory != null, x => x.ReadingCategory == inDto.ReadingCategory) + .Select(x => new GetReadingMedicineTrialOtherQuestionOutDto() + { + Id = x.Id, + QuestionName = x.QuestionName, + TypeValue = x.TypeValue, + ReadingCategory = x.ReadingCategory, + }).ToListAsync(); + + return questionList; + } + + /// + /// + /// 获取预览问题信息 + /// + /// + /// + [HttpPost] + public async Task> GetMedicineQuestionPreview(GetMedicineQuestionPreviewInDto inDto) + { + var trialQuestionList = await _readingMedicineTrialQuestionRepository.Where(x => x.TrialId == inDto.TrialId && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId) + .WhereIf(inDto.ReadingCategory != null, x => x.ReadingCategory == inDto.ReadingCategory) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(x => x.ShowOrder).ToListAsync(); + + List readingQuestionList = trialQuestionList.Where(x => x.ParentId == null).ToList(); + readingQuestionList.ForEach(x => + { + FindChildQuestion(x, trialQuestionList); + }); + + return readingQuestionList; + } + + /// + /// 新增或修改项目医学审核问题 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter))] + public async Task AddOrUpdateReadingMedicineTrialQuestion(ReadingMedicineTrialQuestionAddOrEdit inDto) + { + var existsQuery = _readingMedicineTrialQuestionRepository + .WhereIf(inDto.Id != null, x => x.Id != inDto.Id) + .Where(x => x.TrialId == inDto.TrialId && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId) + .Where(x => x.ShowOrder == inDto.ShowOrder); + + if (await existsQuery.AnyAsync()) + { + return ResponseOutput.NotOk("当前问题序号存在重复"); + } + var entity = await _readingMedicineTrialQuestionRepository.InsertOrUpdateAsync(inDto); + await _readingMedicineTrialQuestionRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + /// + /// 删除项目的医学审核问题 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task DeleteReadingMedicineTrialQuestion(DeleteReadingMedicineTrialQuestion inDto) + { + if (await _readingMedicineTrialQuestionRepository.AnyAsync(x => x.ParentId == inDto.Id)) + { + return ResponseOutput.NotOk("此问题存在子问题,请先删除子问题"); + } + var success = await _readingMedicineTrialQuestionRepository.DeleteFromQueryAsync(t => t.Id == inDto.Id); + var result = await _readingMedicineTrialQuestionRepository.SaveChangesAsync(); + return ResponseOutput.Result(result); + } + + + + + + + #endregion + + + + #region 项目 医学审核问题 验证 确认 + + /// + /// 验证医学审核问题 + /// + /// + /// + /// + public async Task VerifyReadingMedicineQuestion(ConfirmReadingMedicineQuestionInDto inDto) + { + var readingMedicineQuestionList = await _readingMedicineTrialQuestionRepository.Where(x => x.TrialId == inDto.TrialId && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId) + .Select(x => new TrialQuestion() + { + Id = x.Id, + ReadingCategory = x.ReadingCategory, + ParentShowOrder = (int?)x.ParentQuestion.ShowOrder, + ShowOrder = x.ShowOrder, + }).ToListAsync(); + if (readingMedicineQuestionList.Count == 0) + { + throw new BusinessValidationFailedException("当前未添加医学审核问题。请先添加医学审核问题,再进行确认。"); + } + + if (readingMedicineQuestionList.Count() != readingMedicineQuestionList.Select(t => t.ShowOrder).Distinct().Count()) + { + throw new BusinessValidationFailedException("影像医学审核问题显示序号不能重复。"); + } + + + if (readingMedicineQuestionList.Where(t => t.ParentShowOrder != null).Any(t => t.ParentShowOrder > t.ShowOrder)) + { + throw new BusinessValidationFailedException("父问题的显示序号要比子问题的显示序号小,请确认。"); + } + + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).Select(x => new + { + x.IsGlobalReading, + x.IsOncologyReading, + x.IsArbitrationReading, + }).FirstNotNullAsync(); + + + + + + if (criterionInfo.IsGlobalReading && !readingMedicineQuestionList.Any(x => x.ReadingCategory == ReadingCategory.Global)) + { + throw new BusinessValidationFailedException("当前标准启用了全局阅片,但未配置全局医学审核问题"); + } + + if (criterionInfo.IsArbitrationReading && !readingMedicineQuestionList.Any(x => x.ReadingCategory == ReadingCategory.Judge)) + { + throw new BusinessValidationFailedException("当前标准启用了仲裁阅片,但未配置仲裁医学审核问题"); + } + + if (criterionInfo.IsOncologyReading && !readingMedicineQuestionList.Any(x => x.ReadingCategory == ReadingCategory.Oncology)) + { + throw new BusinessValidationFailedException("当前标准启用了肿瘤学阅片,但未配置肿瘤学医学审核问题"); + } + + return ResponseOutput.Ok(); + + } + + /// + /// 确认医学审核问题 + /// + /// + public async Task ConfirmReadingMedicineQuestion(ConfirmReadingMedicineQuestionInDto inDto) + { + + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).Select(x => new + { + x.IsGlobalReading, + x.IsOncologyReading, + x.IsArbitrationReading, + }).FirstNotNullAsync(); + var readingMedicineQuestionList = await _readingMedicineTrialQuestionRepository.Where(x => x.TrialId == inDto.TrialId && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId) + .Select(x => new TrialQuestion() + { + Id = x.Id, + ReadingCategory = x.ReadingCategory, + ParentShowOrder = (int?)x.ParentQuestion.ShowOrder, + ShowOrder = x.ShowOrder, + }).ToListAsync(); + if (readingMedicineQuestionList.Count == 0) + { + throw new BusinessValidationFailedException("当前未添加医学审核问题。请先添加医学审核问题,再进行确认。"); + } + + if (readingMedicineQuestionList.Count() != readingMedicineQuestionList.Select(t => t.ShowOrder).Distinct().Count()) + { + throw new BusinessValidationFailedException("影像医学审核问题显示序号不能重复。"); + } + + + if (readingMedicineQuestionList.Where(t => t.ParentShowOrder != null).Any(t => t.ParentShowOrder > t.ShowOrder)) + { + throw new BusinessValidationFailedException("父问题的显示序号要比子问题的显示序号小,请确认。"); + } + + + var trialInfo = await _trialRepository.Where(x => x.Id == inDto.TrialId).FirstNotNullAsync(); + + if (criterionInfo.IsGlobalReading && !readingMedicineQuestionList.Any(x => x.ReadingCategory == ReadingCategory.Global)) + { + throw new BusinessValidationFailedException("当前项目启用了全局阅片,但未配置全局医学审核问题"); + } + + if (criterionInfo.IsArbitrationReading && !readingMedicineQuestionList.Any(x => x.ReadingCategory == ReadingCategory.Judge)) + { + throw new BusinessValidationFailedException("当前项目启用了仲裁阅片,但未配置仲裁医学审核问题"); + } + + if (criterionInfo.IsOncologyReading && !readingMedicineQuestionList.Any(x => x.ReadingCategory == ReadingCategory.Oncology)) + { + throw new BusinessValidationFailedException("当前项目启用了肿瘤学阅片,但未配置肿瘤学医学审核问题"); + } + + + await _readingMedicineTrialQuestionRepository.BatchUpdateNoTrackingAsync(x => x.TrialId == inDto.TrialId, x => new ReadingMedicineTrialQuestion() + { + IsConfirm = true + }); + + await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() + { + IsConfirmMedicineQuestion = true + }); + + var result = await _trialRepository.SaveChangesAsync(); + return ResponseOutput.Result(result); + + + + } + + #endregion + + + + private void FindChildQuestion(GetMedicineQuestionPreviewOutDto trialReadingQuestion, List questionlists) + { + trialReadingQuestion.Childrens = questionlists.Where(x => x.ParentId == trialReadingQuestion.Id).ToList(); + if (trialReadingQuestion.Childrens != null && trialReadingQuestion.Childrens.Count != 0) + { + trialReadingQuestion.Childrens.ForEach(x => + { + this.FindChildQuestion(x, questionlists); + }); + } + } + + + /// + /// 从系统里面选择问题添加到项目里面 + /// + /// + /// + [HttpPost] + public async Task AddTrialDataFromSystem(AddTrialDataFromSystemInDto inDto) + { + // 直接写?? + var systemList = await _readingMedicineSystemQuestionRepository.Where(x => inDto.SystemQuestionIds.Contains(x.Id)).ToListAsync(); + + var maxOrder = await _readingMedicineTrialQuestionRepository.Where(x => x.TrialId == inDto.TrialId&&x.TrialReadingCriterionId==inDto.TrialReadingCriterionId).OrderByDescending(x => x.ShowOrder).Select(x => x.ShowOrder).FirstOrDefaultAsync(); + + + var needList= systemList.Select(x => new TrialDataFromSystem() + { + Id = NewId.NextGuid(), + ShowOrder = x.ShowOrder, + IsEnable = x.IsEnable, + IsRequired = x.IsRequired, + QuestionName = x.QuestionName, + TrialReadingCriterionId=inDto.TrialReadingCriterionId, + Type = x.Type, + ParentId=x.ParentId, + SystemQuestionId=x.Id, + ReadingCategory=x.ReadingCategory, + TypeValue = x.TypeValue, + TrialId=inDto.TrialId, + }).ToList(); + + needList.ForEach(x => { + maxOrder++; + x.ShowOrder = maxOrder; + }); + + foreach (var item in needList.Where(x => x.ParentId != null)) + { + var parent = needList.Where(x => x.SystemQuestionId == item.ParentId).FirstOrDefault(); + if (parent == null) + { + item.ParentId = null; + item.ParentTriggerValue = String.Empty; + } + else + { + item.ParentId = parent.Id; + } + } + + await _readingMedicineTrialQuestionRepository.AddRangeAsync(needList); + var result = await _readingMedicineTrialQuestionRepository.SaveChangesAsync(); + return ResponseOutput.Result(result); + } + + + + + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/OrganInfoService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/OrganInfoService.cs new file mode 100644 index 0000000..cfbeef0 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/OrganInfoService.cs @@ -0,0 +1,495 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-12 14:07:20 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Infra.EFCore.Common; +using MassTransit; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Service +{ + /// + /// 器官服务层 + /// + [ApiExplorerSettings(GroupName = "Reading")] + public class OrganInfoService : BaseService, IOrganInfoService + { + + private readonly IRepository _organInfoRepository; + private readonly IRepository _dictionaryRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _organTrialInfoRepository; + private readonly IRepository _readingQuestionCriterionTrial; + private readonly IRepository _criterionNidusRepository; + + public OrganInfoService( + IRepository organInfoRepository, + IRepository dictionaryRepository, + IRepository visitTaskRepository, + IRepository organTrialInfoRepository, + IRepository readingQuestionCriterionTrial, + IRepository criterionNidusRepository + ) + { + _organInfoRepository = organInfoRepository; + this._dictionaryRepository = dictionaryRepository; + this._visitTaskRepository = visitTaskRepository; + this._organTrialInfoRepository = organTrialInfoRepository; + this._readingQuestionCriterionTrial = readingQuestionCriterionTrial; + this._criterionNidusRepository = criterionNidusRepository; + } + + + #region 系统器官 + + /// + /// 获取系统器官信息 + /// + /// + /// + [HttpPost] + public async Task> GetOrganInfoList(OrganInfoQuery inQuery) + { + List organs = new List(); + if (inQuery.LesionType != null) + { + organs = await _criterionNidusRepository.Where(x => x.CriterionId == inQuery.SystemCriterionId && x.LesionType == inQuery.LesionType) + .Select(x => x.OrganType).ToListAsync(); + } + var organInfoQueryable = _organInfoRepository + .Where(x => x.SystemCriterionId == inQuery.SystemCriterionId) + .WhereIf(inQuery.LesionType != null, x => organs.Contains(x.OrganType)) + .WhereIf(inQuery.OrganType != null, x => x.OrganType == inQuery.OrganType) + .OrderBy(x=>x.OrganType) + .ThenBy(x=>x.ShowOrder) + .ProjectTo(_mapper.ConfigurationProvider); + return await organInfoQueryable.ToListAsync(); + } + + + /// + /// 获取系统器官分页信息 + /// + /// + /// + [HttpPost] + public async Task> GetOrganPageList(OrganInfoQuery inQuery) + { + + + List organs = new List(); + + if (inQuery.LesionType != null) + { + organs = await _criterionNidusRepository.Where(x => x.CriterionId == inQuery.SystemCriterionId && x.LesionType == inQuery.LesionType) + .Select(x => x.OrganType).ToListAsync(); + } + + var organInfoQueryable = _organInfoRepository + .Where(x => x.SystemCriterionId == inQuery.SystemCriterionId) + .WhereIf(inQuery.LesionType != null, x => organs.Contains(x.OrganType)) + .WhereIf(inQuery.OrganType != null, x => x.OrganType == inQuery.OrganType) + .ProjectTo(_mapper.ConfigurationProvider); + return await organInfoQueryable.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(OrganInfoView.ShowOrder) : inQuery.SortField, inQuery.Asc,true,new string[] { nameof(OrganInfoView.OrganType) , nameof(OrganInfoView.ShowOrder) }); + } + + /// + /// 新增或修改系统器官数据 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateOrganInfo(OrganInfoAddOrEdit addOrEditOrganInfo) + { + var entity = await _organInfoRepository.InsertOrUpdateAsync(addOrEditOrganInfo, true); + return ResponseOutput.Ok(entity.Id.ToString()); + } + + + /// + /// 删除系统器官 + /// + /// + /// + [HttpDelete("{Id:guid}")] + public async Task DeleteOrganInfo(Guid Id) + { + var success = await _organInfoRepository.DeleteFromQueryAsync(t => t.Id == Id, true); + return ResponseOutput.Ok(); + } + + #endregion + + /// + /// 获取阅片器官信息 + /// + /// + /// + [HttpPost] + public async Task> GetReadingOrganList(GetReadingOrganListInDto inDto) + { + var trialReadingCriterionId = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Select(x => x.TrialReadingCriterionId).FirstOrDefaultAsync(); + + var isEn = _userInfo.IsEn_Us; + + var organInfoQueryable = from data in _organInfoRepository.AsQueryable() + + join trialData in _organTrialInfoRepository.AsQueryable().Where( x => x.IsEnable&&x.TrialCriterionId== trialReadingCriterionId) + on data.Id equals trialData.OrganInfoId + join criterionNidus in _criterionNidusRepository.AsQueryable().Where(x=>x.CriterionId== trialReadingCriterionId) + on data.OrganType equals criterionNidus.OrganType + select new ReadingOrganDto() + { + Id = trialData.Id, + OrganInfoId= data.Id, + Part =isEn?data.PartEN: data.Part, + TULAT =isEn?data.TULATEN: data.TULAT, + ShowOrder=data.ShowOrder, + Classification= isEn? data.ClassificationEN:data.Classification, + ClassificationEN=data.ClassificationEN, + TULOC =isEn?data.TULOCEN: data.TULOC, + Remark = data.Remark, + IsLymphNodes = data.IsLymphNodes, + IsCanEditPosition = data.IsCanEditPosition, + IsEnable = trialData.IsEnable, + OrganType = data.OrganType, + PartEN = data.PartEN, + TULATEN = data.TULATEN, + TULOCEN = data.TULOCEN, + LesionType= criterionNidus.LesionType + }; + + var organList = await organInfoQueryable.ToListAsync(); + + List result = organList.GroupBy(x => new { x.LesionType }).Select(x => new GetReadingOrganListOutDto() + { + LesionType = x.Key.LesionType, + OrganList = x.OrderBy(y=>y.ShowOrder).ToList() + }).ToList(); + + return result; + } + + + #region 项目器官 + + /// + /// 获取项目器官信息 + /// + /// + /// + [HttpPost] + public async Task> GetTrialOrganList(GetTrialOrganListInDto inDto) + { + + List organs = new List(); + + if (inDto.TrialReadingCriterionId == null) + { + inDto.TrialReadingCriterionId = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Select(x => x.TrialReadingCriterionId).FirstOrDefaultAsync(); + } + + + if (inDto.LesionType != null) + { + var criterion = await _readingQuestionCriterionTrial.Where(x => x.Id == inDto.TrialReadingCriterionId).FirstOrDefaultAsync(); + organs = await _criterionNidusRepository.Where(x => x.CriterionId == (criterion == null ? default(Guid) : criterion.Id) && x.LesionType == inDto.LesionType) + .Select(x => x.OrganType).ToListAsync(); + } + + var organInfoQueryable = from data in _organInfoRepository + .WhereIf(inDto.OrganType != null, x => x.OrganType == inDto.OrganType) + .WhereIf(inDto.IsLymphNodes != null, x => x.IsLymphNodes == inDto.IsLymphNodes) + .WhereIf(inDto.LesionType != null, x => organs.Contains(x.OrganType)) + .WhereIf(!inDto.Part.IsNullOrEmpty(), x => x.Part.Contains(inDto.Part)||x.PartEN.Contains(inDto.Part)) + .WhereIf(!inDto.TULOC.IsNullOrEmpty(), x => x.TULOC.Contains(inDto.TULOC)|| x.TULOCEN.Contains(inDto.TULOC)) + .WhereIf(!inDto.TULAT.IsNullOrEmpty(), x => x.TULAT.Contains(inDto.TULAT)||x.TULATEN.Contains(inDto.TULAT)) + join trialData in _organTrialInfoRepository.WhereIf(inDto.IsEnable != null, x => x.IsEnable == inDto.IsEnable) + + .WhereIf(inDto.IsEnable != null, x => x.IsEnable == inDto.IsEnable) + .Where(x => x.TrialCriterionId == inDto.TrialReadingCriterionId) + on data.Id equals trialData.OrganInfoId + select new GetTrialOrganListOutDto() + { + Id = trialData.Id, + Part = data.Part, + TULAT = data.TULAT, + TULOC = data.TULOC, + Remark = data.Remark, + ShowOrder=data.ShowOrder, + Classification=data.Classification, + ClassificationEN=data.ClassificationEN, + IsLymphNodes = data.IsLymphNodes, + IsCanEditPosition = data.IsCanEditPosition, + IsEnable = trialData.IsEnable, + OrganType = data.OrganType, + PartEN = data.PartEN, + TULATEN = data.TULATEN, + TULOCEN = data.TULOCEN, + }; + + + return await organInfoQueryable.OrderBy(x=>x.OrganType).ThenBy(x=>x.ShowOrder).ToListAsync(); + } + + + /// + /// 批量添加项目器官 + /// + /// + /// + public async Task BatchAddTrialOrgan(BatchAddTrialOrganInDto inDto) + { + //await _organTrialInfoRepository.BatchDeleteNoTrackingAsync(t => t.TrialId==inDto.TrialId&&t.OrganType == inDto.OrganType); + + List organTrialInfos = inDto.OrganIds.Select(x => new OrganTrialInfo() + { + OrganInfoId = x, + TrialId = inDto.TrialId, + TrialCriterionId = inDto.TrialReadingCriterionId, + //OrganType=inDto.OrganType, + }).ToList(); + + + await _organTrialInfoRepository.AddRangeAsync(organTrialInfos); + + await _organInfoRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + /// + /// 删除系统器官 + /// + /// + /// + [HttpDelete("{Id:guid}")] + public async Task DeleteTrialOrganInfo(Guid Id) + { + var success = await _organTrialInfoRepository.DeleteFromQueryAsync(t => t.Id == Id, true); + return ResponseOutput.Ok(); + } + + + + /// + /// 设置项目器官是否生效 + /// + /// + /// + [HttpPost] + public async Task SetOrganIsEnable(SetOrganIsEnableInDto inDto) + { + await _organTrialInfoRepository.UpdatePartialFromQueryAsync(x => inDto.Ids.Contains(x.Id), x => new OrganTrialInfo() + { + IsEnable = inDto.IsEnable + }); + + await _organTrialInfoRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + #endregion + + + + #region 器官和病灶绑定表 (项目和系统 在同一张表) + + /// + /// 获取标准病灶类型 + /// + /// + /// + [HttpPost] + public async Task> GetCriterionLesionType(GetCriterionLesionTypeInDto inDto) + { + var dicNums = new List(); + dicNums = await _criterionNidusRepository.Where(x => x.CriterionId == inDto.CriterionId).Select(x => ((int)x.LesionType).ToString()).Distinct().ToListAsync(); + var dictionaryId = await _dictionaryRepository.Where(x => x.Code == "LesionType").Select(x => x.Id).FirstOrDefaultAsync(); + + var result = await _dictionaryRepository.Where(x => x.ParentId == dictionaryId && dicNums.Contains(x.Code)).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + return result; + } + + /// + /// 获取标准病灶器官关系信息 + /// + /// + /// + [HttpPost] + public async Task> GetCriterionNidusList(CriterionNidusQuery inQuery) + { + + var criterionNidusQueryable = _criterionNidusRepository + .Where(x => x.CriterionId == inQuery.CriterionId) + .ProjectTo(_mapper.ConfigurationProvider); + + return await criterionNidusQueryable.ToListAsync(); + } + + /// + /// 新增修改标准病灶器官关系信息 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateCriterionNidus(CriterionNidusAddOrEdit inDto) + { + + if (await _criterionNidusRepository.AnyAsync(x => x.OrganType == inDto.OrganType && x.LesionType == inDto.LesionType && x.CriterionId == inDto.CriterionId && x.Id != inDto.Id)) + { + throw new BusinessValidationFailedException("存在相同的数据,操作失败"); + } + var entity = await _criterionNidusRepository.InsertOrUpdateAsync(inDto, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + /// + /// 删除病灶病灶器官关系信息 + /// + /// + /// + [HttpDelete("{id:guid}")] + public async Task DeleteCriterionNidus(Guid id) + { + var success = await _criterionNidusRepository.DeleteFromQueryAsync(t => t.Id == id, true); + return ResponseOutput.Ok(); + } + + #endregion + + /// + /// 同步系统器官 + /// + /// + /// + [HttpPost] + public async Task SynchronizeSystemOrgan(SynchronizeSystemOrganInDto inDto) + { + var organList= await _organInfoRepository.Where(x => x.SystemCriterionId == inDto.FromCriterionId).ToListAsync(); + + organList.ForEach(x => + { + + x.SystemCriterionId = inDto.ToCriterionId; + x.Id = NewId.NextGuid(); + + }); + + await _organInfoRepository.BatchDeleteNoTrackingAsync(x => x.SystemCriterionId == inDto.ToCriterionId); + await _organInfoRepository.AddRangeAsync(organList); + await _organInfoRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + + } + + + /// + /// 同步系统器官到项目 + /// + /// + /// + [HttpPost] + public async Task SynchronizeSystemOrganToTrial(SynchronizeSystemOrganToTrialInDto inDto) + { + // 选中的标准进行修改 + var readingQuestionCriterionTrial = await _readingQuestionCriterionTrial.Where(x => x.Id==inDto.TrialReadingCriterionId).FirstOrDefaultAsync(); + + if (readingQuestionCriterionTrial != null) + { + Guid trialCriterionId = default(Guid); + + trialCriterionId = readingQuestionCriterionTrial.Id; + if (inDto.SystemCriterionId == null) + { + inDto.SystemCriterionId = readingQuestionCriterionTrial.ReadingQuestionCriterionSystemId; + } + await _criterionNidusRepository.BatchDeleteNoTrackingAsync(x => x.CriterionId == trialCriterionId); + List criterionNidusList = await _criterionNidusRepository.Where(x => x.CriterionId == inDto.SystemCriterionId).Select(x => new CriterionNidusData() + { + Id = x.Id, + CriterionId = trialCriterionId, + LesionType = x.LesionType, + OriginalId = x.Id, + OrganType=x.OrganType, + IsSystemCriterion=false, + }).ToListAsync(); + + criterionNidusList.ForEach(x => x.Id = NewId.NextGuid()); + + await _criterionNidusRepository.AddRangeAsync(criterionNidusList); + await _organTrialInfoRepository.BatchDeleteNoTrackingAsync(x => x.TrialCriterionId == readingQuestionCriterionTrial.Id); + List organTrialInfoList = await _organInfoRepository.Where(x =>x.SystemCriterionId== inDto.SystemCriterionId).Select(x => new OrganTrialInfo() + { + //OrganType=x.OrganType, + Id = x.Id, + IsEnable = true, + TrialCriterionId= readingQuestionCriterionTrial.Id, + OrganInfoId = x.Id, + TrialId = readingQuestionCriterionTrial.TrialId, + }).ToListAsync(); + + organTrialInfoList.ForEach(x => x.Id = NewId.NextGuid()); + await _organTrialInfoRepository.AddRangeAsync(organTrialInfoList); + + await _organTrialInfoRepository.SaveChangesAsync(); + } + + + + return ResponseOutput.Ok(); + + } + + + + + + + + #region 注释 + ///// + ///// 获取项目勾选器官信息 + ///// + ///// + ///// + //[HttpPost] + //public async Task> GetTrialCheckOrganList(GetTrialOrganListInDto inDto) + //{ + // var organInfoQueryable = from data in _organInfoRepository.WhereIf(inDto.CriterionNidusId != null, x => x.CriterionNidusId == inDto.CriterionNidusId) + // join trialData in _organTrialInfoRepository.WhereIf(inDto.IsEnable != null, x => x.IsEnable == inDto.IsEnable) + // .Where(x => x.TrialId == inDto.TrialId) + // on data.Id equals trialData.OrganInfoId into jtemp + // from leftjoin in jtemp.DefaultIfEmpty() + // select new GetTrialCheckOrganList() + // { + // Id = data.Id, + // Part = data.Part, + // TULAT = data.TULAT, + // TULOC = data.TULOC, + + // IsCheckd= leftjoin!=null, + // Remark = data.Remark, + // CriterionNidusId = data.CriterionNidusId, + // IsEnable = leftjoin.IsEnable, + + // }; + + + // return await organInfoQueryable.ToListAsync(); + //} + #endregion + + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/ReadingCriterionService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/ReadingCriterionService.cs new file mode 100644 index 0000000..e77c2d5 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/ReadingCriterionService.cs @@ -0,0 +1,662 @@ +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Infra.EFCore.Common; +using MassTransit; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Service.Reading.Dto; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Core.Application.Service.RC +{ + + + [ApiExplorerSettings(GroupName = "Reading")] + public class ReadingQuestionService : BaseService + { + + private readonly IRepository _readingQuestionCriterionSystemRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _readingQuestionTrialRepository; + private readonly IRepository _readingTaskQuestionAnswer; + private readonly IRepository _readingCriterionPageRepository; + private readonly IRepository _readingCriterionDictionaryRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _systemCriterionDictionaryCodeRepository; + private readonly IRepository _readingTableQuestionAnswerRepository; + private readonly IRepository _readingTableAnswerRowInfoRepository; + + private readonly IRepository _readingTableQuestionTrialRepository; + + public ReadingQuestionService( + IRepository readingQuestionCriterionSystemRepository, + IRepository readingQuestionCriterionTrialRepository, + IRepository readingQuestionTrialRepository, + IRepository readingCriterionDictionaryRepository, + IRepository visitTaskRepository, + IRepository systemCriterionDictionaryCodeRepository, + IRepository readingTableQuestionTrialRepository, + IRepository readingTableQuestionAnswerRepository, + IRepository readingTableAnswerRowInfoRepository, + IRepository readingTaskQuestionAnswer, + IRepository readingCriterionPageRepository) + { + this._readingQuestionCriterionSystemRepository = readingQuestionCriterionSystemRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this._readingQuestionTrialRepository = readingQuestionTrialRepository; + this._readingTableQuestionTrialRepository = readingTableQuestionTrialRepository; + this._readingCriterionPageRepository = readingCriterionPageRepository; + this._readingCriterionDictionaryRepository = readingCriterionDictionaryRepository; + this._visitTaskRepository = visitTaskRepository; + this._systemCriterionDictionaryCodeRepository = systemCriterionDictionaryCodeRepository; + this._readingTableQuestionAnswerRepository = readingTableQuestionAnswerRepository; + this._readingTableAnswerRowInfoRepository = readingTableAnswerRowInfoRepository; + this._readingTaskQuestionAnswer = readingTaskQuestionAnswer; + } + + + /// + /// 删除系统标准字典Code + /// + /// + /// + [HttpPost] + public async Task DeleteSystemCriterionDictionary(DeleteSystemCriterionDictionaryIndto inDto) + { + var criterionDictionaryCode= await _systemCriterionDictionaryCodeRepository.Where(x => x.Id == inDto.Id).FirstNotNullAsync(); + + await _readingCriterionDictionaryRepository.BatchDeleteNoTrackingAsync(x => x.ParentCode == criterionDictionaryCode.Code && x.CriterionId == criterionDictionaryCode.SystemCriterionId); + + await _systemCriterionDictionaryCodeRepository.DeleteFromQueryAsync(inDto.Id); + await _systemCriterionDictionaryCodeRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + /// + /// 添加系统标准字典Code + /// + /// + /// + [HttpPost] + public async Task AddSystemCriterionDictionaryCode(AddSystemCriterionDictionaryCodeInDto inDto) + { + + var codes= await _systemCriterionDictionaryCodeRepository.Where(x => x.SystemCriterionId == inDto.SystemCriterionId).Select(x => x.Code).ToListAsync(); + + inDto.CodeList= inDto.CodeList.Except(codes).ToList(); + + + + await _systemCriterionDictionaryCodeRepository.AddRangeAsync(inDto.CodeList.Select(x=> new SystemCriterionDictionaryCode() + { + SystemCriterionId = inDto.SystemCriterionId, + Code = x + }).ToList()); + + await _systemCriterionDictionaryCodeRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + /// + /// 重置项目标准的重置状态 + /// + /// 项目标准Id + /// + [HttpPost] + public async Task ResetTrialCriterionAsyncState(Guid trialReadingCriterionId) + { + await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(x => x.Id == trialReadingCriterionId, x => new ReadingQuestionCriterionTrial + { + SynchronizeOriginalTime = null, + SynchronizeTime = DateTime.Now.AddYears(-20), + IsSigned=false, + ReadingInfoSignTime=null, + + }); + + + return ResponseOutput.Ok(); + } + + /// + /// 设置系统全局阅片阅片信息 + /// + /// + /// + [HttpPost] + [UnitOfWork] + public async Task SetSystemGlobalInfo(SetSystemGlobalInfoInDto inDto) + { + + await _readingCriterionDictionaryRepository.BatchDeleteNoTrackingAsync(x => x.CriterionId == inDto.SystemCriterionId && x.ParentCode == ReadingCommon.CriterionDictionary.GlobalAssess); + + await _readingCriterionDictionaryRepository.AddRangeAsync(inDto.DictionaryList.Select(x => new ReadingCriterionDictionary + { + CriterionId = inDto.SystemCriterionId, + DictionaryId = x.DictionaryId, + IsSystemCriterion = true, + ParentCode = ReadingCommon.CriterionDictionary.GlobalAssess, + IsBaseLineUse=x.IsBaseLineUse, + IsFollowVisitUse=x.IsFollowVisitUse, + })); + + await _readingQuestionCriterionSystemRepository.UpdatePartialFromQueryAsync(inDto.SystemCriterionId, x => new ReadingQuestionCriterionSystem() + { + IsMustGlobalReading = inDto.IsMustGlobalReading + + }); + + var result = await _readingQuestionCriterionTrialRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(result); + } + + /// + /// 获取系统全局阅片信息 + /// + /// + /// + [HttpPost] + public async Task GetSystemGlobalInfo(GetSystemOncologyInfoInDto inDto) + { + GetSystemGlobalInfoOutDto result = new GetSystemGlobalInfoOutDto() + { + IsMustGlobalReading = await _readingQuestionCriterionSystemRepository.Where(x => x.Id == inDto.SystemCriterionId).Select(x => x.IsMustGlobalReading).FirstOrDefaultAsync(), + DictionaryList = await _readingCriterionDictionaryRepository.AsQueryable().Where(x => x.CriterionId == inDto.SystemCriterionId && x.ParentCode == ReadingCommon.CriterionDictionary.GlobalAssess) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(x => x.ShowOrder).ToListAsync() + + }; + + return result; + } + + /// + /// 获取系统肿瘤信息 + /// + /// + /// + [HttpPost] + public async Task GetSystemOncologyInfo(GetSystemOncologyInfoInDto inDto) + { + + GetSystemOncologyInfoOutDto result = new GetSystemOncologyInfoOutDto() { + IsOncologyReading = await _readingQuestionCriterionSystemRepository.Where(x => x.Id == inDto.SystemCriterionId).Select(x => x.IsOncologyReading).FirstOrDefaultAsync(), + DictionaryList = await _readingCriterionDictionaryRepository.AsQueryable().Where(x => x.CriterionId == inDto.SystemCriterionId && x.ParentCode == ReadingCommon.CriterionDictionary.OncologyAssess) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(x => x.ShowOrder).ToListAsync() + + }; + + return result; + } + + /// + /// 设置系统肿瘤学阅片信息 + /// + /// + /// + [HttpPost] + [UnitOfWork] + public async Task SetSystemOncologyInfo(SetSystemOncologyInfoInDto inDto) + { + + await _readingCriterionDictionaryRepository.BatchDeleteNoTrackingAsync(x => x.CriterionId == inDto.SystemCriterionId && x.ParentCode == ReadingCommon.CriterionDictionary.OncologyAssess); + + await _readingCriterionDictionaryRepository.AddRangeAsync(inDto.DictionaryIds.Select(x => new ReadingCriterionDictionary + { + CriterionId = inDto.SystemCriterionId, + DictionaryId = x, + IsSystemCriterion = true, + ParentCode = ReadingCommon.CriterionDictionary.OncologyAssess + })); + + await _readingQuestionCriterionSystemRepository.UpdatePartialFromQueryAsync(inDto.SystemCriterionId, x => new ReadingQuestionCriterionSystem() + { + IsOncologyReading = inDto.IsOncologyReading + + }); + + var result = await _readingQuestionCriterionTrialRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(result); + } + + + #region 系统标准 + /// + /// 获取获取系统阅片标准下拉 + /// + /// + [HttpPost] + public async Task> GetSystemCriterionList() + { + List result = await _readingQuestionCriterionSystemRepository.Select(x => new GetSystemCriterionListOutDto() + { + + CriterionId = x.Id, + CriterionName = x.CriterionName, + + }).ToListAsync(); + + return result; + } + + /// + /// 获取系统问题标准 + /// + /// + [HttpPost] + public async Task> GetReadingQuestionCriterionSystemList(ReadingQuestionCriterionSystemViewInDto inDto) + { + //await AddSystemQuestionCriterion(); + var query = _readingQuestionCriterionSystemRepository.AsQueryable() + .WhereIf(!inDto.CriterionName.IsNullOrEmpty(), x => x.CriterionName.Contains(inDto.CriterionName)) + .ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToPagedListAsync(inDto.PageIndex, inDto.PageSize, inDto.SortField.IsNullOrEmpty() ? nameof(ReadingQuestionCriterionSystemView.ShowOrder) : inDto.SortField, + inDto.Asc); + } + + /// + /// 获取系统问题标准下拉 + /// + /// + [HttpPost] + public async Task> GetSystemCriterionSelectList() + { + var criterionList = await _readingQuestionCriterionSystemRepository.AsQueryable() + .OrderBy(x => x.ShowOrder) + .Select(x => new GetSystemCriterionSelectDto() + { + Id = x.Id, + CriterionName = x.CriterionName, + + }).ToListAsync(); + return criterionList; + } + + /// + /// 新增修改系统问题标准 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateReadingQuestionCriterionSystem(AddOrUpdateReadingQuestionCriterionSystemInDto indto) + { + var entity = await _readingQuestionCriterionSystemRepository.InsertOrUpdateAsync(indto, true); + + if (indto.Id != null) + { + await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(x => x.ReadingQuestionCriterionSystemId == indto.Id, x => new ReadingQuestionCriterionTrial() + { + CriterionName = indto.CriterionName, + CriterionType = indto.CriterionType, + + }); + } + return ResponseOutput.Ok(entity.Id.ToString()); + } + + /// + /// 删除系统问题标准 + /// + /// + /// + [HttpDelete("{id:guid}")] + public async Task DeleteReadingQuestionCriterionSystem(Guid id) + { + + + if (await _readingQuestionCriterionTrialRepository.AnyAsync(x => x.IsConfirm && x.ReadingQuestionCriterionSystemId == id)) + { + throw new BusinessValidationFailedException("当前标准被引用过了,不可以删除"); + } + + await _readingQuestionCriterionSystemRepository.DeleteFromQueryAsync(t => t.Id == id); + var success = await _readingQuestionCriterionSystemRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + + + + /// + /// 设置系统问题标准是否完成配置 + /// + /// + /// + public async Task SetSystemReadingQuestionCriterionIsCompleteConfig(SetSystemReadingQuestionCriterionIsIsCompleteConfig inDto) + { + + if (!inDto.IsCompleteConfig) + { + var trialCriterionIds = await _readingQuestionCriterionTrialRepository.Where(x => x.ReadingQuestionCriterionSystemId == inDto.Id).Select(x => x.Id).ToListAsync(); + if (await _readingTaskQuestionAnswer.AnyAsync(x => trialCriterionIds.Contains(x.ReadingQuestionCriterionTrialId))) + { + return ResponseOutput.NotOk("此标准在项目里面已被使用,操作失败"); + } + } + + var systemCriterion = await _readingQuestionCriterionSystemRepository.Where(x => x.Id == inDto.Id).AsNoTracking().FirstOrDefaultAsync(); + + var confirmTime = systemCriterion.ConfirmTime; + + if (inDto.IsCompleteConfig) + { + confirmTime = DateTime.Now; + } + + await _readingQuestionCriterionSystemRepository.UpdatePartialFromQueryAsync(inDto.Id, x => new ReadingQuestionCriterionSystem() + { + IsCompleteConfig = inDto.IsCompleteConfig, + ConfirmTime = confirmTime, + }); + + if (inDto.IsCompleteConfig) + { + //await SynchronizeSystemCriterion(inDto.Id); + } + else + { + await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(x => x.ReadingQuestionCriterionSystemId == inDto.Id, x => new ReadingQuestionCriterionTrial() + { + IsCompleteConfig = inDto.IsCompleteConfig + }); + } + var result = await _readingQuestionCriterionSystemRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(result); + } + + + #endregion + + #region 项目标准 + + /// + /// 获取项目裁判信息 + /// + /// + /// + [HttpPost] + public async Task GetTrialJudgyInfo(GetTrialJudgyInfoInDto inDto) + { + GetTrialJudgyInfoOutDto result = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).Select(x => new GetTrialJudgyInfoOutDto + { + TrialId = x.TrialId, + IsReadingTaskViewInOrder = x.IsReadingTaskViewInOrder, + ArbitrationRule = x.ArbitrationRule, + IsArbitrationReading = x.IsArbitrationReading + + }).FirstNotNullAsync(); + + return result; + } + + /// + /// 新增修改项目问题标准(项目) + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateReadingQuestionCriterionTrial(AddOrUpdateReadingQuestionCriterionTrialInDto indto) + { + + var entity = await _readingQuestionCriterionTrialRepository.InsertOrUpdateAsync(indto, true); + return ResponseOutput.Ok(entity.Id.ToString()); + } + + + + /// + /// 设置项目标准是否完成配置 + /// + /// + /// + public async Task SetTrialReadingQuestionCriterionIsIsCompleteConfig(SetSystemReadingQuestionCriterionIsIsCompleteConfig inDto) + { + await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(inDto.Id, x => new ReadingQuestionCriterionTrial() + { + IsCompleteConfig = inDto.IsCompleteConfig + }); + + var result = await _readingQuestionCriterionTrialRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(result); + } + + /// + /// 获取项目问题标准(项目) + /// + /// + [HttpPost] + public async Task> GetReadingQuestionCriterionTrialList(ReadingQuestionCriterionTrialViewInDto inDto) + { + await AddSystemDataToTrila(inDto.TrialId); + var query = _readingQuestionCriterionTrialRepository.AsQueryable() + .Where(x => x.TrialId == inDto.TrialId) + .Where(x => (x.ReadingQuestionCriterionSystemId != null && x.IsEnable) || x.ReadingQuestionCriterionSystemId == null) + .WhereIf(!inDto.CriterionName.IsNullOrEmpty(), x => x.CriterionName.Contains(inDto.CriterionName)) + + .ProjectTo(_mapper.ConfigurationProvider); + return await query.ToPagedListAsync(inDto.PageIndex, inDto.PageSize, inDto.SortField.IsNullOrEmpty() ? nameof(ReadingQuestionCriterionTrialView.ShowOrder) : inDto.SortField, + inDto.Asc); + } + + /// + /// 删除项目问题标准(项目) + /// + /// + /// + [HttpDelete("{id:guid}")] + public async Task DeleteReadingQuestionCriterionTrial(Guid id) + { + await _readingQuestionCriterionTrialRepository.DeleteFromQueryAsync(t => t.Id == id); + var success = await _readingQuestionCriterionTrialRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + + /// + /// 设置项目裁判信息 + /// + /// + /// + [HttpPost] + public async Task SetTrialJudgyInfo(SetTrialJudgyInfoInDto inDto) + { + + var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.TrialId == inDto.TrialId && x.Id == inDto.TrialReadingCriterionId).FirstOrDefaultAsync(); + + var judgeCount = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == trialCriterion.Id && x.IsJudgeQuestion) + .WhereIf(trialCriterion.FormType == FormType.SinglePage, x => x.ReadingCriterionPageId == null) + .WhereIf(trialCriterion.FormType == FormType.MultiplePage, x => x.ReadingCriterionPageId != null).CountAsync(); + + if (judgeCount == 0 && (inDto.ArbitrationRule == ArbitrationRule.Visit || inDto.ArbitrationRule == ArbitrationRule.Reading)) + { + throw new BusinessValidationFailedException("无裁判问题却有仲裁对象,操作失败"); + } + await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() + { + ArbitrationRule = inDto.ArbitrationRule, + //IsArbitrationReading = inDto.IsArbitrationReading, + }); + + var result = await _readingQuestionCriterionTrialRepository.SaveChangesAsync(); + return ResponseOutput.Ok(result); + } + + + #endregion + + #region 项目阅片标准问题分页 + + /// + /// 新增修改项目标准分页 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateReadingCriterionPage(ReadingCriterionPageAddOrEdit addOrEditReadingCriterionPage) + { + + var entity = await _readingCriterionPageRepository.InsertOrUpdateAsync(addOrEditReadingCriterionPage, true); + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + /// + /// 删除标准分页 + /// + /// + /// + [HttpDelete("{Id:guid}")] + public async Task DeleteReadingCriterionPage(Guid Id) + { + var success = await _readingCriterionPageRepository.DeleteFromQueryAsync(t => t.Id == Id, true); + return ResponseOutput.Ok(); + } + + + #endregion + + + + #region 全局评估类型 肿瘤学评估类型 + + /// + /// 修改是否是随访使用 + /// + /// + /// + [HttpPost] + public async Task SetDictionaryFollowVisitUse(SetDictionaryFollowVisitUseInDto inDto) + { + await _readingCriterionDictionaryRepository.UpdatePartialFromQueryAsync(inDto.Id, x => new ReadingCriterionDictionary() + { + IsFollowVisitUse = inDto.IsFollowVisitUse + }); + + await _readingCriterionDictionaryRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(true); + } + + + /// + /// 修改是否是基线使用 + /// + /// + /// + [HttpPost] + public async Task SetDictionaryBaseLineUse(SetDictionaryBaseLineUseInDto inDto) + { + await _readingCriterionDictionaryRepository.UpdatePartialFromQueryAsync(inDto.Id, x => new ReadingCriterionDictionary() + { + IsBaseLineUse = inDto.IsBaseLineUse + }); + + await _readingCriterionDictionaryRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(true); + } + + /// + /// 获取标准字典 + /// + /// + /// + [HttpPost] + public async Task> GetAssessType(GetAssessTypeInDto inDto) + { + List result = await _readingCriterionDictionaryRepository.Where(x => x.CriterionId == inDto.CriterionId + ) + .WhereIf(!inDto.ParentCode.IsNullOrEmpty(), x => x.ParentCode ==inDto.ParentCode) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(x => x.ParentCode).ThenBy(x => x.ShowOrder).ToListAsync(); + return result; + } + + /// + /// 设置标准字典 + /// + /// + /// + [HttpPost] + public async Task SetCriterionDictionary(SetCriterionDictionaryInDto inDto) + { + await _readingCriterionDictionaryRepository.BatchDeleteNoTrackingAsync(x => x.CriterionId == inDto.CriterionId && x.ParentCode == inDto.ParentCode); + + + await _readingCriterionDictionaryRepository.AddRangeAsync(inDto.DictionaryIds.Select(x => new ReadingCriterionDictionary() + { + CriterionId = inDto.CriterionId, + DictionaryId = x, + IsSystemCriterion = true, + ParentCode = inDto.ParentCode + })); + + + await _readingCriterionDictionaryRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(true); + } + + /// + /// 设置评估类型 + /// + /// + /// + [HttpPost] + public async Task SetAssessType(SetAssessTypeInDto inDto) + { + await _readingCriterionDictionaryRepository.BatchDeleteNoTrackingAsync(x => x.CriterionId == inDto.CriterionId && x.ParentCode == inDto.ParentCode); + + await _readingCriterionDictionaryRepository.AddRangeAsync(inDto.DictionaryList.Select(x => new ReadingCriterionDictionary() + { + CriterionId = inDto.CriterionId, + DictionaryId = x.DictionaryId, + IsBaseLineUse = x.IsBaseLineUse, + IsFollowVisitUse = x.IsFollowVisitUse, + IsSystemCriterion = true, + ParentCode = inDto.ParentCode + })); + + await _readingCriterionDictionaryRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(true); + } + + #endregion + + /// + /// 添加系统数据到项目里面 + /// + /// + [NonDynamicMethod] + private async Task AddSystemDataToTrila(Guid trialId) + { + var trialUsrSystemIds = _readingQuestionCriterionTrialRepository.Where(x => x.TrialId == trialId && x.ReadingQuestionCriterionSystemId != null) + .Select(x => x.ReadingQuestionCriterionSystemId); + var trialCriterionNames = _readingQuestionCriterionTrialRepository.Where(x => x.TrialId == trialId) + .Select(x => x.CriterionName); + List needAddCriterionList = await _readingQuestionCriterionSystemRepository.Where(x => !trialUsrSystemIds.Contains(x.Id) && x.IsEnable && !trialCriterionNames.Contains(x.CriterionName)).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + List needAddQuestionList = new List(); + needAddCriterionList.ForEach(x => + { + //x.IsEnable = false; + x.TrialId = trialId; + x.ReadingQuestionCriterionSystemId = x.Id; + x.Id = NewId.NextGuid(); + + // 同步问题暂时注释 + //List readingQuestionTrialList = new List(); + //SetChildParentQuestion(criterion.Id, trialId, systemQuestionList, readingQuestionTrialList); + //needAddQuestionList.AddRange(readingQuestionTrialList); + }); + await _readingQuestionCriterionTrialRepository.AddRangeAsync(needAddCriterionList); + await _readingQuestionTrialRepository.AddRangeAsync(needAddQuestionList); + await _readingQuestionTrialRepository.SaveChangesAsync(); + } + + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/ReadingQuestionService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/ReadingQuestionService.cs new file mode 100644 index 0000000..52d8c1e --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/ReadingQuestionService.cs @@ -0,0 +1,1505 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using MassTransit; +using IRaCIS.Core.Infra.EFCore.Common; +using Panda.DynamicWebApi.Attributes; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Infrastructure; +using Newtonsoft.Json; + +namespace IRaCIS.Application.Services +{ + /// + /// 阅片问题.标准 + /// + [ApiExplorerSettings(GroupName = "Reading")] + public class ReadingQuestionService : BaseService, IReadingQuestionService + { + + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _readingQuestionCriterionSystemRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _readingQuestionSystemRepository; + private readonly IRepository _readingQuestionTrialRepository; + private readonly IRepository _clinicalDataTrialSetRepository; + private readonly IRepository _clinicalDataSystemSetRepository; + private readonly IRepository _dictionaryRepository; + private readonly IReadingImageTaskService _iReadingImageTaskService; + private readonly IRepository _readingCriterionDictionaryRepository; + private readonly IRepository _readingTableQuestionTrialRepository; + private readonly IRepository _readingCriterionPageRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _tumorAssessmentRepository; + private readonly IRepository _organInfoRepository; + private readonly IRepository _readingTableQuestionSystemRepository; + private readonly IRepository _readingTaskQuestionAnswer; + private readonly IRepository _previousPDFRepository; + + public ReadingQuestionService( + IRepository subjectVisitRepository, + IRepository readingQuestionCriterionSystemRepository, + IRepository readingQuestionCriterionTrialRepository, + IRepository readingQuestionSystemRepository, + IRepository readingQuestionTrialRepository, + IRepository ClinicalDataTrialSetRepository, + IRepository ClinicalDataSystemSetRepository, + IRepository dictionaryRepository, + IReadingImageTaskService iReadingImageTaskService, + IRepository readingCriterionDictionaryRepository, + IRepository readingTableQuestionTrialRepository, + IRepository readingCriterionPageRepository, + IRepository trialRepository, + IRepository tumorAssessmentRepository, + IRepository organInfoRepository, + IRepository readingTableQuestionSystemRepository, + IRepository readingTaskQuestionAnswer, + IRepository previousPDFRepository + ) + { + this._subjectVisitRepository = subjectVisitRepository; + this._readingQuestionCriterionSystemRepository = readingQuestionCriterionSystemRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this._readingQuestionSystemRepository = readingQuestionSystemRepository; + this._readingQuestionTrialRepository = readingQuestionTrialRepository; + this._clinicalDataTrialSetRepository = ClinicalDataTrialSetRepository; + this._clinicalDataSystemSetRepository = ClinicalDataSystemSetRepository; + this._dictionaryRepository = dictionaryRepository; + this._iReadingImageTaskService = iReadingImageTaskService; + this._readingCriterionDictionaryRepository = readingCriterionDictionaryRepository; + this._readingTableQuestionTrialRepository = readingTableQuestionTrialRepository; + this._readingCriterionPageRepository = readingCriterionPageRepository; + this._trialRepository = trialRepository; + this._tumorAssessmentRepository = tumorAssessmentRepository; + this._organInfoRepository = organInfoRepository; + this._readingTableQuestionSystemRepository = readingTableQuestionSystemRepository; + this._readingTaskQuestionAnswer = readingTaskQuestionAnswer; + this._previousPDFRepository = previousPDFRepository; + } + + #region 获取计算问题 + + ///// + ///// 获取自定义问题预览 + ///// + ///// + //[HttpPost] + //public async Task<(List,bool)> GetCustomQuestionPreview(GetCustomQuestionPreviewInDto inDto) + //{ + // return (await _iReadingImageTaskService.GetReadingQuestion(inDto.TrialReadingCriterionId, null),true); + //} + + + /// + /// 获取自定义表格问题预览 + /// + /// + [HttpPost] + public async Task<(GetReadingTableQuestionOutDto,bool)> GetCustomTableQuestionPreview(GetCustomQuestionPreviewInDto inDto) + { + List tableAnswers = new List(); + List tableAnsweRowInfos = new List(); + return (await _iReadingImageTaskService.GetReadingTableQuestion( + + new GetReadingTableQuestionOrAnswerInDto() + { + TrialReadingCriterionId = inDto.TrialReadingCriterionId, + TableAnswers = tableAnswers, + TableAnsweRowInfos = tableAnsweRowInfos, + IsGetallQuestion = true, + IsGetPreview=true + } + + ), true); + } + + + + public async Task Test() + { + return await _readingQuestionTrialRepository.Select(x => x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us)).FirstAsync(); + } + + /// + /// 获取问题 + /// + /// + /// + [HttpPost] + public async Task> GetCalculateQuestions(GetCalculateQuestionsInDto inDto) + { + var result =await _readingQuestionTrialRepository + + .Where(x => x.ReadingQuestionCriterionTrialId == inDto.TrialCriterionId) + .WhereIf(!inDto.Type.IsNullOrEmpty(), x => x.Type == inDto.Type) + .OrderBy(x => x.ShowOrder) + .Select(x => new GetCalculateQuestionsOutDto + (){ + QuestionId = x.Id, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us) + + }).ToListAsync(); + + var tablequestion = await _readingTableQuestionTrialRepository.Where(x => x.TrialCriterionId == inDto.TrialCriterionId && x.Type == "number").Select(x => + new + { + QuestionId= x.Id, + x.ReadingQuestionId, + QuestionName=x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us) + }).ToListAsync(); + + + result.ForEach(x => + { + x.TableQuestions = tablequestion.Where(y => x.QuestionId == y.ReadingQuestionId).Select(y => new CalculateQuestion() + { + + QuestionId = y.QuestionId, + QuestionName = y.QuestionName + + }).ToList(); + }); + + return result; + } + + + /// + /// 获取表格问题 + /// + /// + /// + [HttpPost] + public async Task> GetCalculateTableQuestions(GetCalculateTableQuestionsInDto inDto) + { + + var result = await _readingTableQuestionTrialRepository + + .Where(x => x.ReadingQuestionId == inDto.QuestionId) + .WhereIf(!inDto.Type.IsNullOrEmpty(), x => x.Type == inDto.Type) + .OrderBy(x => x.ShowOrder) + .Select(x => new GetCalculateTableQuestionsOutDto + () + { + QuestionId = x.Id, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us) + + }).ToListAsync(); + + return result; + } + + + + + #endregion + + + #region 系统标准问题 + + /// + /// 获取系统问题分组 + /// + /// + /// + [HttpPost] + public async Task> GetSystemGroupNameList(GetTrialGroupNameListInDto inDto) + { + var result = await _readingQuestionSystemRepository.Where(x => x.ReadingQuestionCriterionSystemId == inDto.CriterionId && x.Type == ReadingQestionType.Group).OrderBy(t=>t.ShowOrder) + .Select(x => new GetTrialGroupNameOutDto() { + GroupId=x.Id, + GroupName=x.GroupName, + + } ).ToListAsync(); + return result; + + } + + /// + /// 获取系统问题 + /// + /// + [HttpPost] + public async Task> GetReadingQuestionSystemList(ReadingQuestionSystemViewInDto inDto) + { + var query = _readingQuestionSystemRepository.AsQueryable() + .Where(x => x.ReadingQuestionCriterionSystemId == inDto.ReadingQuestionCriterionSystemId) + .WhereIf(!inDto.QuestionName.IsNullOrEmpty(), x => x.QuestionName.Contains(inDto.QuestionName)) + .WhereIf(!inDto.Type.IsNullOrEmpty(), x => x.Type.Contains(inDto.Type)) + .ProjectTo(_mapper.ConfigurationProvider); + return await query.ToPagedListAsync(inDto.PageIndex, inDto.PageSize, nameof(ReadingQuestionSystemView.ShowOrder), + inDto.Asc); + } + + /// + /// 获取系统标准的其他问题 + /// + /// + /// + [HttpPost] + public async Task> GetSystemCriterionOtherQuestion(GetSystemCriterionOtherQuestionInDto inDto) + { + var types = new List() + { + "select","radio" + }; + var questionList = await _readingQuestionSystemRepository.Where(x => x.ReadingQuestionCriterionSystemId == inDto.ReadingQuestionCriterionSystemId) + .Where(x => types.Contains(x.Type)) + .WhereIf(inDto.Id != null, x => x.Id != inDto.Id && x.ParentId != inDto.Id) + .Select(x => new CriterionOtherQuestionOutDto() + { + QuestionId = x.Id, + DictionaryCode=x.DictionaryCode, + QuestionGenre=x.QuestionGenre, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName,_userInfo.IsEn_Us), + TypeValue = x.TypeValue, + GroupName = x.GroupName, + GroupId=x.GroupId, + }).ToListAsync(); + + return questionList; + } + + /// + /// 新增修改系统问题 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateReadingQuestionSystem(AddOrUpdateReadingQuestionSystemInDto indto) + { + if (indto.Id != null) + { + var trialIdList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionSystemId == indto.Id && x.IsJudgeQuestion && x.JudgeType != JudgeTypeEnum.None) + .Select(x => x.TrialId).ToListAsync(); + + //if (trialIdList.Count>0) + //{ + // var trialNames = await _trialRepository.Where(x => trialIdList.Contains(x.Id)).Select(x => x.ExperimentName).ToListAsync(); + // throw new BusinessValidationFailedException("当前问题在项目"+ string.Join(',', trialNames) + "设置了裁判标准了,修改失败"); + //} + } + + if (await _readingQuestionSystemRepository.AnyAsync(x => x.Id != indto.Id && x.ShowOrder == indto.ShowOrder && x.ReadingQuestionCriterionSystemId == indto.ReadingQuestionCriterionSystemId)) + { + throw new BusinessValidationFailedException("问题编号重复"); + } + var entity = await _readingQuestionSystemRepository.InsertOrUpdateAsync(indto, true); + return ResponseOutput.Ok(entity.Id.ToString()); + } + + + /// + /// 删除系统问题 + /// + /// + /// + [HttpDelete("{id:guid}")] + public async Task DeleteReadingQuestionSystem(Guid id) + { + if (await _readingQuestionSystemRepository.AnyAsync(x => x.ParentId == id)) + { + return ResponseOutput.NotOk("此问题存在子问题,请先删除子问题"); + } + await _readingQuestionSystemRepository.DeleteFromQueryAsync(t => t.Id == id); + var success = await _readingQuestionSystemRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + + + #endregion + + #region 系统标准表格问题 + + /// + /// 获取系统的表格问题 + /// + /// + /// + [HttpPost] + public async Task> GetReadingTableQuestionSystemList(ReadingTableQuestionSystemQuery inDto) + { + var readingTableQuestionSystemQueryable = _readingTableQuestionSystemRepository + .WhereIf(inDto.TableQuestionType != null, x => x.TableQuestionType == inDto.TableQuestionType!) + .Where(x => x.ReadingQuestionId == inDto.ReadingQuestionId).ProjectTo(_mapper.ConfigurationProvider); + + var result = await readingTableQuestionSystemQueryable.OrderBy(x => x.ShowOrder).ToListAsync(); + return result; + } + + /// + /// 获取系统表格其他问题 + /// + /// + /// + [HttpPost] + public async Task> GetReadingTableOtherQuestionSystem(GetReadingTableOtherQuestionSystemInDto inDto) + { + var types = new List() + { + "select","radio" + }; + var questionList = await _readingTableQuestionSystemRepository.Where(x => x.ReadingQuestionId == inDto.ReadingQuestionId) + .Where(x => types.Contains(x.Type)) + .WhereIf(inDto.Id != null, x => x.Id != inDto.Id && x.ParentId != inDto.Id) + + .Select(x => new CriterionOtherQuestionOutDto() + { + QuestionId = x.Id, + QuestionName = x.QuestionName, + TypeValue = x.TypeValue, + }).ToListAsync(); + + return questionList; + } + + + /// + /// 新增修改系统表格问题 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateReadingTableQuestionSystem(ReadingTableQuestionSystemAddOrEdit addOrEditReadingTableQuestionSystem) + { + + var entity = await _readingTableQuestionSystemRepository.InsertOrUpdateAsync(addOrEditReadingTableQuestionSystem, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + /// + /// 删除系统表格问题 + /// + /// + /// + [HttpDelete("{Id:guid}")] + public async Task DeleteReadingTableQuestionSystem(Guid Id) + { + if (await _readingTableQuestionSystemRepository.AnyAsync(x => x.ParentId == Id || x.RelevanceId == Id)) + { + return ResponseOutput.NotOk("当前问题存在子问题 删除失败"); + } + + await _readingTableQuestionSystemRepository.DeleteFromQueryAsync(t => t.Id == Id); + var success = await _readingTableQuestionSystemRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + + #endregion + + #region 项目标准问题 + + /// + /// 新增修改项目问题(项目) + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateReadingQuestionTrial(AddOrUpdateReadingQuestionTrialInDto indto) + { + if (indto.Id != null) + { + if (await _readingQuestionTrialRepository.AnyAsync(x => x.Id == indto.Id && x.IsJudgeQuestion && x.JudgeType != JudgeTypeEnum.None)) + { + throw new BusinessValidationFailedException("当前问题已经设置了裁判标准了,修改失败"); + } + } + + if (indto.ParentId == indto.RelevanceId && indto.ParentId != null && indto.ParentTriggerValue != indto.RelevanceValue) + { + throw new BusinessValidationFailedException("显示依赖父问题和必填依赖的问题为同一个,但答案互斥,操作失败"); + } + + + if (await _readingQuestionTrialRepository.AnyAsync(x => x.Id != indto.Id && x.ShowOrder == indto.ShowOrder && x.TrialId == indto.TrialId && x.ReadingQuestionCriterionTrialId == indto.ReadingQuestionCriterionTrialId && x.ReadingCriterionPageId == indto.ReadingCriterionPageId)) + { + throw new BusinessValidationFailedException("问题编号重复"); + } + indto.ParentTriggerValue = string.Join(',', indto.ParentTriggerValueList); + indto.RelevanceValue = string.Join(',', indto.RelevanceValueList); + + + if (indto.Id != null) + { + var relationList = await GetQuestionCalculateRelation(new GetQuestionCalculateRelationInDto() + { + IsGetAll=true, + TrialReadingCriterionId = indto.ReadingQuestionCriterionTrialId, + }); + + var relation = relationList.FirstOrDefault(x => x.QuestionId == indto.Id); + List calculateInfoList = new List(); + try + { + var result = JsonConvert.DeserializeObject>(indto.CalculateQuestions); + calculateInfoList = result == null ? new List() : result; + } + catch (Exception) + { + + + } + relation.CalculateQuestionList = calculateInfoList; + this.VerifyCalculateRelation(relationList, indto.Id.Value, indto.Id.Value); + } + + var entity = await _readingQuestionTrialRepository.InsertOrUpdateAsync(indto, true); + return ResponseOutput.Ok(entity.Id.ToString()); + } + + /// + /// 获取项目问题(项目) + /// + /// + [HttpPost] + public async Task> GetReadingQuestionTrialList(ReadingQuestionTrialViewInDto inDto) + { + var query = _readingQuestionTrialRepository.AsQueryable() + .Where(x => x.ReadingQuestionCriterionTrialId == inDto.ReadingQuestionCriterionTrialId) + .WhereIf(!inDto.QuestionName.IsNullOrEmpty(), x => x.QuestionName.Contains(inDto.QuestionName)||x.QuestionEnName.Contains(x.QuestionEnName)) + .WhereIf(!inDto.Type.IsNullOrEmpty(), x => x.Type.Contains(inDto.Type)) + .Where(x => x.ReadingCriterionPageId == inDto.ReadingCriterionPageId) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(x => x.ShowOrder); + return await query.ToListAsync(); + } + + /// + /// 验证计算关系 + /// + /// + /// + /// + /// + private void VerifyCalculateRelation(List relationList,Guid QuestionId,Guid originalId,int count=1) + { + // 防止有脏数据 循环验证 最多10000次 + if (count >= 10000) + { + throw new BusinessValidationFailedException("计算依赖循环了!"); + } + + var relation = relationList.Where(x=>x.CalculateQuestionList.Any(y=>y.QuestionId== QuestionId||y.TableQuestionId==QuestionId)).ToList(); + + if (relation.Select(x => x.QuestionId).ToList().Contains(originalId)) + { + throw new BusinessValidationFailedException("计算依赖循环了!"); + } + + else + { + + relation.ForEach(x => + { + + VerifyCalculateRelation(relationList, x.QuestionId, originalId,count++); + }); + } + + } + + /// + /// 获取问题计算关系 + /// + /// + /// + [HttpPost] + public async Task> GetQuestionCalculateRelation(GetQuestionCalculateRelationInDto inDto) + { + if (inDto.TrialReadingCriterionId != null) + { + return await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == inDto.TrialReadingCriterionId) + .WhereIf(!inDto.IsGetAll,x=>x.DataSource== DataSources.Automatic&&x.Type== "number") + .Select(x => new CalculateRelationDto() + { + QuestionId = x.Id, + QuestionName= x.QuestionName, + CustomCalculateMark = x.CustomCalculateMark, + CalculateQuestionList = x.CalculateQuestionList, + ValueType = x.ValueType, + Unit = x.Unit, + CustomUnit = x.CustomUnit, + + }).ToListAsync(); + } + else + { + return await _readingTableQuestionTrialRepository.Where(x => x.ReadingQuestionId == inDto.ReadingQuestionId) + .WhereIf(!inDto.IsGetAll, x => x.DataSource == DataSources.Automatic && x.Type == "number") + .Select(x => new CalculateRelationDto() + { + QuestionId = x.Id, + QuestionName = x.QuestionName, + CustomCalculateMark = x.CustomCalculateMark, + CalculateQuestionList = x.CalculateQuestionList, + ValueType = x.ValueType, + Unit = x.Unit, + CustomUnit = x.CustomUnit, + + }).ToListAsync(); + } + } + + + /// + /// 获取项目标准的其他问题(项目) + /// + /// + /// + [HttpPost] + public async Task> GetTrialCriterionOtherQuestion(GetTrialCriterionOtherQuestionInDto inDto) + { + var types = new List() + { + "select","radio" + }; + var questionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == inDto.ReadingQuestionCriterionTrialId) + .Where(x => types.Contains(x.Type)) + .WhereIf(inDto.Id != null, x => x.Id != inDto.Id && x.ParentId != inDto.Id) + .Where(x => x.ReadingCriterionPageId == inDto.ReadingCriterionPageId) + + .Select(x => new CriterionOtherQuestionOutDto() + { + QuestionId = x.Id, + DictionaryCode=x.DictionaryCode, + QuestionGenre=x.QuestionGenre, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + TypeValue = x.TypeValue, + GroupName = x.GroupName, + }).ToListAsync(); + + return questionList; + } + + /// + /// 删除项目问题(项目) + /// + /// + /// + [HttpDelete("{id:guid}")] + public async Task DeleteReadingQuestionTrial(Guid id) + { + if (await _readingQuestionTrialRepository.AnyAsync(x => x.ParentId == id)) + { + return ResponseOutput.NotOk("此问题存在子问题,请先删除子问题"); + } + await _readingQuestionTrialRepository.DeleteFromQueryAsync(t => t.Id == id); + var success = await _readingQuestionTrialRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + + /// + /// 获取项目问题分组 + /// + /// + /// + [HttpPost] + public async Task> GetTrialGroupNameList(GetTrialGroupNameListInDto inDto) + { + var result = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == inDto.CriterionId && x.Type == ReadingQestionType.Group) + .Where(x => x.ReadingCriterionPageId == inDto.ReadingCriterionPageId).OrderBy(t => t.ShowOrder) + .Select(x => new GetTrialGroupNameOutDto + { + GroupId=x.Id, + GroupName=x.GroupName, + }).ToListAsync(); + + return result; + + } + + #endregion + + #region 项目标准表格问题 + + + /// + /// 获取项目的表格问题 + /// + /// + /// + [HttpPost] + public async Task<(List,bool)> GetReadingTableQuestionTrialList(ReadingTableQuestionSystemQuery inDto) + { + + var readingTableQuestionSystemQueryable = _readingTableQuestionTrialRepository + .WhereIf(inDto.TableQuestionType != null, x => x.TableQuestionType == inDto.TableQuestionType) + .Where(x => x.ReadingQuestionId == inDto.ReadingQuestionId).ProjectTo(_mapper.ConfigurationProvider); + + var result = await readingTableQuestionSystemQueryable.OrderBy(x => x.ShowOrder).ToListAsync(); + return (result,true); + } + + /// + /// 获取项目表格其他问题 + /// + /// + /// + [HttpPost] + public async Task> GetReadingTableOtherQuestionTrial(GetReadingTableOtherQuestionSystemInDto inDto) + { + + var types = new List() + { + "select","radio" + }; + var questionList = await _readingTableQuestionTrialRepository.Where(x => x.ReadingQuestionId == inDto.ReadingQuestionId) + .Where(x => types.Contains(x.Type)) + .WhereIf(inDto.Id != null, x => x.Id != inDto.Id && x.ParentId != inDto.Id) + + .Select(x => new CriterionOtherQuestionOutDto() + { + QuestionId = x.Id, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + TypeValue = x.TypeValue, + }).ToListAsync(); + + return questionList; + } + + + + + /// + /// 新增修改想想项目表格问题 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateReadingTableQuestionTrial(ReadingTableQuestionTrialAddOrEdit indto) + { + + + if (await _readingTableQuestionTrialRepository.AnyAsync(x => x.Id != indto.Id && x.ShowOrder == indto.ShowOrder && x.ReadingQuestionId==indto.ReadingQuestionId)) + { + throw new BusinessValidationFailedException("问题编号重复"); + } + + indto.ParentTriggerValue = string.Join(',', indto.ParentTriggerValueList); + indto.RelevanceValue = string.Join(',', indto.RelevanceValueList); + + if (indto.Id != null) + { + var relationList = await GetQuestionCalculateRelation(new GetQuestionCalculateRelationInDto() + { + IsGetAll = true, + ReadingQuestionId = indto.ReadingQuestionId, + }) ; + + var relation = relationList.FirstOrDefault(x => x.QuestionId == indto.Id); + List calculateInfoList = new List(); + try + { + var result = JsonConvert.DeserializeObject>(indto.CalculateQuestions); + calculateInfoList = result == null ? new List() : result; + } + catch (Exception) + { + + + } + relation.CalculateQuestionList = calculateInfoList; + this.VerifyCalculateRelation(relationList, indto.Id.Value, indto.Id.Value); + } + + + var entity = await _readingTableQuestionTrialRepository.InsertOrUpdateAsync(indto, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + + + + + /// + /// 删除项目表格问题 + /// + /// + /// + [HttpDelete("{Id:guid}")] + public async Task DeleteReadingTableQuestionTrial(Guid Id) + { + if (await _readingTableQuestionTrialRepository.AnyAsync(x => x.ParentId == Id || x.RelevanceId == Id)) + { + return ResponseOutput.NotOk("当前问题存在子问题 删除失败"); + } + + await _readingTableQuestionTrialRepository.DeleteFromQueryAsync(t => t.Id == Id); + var success = await _readingTableQuestionTrialRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + + + #endregion + + + + + + + /// + /// 复制一个系统标准到另一系统标准 + /// + /// + /// + /// + [HttpPost] + public async Task CopySystemCriterionData(CopySystemCriterionDataInDto inDto) + { + if (inDto.IsCopyQuestion) + { + var newSystemQuestionList = await _readingQuestionSystemRepository.Where(x => x.ReadingQuestionCriterionSystemId == inDto.SourceSystemCriterionId) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + newSystemQuestionList.ForEach(x => + { + x.Id = NewId.NextGuid(); + x.ReadingQuestionCriterionSystemId = inDto.NewSystemCriterionId; + }); + var copyNewQuestionList = newSystemQuestionList.Clone(); + var needAddDatas = new List(); + foreach (var x in newSystemQuestionList) + { + var question = x.Clone(); + if (question.ParentId != null) + { + question.ParentId = copyNewQuestionList.Where(y => x.ParentId == y.OriginalId).Select(y => y.Id).FirstOrDefault(); + } + if (question.RelevanceId != null) + { + question.RelevanceId = copyNewQuestionList.Where(y => x.RelevanceId == y.OriginalId).Select(y => y.Id).FirstOrDefault(); + } + needAddDatas.Add(question); + }; + + await _readingQuestionSystemRepository.BatchDeleteNoTrackingAsync(x => x.ReadingQuestionCriterionSystemId == inDto.NewSystemCriterionId); + await _readingQuestionSystemRepository.AddRangeAsync(needAddDatas); + + #region 表格问题 + var newSystemTableQuestionList = await _readingTableQuestionSystemRepository.Where(x => x.SystemCriterionId == inDto.SourceSystemCriterionId) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var copeNewSystemTableQuestionList = newSystemTableQuestionList.Clone(); + var needAddTableDatas = new List(); + foreach (var x in newSystemTableQuestionList) + { + var tableQuestion = x.Clone(); + tableQuestion.SystemCriterionId = inDto.NewSystemCriterionId; + tableQuestion.Id = NewId.NextGuid(); + if (tableQuestion.ParentId != null) + { + tableQuestion.ParentId = copeNewSystemTableQuestionList.Where(y => x.ParentId == y.OriginalId).Select(y => y.Id).FirstOrDefault(); + } + if (tableQuestion.RelevanceId != null) + { + tableQuestion.RelevanceId = copeNewSystemTableQuestionList.Where(y => x.RelevanceId == y.OriginalId).Select(y => y.Id).FirstOrDefault(); + } + if (tableQuestion.DependParentId != null) + { + tableQuestion.DependParentId = copeNewSystemTableQuestionList.Where(y => x.DependParentId == y.OriginalId).Select(y => y.Id).FirstOrDefault(); + } + needAddTableDatas.Add(tableQuestion); + } + await _readingTableQuestionSystemRepository.BatchDeleteNoTrackingAsync(x => x.SystemCriterionId == inDto.NewSystemCriterionId); + await _readingTableQuestionSystemRepository.AddRangeAsync(needAddTableDatas); + #endregion + + } + else + { + var organData = await _organInfoRepository.Where(x => x.SystemCriterionId == inDto.SourceSystemCriterionId).ToListAsync(); + + organData.ForEach(x => + { + x.Id = NewId.NextGuid(); + x.SystemCriterionId = inDto.NewSystemCriterionId; + }); + await _organInfoRepository.BatchDeleteNoTrackingAsync(x => x.SystemCriterionId == inDto.NewSystemCriterionId); + await _organInfoRepository.AddRangeAsync(organData); + + } + + + + + await _readingTableQuestionSystemRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } + + /// + /// 同步系统标准 + /// + /// + /// + [HttpPost] + public async Task SynchronizeSystemCriterionQuestion(SynchronizeSystemCriterionInDto inDto) + { + // 先找到项目系统问题Id和项目问题Id的对应关系 + var questionRelation = _readingQuestionSystemRepository.Where(x => x.ReadingQuestionCriterionSystemId == inDto.FromSystemCriterionId).ToDictionary( + x => x.Id, + x => NewId.NextGuid() + ); + + + var newQuestionList = await _readingQuestionSystemRepository.Where(x => x.ReadingQuestionCriterionSystemId == inDto.FromSystemCriterionId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var copyNewQuestionList = newQuestionList.Clone(); + + var needAddDatas = new List(); + + foreach (var x in newQuestionList) + { + var question = x.Clone(); + question.ReadingQuestionCriterionSystemId = inDto.ToSystemCriterionId; + question.Id = questionRelation[question.Id.Value]; + if (question.ParentId != null) + { + question.ParentId = questionRelation[question.ParentId ?? default(Guid)]; + } + if (question.GroupId != null) + { + question.GroupId = questionRelation[question.GroupId ?? default(Guid)]; + } + if (question.RelevanceId != null) + { + question.RelevanceId = questionRelation[question.RelevanceId ?? default(Guid)]; + } + needAddDatas.Add(_mapper.Map(question)); + }; + + await _readingQuestionSystemRepository.BatchDeleteNoTrackingAsync(x => x.ReadingQuestionCriterionSystemId == inDto.ToSystemCriterionId); + await _readingQuestionSystemRepository.AddRangeAsync(needAddDatas); + + + var tableQuestionRelation = _readingTableQuestionSystemRepository.Where(x => x.SystemCriterionId == inDto.FromSystemCriterionId).ToDictionary( + x => x.Id, + x => NewId.NextGuid() + ); + + + var newtableQuestion =await _readingTableQuestionSystemRepository.Where(x => x.SystemCriterionId == inDto.FromSystemCriterionId).ToListAsync(); + + + var copyNewTableQuestionList = newtableQuestion.Clone(); + + var needAddTableDatas = new List(); + foreach (var x in newtableQuestion) + { + var tableQuestion = x.Clone(); + + + tableQuestion.SystemCriterionId = inDto.ToSystemCriterionId; + tableQuestion.Id = tableQuestionRelation[tableQuestion.Id]; + tableQuestion.ReadingQuestionId = questionRelation[tableQuestion.ReadingQuestionId]; + if (tableQuestion.ParentId != null) + { + tableQuestion.ParentId = tableQuestionRelation[tableQuestion.ParentId.Value]; + } + if (tableQuestion.RelevanceId != null) + { + tableQuestion.RelevanceId = tableQuestionRelation[tableQuestion.RelevanceId.Value]; ; + } + + if (tableQuestion.DependParentId != null) + { + tableQuestion.DependParentId = tableQuestionRelation[tableQuestion.DependParentId.Value]; ; + } + + needAddTableDatas.Add(tableQuestion); + } + + + await _readingTableQuestionSystemRepository.BatchDeleteNoTrackingAsync(x => x.SystemCriterionId == inDto.ToSystemCriterionId); + await _readingTableQuestionSystemRepository.AddRangeAsync(needAddTableDatas); + await _readingTableQuestionSystemRepository.SaveChangesAsync(); + + } + + /// + /// 同步标准到项目新(2022-08-10) + /// + /// + /// + public async Task SynchronizeCriterion(SynchronizeCriterionInDto inDto) + { + var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id==inDto.TrialReadingCriterionId).AsNoTracking().FirstOrDefaultAsync(); + + if (trialCriterion != null) + { + if (trialCriterion.ReadingQuestionCriterionSystemId != null) + { + // 先找到项目系统问题Id和项目问题Id的对应关系 + var questionRelation = _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == trialCriterion.Id).ToDictionary( + x => x.ReadingQuestionSystemId??default(Guid), + x => x.Id + ); + + // 将系统里面的问题转为项目问题 + var newTrialQuestionList = await _readingQuestionSystemRepository.Where(x => x.ReadingQuestionCriterionSystemId == trialCriterion.ReadingQuestionCriterionSystemId) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + newTrialQuestionList.ForEach(x => + { + if (questionRelation.ContainsKey(x.Id)) + { + x.Id = questionRelation[x.Id]; + } + else + { + var newid= NewId.NextGuid(); + questionRelation.Add(x.Id, newid); + x.Id = newid; + } + + x.ReadingQuestionCriterionTrialId = trialCriterion.Id; + x.TrialId = trialCriterion.TrialId; + }); + var copyNewQuestionList = newTrialQuestionList.Clone(); + var trialQuestionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == trialCriterion.Id).ToListAsync(); + var needAddDatas = new List(); + + foreach (var x in newTrialQuestionList) + { + var question = x.Clone(); + if (question.ParentId != null) + { + question.ParentId = questionRelation[question.ParentId??default(Guid)]; + } + if (question.GroupId != null) + { + question.GroupId = questionRelation[question.GroupId ?? default(Guid)]; + } + if (question.RelevanceId != null) + { + question.RelevanceId = questionRelation[question.RelevanceId ?? default(Guid)]; + } + + + + + needAddDatas.Add(question); + }; + + await _readingQuestionTrialRepository.BatchDeleteNoTrackingAsync(x => x.ReadingQuestionCriterionTrialId == trialCriterion.Id); + await _readingQuestionTrialRepository.AddRangeAsync(needAddDatas); + var systemCriterion = await _readingQuestionCriterionSystemRepository.Where(x => x.Id == trialCriterion.ReadingQuestionCriterionSystemId).FirstNotNullAsync(); + await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(x => x.Id == trialCriterion.Id, x => new ReadingQuestionCriterionTrial() + { + SynchronizeTime = DateTime.Now, + IsMustGlobalReading = systemCriterion.IsMustGlobalReading, + IseCRFShowInDicomReading=systemCriterion.IseCRFShowInDicomReading, + IsGlobalReading = systemCriterion.IsMustGlobalReading ? true : trialCriterion.IsGlobalReading, + IsReadingPeriod = systemCriterion.IsMustGlobalReading ? true : trialCriterion.IsReadingPeriod, + }) ; + + + #region 表格问题 + + // 先找到项目系统问题Id和项目问题Id的对应关系 + + var questionIds = needAddDatas.Select(x => x.Id).ToList(); + + + var tableQuestionRelation = _readingTableQuestionTrialRepository.Where(x => x.TrialCriterionId== trialCriterion.Id&&x.SystemTableQuestionId!=null).ToDictionary( + x => x.SystemTableQuestionId ?? default(Guid), + x => x.Id + ); + + + var newTrialTableQuestionList = await _readingTableQuestionSystemRepository.Where(x => x.SystemCriterionId == trialCriterion.ReadingQuestionCriterionSystemId) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + newTrialTableQuestionList.ForEach(x => + { + if (tableQuestionRelation.ContainsKey(x.Id)) + { + x.Id = tableQuestionRelation[x.Id]; + } + else + { + var newid = NewId.NextGuid(); + tableQuestionRelation.Add(x.Id, newid); + x.Id = newid; + } + }); + + var copyNewTrialTableQuestionList = newTrialTableQuestionList.Clone(); + + var needAddTableDatas = new List(); + foreach (var x in newTrialTableQuestionList) + { + var tableQuestion = x.Clone(); + tableQuestion.TrialId = trialCriterion.TrialId; + tableQuestion.TrialCriterionId = trialCriterion.Id; + + tableQuestion.ReadingQuestionId = copyNewQuestionList.Where(y => y.ReadingQuestionSystemId == x.ReadingQuestionId).Select(y => y.Id).FirstOrDefault(); + if (tableQuestion.ParentId != null) + { + tableQuestion.ParentId = copyNewTrialTableQuestionList.Where(y => x.ParentId == y.OriginalId).Select(y => y.Id).FirstOrDefault(); + } + if (tableQuestion.RelevanceId != null) + { + tableQuestion.RelevanceId = copyNewTrialTableQuestionList.Where(y => x.RelevanceId == y.OriginalId).Select(y => y.Id).FirstOrDefault(); + } + + if (tableQuestion.DependParentId != null) + { + tableQuestion.DependParentId = copyNewTrialTableQuestionList.Where(y => x.DependParentId == y.OriginalId).Select(y => y.Id).FirstOrDefault(); + } + + needAddTableDatas.Add(tableQuestion); + } + + + await _readingTableQuestionTrialRepository.BatchDeleteNoTrackingAsync(x => x.TrialCriterionId == trialCriterion.Id); + await _readingTableQuestionTrialRepository.AddRangeAsync(needAddTableDatas); + #endregion + + } + } + + } + + + ///// + ///// 更改项目标准(老) + ///// + ///// + ///// + //private async Task UpdateTrialCriterion(Guid systemCriterionId) + //{ + // await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(x => x.ReadingQuestionCriterionSystemId == systemCriterionId, x => new ReadingQuestionCriterionTrial() + // { + // IsCompleteConfig = true, + // }); + // var systemCriterionQuestionList = await _readingQuestionSystemRepository.Where(x => x.ReadingQuestionCriterionSystemId == systemCriterionId).ToListAsync(); + // var trialCriterionList = await _readingQuestionCriterionTrialRepository.Where(x => x.ReadingQuestionCriterionSystemId == systemCriterionId).Select(x => new + // { + // x.Id, + // x.TrialId, + // }).ToListAsync(); + // var trialCriterionIdList = trialCriterionList.Select(x => x.Id).ToList(); + // List needAddQuestionList = new List(); + // foreach (var item in trialCriterionList) + // { + // await _readingQuestionTrialRepository.BatchDeleteNoTrackingAsync(x => trialCriterionIdList.Contains(x.ReadingQuestionCriterionTrialId)); + // List trialQuestionList = new List(); + // SetChildParentQuestion(item.Id, item.TrialId, systemCriterionQuestionList, trialQuestionList); + // needAddQuestionList.AddRange(trialQuestionList); + // } + // await _readingQuestionTrialRepository.AddRangeAsync(needAddQuestionList); + //} + + + ///// + ///// 更改项目标准(新) + ///// + ///// + ///// + //private async Task SynchronizeSystemCriterion(Guid systemCriterionId) + //{ + // var systemCriterion = await _readingQuestionCriterionSystemRepository.FirstOrDefaultAsync(x => x.Id == systemCriterionId); + + // var trialCriterionList = await _readingQuestionCriterionTrialRepository.Where(x => x.ReadingQuestionCriterionSystemId == systemCriterionId).Select(x => new + // { + // x.Id, + // x.TrialId, + // }).ToListAsync(); + + // var trialCriterionIds = trialCriterionList.Select(x => x.Id).ToList(); + // var systemQuestionList = await _readingQuestionSystemRepository.Where(x => x.ReadingQuestionCriterionSystemId == systemCriterionId).ToListAsync(); + // var trialQuestions = await _readingQuestionTrialRepository.Where(x => trialCriterionIds.Contains(x.ReadingQuestionCriterionTrialId)).Select(x => new TrialQuestion + // { + // AnswerCombination = x.AnswerCombination, + // AnswerGroup = x.AnswerGroup, + // Id = x.Id, + // JudgeType = x.JudgeType, + // ReadingCriterionPageId = x.ReadingCriterionPageId, + // RelevanceId = x.RelevanceId, + // RelevanceValue = x.RelevanceValue, + // ImageCount = x.ImageCount, + // ParentId = x.ParentId, + // ReadingQuestionCriterionTrialId = x.ReadingQuestionCriterionTrialId, + // ReadingQuestionSystemId = x.ReadingQuestionSystemId, + // SystemParentId = x.SystemParentId + // }).ToListAsync(); + + // List trialQuestionList = new List(); + // foreach (var item in trialCriterionList) + // { + + // var thisTrialQuestions = trialQuestions.Where(x => x.ReadingQuestionCriterionTrialId == item.Id).ToList(); + + // var query = systemQuestionList.GroupJoin(thisTrialQuestions, a => a.Id, b => b.ReadingQuestionSystemId, (a, b) => new + // { + // a, + // trialQuestion = b + // }).SelectMany(a => a.trialQuestion, (m, n) => new ReadingQuestionTrial + // { + // Id = n.Id, + + // }); + // var needAddQuestionList = systemQuestionList.GroupJoin(thisTrialQuestions, a => a.Id, b => b.ReadingQuestionSystemId, (x, y) => new { system = x, trial = y }) + // .SelectMany( + // a => a.trial.DefaultIfEmpty(), + // (c, d) => new ReadingQuestionTrial + // { + // Id = (c.trial.FirstOrDefault()?.Id) ?? NewId.NextGuid(), + // ShowOrder = c.system.ShowOrder, + // SystemParentId = c.system.ParentId, + // ReadingQuestionSystemId = c.system.Id, + // AnswerCombination = (c.trial.FirstOrDefault()?.AnswerCombination) ?? string.Empty, + // AnswerGroup = (c.trial.FirstOrDefault()?.AnswerGroup) ?? string.Empty, + // GroupName = c.system.GroupName, + // IsEnable = c.system.IsEnable, + + // ShowQuestion = c.system.ShowQuestion, + // IsJudgeQuestion = c.system.IsJudgeQuestion, + // IsRequired = c.system.IsRequired, + // JudgeType = (c.trial.FirstOrDefault()?.JudgeType) ?? JudgeTypeEnum.None, + // ParentId = c.trial.FirstOrDefault()?.ParentId, + // ParentTriggerValue = c.system.ParentTriggerValue, + // QuestionName = c.system.QuestionName, + // ReadingCriterionPageId = c.trial.FirstOrDefault()?.ReadingCriterionPageId, + // RelevanceId = c.trial.FirstOrDefault()?.RelevanceId, + // ImageCount = c.trial.FirstOrDefault()?.ImageCount ?? 0, + // RelevanceValue = c.trial.FirstOrDefault()?.RelevanceValue, + // ReadingQuestionCriterionTrialId = item.Id, + // Remark = c.system.Remark, + // TrialId = item.TrialId, + // Type = c.system.Type, + + // TypeValue = c.system.TypeValue, + // }).ToList(); + // var copydata = needAddQuestionList.Clone(); + // needAddQuestionList.ForEach(x => + // { + // if (x.SystemParentId == null) + // { + // x.ParentId = null; + // } + // else + // { + // x.ParentId = copydata.FirstOrDefault(y => y.ReadingQuestionSystemId == x.SystemParentId).Id; + // } + // }); + + // trialQuestionList.AddRange(needAddQuestionList); + // } + // await _readingQuestionTrialRepository.BatchDeleteNoTrackingAsync(x => trialCriterionIds.Contains(x.ReadingQuestionCriterionTrialId)); + // await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(x => trialCriterionIds.Contains(x.Id), x => new ReadingQuestionCriterionTrial() + // { + // IsEnable = systemCriterion.IsEnable, + // CriterionName = systemCriterion.CriterionName, + // ShowOrder = systemCriterion.ShowOrder, + // IsCompleteConfig = systemCriterion.IsCompleteConfig, + + // }); + + // await _readingQuestionTrialRepository.AddRangeAsync(trialQuestionList); + + + // // + // await _readingQuestionTrialRepository.SaveChangesAsync(); + //} + + + /// + /// 验证是否要同步标准 + /// + /// + /// + [HttpPost] + public async Task VerifyeCriterionNeedSynchronize(VerifyeCriterionNeedSynchronizeInDto inDto) + { + var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).FirstOrDefaultAsync(); + if (trialCriterion == null) + { + return NeedSynchronize.NotNeed; + } + if (trialCriterion.ReadingQuestionCriterionSystemId != null) + { + var systemCriterion = await _readingQuestionCriterionSystemRepository.Where(x => x.Id == trialCriterion.ReadingQuestionCriterionSystemId).FirstNotNullAsync(); + if (systemCriterion.ConfirmTime > trialCriterion.SynchronizeTime) + { + + var systemQuestionList = await _readingQuestionSystemRepository.Where(x => x.IsJudgeQuestion && x.ReadingQuestionCriterionSystemId == trialCriterion.ReadingQuestionCriterionSystemId).ToListAsync(); + var trialQuestionList = await _readingQuestionTrialRepository.Where(x => x.IsJudgeQuestion && x.ReadingQuestionCriterionTrialId == trialCriterion.Id).ToListAsync(); + if (systemQuestionList.Count() != trialQuestionList.Count()) + { + return NeedSynchronize.JudgeNotEqual; + } + + + foreach (var item in trialQuestionList) + { + var systemQuestion = systemQuestionList.Where(x => x.Id == (item.ReadingQuestionSystemId ?? default(Guid))).FirstOrDefault(); + if (systemQuestion == null) + { + return NeedSynchronize.JudgeNotEqual; + } + + + if (systemQuestion.TypeValue != item.TypeValue) + { + return NeedSynchronize.JudgeNotEqual; + } + } + + + return NeedSynchronize.Need; + + } + else + { + return NeedSynchronize.NotNeed; + } + + } + else + { + return NeedSynchronize.NotNeed; + } + } + + + /// + /// 添加系统数据到项目里面 + /// + /// + [NonDynamicMethod] + private async Task AddSystemDataToTrila(Guid trialId) + { + var trialUsrSystemIds = _readingQuestionCriterionTrialRepository.Where(x => x.TrialId == trialId && x.ReadingQuestionCriterionSystemId != null) + .Select(x => x.ReadingQuestionCriterionSystemId); + var trialCriterionNames = _readingQuestionCriterionTrialRepository.Where(x => x.TrialId == trialId) + .Select(x => x.CriterionName); + List needAddCriterionList = await _readingQuestionCriterionSystemRepository.Where(x => !trialUsrSystemIds.Contains(x.Id) && x.IsEnable && !trialCriterionNames.Contains(x.CriterionName)).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + List needAddQuestionList = new List(); + needAddCriterionList.ForEach(x => + { + //x.IsEnable = false; + x.TrialId = trialId; + x.ReadingQuestionCriterionSystemId = x.Id; + x.Id = NewId.NextGuid(); + // 同步问题暂时注释 + //List readingQuestionTrialList = new List(); + //SetChildParentQuestion(criterion.Id, trialId, systemQuestionList, readingQuestionTrialList); + //needAddQuestionList.AddRange(readingQuestionTrialList); + }); + await _readingQuestionCriterionTrialRepository.AddRangeAsync(needAddCriterionList); + await _readingQuestionTrialRepository.AddRangeAsync(needAddQuestionList); + await _readingQuestionTrialRepository.SaveChangesAsync(); + } + + ///// + ///// 设置父子关系 + ///// + ///// 项目标准ID + ///// 项目Id + ///// 系统问题 + ///// 需要添加list + //private void SetChildParentQuestion(Guid ReadingQuestionCriterionTrialId, Guid trialId, List systemQuesitonList, List needQuestionList) + //{ + // var parentIdIsNullList = systemQuesitonList.Where(x => x.ParentId == null).ToList(); + // parentIdIsNullList.ForEach(x => + // { + // var quesiton = x.Clone(); + // var oldId = quesiton.Id; + // var newId = NewId.NextGuid(); + // needQuestionList.Add(new ReadingQuestionTrial() + // { + // Id = newId, + // ShowOrder = quesiton.ShowOrder, + // IsEnable = quesiton.IsEnable, + // IsRequired = quesiton.IsRequired, + // ParentTriggerValue = quesiton.ParentTriggerValue, + // QuestionName = quesiton.QuestionName, + // ReadingQuestionCriterionTrialId = ReadingQuestionCriterionTrialId, + // ReadingQuestionSystemId = quesiton.Id, + // SystemParentId = quesiton.ParentId, + // TrialId = trialId, + // AnswerGroup = string.Empty, + // Type = quesiton.Type, + // GroupName = quesiton.GroupName, + // IsJudgeQuestion = quesiton.IsJudgeQuestion, + // Remark = quesiton.Remark, + // TypeValue = quesiton.TypeValue, + // }); + + // CreateQuestionRelation(ReadingQuestionCriterionTrialId, trialId, oldId, newId, systemQuesitonList, needQuestionList); + // }); + //} + + ///// + ///// 递归处理父子关系 + ///// + ///// + ///// + ///// + ///// + ///// + ///// + //private void CreateQuestionRelation(Guid ReadingQuestionCriterionTrialId, Guid trialId, Guid oldParentId, Guid newParentId, List systemQuesitonList, List needQuestionList) + //{ + // var childList = systemQuesitonList.Where(x => x.ParentId == oldParentId).ToList(); + // childList.ForEach(x => + // { + // var quesiton = x.Clone(); + // var oldId = quesiton.Id; + // var newId = NewId.NextGuid(); + // needQuestionList.Add(new ReadingQuestionTrial() + // { + // Id = newId, + // ShowOrder = quesiton.ShowOrder, + // IsEnable = quesiton.IsEnable, + // IsRequired = quesiton.IsRequired, + // ParentId = newParentId, + // SystemParentId = quesiton.ParentId, + // ReadingQuestionSystemId = x.Id, + // AnswerGroup = string.Empty, + // ParentTriggerValue = quesiton.ParentTriggerValue, + // QuestionName = quesiton.QuestionName, + // ReadingQuestionCriterionTrialId = ReadingQuestionCriterionTrialId, + // TrialId = trialId, + // GroupName = quesiton.GroupName, + // Type = quesiton.Type, + // IsJudgeQuestion = quesiton.IsJudgeQuestion, + // Remark = quesiton.Remark, + // TypeValue = quesiton.TypeValue, + // }); + + // CreateQuestionRelation(ReadingQuestionCriterionTrialId, trialId, oldId, newId, systemQuesitonList, needQuestionList); + // }); + + //} + + + #region 废弃 + + ///// + ///// 新增修改系统问题标准 + ///// + ///// + ///// + //[HttpPost] + //public async Task AddOrUpdateReadingQuestionCriterionSystem(AddOrUpdateReadingQuestionCriterionSystemInDto indto) + //{ + // var entity = await _readingQuestionCriterionSystemRepository.InsertOrUpdateAsync(indto, true); + // return ResponseOutput.Ok(entity.Id.ToString()); + //} + + + ///// + ///// 设置系统标准被禁用 + ///// + ///// + //[NonDynamicMethod] + //public async Task SetSystemCriterionDisable(Guid dictionaryId, Guid? parentId) + //{ + // // 判断是否是阅片 + // if (await _dictionaryRepository.AnyAsync(x => x.Id == parentId && x.Code == "ReadingStandard")) + // { + // // 判断当前阅片是否在项目里面存在 + // var systemCriterion = await _readingQuestionCriterionSystemRepository.FirstOrDefaultAsync(x => x.CriterionId == dictionaryId); + // if (systemCriterion != null) + // { + // var trialCriterionIds = await _readingQuestionCriterionTrialRepository.Where(x => x.ReadingQuestionCriterionSystemId == systemCriterion.Id).Select(x=>x.ReadingQuestionCriterionSystemId).ToListAsync(); + // await _readingQuestionCriterionTrialRepository.BatchDeleteNoTrackingAsync(x => x.ReadingQuestionCriterionSystemId == systemCriterion.Id); + // await _readingQuestionTrialRepository.BatchDeleteNoTrackingAsync(x => trialCriterionIds.Contains(x.ReadingQuestionCriterionTrialId)); + // return ResponseOutput.Ok(); + // } + // } + + // return ResponseOutput.Ok(); + //} + + ///// + ///// 添加系统问题标准 + ///// + ///// + //[NonDynamicMethod] + //private async Task AddSystemQuestionCriterion() + //{ + // var useSystemQuestionCriterionIds = _readingQuestionCriterionSystemRepository.Select(x => x.CriterionId); + // var dictionaryParentId =await _dictionaryRepository.Where(x => x.Code == "ReadingStandard").Select(x => x.Id).FirstOrDefaultAsync(); + // var criterionList = await _dictionaryRepository.Where(x => x.ParentId == dictionaryParentId && !useSystemQuestionCriterionIds.Contains(x.Id)) + // .Select(x => new CriterionList() + // { + // Id = x.Id, + // Value = x.Value, + // ShowOrder=x.ShowOrder, + // }).ToListAsync(); + // List needAddCriterionList = new List(); + // criterionList.ForEach(x => + // { + // needAddCriterionList.Add(new ReadingQuestionCriterionSystem() + // { + // CriterionId = x.Id, + // ShowOrder=x.ShowOrder, + // CriterionName = x.Value, + // IsEnable = false, + // }); + // }); + // await _readingQuestionCriterionSystemRepository.AddRangeAsync(needAddCriterionList); + // await _readingQuestionCriterionSystemRepository.SaveChangesAsync(); + //} + + + ///// + ///// 获取预览问题信息 + ///// + ///// + ///// + //[HttpPost] + //public async Task> GetPreviewTheQuestion(GetPreviewTheQuestionInDto inDto) + //{ + // var trialQuestionQuery = from trialQuestion in _readingQuestionTrialRepository.Where(x=>x.ReadingQuestionCriterionTrialId== inDto.Id) + // select new GetTrialReadingQuestionOutDto() + // { + // ReadingQuestionTrialId = trialQuestion.Id, + // ReadingQuestionCriterionTrialId = trialQuestion.ReadingQuestionCriterionTrialId, + // TrialId = trialQuestion.TrialId, + // Type = trialQuestion.Type, + // ParentTriggerValue = trialQuestion.ParentTriggerValue, + // GroupName = trialQuestion.GroupName, + // QuestionName = trialQuestion.QuestionName, + // IsRequired = trialQuestion.IsRequired, + // ShowOrder = trialQuestion.ShowOrder, + // ParentId = trialQuestion.ParentId, + // TypeValue = trialQuestion.TypeValue, + // Answer = string.Empty + // }; + // var qusetionList = await trialQuestionQuery.OrderBy(x => x.ShowOrder).ToListAsync(); + // List readingQuestionList = qusetionList.Where(x => x.ParentId == null).ToList(); + // readingQuestionList.ForEach(x => + // { + // _readingImageTaskService.FindChildQuestion(x, qusetionList); + // }); + + // return readingQuestionList; + //} + + ///// + ///// 设置项目标准是否生效 + ///// + ///// + ///// + //public async Task SetTrialReadingQuestionCriterionIsIsEnable(SetSystemReadingQuestionCriterionIsIsEnable inDto) + //{ + // await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(inDto.Id, x => new ReadingQuestionCriterionTrial() + // { + // IsEnable = inDto.IsEnable + // }); + + // var result = await _readingQuestionCriterionTrialRepository.SaveChangesAsync(); + + // return ResponseOutput.Ok(result); + //} + + #endregion + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/TumorAssessmentService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/TumorAssessmentService.cs new file mode 100644 index 0000000..596d025 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/TumorAssessmentService.cs @@ -0,0 +1,91 @@ +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Infra.EFCore.Common; +using MassTransit; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Service.Reading.Dto; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Core.Application.Service.TA +{ + + + [ApiExplorerSettings(GroupName = "Reading")] + public class ReadingQuestionService : BaseService + { + private readonly IRepository _tumorAssessmentRepository; + + public ReadingQuestionService( IRepository tumorAssessmentRepository ) + { + this._tumorAssessmentRepository = tumorAssessmentRepository; + } + + + /// + /// 获取疗效对照 + /// + /// + [HttpPost] + public async Task> GetTumorAssessmentList(GetTumorAssessmentListInDto inDto) + { + var result = await _tumorAssessmentRepository + .Where(x => x.CriterionId == inDto.CriterionId) + .WhereIf(inDto.OverallEfficacy != null, x => x.OverallEfficacy == inDto.OverallEfficacy) + .WhereIf(inDto.TargetLesion != null, x => x.TargetLesion == inDto.TargetLesion) + .WhereIf(inDto.NonTargetLesions != null, x => x.NonTargetLesions == inDto.NonTargetLesions) + .WhereIf(inDto.NewLesion != null, x => x.NewLesion == inDto.NewLesion) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + return result; + } + + /// + /// 获取疗效对照 + /// + /// + [HttpPost] + public async Task> GetTumorAssessmentPageList(GetTumorAssessmentListInDto inQuery) + { + var query = _tumorAssessmentRepository + .Where(x => x.CriterionId == inQuery.CriterionId) + .WhereIf(inQuery.OverallEfficacy != null, x => x.OverallEfficacy == inQuery.OverallEfficacy) + .WhereIf(inQuery.TargetLesion != null, x => x.TargetLesion == inQuery.TargetLesion) + .WhereIf(inQuery.NonTargetLesions != null, x => x.NonTargetLesions == inQuery.NonTargetLesions) + .WhereIf(inQuery.NewLesion != null, x => x.NewLesion == inQuery.NewLesion) + .ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(TumorAssessmentView.Id) : inQuery.SortField, inQuery.Asc); + } + + + /// + /// 新增修改疗效对照 + /// + /// + /// + [HttpPost] + public async Task AddOrUpdateTumorAssessment(AddOrUpdateTumorAssessmentInDto indto) + { + + var entity = await _tumorAssessmentRepository.InsertOrUpdateAsync(indto, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + /// + /// 删除疗效对照 + /// + /// + /// + [HttpDelete("{Id:guid}")] + public async Task DeleteTumorAssessment(Guid Id) + { + await _tumorAssessmentRepository.DeleteFromQueryAsync(t => t.Id == Id); + var success = await _tumorAssessmentRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingGlobalTaskService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingGlobalTaskService.cs new file mode 100644 index 0000000..492ff32 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingGlobalTaskService.cs @@ -0,0 +1,456 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using MassTransit; +using IRaCIS.Core.Infra.EFCore.Common; +using Panda.DynamicWebApi.Attributes; +using AutoMapper; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Infrastructure; +using Newtonsoft.Json; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Application.Services +{ + + /// + /// 全局阅片 + /// + public partial class ReadingImageTaskService : BaseService, IReadingImageTaskService + { + /// + /// 提交全局阅片结果 + /// + /// + /// + //[NonDynamicMethod] + public async Task SubmitGlobalReadingInfo(SubmitGlobalReadingInfoInDto inDto) + { + //var result = await this.SaveGlobalReadingInfo(inDto); + await VerifyTaskIsSign(inDto.GlobalTaskId); + await this.SubmitTaskChangeState(inDto.GlobalTaskId); + + return ResponseOutput.Ok(true); + } + + + /// + /// 批量提交全局阅片信息 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task BatchSubmitGlobalReadingInfo(BatchSubmitGlobalReadingInfo inDto) + { + await VerifyTaskIsSign(inDto.GlobalTaskId); + + var criterionType = await _visitTaskRepository.Where(x => x.Id == inDto.GlobalTaskId).Select(x => x.TrialReadingCriterion.CriterionType).FirstNotNullAsync(); + foreach (var item in inDto.VisitTaskAnswerList) + { + foreach (var answer in item.AnswerList) + { + await _readingGlobalTaskInfoRepository.BatchDeleteNoTrackingAsync(x => x.GlobalTaskId == inDto.GlobalTaskId && x.TaskId == item.VisitTaskId && x.GlobalAnswerType == answer.GlobalAnswerType && x.QuestionId == answer.QuestionId); + + + if (criterionType == CriterionType.PCWG3) + { + await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == item.VisitTaskId && x.ReadingQuestionTrialId == answer.QuestionId + && x.Answer != answer.Answer && answer.Answer != string.Empty && answer.Answer != null + , x => new ReadingTaskQuestionAnswer() + { + Answer= answer.Answer, + GlobalChangeAnswer = answer.Answer, + IsGlobalChange = true, + }); + } + else + { + await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == item.VisitTaskId && x.ReadingQuestionTrialId == answer.QuestionId + && x.Answer != answer.Answer && answer.Answer != string.Empty && answer.Answer != null + , x => new ReadingTaskQuestionAnswer() + { + GlobalChangeAnswer = answer.Answer, + IsGlobalChange = true, + }); + } + + } + } + var answers= inDto.VisitTaskAnswerList.SelectMany(x => x.AnswerList.Select(y => new ReadingGlobalTaskInfo() + { + Answer = y.Answer, + QuestionId = y.QuestionId, + SubjectId = inDto.SubjectId, + GlobalTaskId = inDto.GlobalTaskId, + GlobalAnswerType = y.GlobalAnswerType, + TaskId = x.VisitTaskId, + TrialId = inDto.TrialId, + })).ToList(); + + await _readingGlobalTaskInfoRepository.AddRangeAsync(answers); + await _visitTaskRepository.UpdatePartialFromQueryAsync(t => t.Id == inDto.GlobalTaskId, u => new VisitTask() { ReadingTaskState = ReadingTaskState.Reading }); + var result = await _readingGlobalTaskInfoRepository.SaveChangesAsync(); + return ResponseOutput.Ok(result); + } + + + #region 全局阅片相关 + /// + /// 保存全局阅片结果 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SaveGlobalReadingInfo(SaveGlobalReadingInfoInDto inDto) + { + await VerifyTaskIsSign(inDto.GlobalTaskId); + var visitTaskId = inDto.QuestionList.Select(x => x.VisitTaskId).FirstOrDefault(); + + foreach (var item in inDto.QuestionList) + { + + await _readingGlobalTaskInfoRepository.BatchDeleteNoTrackingAsync(x => x.GlobalTaskId == inDto.GlobalTaskId && x.TaskId == visitTaskId && x.GlobalAnswerType == item.GlobalAnswerType && x.QuestionId == item.QuestionId); + + await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == item.VisitTaskId && x.ReadingQuestionTrialId == item.QuestionId + && x.Answer != item.Answer && item.Answer != string.Empty && item.Answer != null + , x => new ReadingTaskQuestionAnswer() + { + GlobalChangeAnswer = item.Answer, + IsGlobalChange = true, + }); + } + + await _readingGlobalTaskInfoRepository.AddRangeAsync(inDto.QuestionList.Select(x => new ReadingGlobalTaskInfo() + { + Answer = x.Answer, + QuestionId = x.QuestionId, + SubjectId = inDto.SubjectId, + GlobalTaskId = inDto.GlobalTaskId, + GlobalAnswerType = x.GlobalAnswerType, + TaskId = x.VisitTaskId, + TrialId = inDto.TrialId, + }).ToList()); + + await _visitTaskRepository.UpdatePartialFromQueryAsync(t => t.Id == inDto.GlobalTaskId, u => new VisitTask() { ReadingTaskState = ReadingTaskState.Reading }); + + var result = await _readingGlobalTaskInfoRepository.SaveChangesAsync(); + return ResponseOutput.Ok(result); + + } + + + /// + /// 获取全局阅片信息 + /// + /// + /// + [HttpPost] + public async Task GetGlobalReadingInfo(GetGlobalReadingInfoInDto inDto) + { + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Include(x=>x.Subject).FirstNotNullAsync(); + if (taskInfo.ReadingCategory != ReadingCategory.Global) + { + throw new BusinessValidationFailedException(_localizer["ReadingGlobal_NotGlobal"]); + } + GetGlobalReadingInfoOutDto result = new GetGlobalReadingInfoOutDto() + { + GlobalTaskId = inDto.VisitTaskId, + ReadingTaskState = taskInfo.ReadingTaskState, + + }; + result.OtherGlobalTaskId = await _visitTaskRepository.Where(x => x.SouceReadModuleId == taskInfo.SouceReadModuleId && x.IsAnalysisCreate == taskInfo.IsAnalysisCreate + && x.IsSelfAnalysis == taskInfo.IsSelfAnalysis && x.TaskState == TaskState.Effect && x.DoctorUserId != taskInfo.DoctorUserId + ).Select(x => x.Id).FirstOrDefaultAsync(); + + result.TaskBlindName = taskInfo.TaskBlindName; + if (taskInfo.IsAnalysisCreate) + { + result.SubjectCode = taskInfo.BlindSubjectCode; + + } + else + { + result.SubjectCode = taskInfo.Subject.Code; + } + + var judgeInfo = await _visitTaskRepository.Where(x => + x.SubjectId == taskInfo.SubjectId + && x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId + && x.TaskState == TaskState.Effect + && x.ArmEnum == taskInfo.ArmEnum + && x.ReadingCategory == ReadingCategory.Judge + && x.IsAnalysisCreate == taskInfo.IsAnalysisCreate + && x.IsSelfAnalysis == taskInfo.IsSelfAnalysis + && x.DoctorUserId == taskInfo.DoctorUserId + && x.ReadingTaskState == ReadingTaskState.HaveSigned + && x.VisitTaskNum == taskInfo.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Judge] + && x.ReReadingApplyState != ReReadingApplyState.Agree).FirstOrDefaultAsync(); + if (judgeInfo != null) + { + result.JudgeTaskId = judgeInfo.Id; + result.JudgeTaskName = judgeInfo.TaskBlindName; + } + + + // 一致性分析按照doctorId 其他按照分组 + + + var queruTask = _visitTaskRepository.Where(x => + x.TrialId == taskInfo.TrialId && + x.SubjectId == taskInfo.SubjectId && + x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && + x.ReadingTaskState == ReadingTaskState.HaveSigned && + x.IsAnalysisCreate == taskInfo.IsAnalysisCreate && + x.ArmEnum == taskInfo.ArmEnum && + x.IsSelfAnalysis == taskInfo.IsSelfAnalysis && + x.DoctorUserId == taskInfo.DoctorUserId && + x.TaskState == TaskState.Effect && + x.VisitTaskNum < taskInfo.VisitTaskNum); + + + if (taskInfo.ReadingTaskState == ReadingTaskState.HaveSigned) + { + queruTask = _visitTaskRepository.Where(x => taskInfo.RelatedVisitTaskIdList.Contains(x.Id)); + } + + result.TaskList = await queruTask + .OrderBy(x => x.VisitTaskNum).Select(x => new GlobalVisitInfo() + { + VisitName = x.TaskName, + BlindName = x.TaskBlindName, + VisitTaskId = x.Id, + ArmEnum = taskInfo.ArmEnum, + VisitNum = x.SourceSubjectVisit.VisitNum, + IsBaseLine=x.SourceSubjectVisit.IsBaseLine, + VisitId = x.SourceSubjectVisitId.Value, + + BeforeQuestionList = x.ReadingTaskQuestionAnswerList.Where(y => y.ReadingQuestionTrial.GlobalReadingShowType!=GlobalReadingShowType.NotShow).OrderBy(y => y.ReadingQuestionTrial.ShowOrder) + .Select(y => new GlobalQuestionInfo() + { + + QuestionId = y.ReadingQuestionTrialId, + QuestionName = y.ReadingQuestionTrial.QuestionName.LanguageName(y.ReadingQuestionTrial.QuestionEnName, _userInfo.IsEn_Us), + AnswerGroup = y.ReadingQuestionTrial.AnswerGroup, + QuestionType = y.ReadingQuestionTrial.QuestionType, + LimitEdit= y.ReadingQuestionTrial.LimitEdit, + MaxAnswerLength=y.ReadingQuestionTrial.MaxAnswerLength, + FileType=y.ReadingQuestionTrial.FileType, + QuestionGenre = y.ReadingQuestionTrial.QuestionGenre, + DictionaryCode = y.ReadingQuestionTrial.DictionaryCode, + GlobalReadingShowType=y.ReadingQuestionTrial.GlobalReadingShowType, + AnswerCombination = y.ReadingQuestionTrial.AnswerCombination, + JudgeType = y.ReadingQuestionTrial.JudgeType, + Type = y.ReadingQuestionTrial.Type, + TypeValue = y.ReadingQuestionTrial.TypeValue, + ValueType = y.ReadingQuestionTrial.ValueType, + IsJudgeQuestion =y.ReadingQuestionTrial.IsJudgeQuestion, + Answer =y.Answer, + }).ToList() + }).ToListAsync(); + + + + var globalReadingQuestion = await _readingGlobalTaskInfoRepository.Where(x => x.GlobalTaskId == inDto.VisitTaskId).ToListAsync(); + var criterionType = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == taskInfo.TrialReadingCriterionId).Select(x => x.CriterionType).FirstOrDefaultAsync(); + + // Before的Answer取自于 上一次全局阅片的结果, 如果没有上一次全局阅片的结果 取检查批次的答案 + + var lastGlobalTask = await _visitTaskRepository.Where(x => x.ReadingCategory == ReadingCategory.Global && + x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && + x.SubjectId == taskInfo.SubjectId && x.IsAnalysisCreate == taskInfo.IsAnalysisCreate && x.TaskState == TaskState.Effect && x.VisitTaskNum < taskInfo.VisitTaskNum) + .Where(x => x.DoctorUserId == taskInfo.DoctorUserId) + .OrderByDescending(x=>x.VisitTaskNum) + .FirstOrDefaultAsync(); + + List? globalAnswers = new List(); + if (lastGlobalTask != null) + { + globalAnswers = await _readingGlobalTaskInfoRepository.Where(x => x.GlobalTaskId == lastGlobalTask.Id).ToListAsync(); + } + + if (criterionType != CriterionType.PCWG3) + { + result.TaskList.ForEach(x => + { + + x.BeforeQuestionList.ForEach(y => + { + + var globalAnswer = globalAnswers.Where(z => z.QuestionId == y.QuestionId&&z.TaskId==x.VisitTaskId).Select(z => z.Answer).FirstOrDefault(); + if (!globalAnswer.IsNullOrEmpty()) + { + y.Answer = globalAnswer; + y.IsGlobalAnswer = true; + } + + }); + }); + } + + result.TaskList.ForEach(x => + { + x.AfterQuestionList = x.BeforeQuestionList.Where(x=>x.IsJudgeQuestion).GroupJoin( + globalReadingQuestion + , l => new { a = l.QuestionId, b = x.VisitTaskId } + , r => new { a = r.QuestionId, b = r.TaskId } + , (l, r) => new { question = l, global = r }) + .SelectMany(lr => lr.global.DefaultIfEmpty(), (lr, r) => new GlobalQuestionInfo + { + Answer = lr.global == null || lr.global.Count() == 0 ? + (inDto.UsingOriginalData ? lr.question.Answer : string.Empty) : + (lr.global.Select(x => x.Answer).FirstOrDefault().IsNullOrEmpty() && inDto.UsingOriginalData ? + lr.question.Answer : lr.global.Select(x => x.Answer).FirstOrDefault() + ), + VisitAnswer = lr.question.Answer, + IsHaveChange = lr.global.Any(x=>x.QuestionId!=null&&!x.Answer.IsNullOrEmpty()) ? true : false, + QuestionId = lr.question.QuestionId, + QuestionName = lr.question.QuestionName, + QuestionType = lr.question.QuestionType, + LimitEdit=lr.question.LimitEdit, + MaxAnswerLength=lr.question.MaxAnswerLength, + FileType=lr.question.FileType, + QuestionGenre = lr.question.QuestionGenre, + DictionaryCode = lr.question.DictionaryCode, + GlobalReadingShowType = lr.question.GlobalReadingShowType, + Type = lr.question.Type, + GlobalAnswerType = GlobalAnswerType.Question, + AnswerGroup = lr.question.AnswerGroup, + AnswerCombination = lr.question.AnswerCombination, + IsJudgeQuestion=lr.question.IsJudgeQuestion, + JudgeType = lr.question.JudgeType, + TypeValue = lr.question.TypeValue, + ValueType= lr.question.ValueType, + + }).ToList(); + + + List questionTypes = new List() + { + }; + if (criterionType != CriterionType.PCWG3) + { + var agreeOrNotAnswer = globalReadingQuestion.Where(y => y.TaskId == x.VisitTaskId && y.GlobalAnswerType == GlobalAnswerType.AgreeOrNot).Select(x => x.Answer).FirstOrDefault() ?? string.Empty; + + if (agreeOrNotAnswer.IsNullOrEmpty()&& lastGlobalTask!=null&& lastGlobalTask.RelatedVisitTaskIdList.Contains(x.VisitTaskId)) + { + agreeOrNotAnswer = "1"; + } + + x.AgreeOrNot = new List() + { + new GlobalQuestionInfo() + { + Answer = agreeOrNotAnswer, + QuestionName = "", + Type = "input", + GlobalAnswerType=GlobalAnswerType.AgreeOrNot, + GlobalReadingShowType= GlobalReadingShowType.AllShow, + } + + }; + + questionTypes.Add(new GetGlobalQuestionType() { GlobalAnswerType = GlobalAnswerType.UpdateType, QuestionName = "评估更新类型" }); + } + else + { + x.AgreeOrNot = new List() { }; + } + + + + + + + questionTypes.Add(new GetGlobalQuestionType() { GlobalAnswerType = GlobalAnswerType.Reason, QuestionName = "全局阅片备注" }); + foreach (var item in questionTypes) + { + x.AfterQuestionList.Add(new GlobalQuestionInfo() + { + Answer = globalReadingQuestion.Where(y => y.TaskId == x.VisitTaskId && y.GlobalAnswerType == item.GlobalAnswerType).Select(x => x.Answer).FirstOrDefault() ?? string.Empty, + QuestionName = item.QuestionName, + Type = "input", + GlobalAnswerType = item.GlobalAnswerType, + GlobalReadingShowType= GlobalReadingShowType.AllShow, + + }); + } + + + + }); + + if (criterionType == CriterionType.PCWG3) + { + + + // 找到上一个全局阅片 + + + + result.TaskList.ForEach(x => + { + foreach (var item in x.AfterQuestionList.Where(x => x.QuestionType == QuestionType.SiteVisitForTumorEvaluation)) + { + var answer = item.Answer; + + var globalanswer = globalAnswers.Where(y => y.TaskId == x.VisitTaskId && y.QuestionId == item.QuestionId).FirstOrDefault(); + + if (globalanswer != null) + { + answer = globalanswer.Answer; + } + else + { + answer = x.BeforeQuestionList.Where(x => x.QuestionType == QuestionType.SiteVisitForTumorEvaluation).Select(x => x.Answer).FirstOrDefault() ?? string.Empty; + } + + if (item.Answer.IsNullOrEmpty()) + { + item.Answer = answer; + } + + item.VisitAnswer = answer; + + } + + + // 在修改前 去除这个问题 + x.BeforeQuestionList = x.BeforeQuestionList.Where(x => x.QuestionType != QuestionType.SiteVisitForTumorEvaluation).ToList(); + + }); + } + + + + var subjectVisitId = await _readModuleRepository.Where(x => x.Id == taskInfo.SouceReadModuleId).Select(x => x.SubjectVisitId).FirstOrDefaultAsync(); + var isBaseLine = await _subjectVisitRepository.Where(x => x.Id == subjectVisitId).Select(x => x.IsBaseLine).FirstOrDefaultAsync(); + List assessTypeList = await _readingCriterionDictionaryRepository.Where(x => x.CriterionId == taskInfo.TrialReadingCriterionId + && x.ParentCode == ReadingCommon.CriterionDictionary.GlobalAssess + ) + //.WhereIf(isBaseLine,x=>x.IsBaseLineUse) + //.WhereIf(!isBaseLine,x=>x.IsFollowVisitUse) + .Select(x => new CriterionDictionaryInfo() + { + Id = x.Id, + DictionaryId = x.DictionaryId, + ChildGroup = x.Dictionary.ChildGroup, + IsBaseLineUse=x.IsBaseLineUse, + IsFollowVisitUse=x.IsFollowVisitUse, + Code = x.Dictionary.Code, + Description = x.Dictionary.Description, + ShowOrder = x.Dictionary.ShowOrder, + ParentCode = x.Dictionary.Parent.Code, + Value = x.Dictionary.Value, + ValueCN = x.Dictionary.ValueCN + }).OrderBy(x => x.ParentCode).ThenBy(x => x.ShowOrder).ToListAsync(); + result.AssessTypeList = assessTypeList; + return result; + } + #endregion + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs new file mode 100644 index 0000000..e9a0fe7 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs @@ -0,0 +1,2391 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using MassTransit; +using IRaCIS.Core.Infra.EFCore.Common; +using Panda.DynamicWebApi.Attributes; +using AutoMapper; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Application.Interfaces; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Microsoft.Extensions.Caching.Memory; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Application.Services +{ + /// + /// IR影像阅片 + /// + [ApiExplorerSettings(GroupName = "Reading")] + public partial class ReadingImageTaskService : BaseService, IReadingImageTaskService + { + + private readonly IRepository _noneDicomStudyRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _readingTableQuestionAnswerRepository; + private readonly IRepository _readingOncologyTaskInfoRepository; + private readonly IVisitTaskHelpeService _visitTaskHelpeService; + private readonly IVisitTaskService _visitTaskService; + private readonly IReadingClinicalDataService _readingClinicalDataService; + private readonly IReadingCalculateService _readingCalculateService; + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _subjectRepository; + private readonly IRepository _readingGlobalTaskInfoRepository; + private readonly IRepository _readingCriterionPageRepository; + private readonly IRepository _readingTaskRelationRepository; + private readonly IRepository _readingJudgeInfoRepository; + private readonly IRepository _readModuleRepository; + private readonly IRepository _dicomInstanceRepository; + private readonly IRepository _organInfoRepository; + private readonly IRepository _readingCriterionDictionaryRepository; + private readonly IRepository _tumorAssessmentRepository; + private readonly IRepository _readingTableAnswerRowInfoRepository; + private readonly IRepository _readingTableQuestionSystemRepository; + private readonly IRepository _readingTableQuestionTrialRepository; + private readonly IRepository _readingTaskQuestionAnswerRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _readingQuestionCriterionSystemRepository; + private readonly IRepository _readingQuestionSystem; + private readonly IRepository _noneDicomStudyFileSystem; + private readonly IRepository _readingQuestionTrialRepository; + + private readonly IMemoryCache _cache; + private readonly ITrialEmailNoticeConfigService _trialEmailNoticeConfigService; + + + public ReadingImageTaskService( + IMapper mapper, + IRepository noneDicomStudyRepository, + IRepository visitTaskRepository, + IRepository TrialRepository, + IRepository ReadingTableQuestionAnswerRepository, + IRepository ReadingOncologyTaskInfoRepository, + IVisitTaskHelpeService visitTaskHelpeService, + IVisitTaskService visitTaskService, + IReadingClinicalDataService readingClinicalDataService, + IReadingCalculateService readingCalculateService, + IRepository subjectVisitRepository, + IRepository subjectRepository, + IRepository readingGlobalTaskInfoRepository, + IRepository readingCriterionPageRepository, + IRepository readingTaskRelationRepository, + IRepository readingJudgeInfoRepository, + IRepository readModuleRepository, + IRepository dicomInstanceRepository, + IRepository organInfoRepository, + IMemoryCache cache, + IRepository readingCriterionDictionaryRepository, + IRepository tumorAssessmentRepository, + IRepository readingTableAnswerRowInfoRepository, + IRepository readingTableQuestionSystemRepository, + IRepository readingTableQuestionTrialRepository, + IRepository readingTaskQuestionAnswerRepository, + IRepository readingQuestionCriterionTrialRepository, + IRepository readingQuestionCriterionSystemRepository, + IRepository ReadingQuestionSystem, + ITrialEmailNoticeConfigService trialEmailNoticeConfigService, + IRepository noneDicomStudyFileSystem, + IRepository readingQuestionTrialRepository + ) + { + base._mapper = mapper; + this._noneDicomStudyRepository = noneDicomStudyRepository; + this._visitTaskRepository = visitTaskRepository; + this._trialRepository = TrialRepository; + this._readingTableQuestionAnswerRepository = ReadingTableQuestionAnswerRepository; + this._readingOncologyTaskInfoRepository = ReadingOncologyTaskInfoRepository; + this._visitTaskHelpeService = visitTaskHelpeService; + this._visitTaskService = visitTaskService; + this._readingClinicalDataService = readingClinicalDataService; + this._readingCalculateService = readingCalculateService; + this._subjectVisitRepository = subjectVisitRepository; + this._subjectRepository = subjectRepository; + this._readingGlobalTaskInfoRepository = readingGlobalTaskInfoRepository; + this._readingCriterionPageRepository = readingCriterionPageRepository; + this._readingTaskRelationRepository = readingTaskRelationRepository; + this._readingJudgeInfoRepository = readingJudgeInfoRepository; + this._readModuleRepository = readModuleRepository; + this._dicomInstanceRepository = dicomInstanceRepository; + this._organInfoRepository = organInfoRepository; + this._readingCriterionDictionaryRepository = readingCriterionDictionaryRepository; + this._tumorAssessmentRepository = tumorAssessmentRepository; + this._readingTableAnswerRowInfoRepository = readingTableAnswerRowInfoRepository; + this._readingTableQuestionSystemRepository = readingTableQuestionSystemRepository; + this._readingTableQuestionTrialRepository = readingTableQuestionTrialRepository; + this._readingTaskQuestionAnswerRepository = readingTaskQuestionAnswerRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this._readingQuestionCriterionSystemRepository = readingQuestionCriterionSystemRepository; + this._readingQuestionSystem = ReadingQuestionSystem; + this._noneDicomStudyFileSystem = noneDicomStudyFileSystem; + this._readingQuestionTrialRepository = readingQuestionTrialRepository; + this._cache = cache; + this._trialEmailNoticeConfigService = trialEmailNoticeConfigService; + } + + /// + /// 维护任务关系 + /// + /// + public async Task MaintainTaskRelated() + { + List relations = new List(); + var visitList = await _visitTaskRepository.Where(x => x.PastResultTaskIds != "[]" && x.RelatedVisitTaskIds != "[]").Select(x => new + { + x.Id, + x.RelatedVisitTaskIds, + x.PastResultTaskIds + }).ToListAsync(); + + + List relatedIds = new List(); + + List PastResultds = new List(); + + foreach (var item in visitList) + { + try + { + PastResultds = JsonConvert.DeserializeObject>(item.PastResultTaskIds) ?? new List(); + } + catch (Exception) + { + + PastResultds = new List(); + } + + try + { + relatedIds = JsonConvert.DeserializeObject>(item.RelatedVisitTaskIds)?? new List(); + } + catch (Exception) + { + + relatedIds = new List(); + } + + relations.AddRange(PastResultds.Select(x => new ReadingTaskRelation() + { + + RelevanceTaskId = x, + TaskId = item.Id, + RelevanceType = RelevanceType.PastResult, + })); + + relations.AddRange(relatedIds.Select(x => new ReadingTaskRelation() + { + + RelevanceTaskId = x, + TaskId = item.Id, + RelevanceType = RelevanceType.Related, + })); + + await _readingTaskRelationRepository.BatchDeleteNoTrackingAsync(x => x.TaskId == item.Id); + + } + + await _readingTaskRelationRepository.AddRangeAsync(relations); + await _readingTaskRelationRepository.SaveChangesAsync(); + } + + + + /// + /// 修改计算问题 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ChangeCalculationAnswer(ChangeCalculationAnswerInDto inDto) + { + var visitTask = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + + var questionAnswerList = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId).ToListAsync(); + + var tableQuestionAnswerList= await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId).ToListAsync(); + + var rowInfoList = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId).ToListAsync(); + + + foreach (var item in inDto.QuestionAnswer) + { + if (questionAnswerList.Any(x => x.ReadingQuestionTrialId == item.QuestionId)) + { + await _readingTaskQuestionAnswerRepository.UpdatePartialFromQueryAsync(x => x.VisitTaskId == inDto.VisitTaskId && x.ReadingQuestionTrialId == item.QuestionId, x => new ReadingTaskQuestionAnswer() + { + Answer = item.Answer, + }); + } + else + { + await _readingTaskQuestionAnswerRepository.AddAsync(new ReadingTaskQuestionAnswer() { + Answer=item.Answer, + ReadingQuestionCriterionTrialId= visitTask.TrialReadingCriterionId, + ReadingQuestionTrialId= item.QuestionId, + SubjectId= visitTask.SubjectId, + VisitTaskId=inDto.VisitTaskId, + TrialId= visitTask.TrialId + }); + } + + + } + + foreach (var item in inDto.TableQuestionAnswer) + { + + if (tableQuestionAnswerList.Any(x => x.QuestionId == item.QuestionId&&x.RowId==item.RowId&&x.TableQuestionId==item.TableQuestionId)) + { + await _readingTableQuestionAnswerRepository.UpdatePartialFromQueryAsync(x => x.VisitTaskId == inDto.VisitTaskId && x.RowId == item.RowId && x.TableQuestionId == item.TableQuestionId, x => new ReadingTableQuestionAnswer() + { + Answer = item.Answer, + }); + } + else + { + + var rowInfo = rowInfoList.Where(x => x.Id == item.RowId).FirstOrDefault()??new ReadingTableAnswerRowInfo (); + + await _readingTableQuestionAnswerRepository.AddAsync(new ReadingTableQuestionAnswer() + { + Answer = item.Answer, + QuestionId=item.QuestionId, + TableQuestionId=item.TableQuestionId, + RowIndex= rowInfo.RowIndex, + RowId=rowInfo.Id, + VisitTaskId = inDto.VisitTaskId, + TrialId = visitTask.TrialId + }); + } + + + } + + await _readingTableQuestionAnswerRepository.SaveChangesAsync(); + } + + /// + /// 阅读临床数据 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ReadClinicalData(ReadClinicalDataInDto inDto) + { + await _visitTaskRepository.UpdatePartialFromQueryAsync(inDto.VisitTaskId, x => new VisitTask + { + IsReadClinicalData = true + }); + await _visitTaskRepository.SaveChangesAsync(); + } + + /// + /// 添加默认值到任务里面 + /// + /// + [NonDynamicMethod] + public async Task AddDefaultValueToTask(Guid visitTaskId) + { + var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + + var questions = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == visitTaskInfo.TrialReadingCriterionId).ToListAsync(); + questions = questions.Where(x => !x.DefaultValue.IsNullOrEmpty()).ToList(); + + var taskAnswerIds = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == visitTaskId).Select(x => x.ReadingQuestionTrialId).ToListAsync(); + questions = questions.Where(x => !taskAnswerIds.Contains(x.Id)).ToList(); + + List questionAnswers = questions.Select(x => new ReadingTaskQuestionAnswer() + { + + Answer = x.DefaultValue, + Id = NewId.NextGuid(), + ReadingQuestionCriterionTrialId = visitTaskInfo.TrialReadingCriterionId, + ReadingQuestionTrialId = x.Id, + SubjectId = visitTaskInfo.SubjectId, + TrialId = visitTaskInfo.TrialId, + VisitTaskId = visitTaskId, + + }).ToList(); + + await _readingTaskQuestionAnswerRepository.AddRangeAsync(questionAnswers); + await _readingTaskQuestionAnswerRepository.SaveChangesAsync(); + + } + + + #region 阅片页面 关联信息查询 以及基本验证 + + /// + /// 根据任务ID获取ReadingTool + /// + /// + /// + [HttpPost] + public async Task GetReadingTool(GetReadingToolInDto indto) + { + var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == indto.VisitTaskId).FirstNotNullAsync(); + + var criterionTrialInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == visitTaskInfo.TrialReadingCriterionId).FirstNotNullAsync(); + GetReadingToolOutDto result = new GetReadingToolOutDto() + { + TrialReadingCriterionId = visitTaskInfo.TrialReadingCriterionId, + ReadingTool = criterionTrialInfo.ReadingTool, + CriterionType = criterionTrialInfo.CriterionType, + }; + + return result; + } + + /// + /// 获取关联的阅片任务 + /// + /// + /// + [HttpPost] + public async Task<(List, object)> GetRelatedVisitTask(GetRelatedVisitTaskInDto inDto) + { + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Include(x => x.TrialReadingCriterion).FirstNotNullAsync(); + var baselineVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == taskInfo.SubjectId && x.IsBaseLine && !x.IsLostVisit).Select(x => x.Id).FirstNotNullAsync(); + + + var taskQuery = _visitTaskRepository.Where(x => + (x.TrialId == taskInfo.TrialId && + x.SubjectId == taskInfo.SubjectId && + x.ArmEnum == taskInfo.ArmEnum && + x.DoctorUserId == taskInfo.DoctorUserId && + x.ReadingTaskState == ReadingTaskState.HaveSigned && + x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && + x.TaskState == TaskState.Effect && + x.VisitTaskNum <= taskInfo.VisitTaskNum && + x.IsAnalysisCreate == taskInfo.IsAnalysisCreate && + x.ReadingCategory == ReadingCategory.Visit) || x.Id == inDto.VisitTaskId); + + if (taskInfo.ReadingTaskState == ReadingTaskState.HaveSigned) + { + taskQuery = _visitTaskRepository.Where(x => taskInfo.RelatedVisitTaskIdList.Contains(x.Id)||x.Id== taskInfo.Id); + } + + var result = await taskQuery + .Select(x => new GetRelatedVisitTaskOutDto() + { + TaskBlindName = x.TaskBlindName, + TaskName = x.TaskName, + ReadingTaskState = x.ReadingTaskState, + VisitId = x.SourceSubjectVisitId, + VisitTaskId = x.Id, + VisitTaskNum = x.VisitTaskNum, + IsBaseLineTask = x.SourceSubjectVisitId == baselineVisitId, + IsCurrentTask = x.Id == inDto.VisitTaskId, + + }).OrderBy(x => x.VisitTaskNum).ToListAsync(); + + + if (!taskInfo.TrialReadingCriterion.IsReadingTaskViewInOrder) + { + result = result.Where(x => x.VisitTaskId == inDto.VisitTaskId).ToList(); + } + + return (result, new + { + ReadingTaskState = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Select(x => x.ReadingTaskState).FirstOrDefaultAsync() + }); + } + + /// + /// 获取既往任务名称和编号 + /// + /// + [HttpPost] + public async Task> GetReadingPastResultList(GetReadingPastResultListInDto inDto) + { + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + + var taskQuery = _visitTaskRepository.Where(x => + x.TrialId == taskInfo.TrialId && + x.SubjectId == taskInfo.SubjectId && + x.VisitTaskNum <= taskInfo.VisitTaskNum && + x.ArmEnum == taskInfo.ArmEnum && + x.Id != inDto.VisitTaskId && + x.DoctorUserId == taskInfo.DoctorUserId && + x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && + x.ReadingTaskState == ReadingTaskState.HaveSigned && + x.TaskState == TaskState.Effect && + x.IsSelfAnalysis == taskInfo.IsSelfAnalysis && + x.IsAnalysisCreate == taskInfo.IsAnalysisCreate && + x.ReadingCategory == taskInfo.ReadingCategory + ); + + if (taskInfo.ReadingTaskState == ReadingTaskState.HaveSigned) + { + taskQuery = _visitTaskRepository.Where(x => taskInfo.PastResultTaskIdList.Contains(x.Id)); + } + + var readingPastResultList = await taskQuery.Select(x => new GetReadingPastResultListOutDto() { + VisitTaskId = x.Id, + TaskBlindName = x.TaskBlindName, + TaskName = x.TaskName, + VisitTaskNum = x.VisitTaskNum, + JudgeResultArm = x.JudgeResultTask == null?null:x.JudgeResultTask.ArmEnum, + + }).OrderBy(x => x.VisitTaskNum).ToListAsync(); + return readingPastResultList; + } + + /// + /// 获取阅片的患者信息 + /// + /// + /// + [HttpPost] + public async Task GetReadingSubjectInfo(GetReadingSubjectInfoInDto inDto) + { + var visitTask = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstOrDefaultAsync(); + var subjectCode = await _subjectRepository.Where(x => x.Id == visitTask.SubjectId).Select(x => x.Code).FirstOrDefaultAsync(); + + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == visitTask.TrialReadingCriterionId).Select(x => new + { + x.IsReadingShowPreviousResults, + x.IsReadingShowSubjectInfo + }).FirstOrDefaultAsync(); + var trialInfo = await _trialRepository.Where(x => x.Id == visitTask.TrialId).Select(x => new + { + x.ClinicalInformationTransmissionEnum, + }).FirstOrDefaultAsync(); + + return new GetReadingSubjectInfoOutDto() + { + VisitTaskId = visitTask.Id, + SubjectId = visitTask.SubjectId, + SubjectCode = visitTask.BlindSubjectCode.IsNullOrEmpty() ? subjectCode : visitTask.BlindSubjectCode, + ReadingCategory = visitTask.ReadingCategory, + TaskBlindName = visitTask.TaskBlindName, + IsReadingShowPreviousResults = criterionInfo.IsReadingShowPreviousResults, + IsReadingShowSubjectInfo = criterionInfo.IsReadingShowSubjectInfo, + + }; + } + + + /// + /// 验证是否为基线检查批次任务 + /// + /// + /// + /// + private async Task VerifyIsBaseLineTask(Guid visitTaskId) + { + var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + if (taskinfo.ReadingCategory != ReadingCategory.Visit) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_NotVisit"]); + } + + if (await _subjectVisitRepository.AnyAsync(x => x.Id == taskinfo.SourceSubjectVisitId && x.IsBaseLine)) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_CantSplit"]); + } + } + + + + /// + /// 验证任务是否签名 + /// + /// + /// + /// + private async Task VerifyTaskIsSign(Guid visitTaskid) + { + if (await _visitTaskRepository.AnyAsync(x => x.Id == visitTaskid && x.ReadingTaskState == ReadingTaskState.HaveSigned)) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_BeSigned"]); + } + + + if (await _visitTaskRepository.AnyAsync(x => x.Id == visitTaskid && x.TaskState != TaskState.Effect)) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_Beinvalid"]); + } + } + + + + #endregion + + + #region 检查批次任务 - Dicom 阅片 表格问题相关查询 + + + /// + /// 获取DIcom阅片问题答案 + /// + /// + /// + [HttpPost] + public async Task<(List, object)> GetDicomReadingQuestionAnswer(GetDicomReadingQuestionAnswerInDto inDto) + { + //await AddDefaultValueToTask(inDto.VisitTaskId); + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + var result = await GetReadingQuestion(taskInfo.TrialReadingCriterionId, taskInfo.Id); + + return (result, new + { + + ReadingTaskState = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Select(x => x.ReadingTaskState).FirstOrDefaultAsync() + }); + } + + /// + /// 获取阅片外层问题 + /// + /// + /// + /// + [NonDynamicMethod] + public async Task> GetReadingQuestion(Guid trialReadingCriterionId, Guid? visitTaskId) + { + + + var criterionIdInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == trialReadingCriterionId).FirstNotNullAsync(); + + + //排除表格问题 + var questions = await _readingQuestionTrialRepository + .WhereIf(criterionIdInfo.IseCRFShowInDicomReading, x => x.ReadingQuestionCriterionTrialId == trialReadingCriterionId && x.Type != ReadingQestionType.Table) + .WhereIf(!criterionIdInfo.IseCRFShowInDicomReading, x => x.IsShowInDicom && x.ReadingQuestionCriterionTrialId == trialReadingCriterionId && x.Type != ReadingQestionType.Table) + + .ProjectTo(_mapper.ConfigurationProvider,new{ + isEn_Us=_userInfo.IsEn_Us + + }).OrderBy(x => x.ShowOrder).ToListAsync(); + + + var answers = new List(); + + if (visitTaskId != null) + { + answers = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == visitTaskId).ToListAsync(); ; + } + + + //排除表格问题 同时排除组问题 + var groupids = questions.Where(x => x.Type != ReadingQestionType.Group).Select(x => x.GroupId).ToList(); + + var result = questions.Where(x => x.Type == ReadingQestionType.Group && groupids.Contains(x.Id)).ToList(); + + foreach (var item in result) + { + GetDicomReadingAnswer(item, questions, answers); + } + + return result; + } + + private void GetDicomReadingAnswer(DicomReadingQuestionAnswer item, List questions, List answers) + { + var answer= answers.Where(x => x.ReadingQuestionTrialId == item.Id).Select(x => x.Answer).FirstIsNullReturnEmpty(); + item.Answer = answer.IsNullOrEmpty() ? item.DefaultValue : answer; + + + item.Childrens = questions.Where(x => x.ParentId == item.Id || (x.GroupId==item.Id&&x.ParentId==null)).ToList(); + if (item.Childrens != null && item.Childrens.Count > 0) + { + foreach (var question in item.Childrens) + { + GetDicomReadingAnswer(question, questions, answers); + } + } + + } + + /// + /// 获取阅片报告 + /// + /// + /// + [HttpPost] + public async Task GetReadingReportEvaluation(GetReadingReportEvaluationInDto indto) + { + return await _readingCalculateService.GetReadingReportEvaluation(indto); + } + + /// + /// 获取表格答案行信息 + /// + /// + /// + /// (QuestionId) 可为空 + /// + /// + [HttpGet] + public async Task> GetTableAnswerRowInfoList(GetTableAnswerRowInfoInDto inDto) + { + await _readingCalculateService.AddTaskLesionAnswerFromLastTask(new AddTaskLesionAnswerFromLastTaskInDto() + { + VisitTaskId = inDto.VisitTaskId + }); + var result = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId) + .WhereIf(inDto.QuestionId != null, x => x.QuestionId == inDto.QuestionId) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex) + .ToListAsync(); + result.ForEach(x => + { + + x.OrderMarkName = x.OrderMark + x.RowIndexNum.GetLesionMark(); + }); + return result; + } + + /// + /// 获取表格问题及答案(2022-08-26) + /// + /// + /// + [HttpPost] + public async Task<(GetReadingQuestionAndAnswerOutDto, object)> GetReadingQuestionAndAnswer(GetReadingQuestionAndAnswerInDto inDto) + { + + //await AddDefaultValueToTask(inDto.VisitTaskId); + var result = new GetReadingQuestionAndAnswerOutDto(); + + var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == taskinfo.TrialReadingCriterionId).FirstNotNullAsync(); + + result.ReadingTaskState = taskinfo.ReadingTaskState; + var baseLineVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == taskinfo.SubjectId && x.IsBaseLine).Select(x => x.Id).FirstOrDefaultAsync(); + + result.IsBaseLineTask = taskinfo.SourceSubjectVisitId == baseLineVisitId; + + var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + + + var tableAnswers = await _readingTableQuestionAnswerRepository + .ProjectTo(_mapper.ConfigurationProvider) + .Where(x => x.VisitTaskId == inDto.VisitTaskId).ToListAsync(); + + var tableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var organIds = tableAnsweRowInfos.Where(x => x.OrganInfoId != null).Select(x => x.OrganInfoId).Distinct().ToList(); + var organList = await _organInfoRepository.Where(x => organIds.Contains(x.Id)).ToListAsync(); + var questionPage = await GetReadingTableQuestion( + + new GetReadingTableQuestionOrAnswerInDto() { + + TrialReadingCriterionId = taskinfo.TrialReadingCriterionId, + TaskId = inDto.VisitTaskId, + TableAnswers = tableAnswers, + TableAnsweRowInfos = tableAnsweRowInfos, + OrganInfos= organList, + } + + + ); + + result.SinglePage = questionPage.SinglePage; + result.MultiPage = questionPage.MultiPage; + result.PublicPage = questionPage.PublicPage; + result.BlindName = visitTaskInfo.TaskBlindName; + result.TaskNum = visitTaskInfo.VisitTaskNum; + + return (result, new + { + readingTaskState = visitTaskInfo.ReadingTaskState, + FormType = criterionInfo.FormType, + TaskNum = visitTaskInfo.VisitTaskNum, + + }); ; + } + + /// + /// 获取自定义问题以及答案 + /// + /// + /// + [HttpPost] + public async Task<(GetReadingTableQuestionOutDto, object)> GetCustomTableQuestionAnswer(GetCustomTableQuestionAnswerInDto inDto) + { + + //await _readingCalculateService.AddTaskLesionAnswerFromLastTask(new AddTaskLesionAnswerFromLastTaskInDto() { + //VisitTaskId=inDto.VisitTaskId + //}); + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Include(x=>x.SourceSubjectVisit).FirstNotNullAsync(); + + var tableAnswers = await _readingTableQuestionAnswerRepository + .ProjectTo(_mapper.ConfigurationProvider) + .Where(x => x.VisitTaskId == inDto.VisitTaskId).ToListAsync(); + + var tableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var organIds = tableAnsweRowInfos.Where(x => x.OrganInfoId != null).Select(x => x.OrganInfoId).Distinct().ToList(); + var organList = await _organInfoRepository.Where(x => organIds.Contains(x.Id)).ToListAsync(); + return (await this.GetReadingTableQuestion( + + new GetReadingTableQuestionOrAnswerInDto() + { + TrialReadingCriterionId = taskInfo.TrialReadingCriterionId, + TaskId = taskInfo.Id, + TableAnswers = tableAnswers, + TableAnsweRowInfos = tableAnsweRowInfos, + IsGetallQuestion = true, + OrganInfos= organList + + } + ),new { + IsBaseline= taskInfo.SourceSubjectVisit!=null&&taskInfo.SourceSubjectVisit.IsBaseLine, + ReadingTaskState= taskInfo.ReadingTaskState + }); + + } + + /// + /// 获取表格问题及答案 只返回表格问题(任务和标准) + /// + /// + /// + [NonDynamicMethod] + public async Task GetReadingTableQuestion(GetReadingTableQuestionOrAnswerInDto inDto) + { + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).FirstNotNullAsync(); + + var qusetionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == inDto.TrialReadingCriterionId).ProjectTo(_mapper.ConfigurationProvider,new { + + isEn_Us=_userInfo.IsEn_Us + + }).OrderBy(x => x.ShowOrder).ToListAsync(); + + + //是否是预览 + if (inDto.IsGetPreview == false) + { + // 是否获取所有问题 + if (inDto.IsGetallQuestion) + { + if (!criterionInfo.IseCRFShowInDicomReading) + { + qusetionList = qusetionList.Where(x => x.IsShowInDicom).OrderBy(x => x.ShowOrder).ToList(); + } + } + else + { + if (!criterionInfo.IseCRFShowInDicomReading) + { + qusetionList = qusetionList.Where(x => x.IsShowInDicom && (x.Type == ReadingQestionType.Table || x.Type == ReadingQestionType.Group)).OrderBy(x => x.ShowOrder).ToList(); + } + var usedGuropIds = qusetionList.Where(x => x.Type == ReadingQestionType.Table).Select(x => x.GroupId).ToList(); + qusetionList = qusetionList.Where(x => usedGuropIds.Contains(x.Id)|| usedGuropIds.Contains(x.GroupId)).ToList(); + } + } + + + + var answers = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.TaskId).ToListAsync(); + qusetionList.ForEach(x => + { + var answer = answers.Where(y => y.ReadingQuestionTrialId == x.Id).Select(x => x.Answer).FirstOrDefault() ?? string.Empty; + x.Answer = answer.IsNullOrEmpty() ? x.DefaultValue : answer; + }); + + #endregion + + var groupList = new List(); + var qusetionIds = qusetionList.Select(x => x.Id).ToList(); + + var tableQuestionList = await _readingTableQuestionTrialRepository.Where(x => qusetionIds.Contains(x.ReadingQuestionId)) + .ProjectTo(_mapper.ConfigurationProvider,new { + + isEn_Us=_userInfo.IsEn_Us + }) + .OrderBy(x => x.ShowOrder).ToListAsync(); + + var result = new GetReadingTableQuestionOutDto(); + + + List baseLineTableAnswer = new List(); + + if(inDto.TaskId!=null) + { + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.TaskId).FirstNotNullAsync(); + // 取基线 + var baseLineVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == taskInfo.SubjectId && x.IsBaseLine).Select(x => x.Id).FirstNotNullAsync(); + + var baselineTaskId = await _visitTaskRepository.Where(x => x.SourceSubjectVisitId == baseLineVisitId + && x.ArmEnum == taskInfo.ArmEnum + && x.DoctorUserId == taskInfo.DoctorUserId + && x.IsAnalysisCreate == taskInfo.IsAnalysisCreate + && x.TaskState == TaskState.Effect + &&x.TrialReadingCriterionId== taskInfo.TrialReadingCriterionId + ).Select(x => x.Id).FirstNotNullAsync(); + + baseLineTableAnswer = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == baselineTaskId).Include(x=>x.ReadingTableQuestionTrial).ToListAsync(); + + } + if (criterionInfo.FormType == FormType.MultiplePage) + { + qusetionList = qusetionList.Where(x => x.ReadingCriterionPageId != null).ToList(); + var readingCriterionPageIds = qusetionList.OrderBy(x => x.PageShowOrder).Select(x => x.ReadingCriterionPageId).Distinct().ToList(); + foreach (var item in readingCriterionPageIds) + { + var newPageQusetionList = qusetionList.Where(x => x.ReadingCriterionPageId == item).ToList(); + var firstData = newPageQusetionList.FirstOrDefault(); + var page = new TrialReadQuestionData() + { + PageName = firstData.PageName, + IsPage = true, + IsPublicPage = firstData.IsPublicPage, + }; + + var pageGroupList = newPageQusetionList.Where(x => x.Type == ReadingQestionType.Group).ToList(); + pageGroupList.ForEach(x => + { + this.FindChildQuestion(x, newPageQusetionList, tableQuestionList, inDto.TableAnswers, inDto.TableAnsweRowInfos,inDto.OrganInfos, baseLineTableAnswer); + }); + + page.Childrens = pageGroupList.Where(x => !(x.Type == ReadingQestionType.Group && x.Childrens.Count() == 0)).ToList(); + groupList.Add(page); + } + + result.PublicPage = groupList.Where(x => x.IsPublicPage.Value).ToList(); + result.MultiPage = groupList.Where(x => !x.IsPublicPage.Value).ToList(); + } + else + { + qusetionList = qusetionList.Where(x => x.ReadingCriterionPageId == null).ToList(); + + groupList = qusetionList.Where(x => x.Type == ReadingQestionType.Group).ToList(); + groupList.ForEach(x => + { + this.FindChildQuestion(x, qusetionList, tableQuestionList, inDto.TableAnswers, inDto.TableAnsweRowInfos, inDto.OrganInfos, baseLineTableAnswer); + }); + + groupList = groupList.Where(x => !(x.Type == ReadingQestionType.Group && x.Childrens.Count() == 0)).ToList(); + + result.SinglePage = groupList; + + + } + + + return result; + } + + /// + /// 获取子元素 + /// + /// + /// + /// + private async void FindChildQuestion(TrialReadQuestionData item, List questionlists, List tableQuestionLists, List tableAnswers, List tableAnsweRowInfos,List organInfos, List baseLineTableAnswer) + { + item.Childrens = questionlists.Where(x => x.ParentId == item.Id || (x.GroupId == item.Id && x.ParentId == null)).ToList(); + item.TableQuestions = new TrialReadTableQuestion(); + + item.TableQuestions.Questions = tableQuestionLists.Where(x => x.ReadingQuestionId == item.Id).OrderBy(x => x.ShowOrder).ToList(); + + item.TableQuestions.Questions.ForEach(x => + { + + x.RelationQuestions = tableQuestionLists.Where(z => (z.DependParentId ?? default(Guid)) == x.Id).Select(x => new GetTrialReadingQuestionOutDto + { + Childrens = new List(), + ShowOrder = x.ShowOrder, + GroupName = string.Empty, + Id = x.Id, + DictionaryCode = x.DictionaryCode, + Type = x.Type, + TableQuestionType = x.TableQuestionType, + DependParentId = x.DependParentId, + IsDepend = x.IsDepend, + QuestionMark = x.QuestionMark, + TypeValue = x.TypeValue, + RelevanceId = x.RelevanceId, + RelevanceValue = x.RelevanceValue, + ImageCount = 0, + ParentId = item.Id, + DataTableColumn = x.DataTableColumn, + LesionType = item.LesionType, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + RelationQuestions = new List(), + Remark = x.Remark, + ValueType = x.ValueType, + Unit = x.Unit, + }).ToList(); + }); + + var thisAnswer = tableAnswers.Where(x => x.QuestionId == item.Id).ToList(); + var orders = thisAnswer.Select(x => x.RowIndex).Distinct().OrderBy(x => x).ToList(); + item.TableQuestions.Answers = new List>(); + + + var needChangeType = new List() { + QuestionMark.Organ, + QuestionMark.Location, + QuestionMark.Part, + }; + + orders.ForEach(x => + { + Dictionary answers = new Dictionary(); + var rowInfo = tableAnsweRowInfos.Where(y => y.RowIndex == x && y.QuestionId == item.Id).FirstOrDefault(); + var rowAnswer = thisAnswer.Where(y => y.RowId == rowInfo.Id).OrderBy(y => y.ShowOrder).ToList(); + + var organInfo = organInfos.Where(x=>x.Id== rowInfo.OrganInfoId).FirstOrDefault(); + + rowAnswer.ForEach(z => + { + + if (organInfo != null && needChangeType.Contains(z.QuestionMark)) + { + if (_userInfo.IsEn_Us) + { + switch (z.QuestionMark) + { + case QuestionMark.Organ: + answers.Add(z.TableQuestionId.ToString(), organInfo.TULOCEN); + + break; + case QuestionMark.Location: + if (organInfo.IsCanEditPosition) + { + answers.Add(z.TableQuestionId.ToString(), z.Answer); + } + else + { + answers.Add(z.TableQuestionId.ToString(), organInfo.TULATEN); + + } + break; + case QuestionMark.Part: + answers.Add(z.TableQuestionId.ToString(), organInfo.PartEN); + break; + + } + + } + else + { + switch (z.QuestionMark) + { + case QuestionMark.Organ: + answers.Add(z.TableQuestionId.ToString(), organInfo.TULOC); + break; + case QuestionMark.Location: + if (organInfo.IsCanEditPosition) + { + answers.Add(z.TableQuestionId.ToString(), z.Answer); + } + else + { + answers.Add(z.TableQuestionId.ToString(), organInfo.TULAT); + + } + break; + case QuestionMark.Part: + answers.Add(z.TableQuestionId.ToString(), organInfo.Part); + break; + + } + } + + } + else + { + answers.Add(z.TableQuestionId.ToString(), z.Answer); + } + + }); + + + + answers.Add("BlindName", rowInfo.BlindName); + answers.Add("IsDicomReading", rowInfo.IsDicomReading.ToString()); + answers.Add("MeasureData", rowInfo == null ? string.Empty : rowInfo.MeasureData); + answers.Add("RowIndex", x.ToString()); + answers.Add("RowId", rowInfo.Id.ToString()); + answers.Add("StudyId", rowInfo.StudyId.ToString()); + answers.Add("OrganInfoId", rowInfo.OrganInfoId.ToString()); + answers.Add("IsCanEditPosition", rowInfo.IsCanEditPosition.ToString()); + answers.Add("InstanceId", rowInfo == null ? string.Empty : rowInfo.InstanceId.ToString()); + answers.Add("SeriesId", rowInfo == null ? string.Empty : rowInfo.SeriesId.ToString()); + answers.Add("IsCurrentTaskAdd", rowInfo == null ? false.ToString() : rowInfo.IsCurrentTaskAdd.ToString()); + answers.Add("SplitOrMergeLesionName", rowInfo.SplitName.IsNullOrEmpty() ? rowInfo.MergeName : rowInfo.SplitName); + answers.Add("SplitOrMergeType", rowInfo.SplitOrMergeType == null ? string.Empty : ((int)rowInfo.SplitOrMergeType).ToString()); + if (rowInfo.LesionType == LesionType.BaselineLesions) + { + answers.Add("BaseLineLesionNumber", baseLineTableAnswer.Where(n=>n.ReadingTableQuestionTrial.QuestionMark==QuestionMark.LesionNumber&&n.RowIndex==rowInfo.RowIndex).Select(x=>x.Answer).FirstIsNullReturnEmpty()); + } + + + item.TableQuestions.Answers.Add(answers); + }); + + if (item.Childrens != null && item.Childrens.Count != 0) + { + item.Childrens.ForEach(x => + { + this.FindChildQuestion(x, questionlists, tableQuestionLists, tableAnswers, tableAnsweRowInfos, organInfos, baseLineTableAnswer); + }); + } + } + + + + + + #region 检查批次任务 - Dicom 阅片 表格问题 病灶的拆分与合并 + + + /// + /// 拆分病灶 + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SplitLesion(SplitLesionInDto inDto) + { + await VerifyTaskIsSign(inDto.VisitTaskId); + await this.VerifyIsBaseLineTask(inDto.VisitTaskId); + var rowAnswer = await _readingTableAnswerRowInfoRepository.Where(x => x.Id == inDto.RowId).AsNoTracking().FirstNotNullAsync(); + var tableAnswers = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && x.RowIndex == rowAnswer.RowIndex && x.QuestionId == inDto.QuestionId).Include(x => x.ReadingTableQuestionTrial).ToListAsync(); + var maxRowIndex = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && x.QuestionId == inDto.QuestionId && x.RowIndex < Math.Floor(rowAnswer.RowIndex + 1)).OrderByDescending(x => x.RowIndex).Select(x => x.RowIndex).FirstOrDefaultAsync(); + var newRowIndex = maxRowIndex + (decimal)0.01; + + rowAnswer.RowIndex = newRowIndex; + rowAnswer.MergeRowId = null; + rowAnswer.FristAddTaskId = inDto.VisitTaskId; + rowAnswer.SplitOrMergeType = SplitOrMergeType.Split; + rowAnswer.SplitRowId = rowAnswer.Id; + rowAnswer.Id = NewId.NextGuid(); + rowAnswer.InstanceId = null; + rowAnswer.SeriesId = null; + rowAnswer.IsCurrentTaskAdd = true; + rowAnswer.MeasureData = string.Empty; + + List needRemoveMark = new List() + { + QuestionMark.MajorAxis, + QuestionMark.ShortAxis, + QuestionMark.State, + }; + + tableAnswers.ForEach(x => + { + x.Id = NewId.NextGuid(); + x.RowIndex = newRowIndex; + x.VisitTaskId = inDto.VisitTaskId; + x.RowId = rowAnswer.Id; + x.Answer = needRemoveMark.Contains(x.ReadingTableQuestionTrial.QuestionMark) ? string.Empty : x.Answer; + x.ReadingTableQuestionTrial = null; + }); + await _readingTableAnswerRowInfoRepository.AddAsync(rowAnswer); + await _readingTableQuestionAnswerRepository.AddRangeAsync(tableAnswers); + await _readingTableAnswerRowInfoRepository.SaveChangesAsync(); + + } + + /// + /// 合并病灶 + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task MergeLesion(MergeLesionInDto inDto) + { + await VerifyTaskIsSign(inDto.VisitTaskId); + await this.VerifyIsBaseLineTask(inDto.VisitTaskId); + + var rowsInfo = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && (x.Id == inDto.MainRowId || x.Id == inDto.MergeRowId)).ToListAsync(); + + if (rowsInfo.Count() != 2) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_NotaTask"]); + } + + + var minaid = rowsInfo.Where(x => x.Id == inDto.MainRowId).Select(x => x.Id).FirstOrDefault(); + var mergeid = rowsInfo.Where(x => x.Id == inDto.MergeRowId).Select(x => x.Id).FirstOrDefault(); + + List needRemoveMark = new List() + { + QuestionMark.MajorAxis, + QuestionMark.ShortAxis, + }; + + var mainAnswer = await _readingTableQuestionAnswerRepository.Where(x => x.RowId == minaid).Include(x => x.ReadingTableQuestionTrial).ToListAsync(); + + foreach (var item in mainAnswer) + { + await _readingTableQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.RowId == mergeid && x.TableQuestionId == item.TableQuestionId, x => new ReadingTableQuestionAnswer() + { + Answer = needRemoveMark.Contains(item.ReadingTableQuestionTrial.QuestionMark) ? string.Empty : item.Answer, + }); + } + + + + await _readingTableAnswerRowInfoRepository.UpdatePartialFromQueryAsync(mergeid, x => new ReadingTableAnswerRowInfo() + { + MergeRowId = minaid, + SplitOrMergeType = SplitOrMergeType.Merge, + }); + + + await _readingTableAnswerRowInfoRepository.UpdatePartialFromQueryAsync(mergeid, x => new ReadingTableAnswerRowInfo() + { + MergeRowId = minaid, + SplitOrMergeType = SplitOrMergeType.Merge, + }); + + await _readingTableAnswerRowInfoRepository.SaveChangesAsync(); + + + } + + + #endregion + + + + #region 检查批次任务 - Dicom 阅片 提交、修改 + + /// + /// 保存影像质量 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SaveImageQuality(ChangeDicomReadingQuestionAnswerInDto inDto) + { + return await ChangeDicomReadingQuestionAnswer(inDto); + } + + ///// + ///// 保存ECRF + ///// + ///// + ///// + //[HttpPost] + //public async Task SaveImageQuality(ChangeDicomReadingQuestionAnswerInDto inDto) + //{ + // return await ChangeDicomReadingQuestionAnswer(inDto); + //} + + + /// + /// 修改肿瘤学评估结果 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ChangeDicomReadingQuestionAnswer(ChangeDicomReadingQuestionAnswerInDto inDto) + { + await VerifyTaskIsSign(inDto.VisitTaskId); + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + var criterionId = taskInfo.TrialReadingCriterionId; + var questionIds = inDto.Answers.Select(x => x.Id).ToList(); + await _readingTaskQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => x.VisitTaskId == inDto.VisitTaskId && questionIds.Contains(x.ReadingQuestionTrialId)); + var needAddAnswer = inDto.Answers.Select(x => new ReadingTaskQuestionAnswer() + { + + Answer = x.Answer, + SubjectId = taskInfo.SubjectId, + ReadingQuestionCriterionTrialId = criterionId, + ReadingQuestionTrialId = x.Id, + TrialId = taskInfo.TrialId, + VisitTaskId = inDto.VisitTaskId, + + }).ToList(); + await _readingTaskQuestionAnswerRepository.AddRangeAsync(needAddAnswer); + await _readingTaskQuestionAnswerRepository.SaveChangesAsync(); + return ResponseOutput.Ok(true); + } + + + + + + + /// + /// 删除表格行数据 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task DeleteReadingRowAnswer(DeleteReadingRowAnswerInDto inDto) + { + await VerifyTaskIsSign(inDto.VisitTaskId); + + var deleteRowInfo = await _readingTableAnswerRowInfoRepository.Where(x => x.Id == inDto.RowId).FirstNotNullAsync(); + if (deleteRowInfo == null) + { + return ResponseOutput.Ok(true); + } + + if (await _readingTableAnswerRowInfoRepository.AnyAsync(x => x.SplitRowId == deleteRowInfo.Id && x.MergeRowId == deleteRowInfo.Id)) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_DeleteError"]); + } + + var index = await _readingCalculateService.GetDeleteLesionStatrIndex(inDto); + + + await _readingTableQuestionAnswerRepository.UpdatePartialFromQueryAsync(x => x.RowId == inDto.RowId, x => new ReadingTableQuestionAnswer() + { + + IsDeleted = true + }); + await _readingTableAnswerRowInfoRepository.UpdatePartialFromQueryAsync(x => x.Id == inDto.RowId,x=>new ReadingTableAnswerRowInfo() { + + IsDeleted=true + }); + await _readingTableAnswerRowInfoRepository.SaveChangesAsync(); + + + var rowInfoList = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && x.QuestionId == inDto.QuestionId,true).Include(x => x.ReadingQuestionTrial).OrderBy(x => x.RowIndex).ToListAsync(); + + var answerlist = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && x.QuestionId == inDto.QuestionId,true).ToListAsync(); + + + + foreach (var item in rowInfoList.Where(x => x.RowIndex % 1 == 0)) + { + string measureDataStr = string.Empty; + if (item.MeasureData != null && item.MeasureData != string.Empty) + { + dynamic measureData = JObject.Parse(item.MeasureData); + measureData.data.remark = item.ReadingQuestionTrial.OrderMark + ((decimal)index).GetLesionMark(); + measureDataStr = JsonConvert.SerializeObject(measureData); + } + + + + foreach (var answerItem in answerlist.Where(x=>x.RowIndex== item.RowIndex)) + { + + answerItem.RowIndex = index; + + } + + + foreach (var rowAnswerItem in rowInfoList.Where(x => x.RowIndex == item.RowIndex)) + { + + rowAnswerItem.RowIndex = index; + rowAnswerItem.MeasureData = measureDataStr; + + } + + //await _readingTableQuestionAnswerRepository.UpdatePartialFromQueryAsync(x => x.VisitTaskId == inDto.VisitTaskId && x.RowIndex == item.RowIndex && x.QuestionId == inDto.QuestionId, x => new ReadingTableQuestionAnswer() + //{ + // RowIndex = index + //}); + + //await _readingTableAnswerRowInfoRepository.UpdatePartialFromQueryAsync(x => x.VisitTaskId == inDto.VisitTaskId && x.RowIndex == item.RowIndex && x.QuestionId == inDto.QuestionId, x => new ReadingTableAnswerRowInfo() + //{ + // RowIndex = index, + // MeasureData = measureDataStr, + //}); + var spiltList = rowInfoList.Where(x => x.RowIndex % 1 != 0 && x.RowIndex > item.RowIndex && x.RowIndex < Math.Floor(item.RowIndex + 1)).OrderBy(x => x.RowIndex).ToList(); + decimal spiltindex = 0.01M; + foreach (var spiltitem in spiltList) + { + string spiltmeasureDataStr = string.Empty; + if (spiltitem.MeasureData != null && spiltitem.MeasureData != string.Empty) + { + dynamic spiltmeasureData = JObject.Parse(spiltitem.MeasureData); + spiltmeasureData.data.remark = item.ReadingQuestionTrial.OrderMark + ((decimal)index + spiltindex).GetLesionMark(); + spiltmeasureDataStr = JsonConvert.SerializeObject(spiltmeasureData); + } + + + foreach (var answerItem in answerlist.Where(x => x.RowIndex == spiltitem.RowIndex)) + { + answerItem.RowIndex = index + spiltindex; + + } + + + foreach (var rowAnswerItem in rowInfoList.Where(x => x.RowIndex == spiltitem.RowIndex)) + { + + rowAnswerItem.RowIndex = index + spiltindex; + rowAnswerItem.MeasureData = spiltmeasureDataStr; + + + } + + + //await _readingTableQuestionAnswerRepository.UpdatePartialFromQueryAsync(x => x.VisitTaskId == inDto.VisitTaskId && x.RowIndex == spiltitem.RowIndex && x.QuestionId == inDto.QuestionId, x => new ReadingTableQuestionAnswer() + //{ + // RowIndex = index + spiltindex + //}); + //await _readingTableAnswerRowInfoRepository.UpdatePartialFromQueryAsync(x => x.VisitTaskId == inDto.VisitTaskId && x.RowIndex == spiltitem.RowIndex && x.QuestionId == inDto.QuestionId, x => new ReadingTableAnswerRowInfo() + //{ + // RowIndex = index + spiltindex, + // MeasureData = spiltmeasureDataStr, + //}); + + spiltindex += 0.01M; + } + index++; + } + + await _readingTableAnswerRowInfoRepository.SaveChangesAsync(); + // 自动计算 + await this._readingCalculateService.CalculateTask(new CalculateTaskInDto() + { + IsChangeOtherTask = false, + VisitTaskId = inDto.VisitTaskId, + }); + return ResponseOutput.Ok(true); + } + + + /// + /// 提交表格问题答案 病灶 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SubmitTableQuestion(SubmitTableQuestionInDto inDto) + { + SubmitTableQuestionOutDto result = new SubmitTableQuestionOutDto(); + + await VerifyTaskIsSign(inDto.VisitTaskId); + + if (inDto.InstanceId != null) + { + if (!(await _dicomInstanceRepository.AnyAsync(x => x.Id == inDto.InstanceId && x.SeriesId == inDto.SeriesId))) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_Idnotcorrespond"]); + } + } + var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Include(x => x.TrialReadingCriterion).FirstNotNullAsync(); + + switch (taskinfo.TrialReadingCriterion.CriterionType) + { + // 对于非靶病灶,如果状态选择为显著增大,请验证: + // 1) 对于非淋巴结病灶,验证当前检查批次该病灶的长径 > 上一检查批次该病灶的长径。 + // 约束条件:两次检查批次该病灶的长径有测量值。 + // 提示语:当前检查批次该非淋巴结病灶的长径小于上一检查批次的值,不能设置为显著增大。 + // 2) 对于淋巴结病灶,验证当前检查批次该病灶的短径 > 上一检查批次该病灶的短径。 + // 约束条件:两次检查批次该病灶的短径有测量值。 + // 提示语:当前检查批次该淋巴结病灶的短径小于上一检查批次的值,不能设置为显著增大。 + case CriterionType.RECIST1Pointt1: + var lastTaskinfo = await _visitTaskRepository + .Where(x => x.IsAnalysisCreate == taskinfo.IsAnalysisCreate && + x.SubjectId==taskinfo.SubjectId&& + x.ReadingCategory == taskinfo.ReadingCategory && + x.DoctorUserId == taskinfo.DoctorUserId && + x.ArmEnum == taskinfo.ArmEnum && + x.TrialReadingCriterionId==taskinfo.TrialReadingCriterionId&& + x.ReadingTaskState==ReadingTaskState.HaveSigned&& + x.TaskState == TaskState.Effect && + x.IsSelfAnalysis == taskinfo.IsSelfAnalysis && + x.VisitTaskNum < taskinfo.VisitTaskNum + ).OrderByDescending(x => x.VisitTaskNum).FirstOrDefaultAsync(); + if (lastTaskinfo != null) + { + var tablequestionList = await _readingTableQuestionTrialRepository.Where(x => x.TrialCriterionId == taskinfo.TrialReadingCriterionId&&x.ReadingQuestionTrial.LesionType==LesionType.NonTargetLesions).ToListAsync(); + + + + // 判断表格问题是否为空 + if (tablequestionList.Count > 0) + { + // 找到状态值 + var stateQuestion = tablequestionList.Where(x => x.QuestionMark == QuestionMark.State).FirstOrDefault(); + + if (stateQuestion != null) + { + // 判断是否为非靶病灶 + if (inDto.QuestionId == tablequestionList[0].ReadingQuestionId && inDto.AnswerList.Any(x => x.TableQuestionId== stateQuestion.Id&&x.Answer.EqEnum(NoTargetState.Increase))) + { + var lymphQuestion = tablequestionList.Where(x => x.QuestionMark == QuestionMark.IsLymph).FirstOrDefault(); + if (lymphQuestion != null) + { + + // 判断是否是淋巴结 + if (inDto.AnswerList.Any(x => x.TableQuestionId == lymphQuestion.Id && x.Answer.EqEnum(YesOrNoOrNa.Yes))) + { + var shortAxisQuestion = tablequestionList.Where(x => x.QuestionMark == QuestionMark.ShortAxis).FirstOrDefault(); + if (shortAxisQuestion != null) + { + var lastAnswer = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == lastTaskinfo.Id && x.TableQuestionId == shortAxisQuestion.Id + && x.RowIndex == inDto.RowIndex + ).Select(x => x.Answer).FirstOrDefaultAsync(); + + var thisAnswer = inDto.AnswerList.Where(x => x.TableQuestionId == shortAxisQuestion.Id).Select(x => x.Answer).FirstOrDefault(); + if (!lastAnswer.IsNullOrEmpty() && !thisAnswer.IsNullOrEmpty()) + { + + var lastvalue = 0m; + var thisvalue = 0m; + try + { + lastvalue = decimal.Parse(lastAnswer); + thisvalue = decimal.Parse(thisAnswer); + } + catch (Exception) + { + + + } + + + if (lastvalue >= thisvalue) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_IsLymphNotbigger"]); + } + + + } + } + } + else + + { + var majorAxisQuestion = tablequestionList.Where(x => x.QuestionMark == QuestionMark.MajorAxis).FirstOrDefault(); + if (majorAxisQuestion != null) + { + var lastAnswer = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == lastTaskinfo.Id && x.TableQuestionId == majorAxisQuestion.Id + && x.RowIndex == inDto.RowIndex + ).Select(x => x.Answer).FirstOrDefaultAsync(); + + var thisAnswer = inDto.AnswerList.Where(x => x.TableQuestionId == majorAxisQuestion.Id).Select(x => x.Answer).FirstOrDefault(); + if (!lastAnswer.IsNullOrEmpty() && !thisAnswer.IsNullOrEmpty()) + { + var lastvalue = 0m; + var thisvalue = 0m; + try + { + lastvalue = decimal.Parse(lastAnswer); + thisvalue = decimal.Parse(thisAnswer); + } + catch (Exception) + { + + + } + + + if (lastvalue >= thisvalue) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_NotLymphNotbigger"]); + } + + } + } + } + } + + + } + } + + } + + } + break; + } + + var questionInfo = await _readingQuestionTrialRepository.Where(x => x.Id == inDto.QuestionId).FirstNotNullAsync(); + var criterionId = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Select(x => x.TrialReadingCriterionId).FirstOrDefaultAsync(); + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == criterionId).FirstNotNullAsync(); + var tableQuestionIds = inDto.AnswerList.Select(x => x.TableQuestionId).ToList(); + var tableAnswerList = await _readingTableQuestionAnswerRepository.Where(x => x.RowId == inDto.RowId).ToListAsync(); + + var tableQuestionIdGroup = tableQuestionIds.GroupBy(x => new { TableQuestionId = x }).Select(x => new TableQuestionData + { + TableQuestionId = x.Key.TableQuestionId, + Count = x.Count() + }).ToList(); + + if (tableQuestionIdGroup.Any(x => x.Count > 1)) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_Twice"]); + } + + if (inDto.RowIndex % 1 == 0) + { + + if (questionInfo.MaxQuestionCount != null && questionInfo.MaxQuestionCount != 0&& inDto.RowId==null) + { + if (questionInfo.MaxQuestionCount < + ( + (await _readingTableAnswerRowInfoRepository.Where(x => ((x.RowIndex % 1) == 0) && x.VisitTaskId == inDto.VisitTaskId + && x.QuestionId == inDto.QuestionId + ).CountAsync()) + 1)) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_MaxQuestion", questionInfo.MaxQuestionCount]); + } + } + + + var tableQuestions = await _readingTableQuestionTrialRepository.Where(x => tableQuestionIds.Contains(x.Id) && x.MaxRowCount != null && x.MaxRowCount != 0).ToListAsync(); + + List questionMarks = new List() + { + QuestionMark.Part, + QuestionMark.Organ, + + }; + + + string msg = string.Empty; + foreach (var item in tableQuestions) + { + + var answer = inDto.AnswerList.Where(x => x.TableQuestionId == item.Id).Select(x => x.Answer).FirstOrDefault(); + if (!answer.IsNullOrEmpty()) + { + Dictionary errorMsgDic = new Dictionary() + { + {CriterionType.RECIST1Pointt1, _localizer["ReadingImage_Maxlesion", item.MaxRowCount.Value]}, + + {CriterionType.PCWG3, _localizer["ReadingImage_PCWGMaximum", item.MaxRowCount.Value]}, + }; + var rowCount = 0; + if ((item.QuestionMark == QuestionMark.Part|| item.QuestionMark == QuestionMark.Organ) &&inDto.OrganInfoId!=null) + { + var organIds= await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && x.QuestionId == inDto.QuestionId && ((x.RowIndex % 1) == 0) && x.OrganInfoId != null && x.RowIndex != inDto.RowIndex).Select(x=>x.OrganInfoId).ToListAsync(); + organIds.Add(inDto.OrganInfoId); + + var orginInfos = await _organInfoRepository.Where(x => organIds.Contains(x.Id)).ToListAsync(); + + List orginInfoList = new List(); + foreach (var organId in organIds) + { + orginInfoList.Add(orginInfos.Where(x=>x.Id==organId).FirstOrDefault()); + } + + var currentOrginInfo = orginInfos.Where(x => x.Id == inDto.OrganInfoId).FirstOrDefault()??new OrganInfo (); + + if (item.QuestionMark == QuestionMark.Part) + { + rowCount = orginInfoList.Where(x=>x.Part== currentOrginInfo.Part).Count(); + } + else + { + rowCount = orginInfoList.Where(x => x.TULOC == currentOrginInfo.TULOC).Count(); + } + + if (rowCount > item.MaxRowCount.Value) + { + if (rowCount > item.MaxRowCount.Value ) + { + + + try + { + msg = errorMsgDic[criterionInfo.CriterionType]; + } + catch (Exception) + { + + msg = _localizer["ReadingImage_Maximum", item.QuestionName.LanguageName(item.QuestionEnName, _userInfo.IsEn_Us), item.MaxRowCount.Value, item.MaxRowCount.Value]; + } + + throw new BusinessValidationFailedException(msg); + } + } + + } + + else + { + rowCount = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && x.TableQuestionId == item.Id && ((x.RowIndex % 1) == 0) && x.Answer == answer && x.RowIndex != inDto.RowIndex).CountAsync(); + + if (rowCount > item.MaxRowCount.Value - 1) + { + + + try + { + msg = errorMsgDic[criterionInfo.CriterionType]; + } + catch (Exception) + { + + msg = _localizer["ReadingImage_Maximum", item.QuestionName.LanguageName(item.QuestionEnName, _userInfo.IsEn_Us), item.MaxRowCount.Value, rowCount]; + } + + throw new BusinessValidationFailedException(msg); + } + } + + + + } + } + } + var isCurrentTaskAddList = await _readingTableAnswerRowInfoRepository.Where(x => x.Id == (inDto.RowId ?? default(Guid))).Select(x => x.IsCurrentTaskAdd).ToListAsync(); + bool isCurrentTaskAdd = true; + if (isCurrentTaskAddList.Count() > 0) + { + isCurrentTaskAdd = isCurrentTaskAddList[0]; + } + + + ReadingTableAnswerRowInfo rowInfo = await _readingTableAnswerRowInfoRepository.Where(x => x.Id == (inDto.RowId ?? default(Guid))).FirstOrDefaultAsync(); + + rowInfo = rowInfo == null ? new ReadingTableAnswerRowInfo() : rowInfo; + + //await _readingTableQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => x.RowId == (inDto.RowId ?? default(Guid))); + //await _readingTableAnswerRowInfoRepository.BatchDeleteNoTrackingAsync(x => x.Id == (inDto.RowId ?? default(Guid))); + + rowInfo.Id = inDto.RowId == null ? NewId.NextGuid() : inDto.RowId.Value; + rowInfo.TrialId = inDto.TrialId; + rowInfo.QuestionId = inDto.QuestionId; + rowInfo.MeasureData = inDto.MeasureData; + rowInfo.BlindName = inDto.BlindName; + rowInfo.IsDicomReading = inDto.IsDicomReading; + rowInfo.IsCurrentTaskAdd = isCurrentTaskAdd; + rowInfo.NumberOfFrames = inDto.NumberOfFrames; + rowInfo.FristAddTaskNum = taskinfo.VisitTaskNum; + rowInfo.FristAddTaskId = inDto.VisitTaskId; + rowInfo.PicturePath = inDto.PicturePath; + rowInfo.RowIndex = inDto.RowIndex; + rowInfo.OrganInfoId = inDto.OrganInfoId; + rowInfo.InstanceId = inDto.InstanceId; + rowInfo.SeriesId = inDto.SeriesId; + rowInfo.VisitTaskId = inDto.VisitTaskId; + rowInfo.OrderMark = questionInfo.OrderMark; + rowInfo.StudyId = inDto.StudyId; + rowInfo.IsCanEditPosition = inDto.IsCanEditPosition; + rowInfo.WL = inDto.WL; + rowInfo.WW = inDto.WW; + result.RowId = rowInfo.Id; + + if (inDto.RowId == null) + { + + //await _readingTableAnswerRowInfoRepository.BatchDeleteNoTrackingAsync(x => x.VisitTaskId == inDto.VisitTaskId && x.RowIndex == inDto.RowIndex && x.QuestionId == inDto.QuestionId); + + List answerList = inDto.AnswerList.Select(x => new ReadingTableQuestionAnswer() + { + Answer = x.Answer, + Id = NewId.NextGuid(), + TrialId = inDto.TrialId, + QuestionId = inDto.QuestionId, + TableQuestionId = x.TableQuestionId, + RowIndex = inDto.RowIndex, + RowId = rowInfo.Id, + VisitTaskId = inDto.VisitTaskId + }).ToList(); + + + await _readingTableAnswerRowInfoRepository.AddAsync(rowInfo); + await _readingTableQuestionAnswerRepository.AddRangeAsync(answerList); + + } + + else + { + await _readingTableAnswerRowInfoRepository.UpdatePartialFromQueryAsync(rowInfo.Id, x => new ReadingTableAnswerRowInfo() + { + TrialId = rowInfo.TrialId, + QuestionId = rowInfo.QuestionId, + MeasureData = rowInfo.MeasureData, + BlindName = rowInfo.BlindName, + IsDicomReading = rowInfo.IsDicomReading, + IsCurrentTaskAdd = isCurrentTaskAdd, + WL = rowInfo.WL, + WW = rowInfo.WW, + OrganInfoId = rowInfo.OrganInfoId, + PicturePath = rowInfo.PicturePath, + NumberOfFrames = rowInfo.NumberOfFrames, + RowIndex = rowInfo.RowIndex, + InstanceId = rowInfo.InstanceId, + SeriesId = rowInfo.SeriesId, + VisitTaskId = rowInfo.VisitTaskId, + StudyId = rowInfo.StudyId, + IsCanEditPosition = rowInfo.IsCanEditPosition, + }) ; + + + foreach (var item in inDto.AnswerList) + { + if (tableAnswerList.Any(x => x.TableQuestionId == item.TableQuestionId)) + { + await _readingTableQuestionAnswerRepository.UpdatePartialFromQueryAsync(x => x.RowId == inDto.RowId && x.TableQuestionId == item.TableQuestionId, x => new ReadingTableQuestionAnswer() + { + + Answer = item.Answer + + }); + } + else + { + await _readingTableQuestionAnswerRepository.AddAsync(new ReadingTableQuestionAnswer() + { + Answer = item.Answer, + QuestionId = inDto.QuestionId, + TableQuestionId = item.TableQuestionId, + RowId = inDto.RowId.Value, + TrialId = taskinfo.TrialId, + RowIndex = rowInfo.RowIndex, + VisitTaskId = inDto.VisitTaskId, + }); + } + + } + + + } + + await _visitTaskRepository.UpdatePartialFromQueryAsync(inDto.VisitTaskId, x => new VisitTask() + { + ReadingTaskState = ReadingTaskState.Reading, + + }); + await _readingTableAnswerRowInfoRepository.SaveChangesAsync(); + await this._readingCalculateService.CalculateTask(new CalculateTaskInDto() + { + IsChangeOtherTask = false, + VisitTaskId = inDto.VisitTaskId, + }); + + + + + return result; + } + + + + /// + /// 提交Dicom阅片信息 + /// + /// + /// + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SubmitDicomVisitTask(SubmitDicomVisitTaskInDto inDto) + { + + + await VerifyTaskIsSign(inDto.VisitTaskId); + + //// 修改编号 + //var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + + //// 获取标准表格外层问题 + //var questionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == taskInfo.TrialReadingCriterionId && x.LesionType != null && x.ReadingTableQuestionTrialList.Any(x => x.QuestionMark == QuestionMark.AutoId)) + // .SelectMany(x => x.ReadingTableQuestionTrialList).Where(x => x.QuestionMark == QuestionMark.AutoId).Select(x => new + // { + // x.ReadingQuestionId, + // TableQuestionId = x.Id, + + // }).ToListAsync(); + + //var questionIds = questionList.Select(x => x.ReadingQuestionId).ToList(); + + //var questionMarkList = await _readingQuestionTrialRepository.Where(x => questionIds.Contains(x.Id)).Select(x => new + //{ + // QuestionId = x.Id, + // x.OrderMark, + + //}).ToListAsync(); + + //var rowInfo = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && questionIds.Contains(x.QuestionId)).ToListAsync(); + //List questionAnswerList = new List(); + //foreach (var item in questionList) + //{ + // await _readingTableQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => x.QuestionId == item.ReadingQuestionId + // && x.TableQuestionId == item.TableQuestionId && x.VisitTaskId == inDto.VisitTaskId); + + // var orderMark = questionMarkList.Where(x => x.QuestionId == item.ReadingQuestionId).Select(x => x.OrderMark).FirstOrDefault(); + + // foreach (var row in rowInfo.Where(x => x.QuestionId == item.ReadingQuestionId)) + // { + // questionAnswerList.Add(new ReadingTableQuestionAnswer() + // { + // Answer = orderMark + row.RowIndex.GetLesionMark(), + // Id = NewId.NextGuid(), + // QuestionId = item.ReadingQuestionId, + // RowId = row.Id, + // RowIndex = row.RowIndex, + // TableQuestionId = item.TableQuestionId, + // TrialId = taskInfo.TrialId, + // VisitTaskId = taskInfo.Id, + + // }); + // } + //} + //await _readingTableQuestionAnswerRepository.AddRangeAsync(questionAnswerList); + //await _readingTableQuestionAnswerRepository.SaveChangesAsync(); + + + + + await this.SubmitTaskChangeState(inDto.VisitTaskId); + return ResponseOutput.Ok(true); + } + + + + + #endregion + + + #region Dicom 非dicom 公用 + + /// + /// 验证默认问题是否回答 + /// + /// + /// + /// + [HttpPost] + public async Task VerifyDefaultQuestionBeAnswer(VerifyVisitTaskQuestionsInDto inDto) + { + + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Include(x=>x.TrialReadingCriterion).Include(x=>x.SourceSubjectVisit).FirstNotNullAsync(); + + var criterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == taskInfo.TrialReadingCriterionId).FirstNotNullAsync(); + + var readingQuestionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == taskInfo.TrialReadingCriterionId) + .WhereIf(taskInfo.SourceSubjectVisit.IsBaseLine,x=> ((x.IsRequired == IsRequired.Required && x.ShowQuestion == ShowQuestion.Show)&&(x.LimitEdit==LimitEdit.None||x.LimitEdit==LimitEdit.OnlyBaseLine))) + .WhereIf(!taskInfo.SourceSubjectVisit.IsBaseLine, x => ((x.IsRequired == IsRequired.Required && x.ShowQuestion == ShowQuestion.Show) && (x.LimitEdit == LimitEdit.None || x.LimitEdit == LimitEdit.OnlyVisit))) + .WhereIf(taskInfo.TrialReadingCriterion.CriterionType==CriterionType.PCWG3, x => x.QuestionType != QuestionType.SiteVisitForTumorEvaluation) + + //.WhereIf(!criterion.IseCRFShowInDicomReading,x=>x.IsShowInDicom) + .ToListAsync(); + + var answerQuestionIds = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId&&x.Answer!=string.Empty).Select(x => x.ReadingQuestionTrialId).ToListAsync(); + + readingQuestionList = readingQuestionList.Where(x => !answerQuestionIds.Contains(x.Id)).ToList(); + + if (readingQuestionList.Count() > 0) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_RequiredQuestion", string.Join(',', readingQuestionList.Select(x => x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us)))]); + } + return ResponseOutput.Ok(true); + } + + + /// + /// 验证检查批次提交 + /// + /// + /// + [HttpPost] + public async Task VerifyVisitTaskQuestions(VerifyVisitTaskQuestionsInDto inDto) + { + await VerifyTaskIsSign(inDto.VisitTaskId); + await VerifyDefaultQuestionBeAnswer(inDto); + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + var isBaseline = await _subjectVisitRepository.Where(x => x.Id == taskInfo.SourceSubjectVisitId).Select(x => x.IsBaseLine).FirstOrDefaultAsync(); + var readingQuestionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == taskInfo.TrialReadingCriterionId + && (x.IsJudgeQuestion || (x.IsRequired == IsRequired.Required && x.ShowQuestion == ShowQuestion.Show)) + ).ToListAsync(); + if (isBaseline) + { + readingQuestionList = readingQuestionList.Where(x => x.LimitEdit != LimitEdit.OnlyVisit).ToList(); + } + else + { + readingQuestionList = readingQuestionList.Where(x => x.LimitEdit != LimitEdit.OnlyBaseLine).ToList(); + } + var answerQuestionIds = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId).Select(x => x.ReadingQuestionTrialId).ToListAsync(); + + readingQuestionList = readingQuestionList.Where(x => !answerQuestionIds.Contains(x.Id)).ToList(); + + if (readingQuestionList.Count() > 0) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_RequiredQuestion", string.Join(',', readingQuestionList.Select(x => x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us)))]); + } + + await _readingCalculateService.VerifyVisitTaskQuestions(inDto); + + var clinicalDataList = await _readingClinicalDataService.GetClinicalDataList(new GetReadingOrTaskClinicalDataListInDto() + { + + SubjectId = taskInfo.SubjectId, + TrialId = taskInfo.TrialId, + VisitTaskId = taskInfo.Id, + }); + + var isBaseLine = false; + if (taskInfo.SourceSubjectVisitId != null) + { + isBaseLine = await _subjectVisitRepository.Where(x => x.Id == taskInfo.SourceSubjectVisitId).Select(x => x.IsBaseLine).FirstOrDefaultAsync(); + } + + var isNeedReadClinicalData = false; + + if (isBaseLine) + { + isNeedReadClinicalData = clinicalDataList.Count() > 0; + } + else + { + isNeedReadClinicalData = clinicalDataList.Where(x => x.ClinicalDataLevel != ClinicalLevel.Subject).Count() > 0; + } + + if (isNeedReadClinicalData && !taskInfo.IsReadClinicalData) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_ClinicalRead"]); + } + + + + return ResponseOutput.Ok(true); + } + + + + + /// + /// 获取下一个阅片任务 + /// + /// + /// + [HttpPost] + public async Task GetNextTask(GetNextTaskInDto inDto) + { + if (inDto.VisitTaskId == null) + { + await VerifyReadingRestTime(); + } + + GetReadingTaskDto? task = new GetReadingTaskDto(); + + var trialReadingCriterionId = inDto.TrialReadingCriterionId; + + + if (trialReadingCriterionId == null && inDto.VisitTaskId == null) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_IDMust"]); + } + + if (inDto.VisitTaskId != null) + { + task = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Select(x => new GetReadingTaskDto() + { + VisitTaskId = x.Id, + TaskBlindName = x.TaskBlindName, + SubjectId = x.SubjectId, + ReadingCategory = x.ReadingCategory, + ArmEnum = x.ArmEnum, + VisistId = x.SourceSubjectVisitId != null ? x.SourceSubjectVisitId.Value : default(Guid), + VisitNum = x.VisitTaskNum, + TrialReadingCriterionId = x.TrialReadingCriterionId, + + }).FirstOrDefaultAsync(); + + trialReadingCriterionId = task.TrialReadingCriterionId; + + } + else if (inDto.SubjectId != null) + { + var subjectTaskList = (await _visitTaskService.GetOrderReadingIQueryable(new GetOrderReadingIQueryableInDto() + { + TrialId = inDto.TrialId, + TrialReadingCriterionId = trialReadingCriterionId.Value, + Page=new PageInput() { + PageIndex=1, + PageSize=99999, + } + })).Item2; + + var index = 0; + subjectTaskList.ForEach(x => + { + x.Index = index; + index++; + }); + + var subjectIndex = subjectTaskList.Where(x => x.SubjectId == inDto.SubjectId && x.SubjectCode == inDto.SubjectCode).Select(x => x.Index).FirstOrDefault(); + + var currentSubject = subjectTaskList.Where(x => x.Index >= subjectIndex && !x.ExistReadingApply).OrderBy(x => x.Index).FirstOrDefault(); + + if (currentSubject == null) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_TaskFinish"]); + } + + task = currentSubject.UnReadCanReadTaskList.Select(x => new GetReadingTaskDto() + { + ReadingCategory = x.ReadingCategory, + SubjectCode = currentSubject.SubjectCode, + SubjectId = currentSubject.SubjectId, + TaskBlindName = x.TaskBlindName, + VisitNum = x.VisitNum, + ArmEnum = x.ArmEnum, + VisistId = x.VisistId ?? default(Guid), + VisitTaskId = x.Id, + TrialReadingCriterionId = x.TrialReadingCriterionId, + }).FirstOrDefault(); + + + + } + else + { + task = await _visitTaskRepository.Where(x => x.TrialId == inDto.TrialId && x.TrialReadingCriterionId == trialReadingCriterionId && x.ReadingTaskState != ReadingTaskState.HaveSigned && x.DoctorUserId == _userInfo.Id + && x.TrialReadingCriterionId == trialReadingCriterionId + && x.TaskState == TaskState.Effect).Select(x => new GetReadingTaskDto() + { + VisitTaskId = x.Id, + ArmEnum= x.ArmEnum, + TaskBlindName = x.TaskBlindName, + ReadingCategory = x.ReadingCategory, + VisistId = x.SourceSubjectVisitId != null ? x.SourceSubjectVisitId.Value : x.ReadModule == null ? default(Guid) : x.ReadModule.SubjectVisitId, + VisitNum = x.VisitTaskNum, + SubjectId = x.SubjectId, + SubjectCode = x.Subject.Code, + TrialReadingCriterionId = x.TrialReadingCriterionId, + }).FirstOrDefaultAsync(); + + } + + if (task == null) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_TaskFinish"]); + } + + if (task.SubjectCode.IsNullOrEmpty()) + { + task.SubjectCode = await _subjectRepository.Where(x => x.Id == task.SubjectId).Select(x => x.Code).FirstOrDefaultAsync(); + } + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => x.Id == task.VisitTaskId && x.FirstReadingTime == null, x => new VisitTask() + { + FirstReadingTime = DateTime.Now, + }); + await _visitTaskRepository.SaveChangesAsync(); + + var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == task.VisitTaskId).FirstNotNullAsync(); + + task.SubjectCode = visitTaskInfo.BlindSubjectCode.IsNullOrEmpty() ? task.SubjectCode : visitTaskInfo.BlindSubjectCode; + + + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == visitTaskInfo.TrialReadingCriterionId).Select(x => new + { + x.IsReadingShowPreviousResults, + x.IsReadingShowSubjectInfo, + x.DigitPlaces, + x.CriterionType, + x.IseCRFShowInDicomReading, + x.IsReadingTaskViewInOrder, + }).FirstOrDefaultAsync(); + + task.IsReadingShowPreviousResults = criterionInfo.IsReadingShowPreviousResults; + task.IsReadingShowSubjectInfo = criterionInfo.IsReadingShowSubjectInfo; + task.IsReadingTaskViewInOrder = criterionInfo.IsReadingTaskViewInOrder; + var isBaseLine = false; + if (visitTaskInfo.SourceSubjectVisitId != null) + { + task.IsExistsNoDicomFile = await _noneDicomStudyRepository.AnyAsync(x => x.SubjectVisitId == visitTaskInfo.SourceSubjectVisitId && x.FileCount > 0); + isBaseLine = await _subjectVisitRepository.Where(x => x.Id == visitTaskInfo.SourceSubjectVisitId).Select(x => x.IsBaseLine).FirstOrDefaultAsync(); + } + + var clinicalDataList = await _readingClinicalDataService.GetClinicalDataList(new GetReadingOrTaskClinicalDataListInDto() + { + + SubjectId = task.SubjectId, + TrialId = inDto.TrialId, + VisitTaskId = task.VisitTaskId, + }); + + task.IsExistsClinicalData = clinicalDataList.Count() > 0; + task.IsReadClinicalData = visitTaskInfo.IsReadClinicalData; + + if (isBaseLine) + { + task.IsNeedReadClinicalData = clinicalDataList.Count() > 0; + } + else + { + task.IsNeedReadClinicalData = clinicalDataList.Where(x => x.ClinicalDataLevel != ClinicalLevel.Subject).Count() > 0; + } + + // 如果已经签名 就不需要再读了 + task.IsNeedReadClinicalData = visitTaskInfo.ReadingTaskState == ReadingTaskState.HaveSigned ? false : task.IsNeedReadClinicalData; + task.DigitPlaces = criterionInfo.DigitPlaces; + task.CriterionType = criterionInfo.CriterionType; + task.IseCRFShowInDicomReading = criterionInfo.IseCRFShowInDicomReading; + + var blindSubjectCode = await _visitTaskRepository.Where(x => x.Id == task.VisitTaskId).Select(x => x.BlindSubjectCode).FirstNotNullAsync(); + task.SubjectCode = blindSubjectCode.IsNullOrEmpty() ? task.SubjectCode : blindSubjectCode; + return task; + } + + /// + /// 验证阅片休息时间 + /// + /// + [HttpPost] + public async Task VerifyReadingRestTime() + { + var cacheKey = _userInfo.Id.ToString(); + var value = _cache.Get(cacheKey); + if (value == null) + { + _cache.Set(cacheKey, DateTime.Now.ToString(), TimeSpan.FromHours(5)); + + } + else + { + #region 两小时 + var cacheDate = DateTime.Parse(value.ToString()); + int timespanMin = (DateTime.Now - cacheDate).Minutes; + if (timespanMin > 120 && timespanMin < 140) + { + throw new BusinessValidationFailedException(_localizer["ReadingImage_NeedRest",2,20]); + } + else if (timespanMin > 140) + { + cacheDate = cacheDate.AddMinutes((Math.Floor((double)(timespanMin / 140))) * 140); + _cache.Set(cacheKey, cacheDate.ToString(), TimeSpan.FromHours(5)); + + } + #endregion + + #region 测试用的5分钟 + //var cacheDate = DateTime.Parse(value.ToString()); + //int timespanMin = (DateTime.Now - cacheDate).Minutes; + //if (timespanMin >= 5 && timespanMin <= 10) + //{ + // throw new BusinessValidationFailedException("您已连续阅片2个小时,请休息20分钟后,再继续阅片。"); + //} + //else if (timespanMin > 10) + //{ + // cacheDate = cacheDate.AddMinutes((Math.Floor((double)(timespanMin / 10))) * 10); + // _cache.Set(cacheKey, cacheDate.ToString(), TimeSpan.FromHours(5)); + + //} + #endregion + + + } + + + + } + + /// + /// 签名提交任务修改状态 + /// + /// + /// + private async Task SubmitTaskChangeState(Guid visitTaskId) + { + await VerifyTaskIsSign(visitTaskId); + await _visitTaskRepository.UpdatePartialFromQueryAsync(visitTaskId, x => new VisitTask() + { + ReadingTaskState = ReadingTaskState.HaveSigned, + SignTime = DateTime.Now, + }); + + await _visitTaskRepository.SaveChangesAsync(); + + // 创建任务关联关系 + await this.CreateTaskRelated(visitTaskId); + + // 触裁判任务 + await this.TriggerJudgeQuestion(visitTaskId); + // 添加阅片期任务 + await this.AddReadingTask(visitTaskId); + + // 完成阅片修改状态 + //await this.FinishReadUpdateState(visitTaskId); + + + + await _visitTaskRepository.SaveChangesAsync(); + + await _trialEmailNoticeConfigService.BaseBusinessScenarioSendEmailAsync(visitTaskId); + } + + + /// + /// 签名时创建任务关联 + /// + /// + private async Task CreateTaskRelated(Guid visitTaskId) + { + var taskInfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + + + // 判断是否有序阅片 + + var isReadingTaskViewInOrder = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == taskInfo.TrialReadingCriterionId).Select(x => x.IsReadingTaskViewInOrder).FirstNotNullAsync(); + + // 判断任务类型 + + // 关联Id + List relatedVisitTaskIdList = new List(); + + // 既往任务Id + List pastResultTaskIdList = new List(); + + + if (isReadingTaskViewInOrder) + { + pastResultTaskIdList = await _visitTaskRepository.Where(x => + x.TrialId == taskInfo.TrialId && + x.SubjectId == taskInfo.SubjectId && + x.VisitTaskNum < taskInfo.VisitTaskNum && + x.ArmEnum == taskInfo.ArmEnum && + x.DoctorUserId == taskInfo.DoctorUserId && + x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && + x.ReadingTaskState == ReadingTaskState.HaveSigned && + x.TaskState == TaskState.Effect && + x.IsSelfAnalysis == taskInfo.IsSelfAnalysis && + x.IsAnalysisCreate == taskInfo.IsAnalysisCreate && + x.ReadingCategory == taskInfo.ReadingCategory && + x.Id != taskInfo.Id + ).Select(x => x.Id).ToListAsync(); + + switch (taskInfo.ReadingCategory) + { + case ReadingCategory.Visit: + case ReadingCategory.Global: + case ReadingCategory.Oncology: + relatedVisitTaskIdList = await _visitTaskRepository.Where(x => + x.TrialId == taskInfo.TrialId && + x.SubjectId == taskInfo.SubjectId && + x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && + x.ReadingTaskState == ReadingTaskState.HaveSigned && + x.IsAnalysisCreate == taskInfo.IsAnalysisCreate && + x.ArmEnum == taskInfo.ArmEnum && + x.IsSelfAnalysis == taskInfo.IsSelfAnalysis && + x.DoctorUserId == taskInfo.DoctorUserId && + x.TaskState == TaskState.Effect && x.VisitTaskNum <= taskInfo.VisitTaskNum).Select(x => x.Id).ToListAsync(); + break; + } + } + else + { + if (taskInfo.ReadingCategory == ReadingCategory.Visit) + { + relatedVisitTaskIdList.Add(taskInfo.Id); + } + } + + await _visitTaskRepository.UpdatePartialFromQueryAsync(taskInfo.Id, x => new VisitTask() + { + + + + PastResultTaskIds = JsonConvert.SerializeObject(pastResultTaskIdList), + RelatedVisitTaskIds = JsonConvert.SerializeObject(relatedVisitTaskIdList), + }); + + await _readingTaskRelationRepository.AddRangeAsync(pastResultTaskIdList.Select(x => new ReadingTaskRelation() + { + RelevanceTaskId = x, + TaskId = visitTaskId, + RelevanceType = RelevanceType.PastResult, + })); + + await _readingTaskRelationRepository.AddRangeAsync(relatedVisitTaskIdList.Select(x => new ReadingTaskRelation() + { + RelevanceTaskId = x, + TaskId = visitTaskId, + RelevanceType = RelevanceType.Related, + })); + + + await _visitTaskRepository.SaveChangesAsync(); + } + + + + /// + /// 阅片期 -全局和肿瘤学任务的生成 + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddReadingTask(Guid visitTaskId,Guid? trialId=null) + { + // ****** 先生成阅片期 阅片期任务阅片完成之后生成肿瘤学的 如果没有阅片期 直接生成肿瘤学 *********//// + #region 建立关系 + // 检查批次阅完产生 全局 + Dictionary typeChangeDic = new Dictionary(); + typeChangeDic.Add(ModuleTypeEnum.InPlanSubjectVisit, ReadingCategory.Visit); + typeChangeDic.Add(ModuleTypeEnum.OutPlanSubjectVisit, ReadingCategory.Visit); + //typeChange.Add(ModuleTypeEnum.Read, ReadingCategory.ReadingPeriod); + typeChangeDic.Add(ModuleTypeEnum.Global, ReadingCategory.Global); + typeChangeDic.Add(ModuleTypeEnum.Referee, ReadingCategory.Judge); + typeChangeDic.Add(ModuleTypeEnum.Oncology, ReadingCategory.Oncology); + #endregion + var taskInfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + List needReadList = new List(); + if (!taskInfo.IsAnalysisCreate) + { + // 任务类型 + switch (taskInfo.ReadingCategory) + { + case ReadingCategory.Visit: + needReadList = await _readModuleRepository.Where(x => x.SubjectVisitId == taskInfo.SourceSubjectVisitId && + + x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && + x.ReadingSetType == ReadingSetType.ImageReading) + .Select(x => new ReadingGenerataTaskDTO + { + IsUrgent = x.IsUrgent ?? false, + SubjectId = x.SubjectId, + VisitNum = x.SubjectVisit.VisitNum, + ReadingName = x.ModuleName, + ReadModuleId = x.Id, + ReadingCategory = typeChangeDic[x.ModuleType], + }).ToListAsync(); + if (needReadList.Any(x => x.ReadingCategory == ReadingCategory.Global)) + { + needReadList = needReadList.Where(x => x.ReadingCategory != ReadingCategory.Oncology).ToList(); + } + //needReadList = needReadList.Where(x => _visitTaskRepository.Where(y => y.SouceReadModuleId == x.ReadModuleId).Count() == 0).ToList(); + await _visitTaskHelpeService.AddTaskAsync(new GenerateTaskCommand() + { + OriginalVisitId = visitTaskId, + ReadingCategory = GenerateTaskCategory.Global, + TrialId = taskInfo.TrialId, + + ReadingGenerataTaskList = needReadList + }); + break; + // 肿瘤学 + case ReadingCategory.Global: + var subjectVisitId = await _readModuleRepository.Where(x => x.Id == taskInfo.SouceReadModuleId).Select(x => x.SubjectVisitId).FirstOrDefaultAsync(); + var oncologyReadId = await _readModuleRepository.Where(x => x.SubjectVisitId == subjectVisitId && x.ModuleType == ModuleTypeEnum.Oncology + && x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId + + + ).Select(x => x.Id).FirstOrDefaultAsync(); + + if (await _visitTaskRepository.AnyAsync(x => x.Id == visitTaskId && x.JudgeVisitTaskId == null)) + { + await AddOncologyTask(oncologyReadId); + } + + + break; + } + } + + + + + + } + + #endregion + + + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingJudgeTaskService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingJudgeTaskService.cs new file mode 100644 index 0000000..8422e91 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingJudgeTaskService.cs @@ -0,0 +1,935 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using MassTransit; +using IRaCIS.Core.Infra.EFCore.Common; +using Panda.DynamicWebApi.Attributes; +using AutoMapper; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Infrastructure; +using Newtonsoft.Json; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Application.Services +{ + + /// + /// 裁判 + /// + public partial class ReadingImageTaskService : BaseService, IReadingImageTaskService + { + #region 配置裁判问题相关 + + /// + /// 获取项目标准的裁判问题 + /// + /// + /// + [HttpPost] + public async Task<(List, object)> GetTrialCriterionJudgeQuestionList(GetTrialCriterionJudgeQuestionListInDto inDto) + { + var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).FirstNotNullAsync(); + + var result = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == inDto.TrialReadingCriterionId && x.IsJudgeQuestion) + .WhereIf(trialCriterion.FormType == FormType.SinglePage, x => x.ReadingCriterionPageId == null) + .WhereIf(trialCriterion.FormType == FormType.MultiplePage, x => x.ReadingCriterionPageId != null) + .Select(x => new GetTrialCriterionJudgeQuestionListOutDto() + { + AnswerGroup = JsonConvert.DeserializeObject>(x.AnswerGroup.IsNullOrEmpty() ? "[]" : x.AnswerGroup), + AnswerCombination = JsonConvert.DeserializeObject>(x.AnswerCombination.IsNullOrEmpty() ? "[]" : x.AnswerCombination), + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + PageName = x.ReadingCriterionPage.PageName, + TypeValue = x.TypeValue, + QuestionGenre=x.QuestionGenre, + DictionaryCode=x.DictionaryCode, + JudgeType = x.JudgeType, + ReadingQuestionTrialId = x.Id + }).ToListAsync(); + + + return (result, new + { + IsSign = trialCriterion.ReadingInfoSignTime != null, + }); + + } + + /// + /// 设置裁判问题的答案分组 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetTrialCriterionJudgeQuestionAnswerGroup(SetTrialCriterionJudgeQuestionAnswerGroupInDto inDto) + { + await _readingQuestionTrialRepository.UpdatePartialFromQueryAsync(inDto.ReadingQuestionTrialId, x => new ReadingQuestionTrial() + { + AnswerGroup = JsonConvert.SerializeObject(inDto.AnswerGroup), + AnswerCombination = JsonConvert.SerializeObject(inDto.AnswerCombination), + JudgeType = inDto.JudgeType, + }); + + var result = await _readingQuestionTrialRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(result); + } + #endregion + + + #region 获取裁判阅片任务信息 + /// + /// 获取裁判阅片任务信息 + /// + /// + [HttpPost] + public async Task GetJudgeReadingInfo(GetJudgeReadingInfo inDto) + { + var visitTask = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstOrDefaultAsync(); + + + var criterionType = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == visitTask.TrialReadingCriterionId).Select(x => x.CriterionType).FirstNotNullAsync(); + GetJudgeReadingInfoOutDto judgeInfo = new GetJudgeReadingInfoOutDto() + { + ReadingTaskState = visitTask.ReadingTaskState, + JudgeResultTaskId = visitTask.JudgeResultTaskId, + JudgeResultRemark = visitTask.JudgeResultRemark, + JudgeResultImagePathList = visitTask.JudgeResultImagePathList, + VisitInfoList = new List() + }; + + var judgeDataInfo = await _readingJudgeInfoRepository.Where(x => x.JudgeTaskId == inDto.VisitTaskId).FirstNotNullAsync(); + + var taskids = new List(); + + taskids.Add(judgeDataInfo.TaskIdOne); + taskids.Add(judgeDataInfo.TaskIdTwo); + var taskList = await _visitTaskRepository.Where(x => taskids.Contains(x.Id)).OrderBy(x => x.ArmEnum).ToListAsync(); + judgeInfo.VisitTaskArmList = taskList.Select(x => new VisitTaskArm() + { + ArmEnum = x.ArmEnum, + VisitTaskId = x.Id + + }).ToList(); + var visitIds = await _visitTaskRepository.Where(x => x.JudgeVisitTaskId == inDto.VisitTaskId).Select(x => new + { + x.Id, + x.ArmEnum, + }).ToListAsync(); + + switch (taskList[0].ReadingCategory) + { + case ReadingCategory.Visit: + + // 判断是否是全局检查批次任务 + if (await VerifyIsGlobalVisitTask(taskList[0].Id)) + { + + var globalVisitTaskIds = await _visitTaskRepository.Where(x=>x.Id== taskList[0].Id).Select(x=>x.RelatedVisitTaskIdList).FirstNotNullAsync(); + + // 找到所有的的任务 + var globalVisitTasks = await _visitTaskRepository.Where(x => globalVisitTaskIds.Contains(x.Id)).Select(x => new { + x.Id, + x.ArmEnum, + x.VisitTaskNum, + x.SourceSubjectVisitId, + x.TaskBlindName, + } ).ToListAsync(); + + var globalVisitTwoTaskIds = await _visitTaskRepository.Where(x => x.Id == taskList[1].Id).Select(x => x.RelatedVisitTaskIdList).FirstNotNullAsync(); + + // 找到所有的的任务 + var globalVisitTwoTasks = await _visitTaskRepository.Where(x => globalVisitTwoTaskIds.Contains(x.Id)).Select(x => new { + x.Id, + x.ArmEnum, + x.VisitTaskNum, + x.SourceSubjectVisitId, + x.TaskBlindName, + }).ToListAsync(); + + var taskNum = globalVisitTasks.Select(x => x.VisitTaskNum).Distinct().OrderBy(x=>x).ToList(); + + + var judgeQuestionAnswer = await _readingTaskQuestionAnswerRepository.Where(x => globalVisitTaskIds.Contains(x.VisitTaskId) && x.ReadingQuestionTrial.IsJudgeQuestion).OrderBy(x => x.ReadingQuestionTrial.ShowOrder) + .Select(x => new GlobalVisitJudgeQuestion() + { + Answer = x.Answer, + ShowOrder = x.ReadingQuestionTrial.ShowOrder, + VisitTaskId = x.VisitTaskId, + QuestionId = x.ReadingQuestionTrial.Id, + QuestionName = x.ReadingQuestionTrial.QuestionName.LanguageName(x.ReadingQuestionTrial.QuestionEnName, _userInfo.IsEn_Us), + QuestionGenre = x.ReadingQuestionTrial.QuestionGenre, + DictionaryCode = x.ReadingQuestionTrial.DictionaryCode, + + }).ToListAsync(); + + var judgeQuestionTwoAnswer = await _readingTaskQuestionAnswerRepository.Where(x => globalVisitTwoTaskIds.Contains(x.VisitTaskId) && x.ReadingQuestionTrial.IsJudgeQuestion).OrderBy(x => x.ReadingQuestionTrial.ShowOrder) + .Select(x => new GlobalVisitJudgeQuestion() + { + Answer = x.Answer, + ShowOrder = x.ReadingQuestionTrial.ShowOrder, + VisitTaskId = x.VisitTaskId, + QuestionId = x.ReadingQuestionTrial.Id, + QuestionName = x.ReadingQuestionTrial.QuestionName.LanguageName(x.ReadingQuestionTrial.QuestionEnName, _userInfo.IsEn_Us), + QuestionGenre = x.ReadingQuestionTrial.QuestionGenre, + DictionaryCode = x.ReadingQuestionTrial.DictionaryCode, + + }).ToListAsync(); + + foreach (var item in taskNum) + { + var globalTaskInfos = globalVisitTasks.Where(x => x.VisitTaskNum == item).OrderBy(x => x.ArmEnum).ToList(); + + JudgeReadingInfoDto judgeReadingInfoDto = new JudgeReadingInfoDto() + { + VisitId = globalTaskInfos[0].SourceSubjectVisitId.Value, + VisitName = globalTaskInfos[0].TaskBlindName, + VisitTaskInfoList = new List(), + }; + + foreach (var globalitem in globalTaskInfos) + { + judgeReadingInfoDto.VisitTaskInfoList.Add(new JudgeReadingQuestion() + { + ArmEnum = globalitem.ArmEnum, + VisitTaskId = globalitem.Id, + JudgeQuestionList = judgeQuestionAnswer.Where(x => x.VisitTaskId == globalitem.Id).OrderBy(x => x.ShowOrder) + .Select(x => new JudgeQuestion() + { + Answer = x.Answer, + QuestionId = x.QuestionId, + QuestionName = x.QuestionName, + QuestionGenre = x.QuestionGenre, + DictionaryCode = x.DictionaryCode, + + }).ToList(), + }); + + + var towtask = globalVisitTwoTasks.Where(x => x.VisitTaskNum == globalitem.VisitTaskNum).FirstOrDefault(); + + judgeReadingInfoDto.VisitTaskInfoList.Add(new JudgeReadingQuestion() + { + ArmEnum = towtask.ArmEnum, + VisitTaskId = towtask.Id, + JudgeQuestionList = judgeQuestionTwoAnswer.Where(x => x.VisitTaskId == towtask.Id).OrderBy(x => x.ShowOrder) + .Select(x => new JudgeQuestion() + { + Answer = x.Answer, + QuestionId = x.QuestionId, + QuestionName = x.QuestionName, + QuestionGenre = x.QuestionGenre, + DictionaryCode = x.DictionaryCode, + + }).ToList(), + }); + } + judgeInfo.VisitInfoList.Add(judgeReadingInfoDto); + } + + } + else + { + JudgeReadingInfoDto judgeReadingInfoDto = new JudgeReadingInfoDto() + { + VisitId = taskList[0].SourceSubjectVisitId.Value, + VisitName = taskList[0].TaskBlindName, + VisitTaskInfoList = new List(), + }; + + foreach (var item in taskList) + { + judgeReadingInfoDto.VisitTaskInfoList.Add(new JudgeReadingQuestion() + { + ArmEnum = item.ArmEnum, + VisitTaskId = item.Id, + JudgeQuestionList = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == item.Id && x.ReadingQuestionTrial.IsJudgeQuestion).OrderBy(x => x.ReadingQuestionTrial.ShowOrder) + .Select(x => new JudgeQuestion() + { + Answer = x.Answer, + QuestionId = x.ReadingQuestionTrial.Id, + QuestionName = x.ReadingQuestionTrial.QuestionName.LanguageName(x.ReadingQuestionTrial.QuestionEnName, _userInfo.IsEn_Us), + QuestionGenre = x.ReadingQuestionTrial.QuestionGenre, + DictionaryCode = x.ReadingQuestionTrial.DictionaryCode, + + }).ToListAsync(), + }); + } + judgeInfo.VisitInfoList.Add(judgeReadingInfoDto); + } + + + break; + + case ReadingCategory.Global: + var taskOneInfo = await this.GetGlobalReadingInfo(new GetGlobalReadingInfoInDto() + { + UsingOriginalData = true, + VisitTaskId = taskList[0].Id + }); + + var taskTwoInfo = await this.GetGlobalReadingInfo(new GetGlobalReadingInfoInDto() + { + UsingOriginalData = true, + VisitTaskId = taskList[1].Id + }); + + + foreach (var item in taskOneInfo.TaskList) + { + GlobalVisitInfo twoItem = taskTwoInfo.TaskList.Where(x => x.VisitId == item.VisitId).FirstOrDefault(); + + JudgeReadingInfoDto judgeReadingInfo = new JudgeReadingInfoDto() + { + VisitId = item.VisitId, + VisitName = item.BlindName, + VisitTaskInfoList = new List(), + }; + + var judgeReadingQuestion = new JudgeReadingQuestion() + { + ArmEnum = item.ArmEnum, + VisitTaskId = item.VisitTaskId, + GlobalVisitTaskId = taskList[0].Id, + JudgeQuestionList = item.AfterQuestionList.Where(x => x.GlobalAnswerType== GlobalAnswerType.Question).Select(x => new JudgeQuestion() + { + + Answer = x.Answer, + QuestionId = x.QuestionId.Value, + QuestionName = x.QuestionName, + QuestionGenre=x.QuestionGenre, + DictionaryCode=x.DictionaryCode, + }).ToList() + }; + + + if (criterionType != CriterionType.PCWG3) + { + // 加全局是否更新 和检查批次点注释 PCWG3不要 + judgeReadingQuestion.JudgeQuestionList.Add(new JudgeQuestion() + { + Answer = item.AfterQuestionList.Any(x => x.IsHaveChange), + QuestionType = JudgeReadingQuestionType.GlobalChange, + + }); + } + + + judgeReadingQuestion.JudgeQuestionList.Add(new JudgeQuestion() + { + Answer = item.AfterQuestionList.Where(x => x.GlobalAnswerType == GlobalAnswerType.Reason).Select(x => x.Answer).FirstOrDefault(), + QuestionType = JudgeReadingQuestionType.VisitRemark, + QuestionName = "", + }); + + judgeReadingInfo.VisitTaskInfoList.Add(judgeReadingQuestion); + if (twoItem != null) + { + var rTwoJudge = new JudgeReadingQuestion() + { + ArmEnum = twoItem.ArmEnum, + + VisitTaskId = twoItem.VisitTaskId, + GlobalVisitTaskId = taskList[1].Id, + JudgeQuestionList = twoItem.AfterQuestionList.Where(x => x.GlobalAnswerType == GlobalAnswerType.Question).Select(x => new JudgeQuestion() + { + + Answer = x.Answer, + QuestionId = x.QuestionId.Value, + QuestionName = x.QuestionName, + QuestionGenre = x.QuestionGenre, + DictionaryCode = x.DictionaryCode, + }).ToList() + }; + + + + if (criterionType != CriterionType.PCWG3) + { + // 加全局是否更新 和检查批次点注释 + rTwoJudge.JudgeQuestionList.Add(new JudgeQuestion() + { + Answer = twoItem.AfterQuestionList.Any(x => x.IsHaveChange), + QuestionType = JudgeReadingQuestionType.GlobalChange, + + }); + } + + + + + rTwoJudge.JudgeQuestionList.Add(new JudgeQuestion() + { + Answer = twoItem.AfterQuestionList.Where(x => x.GlobalAnswerType == GlobalAnswerType.Reason).Select(x => x.Answer).FirstOrDefault(), + QuestionType = JudgeReadingQuestionType.VisitRemark, + QuestionName = "", + }); + + judgeReadingInfo.VisitTaskInfoList.Add(rTwoJudge); + } + + judgeInfo.VisitInfoList.Add(judgeReadingInfo); + + + } + + break; + } + + return judgeInfo; + + } + #endregion + + #region 保存裁判问题 + /// + /// 保存裁判问题 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SaveJudgeVisitTaskResult(SaveJudgeVisitTaskResult inDto) + { + await VerifyTaskIsSign(inDto.VisitTaskId); + await _visitTaskRepository.UpdatePartialFromQueryAsync(inDto.VisitTaskId, x => new VisitTask() + { + JudgeResultTaskId = inDto.JudgeResultTaskId, + JudgeResultRemark = inDto.JudgeResultRemark, + JudgeResultImagePath = string.Join(',',inDto.JudgeResultImagePathList), + + }); + var result = await _visitTaskRepository.SaveChangesAsync(); + return ResponseOutput.Ok(result); + } + #endregion + + + /// + /// 提交裁判问题 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SubmitJudgeVisitTaskResult(SaveJudgeVisitTaskResult inDto) + { + await VerifyTaskIsSign(inDto.VisitTaskId); + await _visitTaskRepository.UpdatePartialFromQueryAsync(inDto.VisitTaskId, x => new VisitTask() + { + JudgeResultTaskId = inDto.JudgeResultTaskId, + ReadingTaskState = ReadingTaskState.HaveSigned, + JudgeResultRemark = inDto.JudgeResultRemark, + SignTime = DateTime.Now, + JudgeResultImagePath = string.Join(',', inDto.JudgeResultImagePathList), + }); + await _visitTaskRepository.SaveChangesAsync(); + // 需要判断是否添加肿瘤学任务 + var taskInfo = await _visitTaskRepository.Where(x => x.JudgeVisitTaskId == inDto.VisitTaskId).FirstNotNullAsync(); + if (taskInfo.ReadingCategory == ReadingCategory.Global) + { + if (taskInfo.SouceReadModuleId == null) + { + throw new BusinessValidationFailedException(_localizer["ReadingJudge_SouceIdNull"]); + } + + var visitId = await _readModuleRepository.Where(x => x.Id == taskInfo.SouceReadModuleId).Select(x => x.SubjectVisitId).FirstOrDefaultAsync(); + + var oncologModuleId = await _readModuleRepository.Where(x => x.SubjectVisitId == visitId && x.ModuleType == ModuleTypeEnum.Oncology + &&x.TrialReadingCriterionId== taskInfo.TrialReadingCriterionId + ).Select(x => x.Id).FirstOrDefaultAsync(); + + await AddOncologyTask(oncologModuleId); + + } + + + var result = await _visitTaskRepository.SaveChangesAsync(); + + + // 创建任务关联关系 + await this.CreateTaskRelated(inDto.VisitTaskId); + + await _trialEmailNoticeConfigService.BaseBusinessScenarioSendEmailAsync(inDto.VisitTaskId); + return ResponseOutput.Ok(result); + } + + /// + /// 判断任务是否是全局检查批次任务 + /// + /// + private async Task VerifyIsGlobalVisitTask(Guid visitTaskId) + { + // 任务 + var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + + if (taskinfo.ReadingCategory != ReadingCategory.Visit) + { + return false; + } + + var criterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == taskinfo.TrialReadingCriterionId).FirstNotNullAsync(); + + var readModule = await _readModuleRepository.Where(x => x.SubjectVisitId == taskinfo.SourceSubjectVisitId && x.TrialReadingCriterionId == taskinfo.TrialReadingCriterionId).FirstOrDefaultAsync(); + + if (criterion.IsReadingPeriod && !criterion.IsGlobalReading && readModule != null) + { + return true; + } + else + { + return false; + } + + } + + /// + /// 触发裁判任务(新) + /// + /// + /// + public async Task TriggerJudgeQuestion(Guid visitTaskId) + { + List visitTaskids = new List(); + + var visitTask = await _visitTaskRepository.Where(x => x.Id == visitTaskId).AsNoTracking().FirstNotNullAsync(); + + // 是否是组件一致性 + var isGroupAnalysis = false; + + // 判断是否是一致性核查产生 + if (visitTask.IsAnalysisCreate) + { + if (visitTask.IsSelfAnalysis == true) + { + visitTaskids = await _visitTaskRepository.Where(x => x.ArmEnum == visitTask.ArmEnum && x.TaskState == TaskState.Effect && x.SourceSubjectVisitId == visitTask.SourceSubjectVisitId + && x.TrialReadingCriterionId == visitTask.TrialReadingCriterionId + && x.SouceReadModuleId == visitTask.SouceReadModuleId && x.ReadingCategory != ReadingCategory.Judge && x.ReadingTaskState == ReadingTaskState.HaveSigned).Select(x => x.Id).ToListAsync(); + } + + else + { + isGroupAnalysis = true; + visitTaskids = await _visitTaskRepository.Where(x => x.TaskState == TaskState.Effect && x.SourceSubjectVisitId == visitTask.SourceSubjectVisitId + && x.TrialReadingCriterionId == visitTask.TrialReadingCriterionId + && x.SouceReadModuleId == visitTask.SouceReadModuleId && x.ReadingCategory != ReadingCategory.Judge && x.ReadingTaskState == ReadingTaskState.HaveSigned).Select(x => x.Id).ToListAsync(); + + } + + + } + else + { + // 这里是非一致性分析产生的 + visitTaskids = await _visitTaskRepository.Where(x => x.ReadingTaskState == ReadingTaskState.HaveSigned && x.ReadingCategory != ReadingCategory.Judge + && x.TaskState == TaskState.Effect + && x.TrialReadingCriterionId == visitTask.TrialReadingCriterionId + && x.IsAnalysisCreate == false + && x.SourceSubjectVisitId == visitTask.SourceSubjectVisitId && x.SouceReadModuleId == visitTask.SouceReadModuleId).Select(x => x.Id).ToListAsync(); + } + + + if (!isGroupAnalysis) + { + await VerifyJudgeResult(visitTask, visitTaskids); + } + else + { + foreach (var item in visitTaskids.Where(x=>x!=visitTask.Id)) + { + var armEnum = await _visitTaskRepository.Where(x => x.Id == item).Select(x=>x.ArmEnum).FirstNotNullAsync(); + await VerifyJudgeResult(visitTask, new List() { visitTask.Id, item }, armEnum); + } + } + + + + + + } + + + private async Task VerifyJudgeResult(VisitTask visitTask,List visitTaskids,Arm? groupArm = null) + { + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == visitTask.TrialReadingCriterionId).Select(x => new + { + x.IsArbitrationReading, + x.ArbitrationRule, + x.IsGlobalReading, + x.IsReadingPeriod, + }).FirstNotNullAsync(); + + var noteEqual = false; + + // 判断项目是否设置了裁判 + if (criterionInfo.IsArbitrationReading) + { + // 判断数量是否为2 是否仲裁 + if (visitTaskids.Count == 2) + { + switch (visitTask.ReadingCategory) + { + // 检查批次 + case ReadingCategory.Visit: + + // 判断是单检查批次裁判还是全局检查批次裁判 + // 查找两个 检查批次的阅片答案 + if (await VerifyIsGlobalVisitTask(visitTask.Id)) + { + // 找到所有的的任务 + var GlobalVisitTaskIds = await _visitTaskRepository.Where(x => x.ReadingTaskState == ReadingTaskState.HaveSigned && x.ReadingCategory != ReadingCategory.Judge + && x.TaskState == TaskState.Effect + && x.TrialReadingCriterionId == visitTask.TrialReadingCriterionId + && x.IsAnalysisCreate == false + && x.ReadingCategory == ReadingCategory.Visit && x.VisitTaskNum <= visitTask.VisitTaskNum).Select(x => x.Id).ToListAsync(); + + + var globalVisitQuestionQuery = from questionAnswer in _readingTaskQuestionAnswerRepository.Where(x => GlobalVisitTaskIds.Contains(x.VisitTaskId)) + join question in _readingQuestionTrialRepository.Where(x => x.IsJudgeQuestion) on new { ReadingQuestionTrialId = questionAnswer.ReadingQuestionTrialId } equals new { ReadingQuestionTrialId = question.Id } + select new TaskAnswerDto() + { + VisitTaskNum = questionAnswer.VisitTask.VisitTaskNum, + Answer = questionAnswer.Answer, + AnswerGroup = question.AnswerGroup, + AnswerCombination = question.AnswerCombination, + JudgeType = question.JudgeType, + QuestionId = question.Id, + VisitTaskId = questionAnswer.VisitTaskId, + }; + var globalVisitAnswerlist = await globalVisitQuestionQuery.ToListAsync(); + + var taskNums = globalVisitAnswerlist.Select(x => x.VisitTaskNum).Distinct().OrderBy(x => x).ToList(); + + foreach (var item in taskNums) + { + List groupTasks = globalVisitAnswerlist.Where(x => x.VisitTaskNum == item).GroupBy(x => new { x.QuestionId, x.AnswerGroup, x.JudgeType, x.AnswerCombination }).Select(x => new GroupTaskAnswerDto + { + QuestionId = x.Key.QuestionId, + AnswerGroup = x.Key.AnswerGroup, + AnswerCombination = x.Key.AnswerCombination, + JudgeType = x.Key.JudgeType, + TaskAnswerList = x.Select(y => y.Answer).ToList(), + }).ToList(); + noteEqual = noteEqual || ComputeJudgeResult(groupTasks); + } + + } + else if (!criterionInfo.IsReadingPeriod) + { + var query = from questionAnswet in _readingTaskQuestionAnswerRepository.Where(x => visitTaskids.Contains(x.VisitTaskId)) + join question in _readingQuestionTrialRepository.Where(x => x.IsJudgeQuestion) on new { ReadingQuestionTrialId = questionAnswet.ReadingQuestionTrialId } equals new { ReadingQuestionTrialId = question.Id } + select new TaskAnswerDto() + { + Answer = questionAnswet.Answer, + AnswerGroup = question.AnswerGroup, + AnswerCombination = question.AnswerCombination, + JudgeType = question.JudgeType, + QuestionId = question.Id, + VisitTaskId = questionAnswet.VisitTaskId, + }; + var questionAnswerlist = await query.ToListAsync(); + + // 将答案进行分组 + List groupTasks = questionAnswerlist.GroupBy(x => new { x.QuestionId, x.AnswerGroup, x.JudgeType, x.AnswerCombination }).Select(x => new GroupTaskAnswerDto + { + QuestionId = x.Key.QuestionId, + AnswerGroup = x.Key.AnswerGroup, + AnswerCombination = x.Key.AnswerCombination, + JudgeType = x.Key.JudgeType, + TaskAnswerList = x.Select(y => y.Answer).ToList(), + }).ToList(); + noteEqual = ComputeJudgeResult(groupTasks); + } + + break; + case ReadingCategory.Global: + var taskOneInfo = await this.GetGlobalReadingInfo(new GetGlobalReadingInfoInDto() + { + UsingOriginalData = true, + VisitTaskId = visitTaskids[0] + }); + + var taskTwoInfo = await this.GetGlobalReadingInfo(new GetGlobalReadingInfoInDto() + { + UsingOriginalData = true, + VisitTaskId = visitTaskids[1] + }); + + // 判断两个任务是否 + if (taskOneInfo.TaskList.Count() != taskTwoInfo.TaskList.Count()) + { + noteEqual = true; + } + else + { + foreach (var item in taskOneInfo.TaskList) + { + GlobalVisitInfo twoItem = taskTwoInfo.TaskList.Where(x => x.VisitId == item.VisitId).FirstOrDefault(); + + if (twoItem == null) + { + noteEqual = true; + break; + } + else + { + var newlist = item.AfterQuestionList.Where(x => x.QuestionId != null && x.IsJudgeQuestion).ToList().Union( + twoItem.AfterQuestionList.Where(x => x.QuestionId != null && x.IsJudgeQuestion).ToList() + ).ToList(); + + List globalGroupTasks = newlist.GroupBy(x => new { x.QuestionId, x.AnswerGroup, x.JudgeType, x.AnswerCombination }).Select(x => new GroupTaskAnswerDto + { + QuestionId = x.Key.QuestionId.Value, + AnswerGroup = x.Key.AnswerGroup, + AnswerCombination = x.Key.AnswerCombination, + JudgeType = x.Key.JudgeType, + TaskAnswerList = x.Select(y => y.Answer).ToList(), + }).ToList(); + noteEqual = noteEqual || ComputeJudgeResult(globalGroupTasks); + } + + } + } + break; + } + + } + else + { + // 这里判断一致性分析产生的全局阅片 + if (visitTask.ReadingCategory == ReadingCategory.Global && visitTask.IsAnalysisCreate && (visitTask.IsSelfAnalysis ?? false)) + { + var taskOneInfo = await this.GetGlobalReadingInfo(new GetGlobalReadingInfoInDto() + { + UsingOriginalData = true, + VisitTaskId = visitTask.Id + }); + + // 找到最后一个任务ID + var lastTask = taskOneInfo.TaskList.Last(); + + if (lastTask == null) + { + noteEqual = true; + } + else + { + var query = from questionAnswet in _readingTaskQuestionAnswerRepository.Where(x => + x.VisitTask.IsAnalysisCreate == false && + x.VisitTask.DoctorUserId == visitTask.DoctorUserId && + x.VisitTask.TaskState == TaskState.Effect && + (x.VisitTask.SourceSubjectVisitId ?? default(Guid)) == lastTask.VisitId) + join question in _readingQuestionTrialRepository.Where(x => x.IsJudgeQuestion) on new { ReadingQuestionTrialId = questionAnswet.ReadingQuestionTrialId } equals new { ReadingQuestionTrialId = question.Id } + select new GlobalQuestionInfo() + { + Answer = questionAnswet.Answer, + AnswerGroup = question.AnswerGroup, + AnswerCombination = question.AnswerCombination, + JudgeType = question.JudgeType, + QuestionId = question.Id, + + }; + + var visitTaskQuestions = await query.ToListAsync(); + + var newlist = visitTaskQuestions.Where(x => x.QuestionId != null).ToList().Union( + lastTask.AfterQuestionList.Where(x => x.QuestionId != null).ToList() + ).ToList(); + + List globalGroupTasks = newlist.GroupBy(x => new { x.QuestionId, x.AnswerGroup, x.JudgeType, x.AnswerCombination }).Select(x => new GroupTaskAnswerDto + { + QuestionId = x.Key.QuestionId.Value, + AnswerGroup = x.Key.AnswerGroup, + AnswerCombination = x.Key.AnswerCombination, + JudgeType = x.Key.JudgeType, + TaskAnswerList = x.Select(y => y.Answer).ToList(), + }).ToList(); + noteEqual = noteEqual || ComputeJudgeResult(globalGroupTasks); + } + + + } + } + + + } + + + + + if (noteEqual) + { + if (visitTask.IsAnalysisCreate) + { + if (groupArm == null) + { + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => x.Id == visitTask.Id, x => new VisitTask() + { + IsAnalysisDiffToOriginalData = true + }); + } + else if (groupArm == Arm.SingleReadingArm || groupArm == Arm.DoubleReadingArm1) + { + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => x.Id == visitTask.Id, x => new VisitTask() + { + IsAnalysisDiffToOriginalData = true, + IsGroupDiffArm1 = true + }); + } + else if (groupArm == Arm.DoubleReadingArm2) + { + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => x.Id == visitTask.Id, x => new VisitTask() + { + IsAnalysisDiffToOriginalData = true, + IsGroupDiffArm2 = true + }); + } + + await _visitTaskRepository.SaveChangesAsync(); + + } + else + { + + if ( + + (visitTask.SourceSubjectVisitId != null && criterionInfo.ArbitrationRule == ArbitrationRule.Visit) + || (visitTask.SouceReadModuleId != null && criterionInfo.ArbitrationRule == ArbitrationRule.Reading) + // 全局检查批次任务仲裁 + || (visitTask.SourceSubjectVisitId != null && criterionInfo.ArbitrationRule == ArbitrationRule.Reading && criterionInfo.IsReadingPeriod && !criterionInfo.IsGlobalReading) + + + ) + { + await this.SaveJudgeTask(new SaveJudgeTaskDto() + { + VisitTaskIds = visitTaskids, + }); + } + + } + + } + else + { + + if (visitTask.IsAnalysisCreate) + { + + + if (groupArm == null) + { + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => x.Id == visitTask.Id, x => new VisitTask() + { + IsAnalysisDiffToOriginalData = false + }); + } + else if (groupArm == Arm.SingleReadingArm || groupArm == Arm.DoubleReadingArm1) + { + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => x.Id == visitTask.Id, x => new VisitTask() + { + IsAnalysisDiffToOriginalData = false, + IsGroupDiffArm1 = false + }); + } + else if (groupArm == Arm.DoubleReadingArm2) + { + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => x.Id == visitTask.Id, x => new VisitTask() + { + IsAnalysisDiffToOriginalData = false, + IsGroupDiffArm2 = false + }); + } + await _visitTaskRepository.SaveChangesAsync(); + } + } + + } + + + /// + /// 计算返回的结果 为True表示不相等 + /// + /// + /// + private bool ComputeJudgeResult(List groupTasks) + { + var noteEqual = false; + foreach (var item in groupTasks) + { + if (item.TaskAnswerList.Count() != 2) + { + noteEqual = true; + break; + } + else + { + var taskAnswer1 = item.TaskAnswerList[0]; + var taskAnswer2 = item.TaskAnswerList[1]; + if (taskAnswer1 != taskAnswer2) + { + + switch (item.JudgeType) + { + case JudgeTypeEnum.AnswerDisaffinity: + noteEqual = true; + break; + case JudgeTypeEnum.AnswerGroup: + var answerGroupList = JsonConvert.DeserializeObject>(item.AnswerGroup).Select(x => new AnswerGroup() + { + GroupId = NewId.NextGuid(), + GroupValue = x + }).ToList(); + var itemAnswerGroupsitem1 = answerGroupList.Where(x => x.GroupValue.Contains($"|{taskAnswer1}|")); + var itemAnswerGroupsitem2 = answerGroupList.Where(x => x.GroupValue.Contains($"|{taskAnswer2}|")); + var unionList = itemAnswerGroupsitem1.Intersect(itemAnswerGroupsitem2).ToList(); + if (unionList.Count < 1) + { + noteEqual = true; + } + break; + case JudgeTypeEnum.AnswerCombination: + var answerCombinationList = JsonConvert.DeserializeObject>(item.AnswerCombination == string.Empty ? "[]" : item.AnswerCombination).ToList(); + answerCombinationList.ForEach(x => + { + if (x.AnswerGroupA.Contains(taskAnswer1) && x.AnswerGroupB.Contains(taskAnswer2)) + { + noteEqual = true; + } + if (x.AnswerGroupB.Contains(taskAnswer1) && x.AnswerGroupA.Contains(taskAnswer2)) + { + noteEqual = true; + } + }); + break; + case JudgeTypeEnum.NotCalculate: + noteEqual = false; + break; + } + } + } + } + + return noteEqual; + } + + /// + /// 添加裁判任务 + /// + /// + private async Task SaveJudgeTask(SaveJudgeTaskDto inDto) + { + var trialId = await _visitTaskRepository.Where(x => inDto.VisitTaskIds.Contains(x.Id)).Select(x => x.TrialId).FirstOrDefaultAsync(); + + await _visitTaskHelpeService.AddTaskAsync(new GenerateTaskCommand() + { + JudgeVisitTaskIdList = inDto.VisitTaskIds, + ReadingCategory = GenerateTaskCategory.Judge, + TrialId = trialId + }); + } + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingNoDicomTaskService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingNoDicomTaskService.cs new file mode 100644 index 0000000..2365fed --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingNoDicomTaskService.cs @@ -0,0 +1,148 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Infra.EFCore.Common; +using Panda.DynamicWebApi.Attributes; +using IRaCIS.Core.Application.Contracts; +using MassTransit; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Application.Services +{ + + /// + /// 非Dicom + /// + public partial class ReadingImageTaskService : BaseService, IReadingImageTaskService + { + /// + /// 提交检查批次阅片问题 + /// + /// + /// + [NonDynamicMethod] + public async Task SubmitVisitTaskQuestions(SubmitVisitTaskQuestionsInDto inDto) + { + await VerifyTaskIsSign(inDto.VisitTaskId); + await this.SaveVisitTaskQuestions(inDto); + + + await this.SubmitTaskChangeState(inDto.VisitTaskId); + return ResponseOutput.Ok(true); + } + + + /// + /// 保存任务问题 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SaveVisitTaskQuestions(SubmitVisitTaskQuestionsInDto inDto) + { + await VerifyTaskIsSign(inDto.VisitTaskId); + var subjectId = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Select(x => x.SubjectId).FirstOrDefaultAsync(); + await _readingTaskQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => x.VisitTaskId == inDto.VisitTaskId && x.ReadingQuestionCriterionTrialId == inDto.ReadingQuestionCriterionTrialId); + List readingTaskAnswerList = inDto.AnswerList.Select(x => new ReadingTaskQuestionAnswer() + { + Id = NewId.NextGuid(), + SubjectId = subjectId, + Answer = x.Answer, + ReadingQuestionCriterionTrialId = inDto.ReadingQuestionCriterionTrialId, + ReadingQuestionTrialId = x.ReadingQuestionTrialId, + VisitTaskId = inDto.VisitTaskId, + TrialId = inDto.TrialId + }).ToList(); + + await _visitTaskRepository.UpdatePartialFromQueryAsync(inDto.VisitTaskId, x => new VisitTask() + { + ReadingTaskState = ReadingTaskState.Reading, + + }); + await _readingTaskQuestionAnswerRepository.AddRangeAsync(readingTaskAnswerList); + var result = await _visitTaskRepository.SaveChangesAsync(); + return ResponseOutput.Ok(result); + } + + /// + /// 获取阅片非Dicom文件 + /// + /// + /// + [HttpPost] + public async Task<(List, object)> GetReadingImageFile(GetReadingImgInDto inDto) + { + var task = await GetNextTask(new GetNextTaskInDto() + { + TrialId = inDto.TrialId, + SubjectId = inDto.SubjectId, + VisitTaskId = inDto.VisistTaskId, + }); + List visitIds = new List(); + if (task.ReadingCategory == ReadingCategory.Visit) + { + visitIds.Add(task.VisistId); + } + else + { + // 阅片期取前面所有的图像 + visitIds.AddRange(await _subjectVisitRepository.Where(x => x.VisitNum <= task.VisitNum && x.SubjectId == task.SubjectId).Select(x => x.Id).ToListAsync()); + } + List result = await _noneDicomStudyRepository.Where(t => visitIds.Contains(t.SubjectVisitId)) + .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken }).ToListAsync(); + + var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisistTaskId).FirstNotNullAsync(); + + var trialInfo = await _trialRepository.Where(x => x.Id == inDto.TrialId).Select(x => new + { + x.ClinicalInformationTransmissionEnum, + }).FirstOrDefaultAsync(); + + + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == taskinfo.TrialReadingCriterionId).Select(x => new + { + x.IsReadingShowPreviousResults, + x.IsReadingShowSubjectInfo, + x.CriterionName, + x.Id, + x.ReadingTool, + + }).FirstOrDefaultAsync(); + + + bool isExistsClinicalData = false; + if (trialInfo.ClinicalInformationTransmissionEnum == 1) + { + isExistsClinicalData = (await _readingClinicalDataService.GetClinicalDataList(new GetReadingOrTaskClinicalDataListInDto() + { + + SubjectId = task.SubjectId, + TrialId = inDto.TrialId, + VisitTaskId = task.VisitTaskId, + })).Count() > 0; + } + + + var taskInfo = await _visitTaskRepository.Where(x => x.Id == task.VisitTaskId).FirstNotNullAsync(); + + return (result, new + { + VisitTaskId = task.VisitTaskId, + SubjectId = task.SubjectId, + SubjectCode = taskInfo.BlindSubjectCode.IsNullOrEmpty() ? task.SubjectCode : taskInfo.BlindSubjectCode, + ReadingCategory = task.ReadingCategory, + TaskBlindName = task.TaskBlindName, + IsReadingShowPreviousResults = criterionInfo.IsReadingShowPreviousResults, + IsReadingShowSubjectInfo = criterionInfo.IsReadingShowSubjectInfo, + IsExistsClinicalData = isExistsClinicalData, + TrialCriterionName = criterionInfo.CriterionName, + TrialCriterionId = criterionInfo.Id + + }); + } + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingOncologyTaskService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingOncologyTaskService.cs new file mode 100644 index 0000000..5e47b9a --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingOncologyTaskService.cs @@ -0,0 +1,322 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using MassTransit; +using IRaCIS.Core.Infra.EFCore.Common; +using Panda.DynamicWebApi.Attributes; +using AutoMapper; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Infrastructure; +using Newtonsoft.Json; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Application.Services +{ + + /// + /// 肿瘤学 + /// + public partial class ReadingImageTaskService : BaseService, IReadingImageTaskService + { + #region 肿瘤学阅片相关 + + /// + /// 获取肿瘤学任务信息 + /// + /// + /// + /// + [HttpPost] + public async Task GetOncologyReadingInfo(GetOncologyReadingInfoInDto inDto) + { + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + + if (taskInfo.ReadingCategory != ReadingCategory.Oncology) + { + throw new BusinessValidationFailedException(_localizer["ReadingOncology_TaskError"]); + } + + var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == taskInfo.TrialReadingCriterionId).FirstOrDefaultAsync(); + + + GetOncologyReadingInfoOutDto result = new GetOncologyReadingInfoOutDto() + { + //TrialEvaluationResult = trialCriterion.EvaluationResult, + IsShowDetail = trialCriterion.IsShowDetail, + TrialEvaluationReason = trialCriterion.EvaluationReason.IsNullOrEmpty() ? ReadingCommon.EvaluationReason : trialCriterion.EvaluationReason, + OncologyTaskId = inDto.VisitTaskId, + ReadingTaskState = taskInfo.ReadingTaskState, + }; + + // 先找到是R1还是R2的阅片 先找到全局阅片 + + var globalTaskInfo = await _visitTaskRepository + .Where(x => x.SubjectId == taskInfo.SubjectId && + x.TaskState == TaskState.Effect && + x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && + x.ReadingCategory == ReadingCategory.Global && + x.VisitTaskNum < taskInfo.VisitTaskNum + ).OrderByDescending(x => x.VisitTaskNum) + .FirstNotNullAsync(); + + + // 最后取哪组的数据 + VisitTask visitTask = new VisitTask(); + + // 判断是否产生裁判 + + if (globalTaskInfo.JudgeVisitTaskId == null) + { + visitTask = globalTaskInfo; + } + else + { + var judgeResultTaskId = await _visitTaskRepository.Where(x => x.Id == globalTaskInfo.JudgeVisitTaskId).Select(x => x.JudgeResultTaskId).FirstOrDefaultAsync(); + if (judgeResultTaskId == null) + { + throw new BusinessValidationFailedException(_localizer["ReadingOncology_Abnormal"]); + } + visitTask = await _visitTaskRepository.Where(x => x.Id == judgeResultTaskId).FirstOrDefaultAsync(); + } + + result.GlobalTaskId = visitTask.Id; + result.SubjectId = visitTask.SubjectId; + + // 获取全局阅片信息 + var globalTaskReadingInfo = await this.GetGlobalReadingInfo(new GetGlobalReadingInfoInDto() + { + UsingOriginalData = true, + VisitTaskId = visitTask.Id + }); + + + // 找到对应的检查批次 + List oncologyVisits = await _visitTaskRepository.Where(x => + x.TrialId == taskInfo.TrialId && + x.SubjectId == taskInfo.SubjectId && + x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && + x.ReadingTaskState == ReadingTaskState.HaveSigned && + x.IsAnalysisCreate == taskInfo.IsAnalysisCreate && + x.ArmEnum == taskInfo.ArmEnum && + x.IsSelfAnalysis == taskInfo.IsSelfAnalysis && + x.DoctorUserId == taskInfo.DoctorUserId && + x.TaskState == TaskState.Effect && + x.VisitTaskNum < taskInfo.VisitTaskNum) + .OrderBy(x => x.VisitTaskNum).Select(x => new OncologyVisitTaskInfo() + { + VisitName = x.SourceSubjectVisit.VisitName, + VisitTaskId = x.Id, + // QuestionList = x.ReadingTaskQuestionAnswerList.Where(y => y.ReadingQuestionTrial.IsJudgeQuestion).OrderBy(y => y.ReadingQuestionTrial.ShowOrder) + //.Select(y => new OncologyQuestion() + //{ + // QuestionId = y.ReadingQuestionTrialId, + // QuestionName = y.ReadingQuestionTrial.QuestionName, + // Answer = y.GlobalChangeAnswer + //}).ToList() + }).ToListAsync(); + + var oncologyReadingQuestions = await _readingOncologyTaskInfoRepository.Where(x => x.OncologyTaskId == inDto.VisitTaskId).ToListAsync(); + + oncologyVisits.ForEach(x => + { + var oncologyData = oncologyReadingQuestions.Where(y => y.VisitTaskId == x.VisitTaskId).FirstOrDefault(); + + if (oncologyData != null) + { + x.EvaluationResult = oncologyData.EvaluationResult; + x.EvaluationReason = oncologyData.EvaluationReason; + } + x.QuestionList = globalTaskReadingInfo.TaskList.Where(y => x.VisitTaskId == y.VisitTaskId).SelectMany(y => y.AfterQuestionList).Where(x => x.GlobalAnswerType == GlobalAnswerType.Question) + .Select(y => new OncologyQuestion + { + QuestionId = y.QuestionId ?? default(Guid), + QuestionName = y.QuestionName, + QuestionGenre=y.QuestionGenre, + DictionaryCode=y.DictionaryCode, + Answer = y.Answer + }).ToList(); + x.IsHaveChange = globalTaskReadingInfo.TaskList.Where(y => x.VisitTaskId == y.VisitTaskId).SelectMany(y => y.AfterQuestionList).Any(y => y.IsHaveChange); + x.VisitRemark = globalTaskReadingInfo.TaskList.Where(y => x.VisitTaskId == y.VisitTaskId).SelectMany(y => y.AfterQuestionList).Where(y => y.GlobalAnswerType == GlobalAnswerType.Reason).Select(x => x.Answer).FirstOrDefault() ?? string.Empty; + + }); + + result.OncologyVisits = oncologyVisits; + + List assessTypeList = await _readingCriterionDictionaryRepository.Where(x => x.CriterionId == taskInfo.TrialReadingCriterionId + && x.ParentCode == ReadingCommon.CriterionDictionary.OncologyAssess + ) + .Select(x => new CriterionDictionaryInfo() + { + Id = x.Id, + DictionaryId = x.DictionaryId, + ChildGroup = x.Dictionary.ChildGroup, + Code = x.Dictionary.Code, + Description = x.Dictionary.Description, + ShowOrder = x.Dictionary.ShowOrder, + ParentCode = x.Dictionary.Parent.Code, + Value = x.Dictionary.Value, + ValueCN = x.Dictionary.ValueCN + }).OrderBy(x => x.ParentCode).ThenBy(x => x.ShowOrder).ToListAsync(); + result.AssessTypeList = assessTypeList; + return result; + + } + + /// + /// 修改肿瘤学阅片信息 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetOncologyReadingInfo(SetOncologyReadingInfoInDto inDto) + { + await VerifyTaskIsSign(inDto.OncologyTaskId); + await _readingOncologyTaskInfoRepository.BatchDeleteNoTrackingAsync(x => x.OncologyTaskId == inDto.OncologyTaskId); + + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.OncologyTaskId).FirstNotNullAsync(); + + List readingOncologies = inDto.OncologyQuestionList.Select(x => new ReadingOncologyTaskInfo() + { + EvaluationReason = x.EvaluationReason, + SubjectId = taskInfo.SubjectId, + EvaluationResult = x.EvaluationResult, + OncologyTaskId = inDto.OncologyTaskId, + TrialId = taskInfo.TrialId, + VisitTaskId = x.VisitTaskId + }).ToList(); + + await _readingOncologyTaskInfoRepository.AddRangeAsync(readingOncologies); + + await _visitTaskRepository.UpdatePartialFromQueryAsync(t => t.Id == inDto.OncologyTaskId, u => new VisitTask() { ReadingTaskState = ReadingTaskState.Reading }); + + var result = await _readingOncologyTaskInfoRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(result); + + + } + #endregion + + + /// + /// 添加肿瘤学阅片任务 其实这里无非是要判断临床数据是否签名 但是对于添加新的阅片期 其实没有临床数据 可以走之前的逻辑 + /// + /// + [NonDynamicMethod] + public async Task AddOncologyTask(Guid oncologModuleId) + { + + // 判断是否读片完成 + var finishReading = false; + + + var readModuleInfo = await _readModuleRepository.Where(x => x.Id == oncologModuleId && x.ModuleType == ModuleTypeEnum.Oncology).FirstOrDefaultAsync(); + + + + // 如果当前是肿瘤学 + if (readModuleInfo != null) + { + // 先找到对应的全局阅片模块Id + var globalreadModuleId = await _readModuleRepository.Where(x => x.SubjectVisitId == readModuleInfo.SubjectVisitId && + x.TrialReadingCriterionId== readModuleInfo.TrialReadingCriterionId&& + x.ModuleType == ModuleTypeEnum.Global).Include(x=>x.SubjectVisit).Select(x => x.Id).FirstOrDefaultAsync(); + + // 找到一个全局阅片任务是否有裁判任务 + + var judgeVisitTaskId = await _visitTaskRepository.Where(x => x.SouceReadModuleId == globalreadModuleId && x.TaskState == TaskState.Effect + && x.ReadingCategory == ReadingCategory.Global + && x.ReadingTaskState == ReadingTaskState.HaveSigned).Select(x => x.JudgeVisitTaskId).FirstOrDefaultAsync(); + + // 获取系统配置 + var readingType = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == readModuleInfo.TrialReadingCriterionId).Select(x => x.ReadingType).FirstOrDefaultAsync(); + + // 判断阅片是否完成 + if (judgeVisitTaskId == null && (await _visitTaskRepository.Where(x => x.SouceReadModuleId == globalreadModuleId && x.TaskState == TaskState.Effect && x.ReadingCategory == ReadingCategory.Global + && x.ReadingTaskState == ReadingTaskState.HaveSigned && !x.IsAnalysisCreate && x.TrialReadingCriterionId == readModuleInfo.TrialReadingCriterionId + ).CountAsync() == (int)readingType)) + { + + finishReading = true; + } + else if (judgeVisitTaskId != null && (await _visitTaskRepository.AnyAsync(x => x.Id == judgeVisitTaskId.Value && x.ReadingTaskState == ReadingTaskState.HaveSigned))) + { + finishReading = true; + } + + if (finishReading) + { + // 获取临床数据 + var clinicalData = await _readingClinicalDataService.GetReadingClinicalList(new GetReadingClinicalDataListIndto() + { + SubjectId = readModuleInfo.SubjectId, + ReadingId = readModuleInfo.Id, + TrialId = readModuleInfo.TrialId, + TrialReadingCriterionId= readModuleInfo.TrialReadingCriterionId, + + }); + + // 判断是否临床数据都已经签名 + if (!clinicalData.Any(x => !x.IsSign)) + { + + List needReadList = new List(); + + needReadList.Add(new ReadingGenerataTaskDTO() + { + IsUrgent = readModuleInfo.IsUrgent ?? false, + SubjectId = readModuleInfo.SubjectId, + ReadingName = readModuleInfo.ModuleName, + //VisitNum = readModuleInfo.SubjectVisit.VisitNum, + ReadModuleId = readModuleInfo.Id, + ReadingCategory = ReadingCategory.Oncology, + }); + + var originalVisitId = await _visitTaskRepository.Where(x => x.SouceReadModuleId == globalreadModuleId && x.TaskState == TaskState.Effect && x.ReadingCategory == ReadingCategory.Global + && x.ReadingTaskState == ReadingTaskState.HaveSigned && !x.IsAnalysisCreate && x.TrialReadingCriterionId == readModuleInfo.TrialReadingCriterionId + ).Select(x => x.Id).FirstOrDefaultAsync(); + await _visitTaskHelpeService.AddTaskAsync(new GenerateTaskCommand() + { + OriginalVisitId= originalVisitId, + ReadingCategory = GenerateTaskCategory.Oncology, + TrialId = readModuleInfo.TrialId, + ReadingGenerataTaskList = needReadList + }); + } + } + + + + } + } + + + /// + /// 提交肿瘤阅片结果 + /// + /// + /// + [NonDynamicMethod] + public async Task SubmitOncologyReadingInfo(SubmitOncologyReadingInfoInDto inDto) + { + await VerifyTaskIsSign(inDto.OncologyTaskId); + //var result = await this.SaveGlobalReadingInfo(inDto); + + //await FinishReadUpdateState(inDto.OncologyTaskId); + await _visitTaskRepository.UpdatePartialFromQueryAsync(inDto.OncologyTaskId, x => new VisitTask() + { + ReadingTaskState = ReadingTaskState.HaveSigned, + SignTime = DateTime.Now, + }); + await _visitTaskRepository.SaveChangesAsync(); + + // 创建任务关联关系 + await this.CreateTaskRelated(inDto.OncologyTaskId); + return ResponseOutput.Ok(true); + } + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingTaskQuestionService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingTaskQuestionService.cs new file mode 100644 index 0000000..cb7a393 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingTaskQuestionService.cs @@ -0,0 +1,373 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Infra.EFCore.Common; +using IRaCIS.Core.Application.Contracts; +using Panda.DynamicWebApi.Attributes; + +namespace IRaCIS.Application.Services +{ + + /// + /// 问题 + /// + public partial class ReadingImageTaskService : BaseService, IReadingImageTaskService + { + #region 获取项目已确认的标准 + /// + /// 获取项目已确认的标准 + /// + /// + /// + [HttpPost] + public async Task> GetTrialConfirmCriterionList(GetConfirmCriterionInDto inDto) + { + var result = await _visitTaskRepository.Where(x => x.TrialId == inDto.TrialId && x.TrialReadingCriterion.IsConfirm) + .WhereIf(inDto.VisitTaskId != null, t => t.Id == inDto.VisitTaskId) + .Select(x => new GetTrialConfirmCriterionListOutDto() + { + ReadingQuestionCriterionTrialId = x.TrialReadingCriterion.Id, + ReadingQuestionCriterionTrialName = x.TrialReadingCriterion.CriterionName + }).Distinct().ToListAsync(); + return result; + } + #endregion + + #region 获取项目的阅片问题 + /// + /// 获取项目的阅片问题ECRF预览 + /// + /// + /// SinglePage 单页 + /// + /// MultiPage 多页 + /// + /// PublicPage 公共 + /// + /// + /// + [HttpPost] + public async Task<(GetTrialReadingQuestionPageDto, object)> GetTrialReadingQuestion(GetTrialReadingQuestionInDto inDto) + { + if (inDto.VisitTaskId != null) + { + //await AddDefaultValueToTask(inDto.VisitTaskId.Value); + } + + var result = new GetTrialReadingQuestionPageDto(); + var readingTaskState = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Select(x => x.ReadingTaskState).FirstOrDefaultAsync(); + + + var qusetionList = await GetReadingAnswerView(inDto.ReadingQuestionCriterionTrialId, inDto.VisitTaskId); + + var formType = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.ReadingQuestionCriterionTrialId).Select(x => x.FormType).FirstOrDefaultAsync(); + var groupList = new List(); + + var qusetionIds = qusetionList.Select(x => x.Id).ToList(); + var tableQuestionList = await _readingTableQuestionTrialRepository.Where(x => qusetionIds.Contains(x.ReadingQuestionId)) + .ProjectTo(_mapper.ConfigurationProvider,x=>new { + + isEn_Us=_userInfo.IsEn_Us + + }).OrderBy(x => x.ShowOrder).ToListAsync(); + if (inDto.FormType != null) + { + formType = inDto.FormType.Value; + } + + if (formType == FormType.MultiplePage) + { + qusetionList = qusetionList.Where(x => x.ReadingCriterionPageId != null).ToList(); + var readingCriterionPageIds = qusetionList.OrderBy(x => x.PageShowOrder).Select(x => x.ReadingCriterionPageId).Distinct().ToList(); + foreach (var item in readingCriterionPageIds) + { + var newPageQusetionList = qusetionList.Where(x => x.ReadingCriterionPageId == item).ToList(); + var firstData = newPageQusetionList.FirstOrDefault(); + var page = new GetTrialReadingQuestionOutDto() + { + PageName = firstData.PageName, + IsPage = true, + IsPublicPage = firstData.IsPublicPage, + }; + + var pageGroupList = newPageQusetionList.Where(x => x.Type == ReadingQestionType.Group || (x.ParentId == null && x.GroupName.IsNullOrEmpty())).ToList(); + pageGroupList.ForEach(x => + { + this.FindChildQuestion(x, newPageQusetionList, tableQuestionList); + }); + + page.Childrens = pageGroupList.Where(x => !(x.Type == ReadingQestionType.Group && x.Childrens.Count() == 0)).ToList(); + groupList.Add(page); + } + + result.PublicPage = groupList.Where(x => x.IsPublicPage.Value).ToList(); + result.MultiPage = groupList.Where(x => !x.IsPublicPage.Value).ToList(); + } + else + { + qusetionList = qusetionList.Where(x => x.ReadingCriterionPageId == null).ToList(); + + groupList = qusetionList.Where(x => x.Type == ReadingQestionType.Group || (x.ParentId == null && x.GroupName.IsNullOrEmpty())).ToList(); + groupList.ForEach(x => + { + this.FindChildQuestion(x, qusetionList, tableQuestionList); + }); + + groupList = groupList.Where(x => !(x.Type == ReadingQestionType.Group && x.Childrens.Count() == 0)).ToList(); + + result.SinglePage = groupList; + + + } + + + return (result, new + { + readingTaskState = readingTaskState, + FormType = formType + + }); + } + + /// + /// 项目配置的阅片问题(不包含表格问题) 以及配置的分页 和任务对应的答案 一维表 -- 非dicom 阅片 + /// + /// + /// + /// + private async Task> GetReadingAnswerView(Guid readingQuestionCriterionTrialId, Guid? visitTaskId) + { + var query = from data in _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == readingQuestionCriterionTrialId) + + join page in _readingCriterionPageRepository.AsQueryable() on data.ReadingCriterionPageId ?? default(Guid) equals page.Id into pageTemp + from leftpage in pageTemp.DefaultIfEmpty() + + join questionAnswer in _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == visitTaskId) on data.Id equals questionAnswer.ReadingQuestionTrialId into questionAnswerTemp + from leftquestionAnswer in questionAnswerTemp.DefaultIfEmpty() + select new GetTrialReadingQuestionOutDto() + { + + Id = data.Id, + ReadingQuestionCriterionTrialId = data.ReadingQuestionCriterionTrialId, + TrialId = data.TrialId, + Type = data.Type, + ParentTriggerValue = data.ParentTriggerValue, + GroupName = data.GroupName, + QuestionName = data.QuestionName.LanguageName(data.QuestionEnName, _userInfo.IsEn_Us), + IsRequired = data.IsRequired, + ShowQuestion = data.ShowQuestion, + LesionType = data.LesionType, + QuestionGenre = data.QuestionGenre, + DictionaryCode = data.DictionaryCode, + GroupId=data.GroupId, + ShowOrder = data.ShowOrder, + RelevanceId = data.RelevanceId, + IsShowInDicom = data.IsShowInDicom, + MaxQuestionCount = data.MaxQuestionCount, + RelevanceValue = data.RelevanceValue, + ImageCount = data.ImageCount, + ParentId = data.ParentId, + TypeValue = data.TypeValue, + Answer = leftquestionAnswer.Answer, + ReadingCriterionPageId = data.ReadingCriterionPageId, + PageName = leftpage.PageName, + PageShowOrder = leftpage.ShowOrder, + IsPublicPage = leftpage.IsPublicPage, + DefaultValue = data.DefaultValue, + }; + + var qusetionList = await query.Where(x => x.ShowQuestion != ShowQuestion.Hide).OrderBy(x => x.ShowOrder).ToListAsync(); + + qusetionList.ForEach(x => + { + x.Answer = x.Answer.IsNullOrEmpty() ? x.DefaultValue : x.Answer; + }); + + return qusetionList; + } + + + /// + /// 找子问题 + /// + /// + /// + /// + [NonDynamicMethod] + public void FindChildQuestion(GetTrialReadingQuestionOutDto item, List questionlists, List tableQuestions) + { + item.Childrens = questionlists.Where(x => x.ParentId == item.Id || (item.Type == ReadingQestionType.Group && x.Type != ReadingQestionType.Group && x.ParentId == null && x.GroupName == item.GroupName)).ToList(); + + + item.Childrens.AddRange(tableQuestions.Where(x => x.ReadingQuestionId == item.Id).Select(x => new GetTrialReadingQuestionOutDto + { + Childrens = new List(), + ShowOrder = x.ShowOrder, + GroupName = string.Empty, + Id = x.Id, + Type = x.Type, + DictionaryCode = x.DictionaryCode, + TableQuestionType = x.TableQuestionType, + DependParentId = x.DependParentId, + IsDepend = x.IsDepend, + QuestionMark = x.QuestionMark, + QuestionGenre = x.QuestionGenre, + TypeValue = x.TypeValue, + RelevanceId = x.RelevanceId, + IsRequired = x.IsRequired, + RelevanceValue = x.RelevanceValue, + ImageCount = 0, + ParentId = item.Id, + DataTableColumn = x.DataTableColumn, + LesionType = item.LesionType, + QuestionName = x.QuestionName, + RelationQuestions = tableQuestions.Where(z => (z.DependParentId ?? default(Guid)) == x.Id).Select(x => new GetTrialReadingQuestionOutDto + { + Childrens = new List(), + ShowOrder = x.ShowOrder, + GroupName = string.Empty, + Id = x.Id, + Type = x.Type, + QuestionGenre = x.QuestionGenre, + TableQuestionType = x.TableQuestionType, + DependParentId = x.DependParentId, + DictionaryCode = x.DictionaryCode, + IsDepend = x.IsDepend, + QuestionMark = x.QuestionMark, + TypeValue = x.TypeValue, + RelevanceId = x.RelevanceId, + RelevanceValue = x.RelevanceValue, + ImageCount = 0, + ParentId = item.Id, + DataTableColumn = x.DataTableColumn, + LesionType = item.LesionType, + QuestionName = x.QuestionName, + RelationQuestions = new List(), + Remark = x.Remark, + + }).ToList(), + Remark = x.Remark, + + })); + if (item.Childrens != null && item.Childrens.Count != 0) + { + item.Childrens.ForEach(x => + { + this.FindChildQuestion(x, questionlists, tableQuestions); + }); + } + } + + #endregion + + #region 获取系统的阅片问题 + + /// + /// 获取系统的阅片问题 + /// + /// + /// + [HttpPost] + public async Task GetSystemReadingQuestion(GetSystemReadingQuestionInDto inDto) + { + var result = new GetSystemReadingQuestionPageDto(); + var qusetionList = await _readingQuestionSystem.Where(x => x.ReadingQuestionCriterionSystemId == inDto.Id) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(x => x.ShowOrder).ToListAsync(); + + + var questionIds = qusetionList.Select(x => x.Id).ToList(); + var tableQuestionList = await _readingTableQuestionSystemRepository.Where(x => questionIds.Contains(x.ReadingQuestionId)) + .ProjectTo(_mapper.ConfigurationProvider,new { + isEn_Us=_userInfo.IsEn_Us, + }).OrderBy(x => x.ShowOrder).ToListAsync(); + + var groupList = new List(); + + //qusetionList = qusetionList.Where(x => x.ParentId == null).ToList(); + + groupList = qusetionList.Where(x => x.Type == ReadingQestionType.Group || (x.ParentId == null && x.GroupName.IsNullOrEmpty())).ToList(); + groupList.ForEach(x => + { + this.FindSystemChildQuestion(x, qusetionList, tableQuestionList); + }); + + groupList = groupList.Where(x => !(x.Type == ReadingQestionType.Group && x.Childrens.Count() == 0)).ToList(); + + result.SinglePage = groupList; + + + return result; + } + + + /// + /// 获取系统 + /// + /// + /// + /// + public void FindSystemChildQuestion(GetSystemReadingQuestionOutDto item, List questionlists, List tableQuestions) + { + item.Childrens = questionlists.Where(x => x.ParentId == item.Id || (item.Type == ReadingQestionType.Group && x.Type != ReadingQestionType.Group && x.ParentId == null && x.GroupName == item.GroupName)).ToList(); + + item.Childrens.AddRange(tableQuestions.Where(x => x.ReadingQuestionId == item.Id).Select(x => new GetSystemReadingQuestionOutDto + { + Childrens = new List(), + ShowOrder = x.ShowOrder, + GroupName = string.Empty, + + Id = x.Id, + Type = x.Type, + DictionaryCode = x.DictionaryCode, + TableQuestionType = x.TableQuestionType, + DependParentId = x.DependParentId, + IsDepend = x.IsDepend, + QuestionMark = x.QuestionMark, + TypeValue = x.TypeValue, + RelevanceId = x.RelevanceId, + IsRequired = x.IsRequired, + RelevanceValue = x.RelevanceValue, + ImageCount = 0, + ParentId = item.Id, + DataTableColumn = x.DataTableColumn, + LesionType = item.LesionType, + QuestionName = x.QuestionName, + RelationQuestions = tableQuestions.Where(z => (z.DependParentId ?? default(Guid)) == x.Id).Select(x => new GetSystemReadingQuestionOutDto + { + Childrens = new List(), + ShowOrder = x.ShowOrder, + GroupName = string.Empty, + Id = x.Id, + DictionaryCode = x.DictionaryCode, + Type = x.Type, + TableQuestionType = x.TableQuestionType, + DependParentId = x.DependParentId, + IsDepend = x.IsDepend, + QuestionMark = x.QuestionMark, + TypeValue = x.TypeValue, + RelevanceId = x.RelevanceId, + RelevanceValue = x.RelevanceValue, + ImageCount = 0, + ParentId = item.Id, + DataTableColumn = x.DataTableColumn, + LesionType = item.LesionType, + QuestionName = x.QuestionName, + RelationQuestions = new List(), + Remark = x.Remark, + + }).ToList(), + Remark = x.Remark, + + })); + + if (item.Childrens != null && item.Childrens.Count != 0) + { + item.Childrens.ForEach(x => + { + this.FindSystemChildQuestion(x, questionlists, tableQuestions); + }); + } + } + #endregion + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingPeriod/ReadModuleService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingPeriod/ReadModuleService.cs new file mode 100644 index 0000000..d228288 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingPeriod/ReadModuleService.cs @@ -0,0 +1,450 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using MassTransit; +using IRaCIS.Core.Infra.EFCore.Common; +using System.Linq.Dynamic.Core; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Application.Services +{ + /// + /// 生成的阅片模块(在大列表上展示的) 阅片期 + /// + [ApiExplorerSettings(GroupName = "Reading")] + public class ReadModuleService : BaseService + { + + public IRepository _subjectVisitRepository; + private readonly IRepository _subjectRepository; + private readonly IRepository _visitstageRepository; + private readonly IRepository _userRepository; + private readonly IRepository _trialRepository; + private readonly IVisitTaskHelpeService _visitTaskHelpeService; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _clinicalDataTrialSetRepository; + private readonly IRepository _readModuleViewRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _dicomInstanceRepository; + private readonly IRepository _noneDicomStudyFileRepository; + private readonly IRepository _readingPeriodSetRepository; + private readonly IRepository _readModuleRepository; + + + public ReadModuleService(IRepository subjectVisitRepository, + IRepository subjectRepository, + IRepository visitstageRepository, + IRepository UserRepository, + IRepository trialRepository, + IVisitTaskHelpeService visitTaskHelpeService, + IRepository visitTaskRepository, + IRepository clinicalDataTrialSetRepository, + IRepository readModuleViewRepository, + IRepository readingQuestionCriterionTrialRepository, + IRepository dicomInstanceRepository, + IRepository noneDicomStudyFileRepository, + IRepository readingPeriodSetRepository, + IRepository readModuleRepository + ) + { + this._subjectVisitRepository = subjectVisitRepository; + this._subjectRepository = subjectRepository; + this._visitstageRepository = visitstageRepository; + this._userRepository = UserRepository; + this._trialRepository = trialRepository; + this._visitTaskHelpeService = visitTaskHelpeService; + this._visitTaskRepository = visitTaskRepository; + this._clinicalDataTrialSetRepository = clinicalDataTrialSetRepository; + this._readModuleViewRepository = readModuleViewRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this._dicomInstanceRepository = dicomInstanceRepository; + this._noneDicomStudyFileRepository = noneDicomStudyFileRepository; + this._readingPeriodSetRepository = readingPeriodSetRepository; + this._readModuleRepository = readModuleRepository; + } + + + /// + /// 获取读片模块 //加了标准参数 + /// + [HttpPost] + public async Task<(PageOutput,object)> GetReadModuleList(GetReadModuleDto dto) + { + + #region MyRegion + + if (dto.SortField.IsNullOrEmpty()) + { + dto.SortField = nameof(ReadModuleView.SubjectCode); + dto.Asc = true; + } + + dto.SortField = dto.Asc ? dto.SortField : dto.SortField + " desc"; + var subjectQuery = _readModuleViewRepository.Where(x => x.TrialReadingCriterionId == dto.TrialReadingCriterionId) + .WhereIf(dto.TrialId != null, x => x.TrialId == dto.TrialId) + .WhereIf(dto.SubjectId != null, x => x.SubjectId == dto.SubjectId) + .WhereIf(dto.TrialSiteCode != null && dto.TrialSiteCode != string.Empty, x => x.TrialSiteCode.Contains(dto.TrialSiteCode)) + .WhereIf(dto.SubjectCode != null && dto.SubjectCode != string.Empty, x => x.SubjectCode.Contains(dto.SubjectCode)) + .WhereIf(dto.ModuleType != null, x => x.ModuleType == dto.ModuleType) + .WhereIf(dto.ReadingStatus != null, x => x.ReadingStatus == dto.ReadingStatus) + .WhereIf(dto.Name != null, x => x.Name.Contains(dto.Name!)).OrderBy(x => x.SiteCode); + + var subjectIds = await subjectQuery.OrderBy(dto.SortField).Select(x => x.SubjectId).Distinct().Skip((dto.PageIndex - 1) * dto.PageSize).Take(dto.PageSize).ToListAsync(); + + List ReadModuleViewList = await subjectQuery.Where(x => subjectIds.Contains(x.SubjectId)).OrderBy(dto.SortField).ToListAsync(); + List getReadList = ReadModuleViewList.GroupBy(x => new { x.SubjectId, x.SiteId, x.TrialSiteCode, x.SubjectCode }) + .Select(x => new GetReadModuleDtoOut() + { + TrialSiteCode = x.Key.TrialSiteCode, + SiteId = x.Key.SiteId, + SubjectCode = x.Key.SubjectCode, + SubjectId = x.Key.SubjectId, + Data = x.OrderBy(x=>x.ModuleType).ThenBy(x=>x.VisitNum).ToList(), + }).ToList(); + PageOutput pageOutput = new PageOutput() + { + PageSize = dto.PageSize, + CurrentPageData = getReadList, + PageIndex = dto.PageIndex, + TotalCount = await subjectQuery.Select(x => x.SubjectId).Distinct().CountAsync(), + }; + var maxcount = 0; + pageOutput.CurrentPageData.ForEach(x => + { + maxcount = maxcount < x.Data.Count ? x.Data.Count : maxcount; + }); + + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == dto.TrialReadingCriterionId).Select(x => new + { + x.IsReadingTaskViewInOrder, + x.IsOncologyReading, + x.IsGlobalReading, + x.IsReadingPeriod, + x.ReadingInfoSignTime, + }).FirstNotNullAsync(); + + return (pageOutput, new + { + MaxLength = maxcount, + IsReadingTaskViewInOrder= criterionInfo.IsReadingTaskViewInOrder, + IsClinicalReading = criterionInfo.IsOncologyReading, + // OnlyExistsMedicalHistory = !(await _clinicalDataTrialSetRepository.AnyAsync(x => x.TrialId == dto.TrialId && x.ClinicalDataSetName != "既往局部治疗史" && x.IsConfirm)), + IsExistsSubjectClinicalData= await _clinicalDataTrialSetRepository.AnyAsync(x => x.TrialId == dto.TrialId&&x.ClinicalDataLevel== ClinicalLevel.Subject && x.IsConfirm&&x.TrialClinicalDataSetCriteriaList.Any(y=>y.TrialReadingCriterionId== dto.TrialReadingCriterionId)), + IsExistsVisitClinicalData = await _clinicalDataTrialSetRepository.AnyAsync(x => x.TrialId == dto.TrialId && x.ClinicalDataLevel == ClinicalLevel.SubjectVisit && x.IsConfirm && x.TrialClinicalDataSetCriteriaList.Any(y => y.TrialReadingCriterionId == dto.TrialReadingCriterionId)), + IsExistsReadingClinicalData = await _clinicalDataTrialSetRepository.AnyAsync(x => x.TrialId == dto.TrialId && x.ClinicalDataLevel == ClinicalLevel.ImageRead && x.IsConfirm && x.TrialClinicalDataSetCriteriaList.Any(y => y.TrialReadingCriterionId == dto.TrialReadingCriterionId)), + IsGlobalReading= criterionInfo.IsGlobalReading, + IsReadingPeriod=criterionInfo.IsReadingPeriod, + ReadingInfoSignTime=criterionInfo.ReadingInfoSignTime, + }) ; + + #endregion + } + + + + /// + /// 获取单条详情信息 + /// + /// + /// + [HttpPost] + public async Task GetReadModule(GetReadModuleSingleIndto dto) + { + var data = await GetReadModuleList(new GetReadModuleDto() + { + SubjectId = dto.SubjectId, + TrialId=dto.TrialId, + TrialReadingCriterionId=dto.TrialReadingCriterionId, + }); + + GetReadModuleSingleOutdto? readModule = data.Item1.CurrentPageData.FirstOrDefault().Data.Where(x => x.Id == dto.Id).Select(x => new GetReadModuleSingleOutdto() + { + Id = x.Id, + ModuleType = x.ModuleType, + SubjectVisitId = x.SubjectVisitId, + SubjectVisitName = x.SubjectVisitName, + + Status = x.ReadingStatus, + }).FirstOrDefault(); + + + var stakeholderIds = new List(); + + var subjectVisit =await _subjectVisitRepository.FirstOrDefaultAsync(x => x.Id == readModule.SubjectVisitId); + + switch (readModule.Status) + { + case ReadingStatusEnum.ImageNotSubmit: + stakeholderIds.AddRange(await _dicomInstanceRepository.Where(x => x.SubjectVisitId == readModule.SubjectVisitId).Select(x => x.CreateUserId).Distinct().ToListAsync()); + stakeholderIds.AddRange(await _noneDicomStudyFileRepository.Where(x => x.NoneDicomStudy.SubjectVisitId == readModule.SubjectVisitId).Select(x => x.CreateUserId).Distinct().ToListAsync()); + break; + case ReadingStatusEnum.ImageQuality: + if (subjectVisit.PreliminaryAuditUserId != null) + { + stakeholderIds.Add(subjectVisit.PreliminaryAuditUserId.Value); + } + if (subjectVisit.ReviewAuditUserId != null) + { + stakeholderIds.Add(subjectVisit.ReviewAuditUserId.Value); + } + break; + case ReadingStatusEnum.TaskAllocate: + if (subjectVisit.CheckUserId != null) + { + stakeholderIds.Add(subjectVisit.CheckUserId.Value); + } + break; + case ReadingStatusEnum.ImageReading: + case ReadingStatusEnum.ReadCompleted: + + var doctorUserId = await _visitTaskRepository.Where(x => (x.SouceReadModuleId == dto.Id || x.SourceSubjectVisitId == dto.Id)&&x.TaskState==TaskState.Effect) + .Where(x => x.DoctorUserId != null).Select(x => x.DoctorUserId.Value).ToListAsync(); + + stakeholderIds.AddRange(doctorUserId); + + break; + }; + + readModule.StakeholderIds = stakeholderIds.Distinct().ToList(); + readModule.StakeholderNames = await _userRepository.Where(x => readModule.StakeholderIds.Contains(x.Id)).Select(x => x.FirstName + "/" + x.LastName).ToListAsync(); + + return readModule; + } + + /// + /// 添加阅片的时候 获取检查批次 //标准参数必传 + /// + /// + /// + public async Task> GetSubjectReadVisitList(GetSubjectReadVisitsInDto inDto) + { + //增加标准 + var maxReadVisit = await _readModuleRepository.Where(x => x.SubjectId == inDto.SubjectId && x.ReadingSetType==inDto.ReadingSetType && x.TrialReadingCriterionId==inDto.TrialReadingCriterionId).Include(x=>x.SubjectVisit).OrderByDescending(x=>x.SubjectVisit.VisitNum).FirstOrDefaultAsync(); + + var maxReadVisitNum= maxReadVisit==null?-1:maxReadVisit.SubjectVisit.VisitNum; + var visitQuery = _subjectVisitRepository.Where(x => x.SubjectId == inDto.SubjectId && x.LatestScanDate != null && !x.IsLostVisit); + var finalVisitNum = await visitQuery.Where(x => x.IsFinalVisit).Select(x => x.VisitNum).FirstOrDefaultAsync(); + + //增加标准 + var readModulequery = _readModuleRepository.AsQueryable().Where(x=>x.TrialReadingCriterionId == inDto.TrialReadingCriterionId); + + var resultlist= await visitQuery.WhereIf(finalVisitNum != null&& finalVisitNum!=0, x => x.VisitNum <= finalVisitNum) + .WhereIf(inDto.ReadingSetType== ReadingSetType.TumorReading, x => readModulequery.Where(y => y.SubjectVisitId == x.Id&&y.TrialReadingCriterionId== inDto.TrialReadingCriterionId && y.ReadingSetType == ReadingSetType.ImageReading).Count() > 0) + .Where(x=>x.VisitNum> maxReadVisitNum) + .Where(x=>!x.IsBaseLine) // 排除基线 + .Where(x => readModulequery.Where(y => y.SubjectVisitId == x.Id&& y.TrialReadingCriterionId == inDto.TrialReadingCriterionId && y.ReadingSetType == inDto.ReadingSetType).Count() == 0).OrderBy(x => finalVisitNum) + .Select(x => new GetSubjectReadVisitsOutDto() + { + SubjectVisitId = x.Id, + VisitName = x.VisitName, + VisitNum = x.VisitNum, + }).OrderBy(x=>x.VisitNum).ToListAsync(); + + return resultlist; + } + + /// + /// 添加阅片期 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddReadModule(ReadModuleAddDto dto) + { + + + var visitQuery = _subjectVisitRepository.Where(x => x.SubjectId == dto.SubjectId); + + visitQuery = visitQuery + //.WhereIf(dto.ExpirationDate != null, x => x.LatestScanDate <= dto.ExpirationDate.Value) + .WhereIf(dto.ExpirationVisitNum != null, x => x.VisitNum == dto.ExpirationVisitNum!); + var visit = visitQuery.OrderByDescending(x => x.VisitNum).FirstOrDefault(); + + var moduleType = dto.ReadingSetType == ReadingSetType.ImageReading ? ModuleTypeEnum.Global : ModuleTypeEnum.Oncology; + + if (visit != null) + { + + if (await _readModuleRepository.AnyAsync(x => x.SubjectVisitId == visit.Id&&x.ModuleType== moduleType && x.TrialReadingCriterionId == dto.TrialReadingCriterionId)) + { + throw new BusinessValidationFailedException($"当前检查批次已经添加过阅片期"); + } + var readModuleData = new ReadModule() + { + Id = NewId.NextGuid(), + SubjectId = dto.SubjectId, + ModuleType = moduleType, + IsUrgent = visit.IsUrgent, + ModuleName = dto.Name, + SubjectVisitId = visit.Id, + ReadingSetType = dto.ReadingSetType, + TrialId = dto.TrialId, + + ReadingStatus = ReadingStatusEnum.TaskAllocate, + + //增加标准 + TrialReadingCriterionId = dto.TrialReadingCriterionId, + + }; + + await _readModuleRepository.AddAsync(readModuleData); + + // 判断是否要添加肿瘤学或者全局阅片任务 + switch (readModuleData.ModuleType) + { + case ModuleTypeEnum.Global: + //增加标准 + var taskListInfo = await _visitTaskRepository.Where(x => x.SourceSubjectVisitId == readModuleData.SubjectVisitId&&x.TaskState==TaskState.Effect && x.ReadingTaskState == ReadingTaskState.HaveSigned + &&!x.IsAnalysisCreate && x.TrialReadingCriterionId==dto.TrialReadingCriterionId + + ).ToListAsync(); + foreach (var taskInfo in taskListInfo) + { + await _visitTaskHelpeService.AddTaskAsync(new GenerateTaskCommand() + { + OriginalVisitId = taskInfo.Id, + ReadingCategory = GenerateTaskCategory.Global, + TrialId = dto.TrialId, + ReadingGenerataTaskList = new List() { + new ReadingGenerataTaskDTO() + { + IsUrgent = readModuleData.IsUrgent ?? false, + SubjectId = readModuleData.SubjectId, + + ReadingName = readModuleData.ModuleName, + ReadModuleId =readModuleData.Id, + ReadingCategory = ReadingCategory.Global, + } + } + }); + } + break; + + case ModuleTypeEnum.Oncology: + + + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == dto.TrialReadingCriterionId).Select(x => new + { + x.ReadingType, + + }).FirstNotNullAsync(); + + + //增加标准 + var globalModule = await _readModuleRepository.Where(x => x.SubjectId == readModuleData.SubjectId && x.SubjectVisitId == readModuleData.SubjectVisitId&& x.ModuleType == ModuleTypeEnum.Global && x.TrialReadingCriterionId == dto.TrialReadingCriterionId).FirstOrDefaultAsync(); + + if (globalModule != null) + { + var globalTaskInfoList = await _visitTaskRepository.Where(x => x.SouceReadModuleId == globalModule.Id + && x.TrialReadingCriterionId == dto.TrialReadingCriterionId + && x.TaskState == TaskState.Effect && x.ReadingTaskState == ReadingTaskState.HaveSigned&&!x.IsAnalysisCreate).ToListAsync(); + + if (globalTaskInfoList.Count() > 0 && globalTaskInfoList.Count == (int)criterionInfo.ReadingType) + { + var isAdd = false; + if (globalTaskInfoList[0].JudgeVisitTaskId == null) + { + isAdd = true; + } + else + { + isAdd = await _visitTaskRepository.AnyAsync(x => x.Id == globalTaskInfoList[0].JudgeVisitTaskId && x.ReadingTaskState == ReadingTaskState.HaveSigned); + } + if (isAdd) + { + await _visitTaskHelpeService.AddTaskAsync(new GenerateTaskCommand() + { + OriginalVisitId = globalTaskInfoList[0].Id, + ReadingCategory = GenerateTaskCategory.Oncology, + TrialId = dto.TrialId, + ReadingGenerataTaskList = new List() { + new ReadingGenerataTaskDTO() + { + IsUrgent = readModuleData.IsUrgent ?? false, + SubjectId = readModuleData.SubjectId, + VisitNum=visit.VisitNum, + ReadingName = readModuleData.ModuleName, + + ReadModuleId =readModuleData.Id, + ReadingCategory = ReadingCategory.Oncology, + } + } + }); + } + } + + + + } + else + { + return ResponseOutput.NotOk("请先添加全局阅片"); + } + break; + } + var res = await _readModuleRepository.SaveChangesAsync(); + return ResponseOutput.Ok(res); + } + else + { + return ResponseOutput.NotOk("未找到符合要求的检查批次"); + } + + + + } + + + + /// + /// 删除 + /// + /// + /// + [HttpDelete("{readModuleId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task DeleteReadModule(Guid readModuleId) + { + + var readModule = await _readModuleRepository.Where(x => x.Id == readModuleId).FirstNotNullAsync(); + + + if(await _repository.Where(t => t.ReadingId == readModuleId).AnyAsync(t => t.ReadingClinicalDataState == ReadingClinicalDataStatus.HaveSigned)) + { + return ResponseOutput.NotOk("临床资料已签名,不允许删除"); + } + + //增加标准 + if (readModule.ModuleType==ModuleTypeEnum.Global&&(await _readModuleRepository.AnyAsync(x=>x.ModuleType==ModuleTypeEnum.Oncology&&x.SubjectVisitId== readModule.SubjectVisitId && x.TrialReadingCriterionId==readModule.TrialReadingCriterionId))) + { + throw new BusinessValidationFailedException("当前检查批次存在肿瘤学阅片,请先删除肿瘤学阅片"); + } + + if (await _visitTaskRepository.AnyAsync(x => readModuleId==x.SouceReadModuleId)) + { + throw new BusinessValidationFailedException("当前阅片已生成任务,操作失败。"); + } + + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => readModuleId==x.SouceReadModuleId, x => new VisitTask() + { + TaskState = TaskState.Adbandon + }); + + + await _readModuleRepository.UpdatePartialFromQueryAsync(t => t.Id == readModuleId, x => new ReadModule() + { + + IsDeleted = true + }); + await _readModuleRepository.SaveChangesAsync(); + return ResponseOutput.Result(true); + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingPeriod/ReadingPeriodSetService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingPeriod/ReadingPeriodSetService.cs new file mode 100644 index 0000000..249f84d --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ReadingPeriod/ReadingPeriodSetService.cs @@ -0,0 +1,558 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.Reading.Dto; +using MassTransit; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Application.Services +{ + /// + /// 阅片期配置 + /// + [ApiExplorerSettings(GroupName = "Reading")] + public class ReadingPeriodSetService : BaseService + { + + public IRepository _subjectVisitRepository; + private readonly IRepository _readingPeriodSetRepository; + private readonly IRepository _readModuleRepository; + private readonly IRepository _visitStageRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IVisitTaskHelpeService _visitTaskHelpeService; + private readonly IRepository _readingPeriodPlanRepository; + private readonly IRepository _siteSetRepository; + + private readonly IRepository _subjectRepository; + + public ReadingPeriodSetService(IRepository subjectVisitRepository, + IRepository ReadingPeriodSetRepository, + IRepository readModuleRepository, + IRepository visitStageRepository, + IRepository trialRepository, + IRepository visitTaskRepository, + IRepository readingQuestionCriterionTrialRepository, + IVisitTaskHelpeService visitTaskHelpeService, + IRepository readingPeriodPlanRepository, + IRepository SiteSetRepository, + IRepository subjectRepository + ) + { + _subjectVisitRepository = subjectVisitRepository; + _readingPeriodSetRepository = ReadingPeriodSetRepository; + this._readModuleRepository = readModuleRepository; + this._visitStageRepository = visitStageRepository; + this._trialRepository = trialRepository; + this._visitTaskRepository = visitTaskRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this._visitTaskHelpeService = visitTaskHelpeService; + this._readingPeriodPlanRepository = readingPeriodPlanRepository; + _siteSetRepository = SiteSetRepository; + + _subjectRepository = subjectRepository; + } + + + + #region 阅片期配置 基本信息维护 以及生效 + + /// + /// 新增或者修改 (增加标准搜索,已修改) + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddOrUpdateReadingPeriodSet(ReadingPeriodSetAddOrEdit addOrEditReadingPeriodSet) + { + if (await _readingPeriodSetRepository.AnyAsync(x => x.Id != addOrEditReadingPeriodSet.Id && x.IsTakeEffect != ReadingPeriodStatus.Revocation + && x.TrialId == addOrEditReadingPeriodSet.TrialId && x.ReadingPeriodName == addOrEditReadingPeriodSet.ReadingPeriodName && x.TrialReadingCriterionId == addOrEditReadingPeriodSet.TrialReadingCriterionId)) + { + return ResponseOutput.NotOk("阅片期名称重复,操作失败"); + } + if (addOrEditReadingPeriodSet.ReadingPeriodName == "Global") + { + return ResponseOutput.NotOk("阅片期名称不能为Global"); + } + if (addOrEditReadingPeriodSet.Id == null) + { + var entity = _mapper.Map(addOrEditReadingPeriodSet); + entity.ReadingPeriodSites = addOrEditReadingPeriodSet.SiteIds.Select(x => new ReadingPeriodSite() + { + ReadingPeriodSetId = entity.Id, + TrialId = entity.TrialId, + SiteId = x, + }).ToList(); + + entity.ReadingPeriodPlanList = addOrEditReadingPeriodSet.SubjectVisitIds.Select(x => new ReadingPeriodPlan + { + ReadingPeriodSetId = entity.Id, + SubjectVisitId = x, + }).ToList(); + await _readingPeriodSetRepository.AddAsync(entity, true); + return ResponseOutput.Ok(entity.Id); + } + else + { + var entity = (await _readingPeriodSetRepository.Where(t => t.Id == addOrEditReadingPeriodSet.Id, true).Include(t => t.ReadingPeriodSites).Include(x => x.ReadingPeriodPlanList).FirstOrDefaultAsync()).IfNullThrowException(); + _mapper.Map(addOrEditReadingPeriodSet, entity); + + entity.ReadingPeriodSites = addOrEditReadingPeriodSet.SiteIds.Select(x => new ReadingPeriodSite() + { + ReadingPeriodSetId = entity.Id, + TrialId = entity.TrialId, + SiteId = x, + }).ToList(); + + entity.ReadingPeriodPlanList = addOrEditReadingPeriodSet.SubjectVisitIds.Select(x => new ReadingPeriodPlan + { + ReadingPeriodSetId = entity.Id, + SubjectVisitId = x, + }).ToList(); + + var success = await _readingPeriodSetRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id); + + } + } + + + + /// + /// 删除 + /// + /// + /// + [HttpDelete("{readingPeriodSetId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task DeleteReadingPeriodSet(Guid readingPeriodSetId) + { + await _readingPeriodSetRepository.UpdatePartialFromQueryAsync(t => t.Id == readingPeriodSetId, x => new ReadingPeriodSet() + { + + IsDeleted = true + }) ; + await _readingPeriodPlanRepository.UpdatePartialFromQueryAsync(t => t.ReadingPeriodSetId == readingPeriodSetId, x => new ReadingPeriodPlan() + { + IsDeleted = true + + }) ; + await _readingPeriodPlanRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } + + + /// + /// 设置阅片期配置是否生效 (增加标准搜索,已修改) + /// + /// + /// + [HttpPut] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetReadingPeriodSetEffect(SetReadingPeriodSetEffect indto) + { + var readingPeriodSet = await _readingPeriodSetRepository.Where(x => x.Id == indto.Id).FirstNotNullAsync(); + + if (indto.IsTakeEffect == ReadingPeriodStatus.TakeEffect) + { + var plans = _readingPeriodPlanRepository.Where(x => x.ReadingPeriodSetId == indto.Id).Include(x => x.SubjectVisit) + .Include(x => x.ReadingPeriodSet).Include(x => x.SubjectVisit).ToList(); + var needAddVisitIds = plans.Select(x => x.SubjectVisitId).ToList(); + + var repeatVisitNames = _readModuleRepository.Where(x => x.TrialReadingCriterionId == readingPeriodSet.TrialReadingCriterionId && x.ReadingSetType == readingPeriodSet.ReadingSetType && needAddVisitIds.Contains(x.SubjectVisitId)).Select(x => x.Subject.Code + "的" + x.SubjectVisit.VisitName).ToList(); + if (repeatVisitNames.Count != 0) + { + return ResponseOutput.NotOk($"{string.Join(",", repeatVisitNames)}已经添加过阅片期,无法设置生效"); + } + List readModules = new List(); + foreach (var item in plans) + { + readModules.Add(new ReadModule() + { + Id = NewId.NextGuid(), + SubjectId = item.SubjectVisit.SubjectId, + ModuleType = item.ReadingPeriodSet.ReadingSetType == ReadingSetType.ImageReading ? ModuleTypeEnum.Global : ModuleTypeEnum.Oncology, + IsUrgent = item.SubjectVisit.IsUrgent, + ModuleName = item.ReadingPeriodSet.ReadingPeriodName, + SubjectVisitId = item.SubjectVisitId, + ReadingSetType = item.ReadingPeriodSet.ReadingSetType, + ReadingPeriodSetId = item.ReadingPeriodSet.Id, + ReadingStatus = ReadingStatusEnum.TaskAllocate, + TrialId = readingPeriodSet.TrialId, + //VisitNum = item.SubjectVisit.VisitNum, + + //增加标准 + TrialReadingCriterionId = readingPeriodSet.TrialReadingCriterionId + }); + }; + + + // 判断是否要添加肿瘤学或者全局阅片任务 + var subjectVisitIds = readModules.Select(x => x.SubjectVisitId).ToList(); + switch (readingPeriodSet.ReadingSetType) + { + case ReadingSetType.ImageReading: + //增加标准 + var taskInfoList = await _visitTaskRepository.Where(x => x.TrialReadingCriterionId == readingPeriodSet.TrialReadingCriterionId && subjectVisitIds.Contains(x.SourceSubjectVisitId ?? default(Guid)) && + + x.TrialReadingCriterionId == readingPeriodSet.TrialReadingCriterionId && + x.TaskState == TaskState.Effect && x.ReadingTaskState == ReadingTaskState.HaveSigned && !x.IsAnalysisCreate).ToListAsync(); + + foreach (var item in taskInfoList) + { + + var readModule = readModules.Where(x => x.SubjectVisitId == item.SourceSubjectVisitId).FirstOrDefault(); + if (readModule != null) + { + await _visitTaskHelpeService.AddTaskAsync(new GenerateTaskCommand() + { + OriginalVisitId = item.Id, + ReadingCategory = GenerateTaskCategory.Global, + TrialId = item.TrialId, + ReadingGenerataTaskList = new List() { + new ReadingGenerataTaskDTO() + { + IsUrgent = readModule.IsUrgent??false, + SubjectId = readModule.SubjectId, + ReadingName = readModule.ModuleName, + ReadModuleId =readModule.Id, + ReadingCategory = ReadingCategory.Global, + } + } + }); + } + } + break; + + case ReadingSetType.TumorReading: + + + + + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == readingPeriodSet.TrialReadingCriterionId).Select(x => new + { + x.ReadingType, + + }).FirstNotNullAsync(); + + //增加标准 + var globalModuleIds = await _readModuleRepository.Where(x => x.TrialReadingCriterionId == readingPeriodSet.TrialReadingCriterionId && subjectVisitIds.Contains(x.SubjectVisitId) && x.ModuleType == ModuleTypeEnum.Global).Select(x => x.Id).ToListAsync(); + + //增加标准 + var globalTaskInfo = await _visitTaskRepository.Where(x => x.TrialReadingCriterionId == readingPeriodSet.TrialReadingCriterionId && globalModuleIds.Contains(x.SouceReadModuleId ?? default(Guid)) + + && x.TaskState == TaskState.Effect && x.ReadingTaskState == ReadingTaskState.HaveSigned && !x.IsAnalysisCreate).GroupBy(x => new { x.SouceReadModuleId }).Select(x => + new { + SouceReadModuleId = x.Key.SouceReadModuleId, + Count = x.ToList().Count, + TaskId = x.Select(x => x.Id).FirstOrDefault(), + ReadModuleId = x.Select(x => x.SouceReadModuleId).FirstOrDefault(), + JudgeTaskId = x.Select(x => x.JudgeVisitTaskId).FirstOrDefault(), + JudgeTaskResultId = x.Select(x => x.JudgeVisitTask.JudgeResultTaskId).FirstOrDefault(), + }).ToListAsync(); + + foreach (var item in globalTaskInfo) + { + + if (item.Count == (int)criterionInfo.ReadingType) + { + //没有裁判 或者有裁判,并且裁判任务做完了 + if (item.JudgeTaskId == null || item.JudgeTaskResultId != null) + { + var readModule = readModules.Where(x => x.Id == item.ReadModuleId).FirstOrDefault(); + + await _visitTaskHelpeService.AddTaskAsync(new GenerateTaskCommand() + { + OriginalVisitId = item.TaskId, + ReadingCategory = GenerateTaskCategory.Oncology, + TrialId = readingPeriodSet.TrialId, + ReadingGenerataTaskList = new List() { + new ReadingGenerataTaskDTO() + { + IsUrgent = readModule.IsUrgent ?? false, + SubjectId = readModule.SubjectId, + + ReadingName = readModule.ModuleName, + ReadModuleId =readModule.Id, + ReadingCategory = ReadingCategory.Oncology, + } + } + }); + } + + } + + + + } + + break; + } + + await _readModuleRepository.AddRangeAsync(readModules); + + + + } + else + { + + List readModuleIds = await _readModuleRepository.Where(x => x.ReadingPeriodSetId == indto.Id).Select(x => x.Id).ToListAsync(); + + if (await _visitTaskRepository.AnyAsync(x => x.TrialReadingCriterionId == readingPeriodSet.TrialReadingCriterionId && readModuleIds.Contains(x.SouceReadModuleId ?? default(Guid)) + && x.TrialReadingCriterionId == readingPeriodSet.TrialReadingCriterionId + && x.ReadingTaskState == ReadingTaskState.HaveSigned && x.TaskState == TaskState.Effect)) + { + throw new BusinessValidationFailedException("当前标准阅片已生成任务并且阅片完成,撤销失败。"); + } + + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => x.TrialReadingCriterionId == readingPeriodSet.TrialReadingCriterionId && readModuleIds.Contains(x.SouceReadModuleId ?? default(Guid)), x => new VisitTask() + { + TaskState = TaskState.Adbandon + }); + + await _readModuleRepository.UpdatePartialFromQueryAsync(x => x.ReadingPeriodSetId == indto.Id,x=>new ReadModule() { + + IsDeleted=true + }); + } + + var readQuery = await _readingPeriodSetRepository.UpdatePartialFromQueryAsync(indto.Id, x => new ReadingPeriodSet() + { + IsTakeEffect = indto.IsTakeEffect, + EffectOfTime = indto.IsTakeEffect == ReadingPeriodStatus.TakeEffect ? DateTime.Now : null, + }); + + var result = await _readingPeriodSetRepository.SaveChangesAsync(); + return ResponseOutput.Result(result); + } + + + + #endregion + + + #region 阅片计划 相关 + + /// + /// 获取选中的计划 (增加标准不影响 因为阅片期设置关联了标准) + /// + /// + /// + [HttpPost] + public async Task> GetPreviewTheReadingPlanList(PreviewTheReadingListDto inDto) + { + var plans = _readingPeriodPlanRepository.Where(x => x.ReadingPeriodSetId == inDto.ReadingPeriodSetId).Include(x => x.SubjectVisit).Include(x => x.SubjectVisit.TrialSite).Include(x => x.SubjectVisit.Subject) + .Include(x => x.ReadingPeriodSet).Select(x => new PreviewTheReadingListOutDto + { + Id = x.Id, + ExpirationDate = x.ReadingPeriodSet.ExpirationDate, + SubjectVisitId = x.SubjectVisitId, + TrialSiteCode = x.SubjectVisit.TrialSite.TrialSiteCode, + LatestScanDate = x.SubjectVisit.LatestScanDate, + ReadingPeriodName = x.ReadingPeriodSet.ReadingPeriodName, + ReadingPeriodSetId = x.ReadingPeriodSetId, + SubjectCode = x.SubjectVisit.Subject.Code, + SubjectId = x.SubjectVisit.SubjectId, + SubjectVisitName = x.SubjectVisit.VisitName, + EffectOfTime = x.ReadingPeriodSet.EffectOfTime, + }); + + return await plans.ToPagedListAsync(inDto.PageIndex, inDto.PageSize, inDto.SortField == null ? nameof(PreviewTheReadingListOutDto.SubjectId) : inDto.SortField, + inDto.Asc); + } + + + /// + /// 添加对应的阅片计划 (后续生效将计划变为模块) (增加标准不影响 因为阅片期设置关联了标准) + /// + /// + /// + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task GenerateReadingTask(ReadingToGenerateInDto inDto) + { + List plans = new List(); + inDto.SubjectVisitIds.ForEach(x => + { + plans.Add(new ReadingPeriodPlan() + { + SubjectVisitId = x, + ReadingPeriodSetId = inDto.ReadingPeriodSetId + }); + }); + + + await _readingPeriodPlanRepository.UpdatePartialFromQueryAsync(x => x.ReadingPeriodSetId == inDto.ReadingPeriodSetId, x => new ReadingPeriodPlan() + { + IsDeleted = true + + }); + + await _readingPeriodPlanRepository.SaveChangesAsync(); + await _readingPeriodPlanRepository.AddRangeAsync(plans); + var result = await _readingPeriodPlanRepository.SaveChangesAsync(); + return ResponseOutput.Result(result); + } + + #endregion + + + + #region 阅片期相关查询 + + /// + /// 分页获取 (增加标准搜索,已修改) + /// + /// + /// + [HttpPost] + public async Task> GetReadingPeriodSetList(ReadingPeriodSetQuery query) + { + var readQuery = _readingPeriodSetRepository.Where(t => t.TrialId == query.TrialId).Include(x => x.ReadingPeriodSites) + .WhereIf(query.ReadingPeriodName != null, x => x.ReadingPeriodName.Contains(query.ReadingPeriodName!)) + .WhereIf(query.TrialReadingCriterionId != null, x => x.TrialReadingCriterionId == query.TrialReadingCriterionId) + .ProjectTo(_mapper.ConfigurationProvider); + var pageList = await readQuery.ToPagedListAsync(query.PageIndex, query.PageSize, query.SortField == null ? nameof(ReadingPeriodSetView.CreateTime) : query.SortField, + query.Asc); + + pageList.CurrentPageData.ForEach(x => + { + x.SubjectVisitName = x.IsGlobal ? "末次检查批次" : x.SubjectVisitName; + }); + return pageList; + } + + + + /// + /// 获取单条 + /// + /// + /// + [HttpPost("{id:guid}")] + public async Task GetReadingPeriodSet(Guid id) + { + return await _readingPeriodSetRepository.AsQueryable().Include(x => x.ReadingPeriodSites).Where(x => x.Id == id).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + } + + /// + /// 获取阅片期配置的截至检查批次的下拉框 (增加标准搜索,已修改) + /// + /// + /// + [HttpPost] + public async Task> GetReadingVisitList(GetReadingVisitListInDto inDto) + { + var maxVisitNum = await _readingPeriodSetRepository + .WhereIf(inDto.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inDto.TrialReadingCriterionId) + + .Where(x => x.TrialId == inDto.TrialId && x.ReadingSetType == inDto.ReadingSetType && x.IsTakeEffect == ReadingPeriodStatus.TakeEffect) + .MaxAsync(x => x.ExpirationVisitNum) ?? 0; + var thisVisitNum = await _readingPeriodSetRepository.Where(x => x.Id == inDto.ReadingPeriodSetId).Select(x => x.ExpirationVisitNum).FirstOrDefaultAsync() ?? -1; + + var globalVisitNum = new List(); + if (inDto.ReadingSetType == ReadingSetType.TumorReading) + { + globalVisitNum = await _readModuleRepository + .WhereIf(inDto.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inDto.TrialReadingCriterionId) + + .Where(x => x.ReadingSetType == ReadingSetType.ImageReading && x.TrialId == inDto.TrialId).Select(x => x.SubjectVisit.VisitNum).Distinct().ToListAsync(); + } + + List result = await _visitStageRepository.Where(x => x.TrialId == inDto.TrialId) + .WhereIf(inDto.ReadingSetType == ReadingSetType.TumorReading, x => globalVisitNum.Contains(x.VisitNum)) + .Where(x=>x.VisitNum>0)// 不能是基线 + .Where(x => x.VisitNum == thisVisitNum || x.VisitNum >= maxVisitNum).Select(x => new GetReadingVisitListOutDto() + { + VisitName = x.VisitName, + VisitNum = x.VisitNum, + VisitStageId = x.Id, + }).ToListAsync(); + return result; + } + + + + /// + /// 获取影像阅片的预览 // 需要清除之前已经选中的 (增加标准搜索,已修改) + /// + /// + [HttpPost] + public async Task> GetPreviewTheReadingList(PreviewTheReadingListInDto inDto) + { + var readModulequery = _readModuleRepository.AsQueryable(); + + // 当前项目 最晚拍片日期不为null 中心筛选 + var visitQuery = _subjectVisitRepository.Where(x => x.TrialId == inDto.TrialId && x.LatestScanDate != null && !x.IsLostVisit) + .WhereIf(inDto.ReadingScope == ReadingScopeEnum.Site, x => inDto.SiteIds.Contains(x.SiteId)) + .Where(x=>!x.IsBaseLine);// 排除基线 + + // 已经存在的检查批次 需要排除 + var existsBubjectVisitsQuery = _readModuleRepository.Where(y => y.ReadingSetType == inDto.ReadingSetType && y.TrialId == inDto.TrialId && y.TrialReadingCriterionId == inDto.TrialReadingCriterionId).Select(x => x.SubjectVisitId); + + visitQuery = visitQuery.Where(x => !existsBubjectVisitsQuery.Contains(x.Id)) + .WhereIf(inDto.ExpirationDate != null, x => x.LatestScanDate <= inDto.ExpirationDate.Value) + .WhereIf(inDto.ExpirationVisitNum != null, x => x.VisitNum == inDto.ExpirationVisitNum) + .WhereIf(inDto.ReadingSetType == ReadingSetType.TumorReading, x => readModulequery.Where(y => y.SubjectVisitId == x.Id && y.ReadingSetType == ReadingSetType.ImageReading).Count() > 0); + + var subjectIdlist = await visitQuery.OrderBy(x => x.SubjectId).Select(x => x.SubjectId).Distinct().Skip((inDto.PageIndex - 1) * inDto.PageSize).Take(inDto.PageSize).ToListAsync(); + + + var totalCount = visitQuery.Select(x => x.SubjectId).Distinct().Count(); + + var visitlist = await visitQuery.Include(x => x.Subject).Include(x => x.TrialSite).Where(x => subjectIdlist.Contains(x.SubjectId)).ToListAsync(); + + var subjectVisits = visitlist.GroupBy(x => x.SubjectId).Select(x => new + { + SubjectId = x.Key, + Visits = x.ToList() + }); + + List visits = new List(); + subjectVisits.ForEach(x => + { + var visit = x.Visits.OrderByDescending(x => x.VisitNum).FirstOrDefault(); + if (visit != null) + { + visits.Add(visit); + } + }); + + PageOutput result = new PageOutput() + { + CurrentPageData = visits + .Select(x => new PreviewTheReadingListOutDto + { + ExpirationDate = inDto.ExpirationDate, + SubjectVisitId = x.Id, + TrialSiteCode = x.TrialSite.TrialSiteCode, + LatestScanDate = x.LatestScanDate, + ReadingPeriodName = inDto.ReadingPeriodName, + SubjectCode = x.Subject.Code, + SubjectId = x.SubjectId, + SubjectVisitName = x.VisitName, + }).ToList(), + PageSize = inDto.PageSize, + PageIndex = inDto.PageIndex, + TotalCount = totalCount, + }; + return result; + } + + #endregion + + + + + + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/ShortcutKey/ShortcutKeyService.cs b/IRaCIS.Core.Application/Service/Reading/ShortcutKey/ShortcutKeyService.cs new file mode 100644 index 0000000..64ccb97 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/ShortcutKey/ShortcutKeyService.cs @@ -0,0 +1,356 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2023-02-13 10:33:22 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Service +{ + /// + /// 快捷键服务 + /// + [ApiExplorerSettings(GroupName = "Reading")] + public class ShortcutKeyService : BaseService + { + private readonly IRepository _dictionaryRepository; + private readonly IRepository _shortcutKeyRepository; + + public ShortcutKeyService( + IRepository DictionaryRepository, + IRepository shortcutKeyRepository + ) + { + _dictionaryRepository = DictionaryRepository; + _shortcutKeyRepository = shortcutKeyRepository; + } + + + /// + /// 获取医生快捷键 + /// + /// + /// + [HttpPost] + public async Task> GetDoctorShortcutKey(DefaultShortcutKeyQuery inQuery) + { + var shortcutKeyList = await _shortcutKeyRepository.Where(x => x.ImageToolType == inQuery.ImageToolType) + .Where(x => x.UserId == _userInfo.Id) + .ToListAsync(); + + var defaultshortcutKeyList = this.GetDefaultShortcutKey(); + + var shortcutKeydic = await _dictionaryRepository.Where(x => x.Parent.Code == "ShortcutKey") + .WhereIf(inQuery.ShortcutKeyEnum != null, x => x.Code == inQuery.ShortcutKeyEnum.ToString()).ToListAsync(); + + + List result = new List(); + + shortcutKeydic.OrderBy(x => x.ShowOrder).ForEach(x => + { + var key = shortcutKeyList.Where(y => y.ShortcutKeyEnum == int.Parse(x.Code)).FirstOrDefault(); + var defaultkey = defaultshortcutKeyList.Where(y => y.ShortcutKeyEnum == int.Parse(x.Code)).FirstOrDefault(); + var isnull = key == null; + + result.Add(new DefaultShortcutKeyView() + { + Id = x.Id, + ImageToolType = inQuery.ImageToolType, + ShortcutKeyEnum = int.Parse(x.Code), + Keyboardkey = isnull ? defaultkey.Keyboardkey : key.Keyboardkey, + AltKey = isnull ? false : key.AltKey, + CtrlKey = isnull ? false : key.CtrlKey, + MetaKey = isnull ? false : key.MetaKey, + ShiftKey = isnull&& int.Parse(x.Code)==11?true: isnull ? false : key.ShiftKey, + Text = isnull ? defaultkey.Text : key.Text, + Code = isnull ? defaultkey.Code : key.Code, + }); + + }); + + + + return result; + } + + /// + /// 获取默认快捷键 + /// + /// + private List GetDefaultShortcutKey() + { + List shortcutKeys = new List() { + new ShortcutKey (){ + + Keyboardkey= "ArrowLeft", + ShortcutKeyEnum = 1, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "←", + Code= "ArrowLeft" + }, new ShortcutKey (){ + + Keyboardkey= "ArrowRight", + ShortcutKeyEnum = 2, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "→", + Code= "ArrowRight" + }, new ShortcutKey (){ + + Keyboardkey= "PageUp", + ShortcutKeyEnum = 3, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "Page Up", + Code= "PageUp" + }, new ShortcutKey (){ + + Keyboardkey= "PageDown", + ShortcutKeyEnum = 4, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "Page Down", + Code= "PageDown" + }, new ShortcutKey (){ + + Keyboardkey= "ArrowUp", + ShortcutKeyEnum = 5, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "↑", + Code= "ArrowUp" + }, new ShortcutKey (){ + + Keyboardkey= "ArrowDown", + ShortcutKeyEnum = 6, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "↓", + Code= "ArrowDown" + }, new ShortcutKey (){ + + Keyboardkey= "l", + ShortcutKeyEnum = 7, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "L", + Code= "KeyL" + }, new ShortcutKey (){ + + Keyboardkey= "r", + ShortcutKeyEnum = 8, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "R", + Code= "KeyR" + }, new ShortcutKey (){ + + Keyboardkey= "h", + ShortcutKeyEnum = 9, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "H", + Code= "KeyH" + }, new ShortcutKey (){ + + Keyboardkey= "v", + ShortcutKeyEnum = 10, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "V", + Code= "KeyV" + }, new ShortcutKey (){ + + Keyboardkey= "+", + ShortcutKeyEnum = 11, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = true, + MetaKey = false, + Text= "+", + Code= "Equal" + }, new ShortcutKey (){ + + Keyboardkey= "-", + ShortcutKeyEnum = 12, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "-", + Code= "Minus" + }, new ShortcutKey (){ + + Keyboardkey= "=", + ShortcutKeyEnum = 13, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "=", + Code= "Equal" + }, new ShortcutKey (){ + + Keyboardkey= "f", + ShortcutKeyEnum = 14, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "F", + Code= "KeyF" + }, new ShortcutKey (){ + + Keyboardkey= "s", + ShortcutKeyEnum = 15, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "S", + Code= "KeyS" + }, new ShortcutKey (){ + + Keyboardkey= "i", + ShortcutKeyEnum = 16, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "I", + Code= "KeyI" + }, new ShortcutKey (){ + + Keyboardkey= "c", + ShortcutKeyEnum = 17, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "C", + Code= "KeyC" + }, new ShortcutKey (){ + + Keyboardkey= "", + ShortcutKeyEnum = 18, + ImageToolType= 0, + AltKey = false, + CtrlKey = false, + ShiftKey = false, + MetaKey = false, + Text= "Space", + Code= "Space" + }, }; + + + return shortcutKeys; + + + } + + + /// + /// 重置为默认快捷键 + /// + /// + [HttpPost] + public async Task RestoreDefaultShortcutKey(RestoreDefaultShortcutKeyInDto inDto) + { + var shortcutKeys = GetDefaultShortcutKey(); + shortcutKeys.ForEach(x => + { + x.UserId = _userInfo.Id; + }); + + await _shortcutKeyRepository.BatchDeleteNoTrackingAsync(x => x.ImageToolType == inDto.ImageToolType && x.UserId == _userInfo.Id); + + await _shortcutKeyRepository.AddRangeAsync(shortcutKeys.Select(x => new ShortcutKey() + { + ImageToolType = inDto.ImageToolType, + Keyboardkey = x.Keyboardkey, + ShortcutKeyEnum = x.ShortcutKeyEnum, + UserId = _userInfo.Id, + AltKey = x.AltKey, + CtrlKey = x.CtrlKey, + MetaKey = x.MetaKey, + ShiftKey = x.ShiftKey, + Text = x.Text, + Code = x.Code, + })); + + return await _shortcutKeyRepository.SaveChangesAsync(); + } + + /// + /// 设置快捷键 + /// + /// + /// + [HttpPost] + public async Task SetShortcutKey(SetDefaultShortcutKey inDto) + { + await _shortcutKeyRepository.BatchDeleteNoTrackingAsync(x => x.ImageToolType == inDto.ImageToolType && x.UserId == _userInfo.Id); + + await _shortcutKeyRepository.AddRangeAsync(inDto.ShortcutKeyList.Select(x => new ShortcutKey() + { + ImageToolType = inDto.ImageToolType, + Keyboardkey = x.Keyboardkey, + ShortcutKeyEnum = x.ShortcutKeyEnum, + UserId = _userInfo.Id, + AltKey = x.AltKey, + CtrlKey = x.CtrlKey, + MetaKey = x.MetaKey, + ShiftKey = x.ShiftKey, + Text = x.Text, + Code = x.Code, + })); + + return await _shortcutKeyRepository.SaveChangesAsync(); + } + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/_MapConfig.cs b/IRaCIS.Core.Application/Service/Reading/_MapConfig.cs new file mode 100644 index 0000000..d954b9f --- /dev/null +++ b/IRaCIS.Core.Application/Service/Reading/_MapConfig.cs @@ -0,0 +1,252 @@ +using AutoMapper; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infra.EFCore.Common; + +namespace IRaCIS.Core.Application.Service +{ + public class ReadingConfig : Profile + { + public ReadingConfig() + { + + + //是否英文环境 + var isEn_Us=false; + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap() + .ForMember(d => d.SubjectVisitName, u => u.MapFrom(s => s.VisitStage==null?string.Empty: s.VisitStage.VisitName)) + .ForMember(d => d.SiteIds, u => u.MapFrom(s => s.ReadingPeriodSites.Select(x => x.SiteId))) + .ForMember(d => d.SiteCodes, u => u.MapFrom(s => s.ReadingPeriodSites.Select(x => x.TrialSite.TrialSiteCode))) + .ForMember(d => d.PlanCount, u => u.MapFrom(s => s.ReadingPeriodPlanList.Count)); + + CreateMap() + .ForMember(d => d.ChildGroup, u => u.MapFrom(s => s.Dictionary.ChildGroup)) + .ForMember(d => d.Code, u => u.MapFrom(s => s.Dictionary.Code)) + .ForMember(d => d.Description, u => u.MapFrom(s => s.Dictionary.Description)) + .ForMember(d => d.ShowOrder, u => u.MapFrom(s => s.Dictionary.ShowOrder)) + .ForMember(d => d.ParentCode, u => u.MapFrom(s => s.Dictionary.Parent.Code)) + .ForMember(d => d.Value, u => u.MapFrom(s => s.Dictionary.Value)) + .ForMember(d => d.ValueCN, u => u.MapFrom(s => s.Dictionary.ValueCN)); + + + + //CreateMap(); + // CreateMap() + //.ForMember(d => d.ClinicalDataLevel, u => u.MapFrom(s => s.ClinicalDataTrialSet.ClinicalDataLevel)) + //.ForMember(d => d.ClinicalDataLevelName, u => u.MapFrom(s => s.ClinicalDataTrialSet.ClinicalDataSetName)) + //.ForMember(d => d.ClinicalUploadType, u => u.MapFrom(s => s.ClinicalDataTrialSet.ClinicalUploadType)); + //.ForMember(d => d.FileCount, u => u.MapFrom(s => s.ReadingClinicalDataPDFList.Count())); + + CreateMap(); + + //.ForMember(d => d.SiteNames, u => u.MapFrom(s => s.ReadingPeriodSites.SelectMany(x => x.Site.SiteName).ToList())); + + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + + CreateMap() + .ForMember(t => t.TrialCriterionNameList, u => u.MapFrom(c => c.TrialClinicalDataSetCriteriaList.Select(t => t.TrialReadingCriterion.CriterionName))) + .ForMember(t => t.TrialCriterionIdList, u => u.MapFrom(c => c.TrialClinicalDataSetCriteriaList.Select(t => t.TrialReadingCriterion.Id))) + .ForMember(d => d.IsUsed, u => u.MapFrom(s => s.ReadingClinicalDataList.Count()>0)); + + CreateMap(); + //.ForMember(t=>t.SystemCriterionNameList,u=>u.MapFrom(c=>c.SystemClinicalDataCriterionList.Select(t=>t.SystemReadingCriterion.CriterionName))) + //.ForMember(t => t.SystemCriterionIdList, u => u.MapFrom(c => c.SystemClinicalDataCriterionList.Select(t => t.SystemReadingCriterion.Id))); + + CreateMap().ReverseMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + + CreateMap(); + + CreateMap() + .ForMember(d => d.OriginalId, u => u.MapFrom(s => s.Id)); + //.ForMember(dest => dest.Instance, opt => opt.Ignore()) + //.ForMember(dest => dest.ReadingQuestionTrial, opt => opt.Ignore()); + + #region 阅片问题 + + + // 忽略列 + CreateMap() + .ForMember(d => d.QuestionName, u => u.MapFrom(s => isEn_Us?s.QuestionEnName:s.QuestionName)) + .ForMember(dest => dest.ReadingCriterionPage, opt => opt.Ignore()) + .ForMember(dest => dest.ParentReadingQuestionTrial, opt => opt.Ignore()) + .ForMember(dest => dest.RelevanceReadingQuestionTrial, opt => opt.Ignore()) + .ForMember(dest => dest.ReadingQuestionCriterionTrial, opt => opt.Ignore()); + + + CreateMap() + .ForMember(d => d.ShowOrder, u => u.MapFrom(s => s.ReadingQuestionTrial.ShowOrder)) + .ForMember(d => d.OrderMark, u => u.MapFrom(s => s.ReadingQuestionTrial.OrderMark)) + .ForMember(d => d.RowIndex, u => u.MapFrom(s => s.RowIndex.ToString())) + .ForMember(d => d.RowIndexNum, u => u.MapFrom(s => s.RowIndex)) + .ForMember(d => d.RowId, u => u.MapFrom(s => s.Id)); + + + + CreateMap() + .ForMember(d => d.QuestionName, u => u.MapFrom(s => isEn_Us ? s.QuestionEnName : s.QuestionName)); + CreateMap() + .ForMember(d => d.ShowOrder, u => u.MapFrom(s => s.ReadingQuestionTrial.ShowOrder)) + .ForMember(d => d.QuestionMark, u => u.MapFrom(s => s.ReadingTableQuestionTrial.QuestionMark)); + + CreateMap() + .ForMember(d => d.QuestionName, u => u.MapFrom(s => isEn_Us ? s.QuestionEnName : s.QuestionName)) + .ForMember(d => d.PageShowOrder, u => u.MapFrom(s => s.ReadingCriterionPage.ShowOrder)) + .ForMember(d => d.PageName, u => u.MapFrom(s => s.ReadingCriterionPage.PageName)) + .ForMember(d => d.IsPublicPage, u => u.MapFrom(s => s.ReadingCriterionPage.IsPublicPage)); + CreateMap(); + + CreateMap(); + CreateMap() + .ForMember(d => d.QuestionName, u => u.MapFrom(s => isEn_Us ? s.QuestionEnName : s.QuestionName)); + + CreateMap() + .ForMember(d => d.DependShowOrder, u => u.MapFrom(s => s.DependParentQuestion.ShowOrder)); + + + CreateMap() + .ForMember(d => d.DependShowOrder, u => u.MapFrom(s => s.DependParentQuestion.ShowOrder)); + CreateMap(); + + CreateMap(); + + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap() + .ForMember(d => d.OriginalId, u => u.MapFrom(s => s.Id)); + + CreateMap() + .ForMember(d => d.OriginalId, u => u.MapFrom(s => s.Id)); + + CreateMap() + .ForMember(d => d.ReadingQuestionSystemId, u => u.MapFrom(s => s.Id)); + + CreateMap() + .ForMember(d => d.MergeName, u => u.MapFrom(s => s.MergeRow == null ? string.Empty : s.MergeRow.ReadingQuestionTrial.OrderMark + s.MergeRow.RowIndex.GetLesionMark())) + .ForMember(d => d.SplitName, u => u.MapFrom(s => s.SplitRow == null ? string.Empty : s.SplitRow.ReadingQuestionTrial.OrderMark + s.SplitRow.RowIndex.GetLesionMark())) + .ForMember(d => d.LesionType, u => u.MapFrom(s => s.ReadingQuestionTrial == null ?null : s.ReadingQuestionTrial.LesionType)); + + + CreateMap() + .ForMember(dest => dest.DependParentQuestion, opt => opt.Ignore()) + .ForMember(d => d.OriginalId, u => u.MapFrom(s => s.Id)) + .ForMember(d => d.SystemTableQuestionId, u => u.MapFrom(s => s.Id)); + + CreateMap() + .ForMember(d => d.OriginalId, u => u.MapFrom(s => s.Id)); + + CreateMap() + .ForMember(dest => dest.ReadingQuestionTrialList, opt => opt.Ignore()) + .ForMember(d => d.ReadingQuestionCriterionSystemId, u => u.MapFrom(s => s.Id)); ; + + + CreateMap(); + CreateMap() + .ForMember(d => d.QuestionCount, u => u.MapFrom(s => s.ReadingQuestionSystemList.Count())); + //.ForMember(d => d.IsEnable, u => u.MapFrom(s => s.Dictionary.IsEnable)) + //.ForMember(d => d.ShowOrder, u => u.MapFrom(s => s.Dictionary.ShowOrder)); + + CreateMap(); + CreateMap(); + CreateMap() + .ForMember(d => d.GroupName, u => u.MapFrom(s => s.GroupInfo==null?s.GroupName:s.GroupInfo.GroupName)) + .ForMember(d => d.ParentQuestionName, u => u.MapFrom(s => s.ParentReadingQuestionSystem==null?string.Empty: s.ParentReadingQuestionSystem.QuestionName)) + .ForMember(d => d.ParentQuestionShowOrder, u => u.MapFrom(s => s.ParentReadingQuestionSystem.ShowOrder)) + .ForMember(d => d.ParentDictionaryCode, u => u.MapFrom(s => s.ParentReadingQuestionSystem.DictionaryCode)) + .ForMember(d => d.ParentQuestionGenre, u => u.MapFrom(s => s.ParentReadingQuestionSystem.QuestionGenre)) + .ForMember(d => d.RelevanceShowOrder, u => u.MapFrom(s => s.RelevanceReadingQuestionSystem.ShowOrder)) + .ForMember(d => d.RelevanceDictionaryCode, u => u.MapFrom(s => s.RelevanceReadingQuestionSystem.DictionaryCode)) + .ForMember(d => d.RelevanceQuestionGenre, u => u.MapFrom(s => s.RelevanceReadingQuestionSystem.QuestionGenre)); + + + CreateMap(); + CreateMap() + .ForMember(d => d.QuestionCount, u => u.MapFrom(s => s.ReadingQuestionTrialList.Count())); + + + CreateMap(); + CreateMap() + .ForMember(d => d.GroupName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupName : s.GroupInfo.GroupName)) + .ForMember(d => d.GroupEnName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupEnName : s.GroupInfo.GroupEnName)) + .ForMember(d => d.ParentQuestionName, u => u.MapFrom(s => s.ParentReadingQuestionTrial == null ? string.Empty : s.ParentReadingQuestionTrial.QuestionName)) + .ForMember(d => d.ParentQuestionShowOrder, u => u.MapFrom(s => s.ParentReadingQuestionTrial.ShowOrder)) + .ForMember(d => d.ParentQuestionGenre, u => u.MapFrom(s => s.ParentReadingQuestionTrial.QuestionGenre)) + .ForMember(d => d.ParentDictionaryCode, u => u.MapFrom(s => s.ParentReadingQuestionTrial.DictionaryCode)) + .ForMember(d => d.RelevanceShowOrder, u => u.MapFrom(s => s.RelevanceReadingQuestionTrial.ShowOrder)) + .ForMember(d => d.RelevanceDictionaryCode, u => u.MapFrom(s => s.RelevanceReadingQuestionTrial.DictionaryCode)) + .ForMember(d => d.RelevanceQuestionGenre, u => u.MapFrom(s => s.RelevanceReadingQuestionTrial.QuestionGenre)); ; + #endregion + + #region IR阅片 + CreateMap() + .ForMember(d => d.QuestionName, u => u.MapFrom(s => isEn_Us ? s.QuestionEnName : s.QuestionName)) + .ForMember(x=>x.Id, y=>y.MapFrom(z=>z.Id)); + + //CreateMap() + // .ForMember(x => x.VisitTaskId, y => y.MapFrom(z => z.Id)) + // .ForMember(x => x.JudgeResultArm, y => y.MapFrom(z => (z.JudgeResultTask==null?null:z.JudgeResultTask.ArmEnum))); + #endregion + + #region 医学审核 + CreateMap(); + CreateMap() + .ForMember(x => x.ParentShowOrder, y => y.MapFrom(n => n.ParentQuestion.ShowOrder)); + + + CreateMap(); + + CreateMap(); + CreateMap() + .ForMember(x=>x.ParentShowOrder,y=>y.MapFrom(n=>n.ParentQuestion.ShowOrder)); + + CreateMap(); + + + CreateMap() + .ForMember(x => x.TaskMedicalReviewId, y => y.MapFrom(n => n.Id)); + + CreateMap() + //.ForMember(x => x.FileList, y => y.MapFrom(n => n.FileList)) + .ForMember(x => x.CreateUserName, y => y.MapFrom(n => n.CreateUser.UserName)); + + #endregion + + #region 阅片部位 + + + CreateMap(); + CreateMap().ReverseMap(); + + CreateMap(); + CreateMap(); + #endregion + } + } + +} diff --git a/IRaCIS.Core.Application/Service/ReadingAndReport/DTO/GlobalReportDTO.cs b/IRaCIS.Core.Application/Service/ReadingAndReport/DTO/GlobalReportDTO.cs new file mode 100644 index 0000000..02bb2b3 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingAndReport/DTO/GlobalReportDTO.cs @@ -0,0 +1,91 @@ +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Application.Contracts +{ + public class HistoryVisitRSDTO + { + + public Guid StudyId { get; set; } + public string StudyCode { get; set; } = String.Empty; + + public decimal VisitNum { get; set; } + + public string VisitName { get; set; } = String.Empty; + + public string OverallResponse { get; set; } = String.Empty; + + public string TpCode { get; set; } = String.Empty; + + public GlobalRSSelectView GlobalRSSelect { get; set; } = new GlobalRSSelectView(); + + } + + public class GlobalRSSelectView + { + public bool? Agree { get; set; } + public string NewRS { get; set; } = String.Empty; + public string Note { get; set; } = String.Empty; + } + + public class HistoryGlobalRsDTO + { + + public decimal VisitNum { get; set; } + + public string VisitName { get; set; } = String.Empty; + + public string OverallResponse { get; set; } = String.Empty; + } + + public class PreviousGlobalReadsView + { + public string SubjectNote { get; set; } = String.Empty; + + public List PreviousGlobalReadsList { get; set; } = new List(); + } + + + public class GlobalTaskReportCommand + { + public Guid GlobalId { get; set; } + public string SubjectNote { get; set; } = String.Empty; + public string SubjectCode { get; set; } = String.Empty; + public Guid SubjectId { get; set; } + + public List GlobalRSReportList { get; set; }=new List(); + } + + public class GlobalReportCommand + { + public Guid GlobalId { get; set; } + public string TpCode { get; set; } = String.Empty; + public int VisitNum { get; set; } + public bool? Agree { get; set; } + public string NewRS { get; set; } = String.Empty; + public string Note { get; set; } = String.Empty; + + } + + + public class AdReportDTO + { + public WorkloadAD ADInfo { get; set; } = new WorkloadAD(); + public PreviousGlobalReadsView Global1 { get; set; } = new PreviousGlobalReadsView(); + public PreviousGlobalReadsView Global2 { get; set; } = new PreviousGlobalReadsView(); + + public IEnumerable Global1VisitRS { get; set; }=new List(); + public IEnumerable Global2VisitRS { get; set; } = new List(); + + } + + + public class ADReportCommand + { + + public Guid AdId { get; set; } + public Guid SelectGlobalId { get; set; } + public string ADNote { get; set; } = String.Empty; + } + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/ReadingAndReport/DTO/ReportDTO.cs b/IRaCIS.Core.Application/Service/ReadingAndReport/DTO/ReportDTO.cs new file mode 100644 index 0000000..fe88118 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingAndReport/DTO/ReportDTO.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IRaCIS.Core.Application.Contracts +{ + public class LesionInformation + { + public Guid TUId { get; set; } + public int LesionType { get; set; } //病灶类型 + public string STUDYID { get; set; } = string.Empty; // 项目ID + public string USUBJID { get; set; } = string.Empty;// 患者ID + public int TUSEQ { get; set; } // 病灶序号 + + // 病灶分组,主要是用于将分裂或者结合在一起的已经被标识的肿瘤分类 + public string TUGRPID { get; set; } = string.Empty; + + //内部或外部的肿瘤/病灶标识。 例如:医学影像 ID + public string TUREFID { get; set; } = string.Empty; + + public string TUSPID { get; set; } = string.Empty;// 申办方标识 + + // 关联测量结果,及TUGRPID一起,标识分裂及合并 + public string TULNKID { get; set; } = string.Empty; + + /// + /// 检查项目 + /// TUMIDENT (Tumor Identification ) + /// TUSPLIT (Tumor Split ) + /// TUMERGE (Tumor Merged) + /// + public string TUTESTCD { get; set; } = string.Empty; + public string TUTEST { get; set; } = string.Empty; + + public string TUORRES { get; set; } = string.Empty;//TARGET, NON-TARGET, NEW + + public string TUSTRESC { get; set; } = string.Empty;//包含从 TUORRES拷贝的所有发现的结果 + public string TUNAM { get; set; } = string.Empty;//完成肿瘤标识的供应商名称或标识 + + public string LocDescription { get; set; } = string.Empty;// 部位描述 + public string TULOC { get; set; } = string.Empty; // 解剖学部位 + public string TULAT { get; set; } = string.Empty;//解剖学部位或者样本更详细的偏侧性修饰语,比如,左侧, 右侧,双侧。 + public string TUDIR { get; set; } = string.Empty;//解剖学部位或者样本更详细的方向性修饰语,比如,上边的 ,里面的。 + public string TUPORTOT { get; set; } = string.Empty;//解剖学部位或者样本更详细的分布,安排,分配的修饰语, 比如,全部的,单一的,部分的,多数的。 + + public string TUMETHOD { get; set; } = string.Empty; //用来标识肿瘤的办法。 例如,核磁共振,CT扫描。 + + public string TUEVAL { get; set; } = string.Empty; //评估者的角色。 例如:研究者,独立评估者。 + public string TUEVALID { get; set; } = string.Empty;//这个特定的评估者变量是与 TUEVAL一起使用来提供更详 细的信息 + public string TUACPTFL { get; set; } = string.Empty; + + // 检查批次信息 + public decimal VISITNUM { get; set; } = 0; + public string VISIT { get; set; } = string.Empty; + public int VISITDY { get; set; } + public string EPOCH { get; set; } = string.Empty; + public string TUDTC { get; set; } = string.Empty; + public int TUDY { get; set; } = 0; + + //TR + + public string TRTESTCD { get; set; } = string.Empty; + public string TRTEST { get; set; } = string.Empty; + public string TRORRES { get; set; } = string.Empty;//原始收到或采集的肿瘤测量/评估结 + public string TRORRESU { get; set; } = string.Empty;//原始单位 + public string TRSTRESC { get; set; } = string.Empty;//标准化结 果 + public double TRSTRESN { get; set; }//标准化结 果(N) + public string TRSTRESU { get; set; } = string.Empty;// 标准化单位 + public string TRSTAT { get; set; } = string.Empty; // 未做状态 + public string TRREASND { get; set; } = string.Empty; // 未做原因 + public string Note { get; set; } = string.Empty; + public string TRGRPID { get; set; } = string.Empty; + public string TRDTC { get; set; } = string.Empty; + public int TRDY { get; set; } = 0; + ////RS + //public string RSCAT { get; set; } // 类别,用来识别对反应评估中使用的标准,以及适当时提供版本号 + //public string RSORRES { get; set; } //原始接收、采集或者计算的肿瘤反应评估的结果。 + //public string RSSTRESC { get; set; } //标准化结果 + + public bool CoveredLesion { get; set; } + } + public class VisitLesion : LesionInformation + { + public LesionInformation CurrentLesion { get; set; } = new LesionInformation(); + } + + public class EfficacyAssessment + { + public string TargetLesion { get; set; } = string.Empty; + public string Non_targetLesion { get; set; } = string.Empty; + public string OverallAssessment { get; set; } = string.Empty; + + public string TargetLesionNote { get; set; } = string.Empty; + public string Non_targetLesionNote { get; set; } = string.Empty; + public string OverallAssessmentNote { get; set; } = string.Empty; + } + + public class PreviousOverallAssessment + { + public string VisitName { get; set; } = string.Empty; + public string OverallAssessment { get; set; } = string.Empty; + } + public class VisitLesionInfo + { + //基线病灶信息及本次检查批次测量信息 + public IList BLLesionList { get; set; } = new List(); + // public IList BLVisitLesionList { get; set; } = new List(); + //public IList CurrentVisitLesionList { get; set; } = new List(); + + // 以往病灶信息 + public IList PreviousNewLesionList { get; set; } = new List(); + //本次疑似新病灶 + public IList EquivocalNewLesionList { get; set; } = new List(); + public IList UnequivocalNewLesionList { get; set; } = new List(); + public ReportDTO ReportResult { get; set; } = new ReportDTO(); + public EfficacyAssessment Efficacy { get; set; } = new EfficacyAssessment(); + public List PreviousOverallAssessment { get; set; } = new List(); + + public MinVisit MinVisitInfo { get; set; } = new MinVisit(); + } + + public class MinVisit + { + public double MinSum { get; set; } = 0.0; + public string MinVistName { get; set; } = string.Empty; + } + public class ReportCommand + { + public List LesionInformationList { get; set; } = new List(); + + //RS + public string RSCAT { get; set; } = string.Empty;// 类别,用来识别对反应评估中使用的标准,以及适当时提供版本号 + public string RSORRES { get; set; } = string.Empty; //原始接收、采集或者计算的肿瘤反应评估的结果。 + public string RSSTRESC { get; set; } = string.Empty; //标准化结果 + } + public class TUDTO + { + public int LesionType { get; set; } + public string STUDYID { get; set; } = string.Empty; + public string USUBJID { get; set; } = string.Empty; + public int TUSEQ { get; set; } + public string TUGRPID { get; set; } = string.Empty; + public string TUREFID { get; set; } = string.Empty;//内部或外部的肿瘤/病灶标识。 例如:医学影像 ID + public string TUSPID { get; set; } = string.Empty; + public string TULNKID { get; set; } = string.Empty; + public string TUTESTCD { get; set; } = string.Empty; + public string TUTEST { get; set; } = string.Empty; + + public double TUORRES { get; set; } = 0; + public string TUSTRESC { get; set; } = string.Empty; + public string TUNAM { get; set; } = string.Empty; + public string TULOC { get; set; } = string.Empty; + public string TULAT { get; set; } = string.Empty; + public string TUDIR { get; set; } = string.Empty; + public string TUPORTOT { get; set; } = string.Empty; + public string TUMETHOD { get; set; } = string.Empty; + public string TUEVAL { get; set; } = string.Empty; + public string TUEVALID { get; set; } = string.Empty; + public string TUACPTFL { get; set; } = string.Empty; + public double VISITNUM { get; set; } = 0; + public string VISIT { get; set; } = string.Empty; + public string VISITDY { get; set; } = string.Empty; + public string EPOCH { get; set; } = string.Empty; + public string TUDTC { get; set; } = string.Empty; + public int TUDY { get; set; } = 0; + } + public class TRDTO + { + public string STUDYID { get; set; } = string.Empty; + public string DOMAIN { get; set; } = string.Empty; + public string USUBJID { get; set; } = string.Empty; + public int TRSEQ { get; set; } + public string TRGRPID { get; set; } = string.Empty; + public string TRREFID { get; set; } = string.Empty; + public string TRSPID { get; set; } = string.Empty; + public string TRLNKID { get; set; } = string.Empty; + public string TRLNKGRP { get; set; } = string.Empty; + public string TRTESTCD { get; set; } = string.Empty; + public string TRTEST { get; set; } = string.Empty; + public string TRORRES { get; set; } = string.Empty; + public string TRORRESU { get; set; } = string.Empty; + public string TRSTRESC { get; set; } = string.Empty; + public double TRSTRESN { get; set; } = 0; + public string TRSTRESU { get; set; } = string.Empty; + public string TRSTAT { get; set; } = string.Empty; + public string TRREASND { get; set; } = string.Empty; + public string TRNAM { get; set; } = string.Empty; + public string TRMETHOD { get; set; } = string.Empty; + public string TREVAL { get; set; } = string.Empty; + public string TREVALID { get; set; } = string.Empty; + public string TRACPTFL { get; set; } = string.Empty; + public decimal VISITNUM { get; set; } = 0; + public string VISIT { get; set; } = string.Empty; + public int VISITDY { get; set; } = 0; + public string EPOCH { get; set; } = string.Empty; + public string TRDTC { get; set; } = string.Empty; + public int TRDY { get; set; } = 0; + public string Note { get; set; } = string.Empty; + public bool CoveredLesion { get; set; } = true; + } + + public class RSDTO + { + public string STUDYID { get; set; } = string.Empty; + public string DOMAIN { get; set; } = string.Empty; + public string USUBJID { get; set; } = string.Empty; + public int RSSEQ { get; set; } + public string RSGRPID { get; set; } = string.Empty; + public string RSREFID { get; set; } = string.Empty; + public string RSSPID { get; set; } = string.Empty; + public string RSLNKID { get; set; } = string.Empty; + public string RSLNKGRP { get; set; } = string.Empty; + public string RSTESTCD { get; set; } = string.Empty; + public string RSTEST { get; set; } = string.Empty; + public string RSCAT { get; set; } = string.Empty; + public string RSORRES { get; set; } = string.Empty; + public string RSSTRESC { get; set; } = string.Empty; + public string RSSTAT { get; set; } = string.Empty; + public string RSREASND { get; set; } = string.Empty; + public string RSNAM { get; set; } = string.Empty; + + public string RSEVAL { get; set; } = string.Empty; + public string RSEVALID { get; set; } = string.Empty; + public string RSACPTFL { get; set; } = string.Empty; + public decimal VISITNUM { get; set; } = 0; + public string VISIT { get; set; } = string.Empty; + public int VISITDY { get; set; } = 0; + public string EPOCH { get; set; } = string.Empty; + public string RSDTC { get; set; } = string.Empty; + public int RSDY { get; set; } = 0; + public Guid StudyGuid { get; set; } = Guid.Empty; + public Guid TrialGuid { get; set; } = Guid.Empty; + public Guid SubjectGuid { get; set; } = Guid.Empty; + + public string Note { get; set; } = string.Empty; + } + public class ReportDTO + { + public bool Qualified { get; set; } = true; + public string DiseaseProgression { get; set; } = string.Empty; + public string NotEvaluable { get; set; } = string.Empty; + public string Timepoint { get; set; } = string.Empty; + public string TrialCode { get; set; } = string.Empty; + public string SubjectCode { get; set; } = string.Empty; + public decimal? VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + public int? DiseaseSituation { get; set; } + public string Comment { get; set; } = String.Empty; + public Guid? TPId { get; set; } + public Guid? DicomStudyId { get; set; } + public string TpCode { get; set; } = string.Empty; + + public bool AffectRead { get; set; }//是否影响读片 + public string AffectReadNote { get; set; } = String.Empty;//备注说明 + } + + + public class VisitReportCommand + { + public List LesionInformation { get; set; } = new List(); + public List TRList { get; set; } = new List(); + public List RSList { get; set; } = new List(); + public ReportDTO ReportResult { get; set; } = new ReportDTO(); + } + + public class BaseLineReportCommand + { + //public Guid TimepointId { get; set; } + public string SumOfDiameter { get; set; } = String.Empty; + public string SumDiameterOfNonLymphNode { get; set; } = String.Empty; + public IList LesionInformation { get; set; } = new List(); + public ReportDTO ReportResult { get; set; } = new ReportDTO(); + } + + public class BaseLineReportDTO + { + public IList LesionInformation { get; set; } = new List(); + public ReportDTO ReportResult { get; set; } = new ReportDTO(); + } +} diff --git a/IRaCIS.Core.Application/Service/ReadingAndReport/DTO/WorkloadReadingDTO.cs b/IRaCIS.Core.Application/Service/ReadingAndReport/DTO/WorkloadReadingDTO.cs new file mode 100644 index 0000000..965927a --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingAndReport/DTO/WorkloadReadingDTO.cs @@ -0,0 +1,39 @@ +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + public class WorkloadReadingDTO + { + public Guid Id { get; set; } + + public Guid StudyId { get; set; } + public Guid TrialId { get; set; } + public string TrialCode { get; set; } = string.Empty; + public string TrialIndication { get; set; } = string.Empty; + public string Sponsor { get; set; } = string.Empty; + public int Expedited { get; set; } + + public Guid SubjectId { get; set; } + public string SubjectCode { get; set; } = string.Empty; + + public string VisitName { get; set; } = string.Empty; + public decimal VisitNum { get; set; } + + + public Guid WorkloadId { get; set; } + public string WorkloadCode { get; set; } = string.Empty; + public int WorkloadType { get; set; } + public int Status { get; set; } + + public DateTime UpdateTime { get; set; } + } + + public class WorkloadQueryParam : PageInput + { + public Guid? TrialId { get; set; } = Guid.Empty; + public string SubjectCode { get; set; } = string.Empty; + public int? Status { get; set; } + public int WorkloadType { get; set; } + public int? Expedited { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/ReadingAndReport/GlobalReportService.cs b/IRaCIS.Core.Application/Service/ReadingAndReport/GlobalReportService.cs new file mode 100644 index 0000000..e63303c --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingAndReport/GlobalReportService.cs @@ -0,0 +1,197 @@ +//using IRaCIS.Core.Application.Contracts; +//using System.Linq.Dynamic.Core; +//using AutoMapper; +//using IRaCIS.Application.Contracts; +//using IRaCIS.Core.Infra.EFCore; +//using IRaCIS.Core.Domain.Models; +//using IRaCIS.Core.Infrastructure.Extention; +//using Microsoft.AspNetCore.Mvc; + +//namespace IRaCIS.Core.Application.Services +//{ +// [ ApiExplorerSettings(GroupName = "Reading")] +// public class GlobalReportService : BaseService, IGlobalService +// { +// private readonly IRepository _globalRsRepository; +// private readonly IRepository _globalResultRepository; +// private readonly IRSRepository _rsRepository; +// private readonly IRepository _visitStageRepository; + +// private readonly IRepository _subjectVisitRepository; +// private readonly IWorkloadADRepository _workloadAdRepository; +// private readonly IWorkloadGlobalRepository _workloadGlobalRepository; +// private readonly IRepository _trialRepository; +// private readonly IRepository _subjectRepository; + +// public GlobalReportService(IRepository globalRsRepository, IRepository globalResultRepository, +// IRSRepository rsRepository, IRepository visitStageRepository, IRepository subjectVisitRepository, IWorkloadADRepository workloadAdRepository, +// IWorkloadGlobalRepository workloadGlobalRepository, IRepository trialRepository, IRepository subjectRepository) +// { +// _globalRsRepository = globalRsRepository; +// _globalResultRepository = globalResultRepository; +// _rsRepository = rsRepository; +// _visitStageRepository = visitStageRepository; + +// _subjectVisitRepository = subjectVisitRepository; +// _workloadAdRepository = workloadAdRepository; +// _workloadGlobalRepository = workloadGlobalRepository; +// _trialRepository = trialRepository; +// _subjectRepository = subjectRepository; +// } + +// [HttpGet("{trialId:guid}/{subjectId:guid}/{visitNum:decimal}/{globalId:guid}/{globalCode}")] +// public IEnumerable GetHistoryVisitRsList(Guid trialId, Guid subjectId, decimal visitNum, Guid globalId, string globalCode) +// { + +// var tpGroup = 'T' + globalCode.Trim().Substring(globalCode.Trim().Length - 2); + + +// var query = from rs in _rsRepository.AsQueryable() +// .Where(t => t.SubjectGuid == subjectId && t.TrialGuid == trialId && t.RSTESTCD == "OVRLRESP" && t.VISITNUM <= visitNum && t.TpCode.Contains(tpGroup)) + +// join globalRs in _globalRsRepository.Where(t => t.GlobalId == globalId) on new { rs.TpCode, VisitNum = rs.VISITNUM } equals new { globalRs.TpCode, globalRs.VisitNum } into cc +// from globalRs in cc.DefaultIfEmpty() + +// join visitStage in _visitStageRepository.Where(t => t.TrialId == trialId) on rs.VISITNUM equals visitStage.VisitNum +// select new HistoryVisitRSDTO +// { +// StudyId = rs.StudyGuid, +// StudyCode = rs.STUDYID, +// VisitNum = visitStage.VisitNum, +// VisitName = visitStage.VisitName, +// OverallResponse = rs.RSORRES, +// TpCode = rs.TpCode, +// GlobalRSSelect = new GlobalRSSelectView() +// { +// Agree = globalRs.Agree, +// NewRS = globalRs.NewRS, +// Note = globalRs.Note +// } +// }; + +// var visitRsList = query.OrderBy(t => t.VisitNum).ToList(); + +// return visitRsList; + +// } +// #pragma warning disable +// [HttpGet("{trialId:guid}/{subjectId:guid}/{visitNum:decimal}/{globalId:guid}")] +// public PreviousGlobalReadsView GetHistoryGlobalRsList(Guid trialId, Guid subjectId, decimal visitNum, +// Guid globalId) +// { +// //subjectCode = subjectCode.Trim(); + +// var query = from globalResult in _globalResultRepository.AsQueryable() +// .Where(t => t.SubjectId == subjectId && t.VisitNum < visitNum) +// join visitStage in _visitStageRepository.Where(t => t.TrialId == trialId) on globalResult.VisitNum equals visitStage.VisitNum +// select new HistoryGlobalRsDTO +// { +// VisitNum = visitStage.VisitNum, +// VisitName = visitStage.VisitName, +// OverallResponse = globalResult.Result +// }; + +// var subjectNote = _globalResultRepository.FirstOrDefault(t => t.GlobalId == globalId)?.SubjectNote; + +// return new PreviousGlobalReadsView() +// { +// PreviousGlobalReadsList = query.ToList(), +// SubjectNote = subjectNote +// }; + +// } + +// public IResponseOutput AddGlobalReport(GlobalTaskReportCommand globalTaskReportCommand) +// { +// //删除上一次保存得记录 +// _globalResultRepository.Delete(t => t.GlobalId == globalTaskReportCommand.GlobalId); +// _globalRsRepository.Delete(t => t.GlobalId == globalTaskReportCommand.GlobalId); + + +// var globalRsList = _mapper.Map>(globalTaskReportCommand.GlobalRSReportList); + +// _globalRsRepository.AddRange(globalRsList); + +// var first = globalRsList.OrderByDescending(t => t.VisitNum).First(); + +// var globalResult = new GlobalResult() +// { +// VisitNum = first.VisitNum, +// SubjectCode = globalTaskReportCommand.SubjectCode, +// SubjectNote = globalTaskReportCommand.SubjectNote, +// SubjectId = globalTaskReportCommand.SubjectId, + +// GlobalId = first.GlobalId, +// Result = first.NewRS +// }; + +// _globalResultRepository.Add(globalResult); + +// return ResponseOutput.Result(_globalResultRepository.SaveChanges()) ; + +// } + +// //public string GetCommentsForSubject(Guid globalId) +// //{ +// // return _globalResultRepository.FirstOrDefault(t => t.GlobalId == globalId)?.SubjectNote; +// //} +// [HttpGet("{adId}")] +// public AdReportDTO GetAdReport(Guid adId) +// { +// var adReport = new AdReportDTO(); +// var ad = _workloadAdRepository.FirstOrDefault(t => t.Id == adId); +// var globalId1 = ad.Global1Id; +// var globalId2 = ad.Global2Id; + +// var query = from global in _workloadGlobalRepository.Where(t => t.Id == globalId1 || t.Id == globalId2) +// join trial in _trialRepository.Where(t => t.Id == ad.TrialId) on global.TrialId equals trial.Id +// join subject in _subjectRepository.AsQueryable() on global.SubjectId equals subject.Id +// select new WorkloadReadingDTO +// { +// Id = global.Id, +// WorkloadId = global.Id, +// WorkloadType = 1, +// Status = global.Status, +// UpdateTime = global.UpdateTime, +// TrialId = global.TrialId, +// SubjectId = global.SubjectId, +// SubjectCode = subject.Code, +// //VisitNum = visit.VisitNum, +// //VisitName = visit.VisitName, +// VisitNum = global.VisitNum, +// VisitName = global.VisitName, + +// Expedited = trial.Expedited, +// TrialCode = trial.TrialCode, +// TrialIndication = trial.Indication, +// WorkloadCode = global.GlobalCode +// }; + +// var globalList = query.ToList(); + +// var global1 = globalList.First(t => t.Id == globalId1); +// var global2 = globalList.First(t => t.Id == globalId2); + +// adReport.Global1VisitRS = GetHistoryVisitRsList(global1.TrialId, global1.SubjectId, +// global1.VisitNum, global1.Id, global1.WorkloadCode); + +// adReport.Global2VisitRS = GetHistoryVisitRsList(global2.TrialId, global2.SubjectId +// ,global2.VisitNum, global2.Id, global2.WorkloadCode); + +// adReport.Global1 = GetHistoryGlobalRsList(global1.TrialId, global1.SubjectId, global1.VisitNum, global1.Id); +// adReport.Global2 = GetHistoryGlobalRsList(global2.TrialId, global2.SubjectId, global2.VisitNum, global2.Id); + +// adReport.ADInfo = ad; + +// return adReport; + +// } + +// public IResponseOutput AddAdjudicationReport(ADReportCommand adReportCommand) +// { +// return ResponseOutput.Result(_workloadAdRepository.Update(t => t.Id == adReportCommand.AdId, +// u => new WorkloadAD() +// { AdNote = adReportCommand.ADNote, SelectGlobalId = adReportCommand.SelectGlobalId })) ; +// } +// } +//} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/ReadingAndReport/Interface/IGlobalService.cs b/IRaCIS.Core.Application/Service/ReadingAndReport/Interface/IGlobalService.cs new file mode 100644 index 0000000..9211036 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingAndReport/Interface/IGlobalService.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IGlobalService + { + //string GetCommentsForSubject(Guid globalId); + IEnumerable GetHistoryVisitRsList(Guid trialId, Guid subjectId, decimal visitNum, Guid globalId,string globalCode); + + PreviousGlobalReadsView GetHistoryGlobalRsList(Guid trialId, Guid subjectId, decimal visitNum, Guid globalId); + + IResponseOutput AddGlobalReport(GlobalTaskReportCommand globalTaskReportCommand); + + + + AdReportDTO GetAdReport(Guid adId); + IResponseOutput AddAdjudicationReport(ADReportCommand adReportCommand); + } + + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/ReadingAndReport/Interface/IReportService.cs b/IRaCIS.Core.Application/Service/ReadingAndReport/Interface/IReportService.cs new file mode 100644 index 0000000..24f5227 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingAndReport/Interface/IReportService.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IReportService + { + //bool SaveReport(ReportCommand reportCommand); + + /// 添加基线期病灶标识及测量信息 + bool AddLesion(List lesionInformation, ReportDTO report); + ///// 获取基线期信息 + //IList GetBLLesion(string trialCode, string subjectCode); + + /// + /// 获取检查批次病灶信息 + /// + VisitLesionInfo GetVisitLineLesion(string trialCode, string subjectCode, decimal visitNum,string tpCode); + + bool SaveVisitReport(VisitReportCommand visitReportCommand); + bool AddBaseLineLesion(BaseLineReportCommand baseLineReportCommand); + BaseLineReportDTO getBLLineLesion(string trialCode, string subjectCode, string tpCode); + + bool SubmiteReport(Guid tpId); + } +} diff --git a/IRaCIS.Core.Application/Service/ReadingAndReport/Interface/IReviewerReadingService.cs b/IRaCIS.Core.Application/Service/ReadingAndReport/Interface/IReviewerReadingService.cs new file mode 100644 index 0000000..3c0531a --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingAndReport/Interface/IReviewerReadingService.cs @@ -0,0 +1,10 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IReviewerReadingService + { + PageOutput GetWorkloadList(WorkloadQueryParam param); + } +} diff --git a/IRaCIS.Core.Application/Service/ReadingAndReport/ReadingService.cs b/IRaCIS.Core.Application/Service/ReadingAndReport/ReadingService.cs new file mode 100644 index 0000000..f4e5e1a --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingAndReport/ReadingService.cs @@ -0,0 +1,184 @@ +//using IRaCIS.Application.Interfaces; +//using IRaCIS.Application.Contracts; +//using IRaCIS.Core.Infra.EFCore; + +//using IRaCIS.Core.Domain.Models; +//using System.Linq.Expressions; +//using IRaCIS.Core.Infrastructure.ExpressionExtend; +//using IRaCIS.Core.Infrastructure.Extention; +//using Microsoft.AspNetCore.Mvc; + +//namespace IRaCIS.Core.Application.Services +//{ +// [ApiExplorerSettings(GroupName = "Reading")] +// public class ReadingService : BaseService, IReviewerReadingService +// { +// private readonly IWorkloadTPRepository _workloadTPRepository; +// private readonly IWorkloadGlobalRepository _workloadGlobalRepository; +// private readonly IWorkloadADRepository _workloadADRepository; +// private readonly IRepository _trialRepository; + +// private readonly IRepository _subjectRepository; +// private readonly IRepository _subjectVisitRepository; + +// public ReadingService(IWorkloadTPRepository workloadTPRepository, +// IWorkloadGlobalRepository workloadGlobalRepository, +// IWorkloadADRepository workloadADRepository, +// IRepository trialRepository, +// IRepository subjectRepository, +// IRepository subjectVisitRepository, +// IUserInfo userInfo) +// { +// _workloadTPRepository = workloadTPRepository; +// _workloadGlobalRepository = workloadGlobalRepository; +// _workloadADRepository = workloadADRepository; +// _trialRepository = trialRepository; + +// _subjectRepository = subjectRepository; +// _subjectVisitRepository = subjectVisitRepository; + +// } +// /// +// /// 获取医生个人任务列表 +// /// WorkloadType 1-tp,2-global,3-ad +// /// +// /// +// /// +// [HttpPost] +// public PageOutput GetWorkloadList(WorkloadQueryParam param) +// { +// #pragma warning disable +// IQueryable query = null; +// Expression> subjectLambda = x => true; +// if (param.SubjectCode != string.Empty) +// { +// subjectLambda = subjectLambda.And(o => o.Code.Contains(param.SubjectCode)); +// } +// Expression> trialLambda = x => true; +// if (param.Expedited != null) +// { +// trialLambda = trialLambda.And(o => o.Expedited == param.Expedited); +// } +// if (param.TrialId != null && param.TrialId != Guid.Empty) +// { +// trialLambda = trialLambda.And(o => o.Id == param.TrialId); +// } + + +// if (param.WorkloadType == 1)// TP +// { +// Expression> workloadLambda = x => true; +// workloadLambda = workloadLambda.And(u => u.ReviewerId == _userInfo.Id); +// if (param.Status != null) +// { +// if (param.Status == 30)//30 的时候,待读和正在读(未提交的) +// { +// workloadLambda = workloadLambda.And(o => o.Status == 30 || o.Status == 40); +// } +// else +// workloadLambda = workloadLambda.And(o => o.Status == param.Status); +// } +// query = from tp in _workloadTPRepository.Where(workloadLambda) +// join trial in _trialRepository.Where(trialLambda) on tp.TrialId equals trial.Id +// join subject in _subjectRepository.Where(subjectLambda) on tp.SubjectId equals subject.Id +// join visit in _subjectVisitRepository.AsQueryable() on tp.SubjectVisitId equals visit.Id +// select new WorkloadReadingDTO +// { +// Id = tp.Id, +// StudyId = tp.StudyId, +// WorkloadId = tp.Id, +// WorkloadType = 1, +// Status = tp.Status, +// UpdateTime = tp.UpdateTime, +// TrialId = tp.TrialId, +// SubjectId = tp.SubjectId, +// SubjectCode = subject.Code, +// VisitNum = visit.VisitNum, +// VisitName = visit.VisitName, +// Expedited = trial.Expedited, +// TrialCode = trial.TrialCode, +// TrialIndication = trial.Indication, +// WorkloadCode = tp.TimepointCode +// }; +// } +// else if (param.WorkloadType == 2)//Global +// { +// Expression> workloadLambda = x => true; +// workloadLambda = workloadLambda.And(u => u.ReviewerId == _userInfo.Id); +// if (param.Status != null) +// { +// if (param.Status == 30) +// { +// workloadLambda = workloadLambda.And(o => o.Status == 30 || o.Status == 40); +// } +// else +// workloadLambda = workloadLambda.And(o => o.Status == param.Status); +// } +// query = from global in _workloadGlobalRepository.Where(workloadLambda) +// join trial in _trialRepository.Where(trialLambda) on global.TrialId equals trial.Id +// join subject in _subjectRepository.Where(subjectLambda) on global.SubjectId equals subject.Id +// //join visit in _subjectVisitRepository.GetAll() on global.VisitId equals visit.Id +// select new WorkloadReadingDTO +// { +// Id = global.Id, +// WorkloadId = global.Id, +// WorkloadType = 1, +// Status = global.Status, +// UpdateTime = global.UpdateTime, +// TrialId = global.TrialId, +// SubjectId = global.SubjectId, +// SubjectCode = subject.Code, +// //VisitNum = visit.VisitNum, +// //VisitName = visit.VisitName, +// VisitNum = global.VisitNum, +// VisitName = global.VisitName, + +// Expedited = trial.Expedited, +// TrialCode = trial.TrialCode, +// TrialIndication = trial.Indication, +// WorkloadCode = global.GlobalCode +// }; +// } +// else if (param.WorkloadType == 3)//AD +// { +// Expression> workloadLambda = x => true; +// workloadLambda = workloadLambda.And(u => u.ReviewerId == _userInfo.Id); +// if (param.Status != null) +// { +// if (param.Status == 30)//30 的时候,待读和正在读(未提交的) +// { +// workloadLambda = workloadLambda.And(o => o.Status == 30 || o.Status == 40); +// } +// else +// workloadLambda = workloadLambda.And(o => o.Status == param.Status); +// } +// query = from ad in _workloadADRepository.Where(workloadLambda) +// join trial in _trialRepository.Where(trialLambda) on ad.TrialId equals trial.Id +// join subject in _subjectRepository.Where(subjectLambda) on ad.SubjectId equals subject.Id +// //join visit in _subjectVisitRepository.GetAll() on ad.visi equals visit.Id +// select new WorkloadReadingDTO +// { +// Id = ad.Id, +// WorkloadId = ad.Id, +// WorkloadType = 1, +// Status = ad.Status, +// UpdateTime = ad.UpdateTime, +// TrialId = ad.TrialId, +// SubjectId = ad.SubjectId, +// SubjectCode = subject.Code, +// //VisitNum = visit.VisitNum, +// //VisitName = visit.VisitName, +// Expedited = trial.Expedited, +// TrialCode = trial.TrialCode, +// TrialIndication = trial.Indication, +// WorkloadCode = ad.ADCode +// }; +// } +// query.OrderByDescending(u => u.UpdateTime); +// var count = query.Count(); +// query = query.Skip((param.PageIndex - 1) * param.PageSize).Take(param.PageSize); +// return new PageOutput(param.PageIndex, +// param.PageSize, count, query.ToList()); +// } +// } +//} diff --git a/IRaCIS.Core.Application/Service/ReadingAndReport/ReportService.cs b/IRaCIS.Core.Application/Service/ReadingAndReport/ReportService.cs new file mode 100644 index 0000000..8320b17 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingAndReport/ReportService.cs @@ -0,0 +1,635 @@ +//using AutoMapper; +//using IRaCIS.Core.Infrastructure.ExpressionExtend; +//using IRaCIS.Core.Application.Contracts; +//using IRaCIS.Core.Infra.EFCore; +//using IRaCIS.Core.Domain.Models; + +//using IRaCIS.Core.Domain.Share; +//using Microsoft.AspNetCore.Authorization; +//using Microsoft.AspNetCore.Mvc; + +//namespace IRaCIS.Core.Application.Services +//{ +// [ ApiExplorerSettings(GroupName = "Reading")] +// public class ReportService : BaseService, IReportService +// { +// private readonly ITURepository _tURepository; +// private readonly ITRRepository _tRRepository; +// private readonly IRSRepository _rSRepository; +// private readonly IRepository _reportRepository; +// private readonly IRepository _visitStageRepository; +// private readonly IRepository _subjectVisitRepository; +// private readonly IWorkloadTPRepository _workloadTPRepository; +// private readonly IWorkloadGlobalRepository _workloadGlobalRepository; + + +// #pragma warning disable +// private string linkGroupId; +// public ReportService(ITURepository tURepository, ITRRepository tRRepository, +// IRSRepository rSRepository, IRepository reportRepository, +// IWorkloadTPRepository workloadTPRepository, +// IRepository visitStageRepository, +// IRepository subjectVisitRepository, +// IWorkloadGlobalRepository workloadGlobalRepository, +// IMapper mapper) +// { +// _tURepository = tURepository; +// _tRRepository = tRRepository; +// _rSRepository = rSRepository; +// _reportRepository = reportRepository; +// _workloadTPRepository = workloadTPRepository; +// _visitStageRepository = visitStageRepository; +// _subjectVisitRepository = subjectVisitRepository; +// _workloadGlobalRepository = workloadGlobalRepository; + + +// } + +// public bool AddLesion(List lesionList, ReportDTO report) +// { +// TU lastTu = _tURepository.AsQueryable().OrderByDescending("TUSEQ").FirstOrDefault(); +// int tuSeq = 101; +// if (lastTu != null) { tuSeq = lastTu.TUSEQ + 1; } +// TR lastTr = _tRRepository.AsQueryable().OrderByDescending("TRSEQ").FirstOrDefault(); +// int trSeq = 1; +// if (lastTr != null) { trSeq = lastTr.TRSEQ + 1; } + +// foreach (var lesion in lesionList) +// { +// var linkId = lesion.TULNKID; +// if (string.IsNullOrWhiteSpace(lesion.TULNKID)) +// { +// linkId = (lesion.TUORRES == "TARGET") ? "T" + tuSeq.ToString() : "NT" + tuSeq.ToString(); +// } +// _tURepository.Add(new TU +// { +// STUDYID = lesion.STUDYID, +// LesionType = lesion.LesionType, +// USUBJID = lesion.USUBJID, +// TUSEQ = tuSeq, +// TULNKID = linkId, + +// TUTESTCD = lesion.TUTESTCD, +// TUTEST = lesion.TUTEST, +// TUORRES = lesion.TUORRES, +// TUSTRESC = lesion.TUSTRESC, + +// TULOC = lesion.TULOC, +// LocDescription = lesion.LocDescription, +// TULAT = lesion.TULAT, +// TUDIR = lesion.TUDIR, +// TUPORTOT = lesion.TUPORTOT, +// TUMETHOD = lesion.TUMETHOD, +// TUEVAL = lesion.TUEVAL, + +// VISIT = lesion.VISIT, +// VISITNUM = lesion.VISITNUM, +// VISITDY = lesion.VISITDY, + +// TUREFID = lesion.TUREFID, +// TUACPTFL = lesion.TUACPTFL, +// TUEVALID = _userInfo.ReviewerCode,//lesion.TUEVALID, +// TUGRPID = string.Empty, +// TUDY = lesion.TUDY,// 检查日 +// EPOCH = lesion.EPOCH, +// TUDTC = lesion.TUDTC, +// TUNAM = lesion.TUNAM, +// TUSPID = lesion.TUSPID, +// TpCode = report.TpCode +// }); +// _tRRepository.Add(new TR +// { +// STUDYID = lesion.STUDYID, + +// USUBJID = lesion.USUBJID, +// TRSEQ = trSeq, +// TRLNKID = linkId, +// TRGRPID = lesion.TRGRPID, +// TRLNKGRP = linkGroupId, + +// TRTESTCD = lesion.TRTESTCD, +// TRTEST = lesion.TRTEST, +// TRORRES = lesion.TRORRES, +// TRORRESU = lesion.TRORRESU, + +// TRNAM = lesion.TUNAM, +// TRMETHOD = lesion.TUMETHOD, +// TREVAL = lesion.TUEVAL, +// TREVALID = _userInfo.ReviewerCode,//lesion.TUEVALID, +// TRACPTFL = lesion.TUACPTFL, +// CoveredLesion = true, +// Note = lesion.Note, + +// VISITNUM = lesion.VISITNUM, +// VISIT = lesion.VISIT, +// VISITDY = lesion.VISITDY, +// EPOCH = lesion.EPOCH, +// TRDTC = lesion.TRDTC, +// TRDY = lesion.TRDY, +// TpCode = report.TpCode +// }); +// tuSeq++; +// trSeq++; +// } + +// return _rSRepository.SaveChanges(); +// } + +// private IList GetBLLesion(string trialCode, string SubjectCode, string tpcodeGroup) +// { +// int[] t = { 1, 2, 3 }; +// var query = from tu in _tURepository.Where(u => t.Contains(u.LesionType) +// && u.STUDYID == trialCode && u.USUBJID == SubjectCode && u.TpCode.Contains(tpcodeGroup) /*u.TUEVALID==_userInfo.ReviewerCode*/) +// join tr in _tRRepository.Where(u => u.VISITNUM == 1 && u.STUDYID == trialCode +// && u.USUBJID == SubjectCode && u.TREVALID == _userInfo.ReviewerCode) +// on tu.TULNKID equals tr.TRLNKID into temp +// from tr in temp.DefaultIfEmpty() +// select new LesionInformation +// { +// TUId = tu.Id, +// LesionType = tu.LesionType, +// TULNKID = tu.TULNKID, +// TUSEQ = tu.TUSEQ, +// TULOC = tu.TULOC, +// LocDescription = tu.LocDescription, +// TULAT = tu.TULAT, +// TUDIR = tu.TUDIR, +// TUPORTOT = tu.TUPORTOT, +// TRORRES = tr.TRORRES, +// VISITNUM = tu.VISITNUM, +// Note = tr.Note +// }; +// return query.ToList(); +// } + +// /// 获取基线期病灶信息及其他信息 +// [HttpGet, Route("getBLLineLesion/{trialCode}/{subjectCode}/{tpCode}")] +// public BaseLineReportDTO getBLLineLesion(string trialCode, string subjectCode, string tpCode) +// { +// var tpcodeGroup = tpCode.Substring(tpCode.Length - 3, 3); +// var report = _reportRepository.FirstOrDefault(u => u.TrialCode == trialCode && u.SubjectCode == subjectCode && u.VisitNum == 1); ; + +// BaseLineReportDTO result = new BaseLineReportDTO +// { +// LesionInformation = GetBLLesion(trialCode, subjectCode, tpcodeGroup).Where(u => u.VISITNUM == 1).ToList(), +// ReportResult = _mapper.Map(report) +// }; +// return result; +// } + +// /// 获取其他检查批次病灶信息 +// [AllowAnonymous] +// [HttpGet, Route("getVisitLineLesion/{trialCode}/{subjectCode}/{visitNum:decimal}/{tpCode}")] +// public VisitLesionInfo GetVisitLineLesion(string trialCode, string subjectCode, decimal visitNum, string tpCode) +// { +// var tpcodeGroup = tpCode.Substring(tpCode.Length - 3, 3); +// VisitLesionInfo visitLesionInfo = new VisitLesionInfo(); +// var blLesionList = GetBLLesion(trialCode, subjectCode, tpcodeGroup); +// int[] t = { 1, 2, 3 }; +// var query = from tu in _tURepository.Where(u => u.STUDYID == trialCode && u.USUBJID == subjectCode && u.TpCode.Contains(tpcodeGroup) /*u.TUEVALID == _userInfo.ReviewerCode*/) +// join tr in _tRRepository.Where(u => u.VISITNUM == visitNum && u.STUDYID == trialCode && u.USUBJID == subjectCode && u.TpCode.Contains(tpcodeGroup)) +// on tu.TULNKID equals tr.TRLNKID into temp +// from tr in temp.DefaultIfEmpty() +// select new LesionInformation +// { +// TUId = tu.Id, +// TUSEQ = tu.TUSEQ, +// TULOC = tu.TULOC, +// LesionType = tu.LesionType, +// TULNKID = tu.TULNKID, +// LocDescription = tu.LocDescription, +// TULAT = tu.TULAT, +// TUDIR = tu.TUDIR, +// TUPORTOT = tu.TUPORTOT, +// TRORRESU = tr.TRORRESU, +// TRORRES = tr.TRORRES, +// Note = tr.Note, +// CoveredLesion = tr.CoveredLesion, +// VISITNUM = tr.VISITNUM +// }; +// var list = query.ToList(); +// var currentVisitLesion = list.Where(u => t.Contains(u.LesionType) && u.VISITNUM == visitNum).ToList(); +// foreach (var item in blLesionList) +// { +// var temp = _mapper.Map(item); +// temp.CurrentLesion = currentVisitLesion.Where(u => u.STUDYID == item.STUDYID +// && u.USUBJID == item.USUBJID && u.TUEVAL == item.TUEVAL +// && u.TULNKID == item.TULNKID).FirstOrDefault() ?? new LesionInformation(); +// visitLesionInfo.BLLesionList.Add(temp); +// } + +// List blIdList = blLesionList.Select(u => u.TUId).ToList(); +// List visitIdList = currentVisitLesion.Where(u => t.Contains(u.LesionType)).Select(u => u.TUId).ToList(); +// var visitLesion = visitIdList.Except(blIdList); + +// foreach (var item in visitLesion) +// { +// VisitLesion tempLesion = new VisitLesion(); +// tempLesion.CurrentLesion = currentVisitLesion.Where(u => u.TUId == item).FirstOrDefault() ?? new LesionInformation(); +// tempLesion.LesionType = tempLesion.CurrentLesion.LesionType; +// tempLesion.TUSEQ = tempLesion.CurrentLesion.TUSEQ; +// visitLesionInfo.BLLesionList.Add(tempLesion); +// } +// // 以往新病灶,病灶类型为5,且检查批次编号小于当前检查批次,且不是基线 +// var previousLesion = list.Where(u => u.VISITNUM < visitNum && u.VISITNUM != 1 && u.LesionType == 5).ToList(); +// foreach (var item in previousLesion) +// { +// var temp = _mapper.Map(item); +// temp.CurrentLesion = currentVisitLesion.Where(u => u.TULNKID==item.TULNKID && u.STUDYID == item.STUDYID && u.USUBJID == item.USUBJID && u.TUEVAL == item.TUEVAL).FirstOrDefault() ?? new LesionInformation(); +// visitLesionInfo.PreviousNewLesionList.Add(temp); +// } + +// visitLesionInfo.EquivocalNewLesionList = list.Where(u => u.VISITNUM <= visitNum && u.LesionType == 4).ToList(); +// visitLesionInfo.UnequivocalNewLesionList = list.Where(u => u.VISITNUM == visitNum && u.LesionType == 5).ToList(); + +// var report = _reportRepository.FirstOrDefault(u => u.TrialCode == trialCode && u.SubjectCode == subjectCode && u.VisitNum == visitNum); ; +// visitLesionInfo.ReportResult = _mapper.Map(report); + +// var rs = _rSRepository.Where(u => u.STUDYID == trialCode && u.USUBJID == subjectCode +// && u.RSEVALID == _userInfo.ReviewerCode && u.VISITNUM == visitNum); + +// var target = rs.FirstOrDefault(u => u.RSTESTCD == "TRGRESP"); +// var non_target = rs.FirstOrDefault(u => u.RSTESTCD == "NTRGRESP"); +// var overall = rs.FirstOrDefault(u => u.RSTESTCD == "OVRLRESP"); + +// visitLesionInfo.Efficacy = new EfficacyAssessment +// { + +// TargetLesion = target?.RSORRES, +// TargetLesionNote = target?.Note, +// Non_targetLesion = non_target?.RSORRES, +// Non_targetLesionNote = non_target?.Note, +// OverallAssessment = overall?.RSORRES, +// OverallAssessmentNote = overall?.Note +// }; + +// var preRS = _rSRepository.Where(u => u.STUDYID == trialCode && u.USUBJID == subjectCode +// && u.VISITNUM < visitNum && u.RSTESTCD == "OVRLRESP" +// && u.RSEVAL == _userInfo.ReviewerCode /*&& u.VISITNUM == visitNum*/) +// .Select(u => new PreviousOverallAssessment +// { +// VisitName = u.VISIT, +// OverallAssessment = u.RSORRES +// }).ToList(); +// visitLesionInfo.PreviousOverallAssessment = preRS; + +// var targetList = _tRRepository.Where(u => (!string.IsNullOrWhiteSpace(u.TRLNKID)) && (!u.TRLNKID.Contains("N")) && u.STUDYID == trialCode && +// u.USUBJID == subjectCode && u.TpCode.Contains(tpcodeGroup) && u.VISITNUM < visitNum).ToList(); + +// var q = from ttt in targetList +// group ttt by ttt.VISIT into s +// select new MinVisit +// { +// MinSum = s.Sum(p => p.TRORRES_Double), +// MinVistName = s.Key +// }; +// var tempList = q.ToList(); +// MinVisit min = new MinVisit(); +// if (tempList.Count>0) +// { +// min = tempList.FirstOrDefault(); +// } +// foreach (var item in tempList) +// { +// if (min.MinSum > item.MinSum) +// { +// min = item; +// } +// } +// visitLesionInfo.MinVisitInfo = min; + +// return visitLesionInfo; +// } + +// /// +// /// 提交报告 +// /// +// [AllowAnonymous] +// [HttpPost("{tpId:guid}")] +// public bool SubmiteReport(Guid tpId) +// { +// // 提交报告 同时查询VisitStage(项目检查批次计划,看是否需要添加全局) + +// var query = from workloadTp in _workloadTPRepository.Where(u => u.Id == tpId) +// join subjectVisit in _subjectVisitRepository.AsQueryable() +// on workloadTp.SubjectVisitId equals subjectVisit.Id +// join visitStage in _visitStageRepository.AsQueryable() +// on new { workloadTp.TrialId, subjectVisit.VisitNum } equals new { visitStage.TrialId, visitStage.VisitNum } +// select new +// { +// WorkloadTp = workloadTp, +// VisitNum = subjectVisit.VisitNum, +// VisitName = subjectVisit.VisitName, +// NeedGlobal = visitStage.NeedGlobal +// }; +// var workload = query.ToList().FirstOrDefault(); +// if (workload != null) +// { +// if (workload.NeedGlobal)// 需要产生全局 +// { +// var group = workload.WorkloadTp.TimepointCode.Substring(workload.WorkloadTp.TimepointCode.Length - 2, 2); +// _workloadGlobalRepository.Add(new WorkloadGlobal +// { +// GlobalCode = workload.WorkloadTp.TimepointCode + "G" + group, +// ReviewerId = Guid.Empty, +// SiteId = workload.WorkloadTp.SiteId, +// Status = -1, +// SubjectId = workload.WorkloadTp.SubjectId, +// TrialId = workload.WorkloadTp.TrialId, +// VisitId = workload.WorkloadTp.SubjectVisitId, +// VisitNum = workload.VisitNum, +// VisitName = workload.VisitName, +// UpdateTime = DateTime.Now +// }); +// } +// } +// return _workloadTPRepository.Update(u => u.Id == tpId, s => new WorkloadTP +// { +// Status = (int)WorkloadStatus.ReviewFinish +// }); +// } + + +// /// +// /// 保存 基线期病灶及测量信息及其他信息,不会改变状态 +// /// +// [AllowAnonymous] +// [HttpPost] +// public bool AddBaseLineLesion(BaseLineReportCommand baseLineReportCommand) +// { +// if (baseLineReportCommand.LesionInformation.Count < 1) return false; + +// var addedLesion = baseLineReportCommand.LesionInformation[0]; + +// //删除已经有的数据 +// _tURepository.Delete(u => u.VISITNUM == addedLesion.VISITNUM && u.USUBJID == addedLesion.USUBJID && u.TUEVALID == _userInfo.ReviewerCode); +// _tRRepository.Delete(u => u.VISITNUM == addedLesion.VISITNUM && u.USUBJID == addedLesion.USUBJID && u.TREVALID == _userInfo.ReviewerCode); +// _reportRepository.Delete(u => u.TPId == baseLineReportCommand.ReportResult.TPId); + +// _workloadTPRepository.Update(u => u.Id == baseLineReportCommand.ReportResult.TPId, s => new WorkloadTP +// { +// Status = (int)WorkloadStatus.Reading +// }); + +// //序号按照每个项目、每个患者、每个评估者,一条记录 +// TU lastTu = _tURepository.Where(u => u.STUDYID == addedLesion.STUDYID && u.USUBJID == addedLesion.USUBJID +// && u.TUEVAL == addedLesion.TUEVAL).OrderByDescending("TUSEQ").FirstOrDefault(); +// int tuSeq = 101; +// if (lastTu != null) { tuSeq = lastTu.TUSEQ; } +// TR lastTr = _tRRepository.AsQueryable().OrderByDescending("TRSEQ").FirstOrDefault(); +// int trSeq = 1; +// if (lastTr != null) { trSeq = lastTr.TRSEQ; } + +// linkGroupId = Guid.NewGuid().ToString(); +// foreach (var lesion in baseLineReportCommand.LesionInformation) +// { +// var linkId = lesion.TULNKID; +// if (string.IsNullOrWhiteSpace(lesion.TULNKID)) +// { +// linkId = (lesion.TUORRES == "TARGET") ? "T" + tuSeq.ToString() : "NT" + tuSeq.ToString(); +// } +// _tURepository.Add(new TU +// { +// STUDYID = lesion.STUDYID, +// LesionType = lesion.LesionType, +// USUBJID = lesion.USUBJID, +// TUSEQ = tuSeq, +// TULNKID = linkId, + +// TUTESTCD = lesion.TUTESTCD, +// TUTEST = lesion.TUTEST, +// TUORRES = lesion.TUORRES, +// TUSTRESC = lesion.TUSTRESC, + +// TULOC = lesion.TULOC, +// LocDescription = lesion.LocDescription, +// TULAT = lesion.TULAT, +// TUDIR = lesion.TUDIR, +// TUPORTOT = lesion.TUPORTOT, +// TUMETHOD = lesion.TUMETHOD, +// TUEVAL = lesion.TUEVAL, +// VISIT = lesion.VISIT, +// VISITNUM = lesion.VISITNUM, +// VISITDY = lesion.VISITDY, + +// TUREFID = lesion.TUREFID, +// TUACPTFL = lesion.TUACPTFL, +// TUEVALID = _userInfo.ReviewerCode,//lesion.TUEVALID, +// TUGRPID = string.Empty, +// TUDY = lesion.TUDY,// 检查日 +// EPOCH = lesion.EPOCH, +// TUDTC = lesion.TUDTC, +// TUNAM = lesion.TUNAM, +// TUSPID = lesion.TUSPID, + +// TpCode = baseLineReportCommand.ReportResult.TpCode +// }); +// _tRRepository.Add(new TR +// { +// STUDYID = lesion.STUDYID, + +// USUBJID = lesion.USUBJID, +// TRSEQ = trSeq, +// TRLNKID = linkId, +// TRGRPID = lesion.TRGRPID, +// TRLNKGRP = linkGroupId, + +// TRTESTCD = lesion.TRTESTCD, +// TRTEST = lesion.TRTEST, +// TRORRES = lesion.TRORRES, +// TRORRESU = lesion.TRORRESU, + +// TRNAM = lesion.TUNAM, +// TRMETHOD = lesion.TUMETHOD, +// TREVAL = lesion.TUEVAL, +// TREVALID = _userInfo.ReviewerCode,//lesion.TUEVALID, +// TRACPTFL = lesion.TUACPTFL, + +// Note = lesion.Note, + +// VISITNUM = lesion.VISITNUM, +// VISIT = lesion.VISIT, +// VISITDY = lesion.VISITDY, +// EPOCH = lesion.EPOCH, +// TRDTC = lesion.TRDTC, +// TRDY = lesion.TRDY, +// TpCode = baseLineReportCommand.ReportResult.TpCode +// }); +// tuSeq++; +// trSeq++; +// } + +// _tRRepository.Add(new TR +// { +// STUDYID = addedLesion.STUDYID, + +// USUBJID = addedLesion.USUBJID, +// TRSEQ = trSeq, +// TRLNKID = string.Empty, +// TRGRPID = addedLesion.TRGRPID, +// TRLNKGRP = linkGroupId, + +// TRTESTCD = addedLesion.TRTESTCD, +// TRTEST = "Sum of Diameter", +// TRORRES = baseLineReportCommand.SumOfDiameter, +// TRORRESU = "mm",//addedLesion.TRORRESU, + +// TRNAM = addedLesion.TUNAM, +// TRMETHOD = addedLesion.TUMETHOD, +// TREVAL = addedLesion.TUEVAL, +// TREVALID = _userInfo.ReviewerCode,//addedLesion.TUEVALID, +// TRACPTFL = addedLesion.TUACPTFL, + +// Note = string.Empty, + +// VISITNUM = addedLesion.VISITNUM, +// VISIT = addedLesion.VISIT, +// VISITDY = addedLesion.VISITDY, +// EPOCH = addedLesion.EPOCH, +// TRDTC = addedLesion.TRDTC, +// TRDY = addedLesion.TRDY, +// TpCode = baseLineReportCommand.ReportResult.TpCode +// }); +// trSeq++; +// _tRRepository.Add(new TR +// { +// STUDYID = addedLesion.STUDYID, + +// USUBJID = addedLesion.USUBJID, +// TRSEQ = trSeq, +// TRLNKID = string.Empty, +// TRGRPID = addedLesion.TRGRPID, +// TRLNKGRP = linkGroupId, + +// TRTESTCD = addedLesion.TRTESTCD, +// TRTEST = "Sum Diameters of Non Lymph Node Tumors", +// TRORRES = baseLineReportCommand.SumDiameterOfNonLymphNode, +// TRORRESU = "mm",//addedLesion.TRORRESU, + +// TRNAM = addedLesion.TUNAM, +// TRMETHOD = addedLesion.TUMETHOD, +// TREVAL = addedLesion.TUEVAL, +// TREVALID = _userInfo.ReviewerCode,//addedLesion.TUEVALID, +// TRACPTFL = addedLesion.TUACPTFL, + +// Note = string.Empty, + +// VISITNUM = addedLesion.VISITNUM, +// VISIT = addedLesion.VISIT, +// VISITDY = addedLesion.VISITDY, +// EPOCH = addedLesion.EPOCH, +// TRDTC = addedLesion.TRDTC, +// TRDY = addedLesion.TRDY, +// TpCode = baseLineReportCommand.ReportResult.TpCode +// }); +// trSeq++; +// _reportRepository.Add(_mapper.Map(baseLineReportCommand.ReportResult)); + +// return _reportRepository.SaveChanges(); +// } + +// /// +// /// 添加检查批次报告信息 +// /// LesionInformation 为新病灶及测量信息(包括分裂及合并产生的) +// /// TRList 已经存在的病灶的测量信息 +// /// RSList 疗效信息 +// /// +// [AllowAnonymous] +// [HttpPost, Route("saveVisitReport")] +// public bool SaveVisitReport(VisitReportCommand visitReportCommand) +// { +// linkGroupId = Guid.NewGuid().ToString(); +// var visitNum = visitReportCommand.ReportResult.VisitNum; +// var subjectId = visitReportCommand.ReportResult.SubjectCode; + +// //删除已经有的数据 +// _tURepository.Delete(u => u.VISITNUM == visitNum && u.USUBJID == subjectId && u.TUEVALID == _userInfo.ReviewerCode); +// _tRRepository.Delete(u => u.VISITNUM == visitNum && u.USUBJID == subjectId && u.TREVALID == _userInfo.ReviewerCode); +// _reportRepository.Delete(u => u.TPId == visitReportCommand.ReportResult.TPId); +// _rSRepository.Delete(u => u.VISITNUM == visitNum && u.RSEVALID == _userInfo.ReviewerCode); + +// var report = visitReportCommand.ReportResult; + +// _workloadTPRepository.Update(u => u.Id == report.TPId, s => new WorkloadTP +// { +// Status = (int)WorkloadStatus.Reading +// }); +// if (visitReportCommand.LesionInformation.Count > 0) +// { +// AddLesion(visitReportCommand.LesionInformation, report); +// } + +// TR lastTr = _tRRepository.AsQueryable().OrderByDescending("TRSEQ").FirstOrDefault(); +// int trSeq = 1; +// if (lastTr != null) { trSeq = lastTr.TRSEQ + 1; } + +// foreach (var lesion in visitReportCommand.TRList) +// { +// _tRRepository.Add(new TR +// { +// STUDYID = lesion.STUDYID, + +// USUBJID = lesion.USUBJID, +// TRSEQ = trSeq, +// TRLNKID = lesion.TRLNKID, +// TRGRPID = lesion.TRGRPID, +// TRLNKGRP = linkGroupId, + +// TRTESTCD = lesion.TRTESTCD, +// TRTEST = lesion.TRTEST, +// TRORRES = lesion.TRORRES, +// TRORRESU = lesion.TRORRESU, + +// TRNAM = lesion.TRNAM, +// TRMETHOD = lesion.TRMETHOD, +// TREVAL = lesion.TREVAL, +// TREVALID = _userInfo.ReviewerCode,// lesion.TREVALID, +// TRACPTFL = lesion.TRACPTFL, + +// Note = lesion.Note, +// CoveredLesion = lesion.CoveredLesion, +// VISITNUM = lesion.VISITNUM, +// VISIT = lesion.VISIT, +// VISITDY = lesion.VISITDY, +// EPOCH = lesion.EPOCH, +// TRDTC = lesion.TRDTC, +// TRDY = lesion.TRDY, +// TpCode = visitReportCommand.ReportResult.TpCode +// }); +// trSeq++; +// } +// foreach (var rs in visitReportCommand.RSList) +// { +// _rSRepository.Add(new RS +// { +// STUDYID = rs.STUDYID, +// USUBJID = rs.USUBJID, +// RSSEQ = 0,// +// RSLNKGRP = linkGroupId, +// RSTESTCD = rs.RSTESTCD, +// RSTEST = rs.RSTEST, +// RSCAT = rs.RSCAT, +// RSORRES = rs.RSORRES, +// RSSTRESC = rs.RSSTRESC, +// RSSTAT = rs.RSSTAT, +// RSREASND = rs.RSCAT, +// RSEVAL = rs.RSEVAL, +// RSEVALID = _userInfo.ReviewerCode,// +// VISITNUM = rs.VISITNUM, +// VISIT = rs.VISIT, +// RSDTC = rs.RSDTC, +// RSDY = rs.RSDY, +// TpCode = visitReportCommand.ReportResult.TpCode, +// StudyGuid = rs.StudyGuid, +// TrialGuid = rs.TrialGuid, +// SubjectGuid = rs.SubjectGuid, +// Note = rs.Note +// }); +// } + +// _reportRepository.Add(_mapper.Map(visitReportCommand.ReportResult)); + +// return _rSRepository.SaveChanges(); +// } +// } +//} diff --git a/IRaCIS.Core.Application/Service/ReadingAndReport/_MapConfig.cs b/IRaCIS.Core.Application/Service/ReadingAndReport/_MapConfig.cs new file mode 100644 index 0000000..a6da341 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingAndReport/_MapConfig.cs @@ -0,0 +1,23 @@ +using AutoMapper; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.Dicom.DTO; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Application.Service +{ + public class ReadingAndReportConfig : Profile + { + public ReadingAndReportConfig() + { + CreateMap(); + + CreateMap(); + CreateMap(); + + CreateMap(); + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/Dto/CriterionCalculateDto.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/Dto/CriterionCalculateDto.cs new file mode 100644 index 0000000..bab13fb --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/Dto/CriterionCalculateDto.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.ViewModel +{ + public class AddTaskLesionAnswerFromLastTaskInDto + { + public Guid VisitTaskId { get; set; } + } + + public class VisitStudyTime + { + /// + /// 检查批次Id + /// + public Guid SubjectVisitId { get; set; } + + public DateTime? StudyTime { get; set; } + } + + public class SiteVisitForTumor + { + /// + /// 检查批次Id + /// + public Guid VisitTaskId { get; set; } + + public Guid? SubjectVisitId { get; set; } + + /// + /// 任务Num + /// + public decimal VisitTaskNum { get; set; } + + /// + /// 检查日期 + /// + public DateTime? StudyTime { get; set; } + + + } + + public class AddTaskLesionAnswerFromLastTaskOutDto + { + public bool IsBaseLine { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/GeneralCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/GeneralCalculateService.cs new file mode 100644 index 0000000..e51c3e2 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/GeneralCalculateService.cs @@ -0,0 +1,184 @@ +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.ReadingCalculate +{ + public class GeneralCalculateService : BaseService, IGeneralCalculateService + { + + private readonly IRepository _readingTableQuestionAnswerRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _readingTableQuestionTrialRepository; + private readonly IRepository _readingTableAnswerRowInfoRepository; + private readonly IRepository _readingQuestionTrialRepository; + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _tumorAssessmentRepository; + private readonly IRepository _readingTaskQuestionAnswerRepository; + + public GeneralCalculateService( + IRepository readingTableQuestionAnswerRepository, + IRepository visitTaskRepository, + IRepository readingQuestionCriterionTrialRepository, + IRepository readingTableQuestionTrialRepository, + IRepository readingTableAnswerRowInfoRepository, + IRepository readingQuestionTrialRepository, + IRepository subjectVisitRepository, + IRepository tumorAssessmentRepository, + IRepository readingTaskQuestionAnswerRepository + ) + { + this._readingTableQuestionAnswerRepository = readingTableQuestionAnswerRepository; + this._visitTaskRepository = visitTaskRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this._readingTableQuestionTrialRepository = readingTableQuestionTrialRepository; + this._readingTableAnswerRowInfoRepository = readingTableAnswerRowInfoRepository; + this._readingQuestionTrialRepository = readingQuestionTrialRepository; + this._subjectVisitRepository = subjectVisitRepository; + this._tumorAssessmentRepository = tumorAssessmentRepository; + this._readingTaskQuestionAnswerRepository = readingTaskQuestionAnswerRepository; + } + + /// + /// 获取ReadingCalculateDto + /// + /// + /// + public async Task GetReadingCalculateDto(Guid visitTaskId) + { + var visitTask = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + + var criterionInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == visitTask.TrialReadingCriterionId).FirstNotNullAsync(); + var subjectVisit = await _subjectVisitRepository.Where(x => x.Id == (visitTask.SourceSubjectVisitId ?? default(Guid))).FirstOrDefaultAsync(); + + var baseLineVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == visitTask.SubjectId && x.IsBaseLine).Select(x => x.Id).FirstOrDefaultAsync(); + + + var rowInfoList = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == visitTaskId).ToListAsync(); + + var baseLinetaskId = await _visitTaskRepository.Where(x => x.SourceSubjectVisitId == baseLineVisitId && x.TaskState == TaskState.Effect + && x.TrialReadingCriterionId == visitTask.TrialReadingCriterionId + && x.ArmEnum == visitTask.ArmEnum).Select(x => x.Id).FirstOrDefaultAsync(); + + List questionInfos = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == visitTask.TrialReadingCriterionId).Select(x => new QuestionInfo() + { + LesionType = x.LesionType, + QuestionId = x.Id, + QuesionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + QuestionType = x.QuestionType, + ValueType = x.ValueType, + }).ToListAsync(); + + var questionAnswers = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == visitTaskId).Select(x => new + { + x.ReadingQuestionTrialId, + x.Answer + }).ToListAsync(); + + var tableQuestion = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == visitTaskId).Include(x => x.ReadingTableQuestionTrial).Select(x => new TableQuestionInfo() + { + Answer = x.Answer, + AnswerId=x.Id, + QuestionMark = x.ReadingTableQuestionTrial.QuestionMark, + TableQuestionId = x.TableQuestionId, + QuestionId = x.QuestionId, + QuestionType = x.ReadingQuestionTrial.QuestionType, + RowIndex = x.RowIndex, + RowId = x.RowId, + }).ToListAsync(); + + foreach (var item in questionInfos) + { + item.Answer = questionAnswers.Where(y => y.ReadingQuestionTrialId == item.QuestionId).Select(x => x.Answer).FirstOrDefault() ?? string.Empty; + + + var thisItemRowInfo = rowInfoList.Where(x => x.QuestionId == item.QuestionId).ToList(); + + var thisItemTableQuestions = tableQuestion.Where(x => x.QuestionId == item.QuestionId).ToList(); + + item.TableRowInfoList = thisItemRowInfo.Select(x => new TableRowInfo() + { + RowIndex = x.RowIndex, + MeasureData = x.MeasureData, + FristAddTaskNum=x.FristAddTaskNum, + TableQuestionList = tableQuestion.Where(y => y.QuestionId == item.QuestionId && y.RowId == x.Id).ToList(), + + }).ToList(); + + + + + } + + ReadingCalculateDto readingData = new ReadingCalculateDto() + { + SubjectId = visitTask.SubjectId, + TaskBlindName= visitTask.TaskBlindName, + VisitTaskId = visitTaskId, + SubjectVisitId = visitTask.SourceSubjectVisitId!.Value, + QuestionInfo = questionInfos, + CriterionId = visitTask.TrialReadingCriterionId, + TrialId = visitTask.TrialId, + IsAnalysisCreate = visitTask.IsAnalysisCreate, + IsSelfAnalysis = visitTask.IsSelfAnalysis, + IsBaseLine = subjectVisit!.IsBaseLine, + DoctorUserId = visitTask.DoctorUserId, + TrialReadingCriterionId = visitTask.TrialReadingCriterionId, + BaseLineTaskId = baseLinetaskId, + ArmEnum = visitTask.ArmEnum, + VisitName = subjectVisit.VisitName, + BlindName = subjectVisit.BlindName, + VisitTaskNum = visitTask.VisitTaskNum, + DigitPlaces= criterionInfo.DigitPlaces??2, + }; + + return readingData; + } + + /// + /// 获取阅片报告任务List + /// + /// + /// + public async Task> GetReadingReportTaskList(Guid visitTaskId) + { + var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + + var taskquery = _visitTaskRepository + .Where(x => (x.SubjectId == visitTaskInfo.SubjectId && x.TaskState == TaskState.Effect + && x.IsAnalysisCreate == visitTaskInfo.IsAnalysisCreate + && x.DoctorUserId == visitTaskInfo.DoctorUserId + && x.IsSelfAnalysis == visitTaskInfo.IsSelfAnalysis + && x.VisitTaskNum <= visitTaskInfo.VisitTaskNum + && x.ArmEnum == visitTaskInfo.ArmEnum + && x.TrialReadingCriterionId == visitTaskInfo.TrialReadingCriterionId + && x.ReadingCategory == ReadingCategory.Visit && x.ReadingTaskState == ReadingTaskState.HaveSigned) || x.Id == visitTaskId + ); + if(visitTaskInfo.ReadingTaskState==ReadingTaskState.HaveSigned) + { + taskquery = _visitTaskRepository.Where(x => visitTaskInfo.RelatedVisitTaskIdList.Contains(x.Id)||x.Id==visitTaskInfo.Id); + } + + + var taskInfoList = await taskquery.OrderBy(x => x.VisitTaskNum).Select(x => new VisitTaskInfo() + { + BlindName = x.TaskBlindName, + IsBaseLine = x.SourceSubjectVisit.IsBaseLine, + VisitTaskId = x.Id, + TaskName = x.TaskName, + LatestScanDate= x.SourceSubjectVisit!=null?x.SourceSubjectVisit.LatestScanDate : null, + VisitTaskNum = x.VisitTaskNum, + IsCurrentTask = x.Id == visitTaskId, + + }).ToListAsync(); + + return taskInfoList; + } + } +} diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/Interface/ICriterionCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/Interface/ICriterionCalculateService.cs new file mode 100644 index 0000000..f11afcf --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/Interface/ICriterionCalculateService.cs @@ -0,0 +1,57 @@ +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Application.ViewModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service +{ + public interface ICriterionCalculateService + { + + /// + /// 自动计算 并修改值 + /// + /// + /// + Task CalculateTask(CalculateTaskInDto inDto); + + /// + /// 验证检查批次提交 + /// + /// + /// + Task VerifyVisitTaskQuestions(VerifyVisitTaskQuestionsInDto inDto); + + /// + /// 将上一次的检查批次病灶添加到这一次 + /// + /// + /// + Task AddTaskLesionAnswerFromLastTask(AddTaskLesionAnswerFromLastTaskInDto inDto); + + /// + /// 获取报告验证的信息(这里每个标准可能不一样 返回用object) + /// + /// + /// + Task GetReportVerify(GetReportVerifyInDto inDto); + + /// + /// 获取阅片报告 + /// + /// + /// + Task GetReadingReportEvaluation(GetReadingReportEvaluationInDto indto); + + /// + /// 删除病灶获取起始病灶序号 + /// + /// + Task GetDeleteLesionStatrIndex(DeleteReadingRowAnswerInDto inDto); + + + } +} diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/Interface/IGeneralCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/Interface/IGeneralCalculateService.cs new file mode 100644 index 0000000..d287260 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/Interface/IGeneralCalculateService.cs @@ -0,0 +1,27 @@ +using IRaCIS.Core.Application.Service.Reading.Dto; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service +{ + public interface IGeneralCalculateService + { + + /// + /// 获取ReadingCalculateDto + /// + /// + /// + Task GetReadingCalculateDto(Guid visitTaskId); + + /// + /// 获取阅片报告任务List + /// + /// + /// + Task> GetReadingReportTaskList(Guid visitTaskId); + } +} diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/Interface/IReadingCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/Interface/IReadingCalculateService.cs new file mode 100644 index 0000000..b1dee54 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/Interface/IReadingCalculateService.cs @@ -0,0 +1,55 @@ +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Application.ViewModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service +{ + public interface IReadingCalculateService + { + /// + /// 自动计算 并修改值 + /// + /// + /// + Task CalculateTask(CalculateTaskInDto inDto); + + /// + /// 验证检查批次提交 + /// + /// + /// + Task VerifyVisitTaskQuestions(VerifyVisitTaskQuestionsInDto inDto); + + /// + /// 将上一次的检查批次病灶添加到这一次 + /// + /// + /// + Task AddTaskLesionAnswerFromLastTask(AddTaskLesionAnswerFromLastTaskInDto inDto); + + /// + /// 获取报告验证的信息(这里每个标准可能不一样 返回用object) + /// + /// + /// + Task GetReportVerify(GetReportVerifyInDto inDto); + + /// + /// 获取阅片报告 + /// + /// + /// + Task GetReadingReportEvaluation(GetReadingReportEvaluationInDto indto); + + + /// + /// 删除病灶获取起始病灶序号 + /// + /// + Task GetDeleteLesionStatrIndex(DeleteReadingRowAnswerInDto inDto); + } +} diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/PCWG3CalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/PCWG3CalculateService.cs new file mode 100644 index 0000000..193cd28 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/PCWG3CalculateService.cs @@ -0,0 +1,1095 @@ +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using Panda.DynamicWebApi.Attributes; +using IRaCIS.Core.Infra.EFCore.Common; +using Microsoft.Extensions.Caching.Memory; + +using IRaCIS.Core.Infrastructure; +using MassTransit; + +namespace IRaCIS.Core.Application.Service.ReadingCalculate +{ + [ApiExplorerSettings(GroupName = "Reading")] + public class PCWG3CalculateService : BaseService, ICriterionCalculateService + { + private readonly IRepository _readingTableQuestionAnswerRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _readingTableQuestionTrialRepository; + private readonly IRepository _readingTableAnswerRowInfoRepository; + private readonly IRepository _readingQuestionTrialRepository; + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _organInfoRepository; + private readonly IRepository _dicomStudyRepository; + private readonly IRepository _noneDicomStudyRepository; + private readonly IRepository _tumorAssessmentRepository; + private readonly IGeneralCalculateService _generalCalculateService; + private readonly IRepository _readingTaskQuestionAnswerRepository; + + public PCWG3CalculateService( + IRepository readingTableQuestionAnswerRepository, + IRepository visitTaskRepository, + IRepository readingQuestionCriterionTrialRepository, + IRepository readingTableQuestionTrialRepository, + IRepository readingTableAnswerRowInfoRepository, + IRepository readingQuestionTrialRepository, + IRepository subjectVisitRepository, + IRepository organInfoRepository, + IRepository dicomStudyRepository, + IRepository noneDicomStudyRepository, + IRepository tumorAssessmentRepository, + IGeneralCalculateService generalCalculateService, + IRepository readingTaskQuestionAnswerRepository + ) + { + this._readingTableQuestionAnswerRepository = readingTableQuestionAnswerRepository; + this._visitTaskRepository = visitTaskRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this._readingTableQuestionTrialRepository = readingTableQuestionTrialRepository; + this._readingTableAnswerRowInfoRepository = readingTableAnswerRowInfoRepository; + this._readingQuestionTrialRepository = readingQuestionTrialRepository; + this._subjectVisitRepository = subjectVisitRepository; + this._organInfoRepository = organInfoRepository; + this._dicomStudyRepository = dicomStudyRepository; + this._noneDicomStudyRepository = noneDicomStudyRepository; + this._tumorAssessmentRepository = tumorAssessmentRepository; + this._generalCalculateService = generalCalculateService; + this._readingTaskQuestionAnswerRepository = readingTaskQuestionAnswerRepository; + } + + private List siteVisitForTumorList = null; + + + #region 删除病灶获取起始病灶序号 + /// + /// 删除病灶获取起始病灶序号 + /// + /// + public async Task GetDeleteLesionStatrIndex(DeleteReadingRowAnswerInDto inDto) + { + var rowInfo = await _readingTableAnswerRowInfoRepository.Where(x => x.Id == inDto.RowId).Include(x => x.ReadingQuestionTrial).FirstNotNullAsync(); + if (rowInfo.ReadingQuestionTrial.LesionType == LesionType.NewLesions) + { + var minIndex= await _readingTableAnswerRowInfoRepository.Where(x => x.QuestionId == rowInfo.QuestionId && x.VisitTaskId == inDto.VisitTaskId).OrderBy(x => x.RowIndex).Select(x=>x.RowIndex).FirstOrDefaultAsync(); + return (int)minIndex; + } + else + { + return 1; + } + + + } + #endregion + + #region 获取阅片报告 + /// + /// 获取阅片报告 + /// + /// + /// + [HttpPost] + public async Task GetReadingReportEvaluation(GetReadingReportEvaluationInDto indto) + { + GetReadingReportEvaluationOutDto result = new GetReadingReportEvaluationOutDto(); + + + result.CalculateResult = await this.GetReportVerify(new GetReportVerifyInDto() + { + VisitTaskId = indto.VisitTaskId + }); + + var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == indto.VisitTaskId).FirstNotNullAsync(); + + + result.ReadingTaskState = visitTaskInfo.ReadingTaskState; + var taskInfoList = await _generalCalculateService.GetReadingReportTaskList(indto.VisitTaskId); + taskInfoList = taskInfoList.Where(x => x.VisitTaskNum <= visitTaskInfo.VisitTaskNum).ToList(); + result.VisitTaskList = taskInfoList; + + var visitTaskIds = taskInfoList.Select(x => x.VisitTaskId).ToList(); + + var criterionId = visitTaskInfo.TrialReadingCriterionId; + var questionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == criterionId) + //.Where(x => x.LesionType != LesionType.BaselineLesions) + .Where(x=>x.QuestionType!=QuestionType.TherapeuticEffectEvaluationGroup) + .ToListAsync(); + + // 新病灶 + var questionNewLesions = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == visitTaskInfo.TrialReadingCriterionId && x.LesionType == LesionType.NewLesions).FirstNotNullAsync(); + + var tableQuestionList = await _readingTableQuestionTrialRepository.Where(x => x.TrialCriterionId == criterionId).OrderBy(x => x.ShowOrder).ToListAsync(); + var tableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == indto.VisitTaskId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var answers = await _readingTaskQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)) + .Where(x => x.ReadingQuestionTrialId != questionNewLesions.Id||x.VisitTaskId== visitTaskInfo.Id) + .ToListAsync(); + var tableAnswers = await _readingTableQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)) + .Where(x => x.QuestionId != questionNewLesions.Id || x.VisitTaskId == visitTaskInfo.Id) + .ToListAsync(); + + + var alltableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync(); + + var organIds = alltableAnsweRowInfos.Where(x => x.OrganInfoId != null).Select(x => x.OrganInfoId).Distinct().ToList(); + var organInfos = await _organInfoRepository.Where(x => organIds.Contains(x.Id)).ToListAsync(); + + var needChangeType = new List() { + QuestionMark.Organ, + QuestionMark.Location, + QuestionMark.Part, + }; + + + // 第一级 分组 + + #region 构造问题 + List questions = questionList.Where(x => x.Type == ReadingQestionType.Group).OrderBy(x => x.ShowOrder).Select(x => new ReadingReportDto() + { + QuestionId = x.Id, + GroupName = x.GroupName, + GroupEnName=x.GroupEnName, + IsShowInDicom = x.IsShowInDicom, + Type = x.Type, + GroupId=x.GroupId, + QuestionType = x.QuestionType, + LesionType = x.LesionType, + QuestionGenre = x.QuestionGenre, + DataSource = x.DataSource, + DictionaryCode = x.DictionaryCode, + TypeValue = x.TypeValue, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + ShowOrder = x.ShowOrder, + ValueType = x.ValueType, + CustomUnit=x.CustomUnit, + Unit = x.Unit, + ReportLayType=ReportLayType.Group, + }).ToList(); + + // 分组 + foreach (var item in questions) + { + // 问题 + item.Childrens = questionList.Where(x => x.GroupId==item.QuestionId).OrderBy(x => x.ShowOrder).Select(x => new ReadingReportDto() + { + GroupName = x.GroupName, + GroupEnName=x.GroupEnName, + QuestionId = x.Id, + IsShowInDicom = x.IsShowInDicom, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + LesionType = x.LesionType, + QuestionGenre = x.QuestionGenre, + DataSource = x.DataSource, + DictionaryCode = x.DictionaryCode, + Type = x.Type, + QuestionType = x.QuestionType, + TypeValue = x.TypeValue, + ShowOrder = x.ShowOrder, + OrderMark = x.OrderMark, + ValueType = x.ValueType, + Unit = x.Unit, + CustomUnit=x.CustomUnit, + ReportLayType = ReportLayType.Question, + }).ToList(); + + // 问题 + foreach (var question in item.Childrens) + { + + foreach (var task in taskInfoList) + { + + var answer = answers.Where(x => x.VisitTaskId == task.VisitTaskId && x.ReadingQuestionTrialId == question.QuestionId).FirstOrDefault(); + question.Answer.Add(new TaskQuestionAnswer() + { + Answer = answer == null ? string.Empty : answer.Answer, + IsGlobalChange = answer == null ? false : answer.IsGlobalChange, + GlobalChangeAnswer = answer == null ? string.Empty : answer.GlobalChangeAnswer, + TaskName = task.TaskName, + VisitTaskId = task.VisitTaskId, + + }); + } + + // 构造表格行数据 + + + var rowlist = tableAnsweRowInfos.Where(x => x.QuestionId == question.QuestionId).OrderBy(x => x.RowIndex).ToList(); + + + question.Childrens = rowlist.OrderBy(x=>x.RowIndex).Select(x => new ReadingReportDto() + { + QuestionName = question.OrderMark + x.RowIndex.GetLesionMark(), + SplitOrMergeLesionName = x.MergeName.IsNullOrEmpty() ? x.SplitName : x.MergeName, + SplitOrMergeType = x.SplitOrMergeType, + LesionType = question.LesionType, + IsCanEditPosition = x.IsCanEditPosition, + RowIndex = x.RowIndex, + BlindName = x.BlindName, + RowId =x.Id, + ReportLayType = ReportLayType.Lesions, + FristAddTaskNum= x.FristAddTaskNum, + }).ToList(); + + + foreach (var row in question.Childrens) + { + // tableQuestion + row.Childrens = tableQuestionList.Where(x => x.ReadingQuestionId == question.QuestionId).Select(x => new ReadingReportDto() + { + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + QuestionId = x.ReadingQuestionId, + TableQuestionId = x.Id, + + Type = x.Type, + LesionType = question.LesionType, + TableQuestionType = x.TableQuestionType, + DataSource = x.DataSource, + DictionaryCode = x.DictionaryCode, + QuestionMark = x.QuestionMark, + TypeValue = x.TypeValue, + RowIndex = row.RowIndex, + ShowOrder = x.ShowOrder, + ValueType = x.ValueType, + RowId=row.RowId, + Unit = x.Unit, + ReportLayType = ReportLayType.TableQuestion, + FristAddTaskNum= row.FristAddTaskNum, + }).ToList(); + + + foreach (var tableQuestion in row.Childrens) + { + foreach (var task in taskInfoList) + { + var rowinfo = alltableAnsweRowInfos.Where(x => x.VisitTaskId == task.VisitTaskId && x.QuestionId == tableQuestion.QuestionId && x.RowIndex == tableQuestion.RowIndex).FirstOrDefault(); + var taskQuestionAnswer = new TaskQuestionAnswer() + { + Answer = tableAnswers.Where(x => x.VisitTaskId == task.VisitTaskId && x.QuestionId == tableQuestion.QuestionId && x.RowIndex == tableQuestion.RowIndex && x.TableQuestionId == tableQuestion.TableQuestionId).Select(x => x.Answer).FirstIsNullReturnEmpty(), + TaskName = task.TaskName, + VisitTaskId = task.VisitTaskId, + }; + if (rowinfo != null && rowinfo.OrganInfoId != null) + { + var organInfo = organInfos.Where(x => x.Id == rowinfo.OrganInfoId).FirstOrDefault(); + + + if (organInfo != null && needChangeType.Contains(tableQuestion.QuestionMark)) + { + if (_userInfo.IsEn_Us) + { + switch (tableQuestion.QuestionMark) + { + case QuestionMark.Organ: + taskQuestionAnswer.Answer = organInfo.TULOCEN; + + break; + case QuestionMark.Location: + if (organInfo.IsCanEditPosition) + { + + } + else + { + taskQuestionAnswer.Answer = organInfo.TULATEN; + + } + break; + case QuestionMark.Part: + + taskQuestionAnswer.Answer = organInfo.PartEN; + + break; + + } + + } + else + { + switch (tableQuestion.QuestionMark) + { + case QuestionMark.Organ: + taskQuestionAnswer.Answer = organInfo.TULOC; + break; + case QuestionMark.Location: + if (organInfo.IsCanEditPosition) + { + + } + else + { + taskQuestionAnswer.Answer = organInfo.TULAT; + + } + break; + case QuestionMark.Part: + taskQuestionAnswer.Answer = organInfo.Part; + break; + + } + } + + } + } + tableQuestion.Answer.Add(taskQuestionAnswer); + } + } + + + } + + + }; + } + #endregion + + + + result.TaskQuestions = questions; + + + + return result; + + } + #endregion + + /// + /// 将上一次的病灶信息添加到这一次 + /// + /// + /// + public async Task AddTaskLesionAnswerFromLastTask(AddTaskLesionAnswerFromLastTaskInDto inDto) + { + + var visitTaskId = inDto.VisitTaskId; + + var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + + var baseLineVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == taskinfo.SubjectId && x.IsBaseLine).Select(x => x.Id).FirstOrDefaultAsync(); + + // 判断当前任务是否是基线 + if (taskinfo.SourceSubjectVisitId != baseLineVisitId) + { + // 判断当前任务是是否有表格问题答案 + if (!(await _readingTableQuestionAnswerRepository.AnyAsync(x => x.VisitTaskId == visitTaskId))) + { + var LastVisitTaskId = await _visitTaskRepository.Where(x => x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == taskinfo.TrialReadingCriterionId && + x.IsAnalysisCreate == taskinfo.IsAnalysisCreate && + x.DoctorUserId ==taskinfo.DoctorUserId && + x.IsSelfAnalysis == taskinfo.IsSelfAnalysis && + x.SubjectId == taskinfo.SubjectId && x.ReadingTaskState == ReadingTaskState.HaveSigned && x.VisitTaskNum < taskinfo.VisitTaskNum && x.TaskState == TaskState.Effect && x.ArmEnum == taskinfo.ArmEnum + ).OrderByDescending(x => x.VisitTaskNum).Select(x => x.Id).FirstOrDefaultAsync(); + + // 上一次的表格答案 + var copyTableAnswers = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == LastVisitTaskId + ).Select(x => new CopyTableAnswerDto() + { + Answer = x.Answer, + QuestionId = x.QuestionId, + RowId = x.RowId, + QuestionMark = x.ReadingTableQuestionTrial.QuestionMark, + TableQuestionId = x.TableQuestionId, + RowIndex = x.RowIndex, + TrialId = x.TrialId + }).ToListAsync(); + + // 上一次的行信息 + var tableRowAnswers = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == LastVisitTaskId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + tableRowAnswers.ForEach(x => + { + x.VisitTaskId = visitTaskId; + x.IsCurrentTaskAdd = false; + x.Id = NewId.NextGuid(); + x.SeriesId = null; + x.InstanceId = null; + x.MeasureData = string.Empty; + x.PicturePath = string.Empty; + }); + + tableRowAnswers.ForEach(x => + { + x.SplitRowId = tableRowAnswers.Where(y => y.OriginalId == x.SplitRowId).Select(y => y.Id).FirstOrDefault(); + x.MergeRowId = tableRowAnswers.Where(y => y.OriginalId == x.MergeRowId).Select(y => y.Id).FirstOrDefault(); + + }); + + List notNeedCopyMarks = new List() + { + QuestionMark.State, + QuestionMark.LesionNumber, + }; + + var tableAnswers = copyTableAnswers.Select(x => new ReadingTableQuestionAnswer + { + Id = NewId.NextGuid(), + Answer = notNeedCopyMarks.Contains(x.QuestionMark) ? string.Empty : x.Answer, + QuestionId = x.QuestionId, + RowIndex = x.RowIndex, + RowId = tableRowAnswers.Where(y => y.OriginalId == x.RowId).Select(x => x.Id).FirstOrDefault(), + TableQuestionId = x.TableQuestionId, + TrialId = x.TrialId, + VisitTaskId = visitTaskId, + }).ToList(); + + + try + { + // 新病灶外层问题 + var newLesionQuestion = await _readingQuestionTrialRepository.Where(x => x.LesionType == LesionType.NewLesions && x.ReadingQuestionCriterionTrialId == taskinfo.TrialReadingCriterionId).FirstNotNullAsync(); + + // 既往病灶外层问题 + var alwaysNewLesionsQuestion = await _readingQuestionTrialRepository.Where(x => x.LesionType == LesionType.AlwaysNewLesions && x.ReadingQuestionCriterionTrialId == taskinfo.TrialReadingCriterionId).FirstNotNullAsync(); + + // 新病灶表格问题 + var newLesionTableQuestions = await _readingTableQuestionTrialRepository.Where(x => x.ReadingQuestionId == newLesionQuestion.Id).ToListAsync(); + + // 既往病灶表格问题 + var alwaysNewLesionsTableQuestions = await _readingTableQuestionTrialRepository.Where(x => x.ReadingQuestionId == alwaysNewLesionsQuestion.Id).ToListAsync(); + + Dictionary alwaysTableQuestionIdDic = new Dictionary(); + + newLesionTableQuestions.ForEach(x => + { + + alwaysTableQuestionIdDic.Add( + x.Id, + alwaysNewLesionsTableQuestions.Where(y => y.QuestionName == x.QuestionName).Select(y => y.Id).FirstOrDefault() + ); + }); + + foreach (var item in tableRowAnswers.Where(x => x.QuestionId == newLesionQuestion.Id).OrderBy(x=>x.RowIndex)) + { + + item.QuestionId = alwaysNewLesionsQuestion.Id; + + + foreach (var tableAnswer in tableAnswers.Where(y => y.RowId == item.Id)) + { + tableAnswer.QuestionId = alwaysNewLesionsQuestion.Id; + tableAnswer.TableQuestionId = alwaysTableQuestionIdDic[tableAnswer.TableQuestionId]; + + } + } + + } + catch (Exception) + { + + throw new BusinessValidationFailedException(_localizer["ReadingCalculate_Abnormal"]); + } + + + + + var addList = _mapper.Map>(tableRowAnswers); + + await _readingTableAnswerRowInfoRepository.AddRangeAsync(addList); + await _readingTableQuestionAnswerRepository.AddRangeAsync(tableAnswers); + await _readingTableQuestionAnswerRepository.SaveChangesAsync(); + + } + } + + return new AddTaskLesionAnswerFromLastTaskOutDto() + { + + IsBaseLine = taskinfo.SourceSubjectVisitId == baseLineVisitId, + }; + } + + /// + /// 测试计算 + /// + /// + /// + /// + [HttpPost] + public async Task TestCalculate(Guid visitTaskId, QuestionType type) + { + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(visitTaskId); + await ReadingCalculate(readingData, new List() { type }); + } + + /// + /// 计算任务 + /// + /// + /// + [HttpPost] + public async Task CalculateTask(CalculateTaskInDto inDto) + { + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(inDto.VisitTaskId); + readingData.IsChangeOtherTask = inDto.IsChangeOtherTask; + await ReadingCalculate(readingData); + } + + + /// + /// 自动计算 + /// + /// + /// + /// + public async Task ReadingCalculate(ReadingCalculateDto inDto, List calculateType = null) + { + + #region 计算 这里顺序非常重要 后面计算的值要依赖前面计算的结果 + var needAddList = new List(); + + + List calculateList = new List() + { + // 基线病灶计数 + new ReadingCalculateData (){QuestionType=QuestionType.BaseLineLesionsCount,GetIntFun=GetBaseLineLesionsCount}, + + // 新病灶计数 + new ReadingCalculateData (){QuestionType=QuestionType.NewLesionsCount,GetIntFun=GetNewLesionsCount}, + + // 既往新病灶 + new ReadingCalculateData (){QuestionType=QuestionType.AlwaysNewLesionsCount,GetIntFun=GetAlwaysNewLesionsCount}, + + // 自治疗后第二个检查批次点以来持续的新骨病变数量 + new ReadingCalculateData (){QuestionType=QuestionType.NewBoneLesionsCount,GetIntFun=GetNewBoneLesionCount}, + + // 间隔天数 + new ReadingCalculateData (){QuestionType=QuestionType.DaysBetween,GetIntFun=GetNumberOfDaysBetween}, + + // 检查批次点肿瘤评估 + new ReadingCalculateData (){QuestionType=QuestionType.SiteVisitForTumorEvaluation,GetStringFun=GetSiteVisitForTumorEvaluation}, + + + + }; + + if (calculateType != null) + { + calculateList = calculateList.Where(x => calculateType.Contains(x.QuestionType)).ToList(); + } + + + + foreach (var calculate in calculateList) + { + var item = inDto.QuestionInfo.FirstOrDefault(x => x.QuestionType == calculate.QuestionType); + + if (item != null) + { + //计算答案 + if (inDto.IsOnlyChangeAllTask == false) + { + + #region 计算答案 + + + + switch (calculate.QuestionType) + { + case QuestionType.BaseLineLesionsCount: + item.Answer = (await calculate.GetIntFun(inDto)).ToString(); + break; + + case QuestionType.NewLesionsCount: + + if (inDto.VisitTaskNum >= 1) + { + item.Answer = (await calculate.GetIntFun(inDto)).ToString(); + } + else + { + item.Answer = nameof(YesOrNoOrNa.NA); + } + break; + case QuestionType.AlwaysNewLesionsCount: + if (inDto.VisitTaskNum >1) + { + item.Answer = (await calculate.GetIntFun(inDto)).ToString(); + } + else + { + item.Answer = nameof(YesOrNoOrNa.NA); + } + break; + case QuestionType.NewBoneLesionsCount: + + + item.Answer = (await calculate.GetIntFun(inDto)).ToString(); + + if (item.Answer=="-1") + { + item.Answer = nameof(YesOrNoOrNa.NA); + } + break; + case QuestionType.DaysBetween: + item.Answer = (await calculate.GetIntFun(inDto)).ToString(); + break; + case QuestionType.SiteVisitForTumorEvaluation: + item.Answer = await calculate.GetStringFun(inDto); + break; + default: + item.Answer =string.Empty; + break; + } + + #endregion + + // 修改修约小数位数 + try + { + List valueOfTypes = new List() { + + ValueOfType.Decimals, + ValueOfType.Percentage + }; + + if (valueOfTypes.Contains(item.ValueType)) + { + item.Answer = decimal.Round(decimal.Parse(item.Answer.IsNullOrEmpty() ? "0": item.Answer), inDto.DigitPlaces).ToString(); + } + } + catch (Exception) + { + + + } + needAddList.Add(new ReadingTaskQuestionAnswer() + { + Answer = item.Answer, + ReadingQuestionTrialId = item.QuestionId, + }); + } + + // 修改全局 + if (inDto.IsChangeOtherTask && calculate.ChangeAllTaskFun != null) + { + await calculate.ChangeAllTaskFun(new ChangeAllTaskDto() + { + calculateDto = inDto, + QuestionId = item.QuestionId, + }); + } + + } + } + + + + var questionIds = needAddList.Select(x => x.ReadingQuestionTrialId).ToList(); + + await _readingTaskQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => questionIds.Contains(x.ReadingQuestionTrialId) && x.VisitTaskId == inDto.VisitTaskId); + needAddList.ForEach(x => + { + x.SubjectId = inDto.SubjectId; + x.ReadingQuestionCriterionTrialId = inDto.CriterionId; + x.VisitTaskId = inDto.VisitTaskId; + x.TrialId = inDto.TrialId; + x.SubjectId = inDto.SubjectId; + }); + + await _readingTaskQuestionAnswerRepository.AddRangeAsync(needAddList); + + await _readingTaskQuestionAnswerRepository.SaveChangesAsync(); + #endregion + + + } + + + #region 基线病灶计数 + /// + /// 基线病灶计数 + /// + /// + /// + public async Task GetBaseLineLesionsCount(ReadingCalculateDto inDto) + { + + try + { + return (int)(inDto.QuestionInfo.Where(x => x.LesionType == LesionType.BaselineLesions).SelectMany(x => x.TableRowInfoList) + + .Sum(x => x.TableQuestionList.Where(y => y.QuestionMark == QuestionMark.LesionNumber).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0())); + } + catch + { + + return 0; + } + + } + + #endregion + + #region 新病灶计数 + /// + /// 获取新病灶计数 + /// + /// + /// + public async Task GetNewLesionsCount(ReadingCalculateDto inDto) + { + return inDto.QuestionInfo.Where(x => x.LesionType == LesionType.NewLesions).SelectMany(x => x.TableRowInfoList) + .Where(x=>x.TableQuestionList.Any(y=>y.QuestionMark==QuestionMark.State&&y.Answer== EvaluationOfState.Exists.GetEnumInt())) + .Count(); + } + + #endregion + + + #region 既往新病灶计数 + /// + /// 既往新病灶计数 + /// + /// + /// + public async Task GetAlwaysNewLesionsCount(ReadingCalculateDto inDto) + { + return inDto.QuestionInfo.Where(x => x.LesionType == LesionType.AlwaysNewLesions).SelectMany(x => x.TableRowInfoList) + .Where(x => x.TableQuestionList.Any(y => y.QuestionMark == QuestionMark.State && y.Answer == EvaluationOfState.Exists.GetEnumInt())) + .Count(); + } + + #endregion + + + #region 自治疗后第二个检查批次点以来持续的新骨病变数量 + /// + /// 自治疗后第二个检查批次点以来持续的新骨病变数量 + /// + /// + /// + public async Task GetNewBoneLesionCount(ReadingCalculateDto inDto) + { + + var taskList = await GetSiteVisitForTumorList(inDto); + var result = 0; + var findindex = taskList.OrderBy(x => x.VisitTaskNum).ToList().FindIndex(x => x.VisitTaskId == inDto.VisitTaskId); + + + if (findindex < 3) + { + return -1; + } + else + { + var twoindex = taskList.OrderBy(x => x.VisitTaskNum).ToList()[2]; + + + result = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.AlwaysNewLesions).SelectMany(x => x.TableRowInfoList) + .Where(x => x.FristAddTaskNum >= twoindex.VisitTaskNum && x.TableQuestionList.Any(y => y.QuestionMark == QuestionMark.State && y.Answer == EvaluationOfState.Exists.GetEnumInt())).Count(); + + //+ inDto.QuestionInfo.Where(x => x.LesionType == LesionType.NewLesions).SelectMany(x => x.TableRowInfoList) + // .Where(x => x.TableQuestionList.Any(y => y.QuestionMark == QuestionMark.State && y.Answer == EvaluationOfState.Exists.GetEnumInt())) + // .Count(); + } + + + + + return result; + } + + #endregion + + + #region 间隔天数 + /// + /// 获取 + /// + /// + /// + public async Task GetNumberOfDaysBetween(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return -1; + } + var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + var taskList = await GetSiteVisitForTumorList(inDto); + var lastTask = taskList.Where(x => x.VisitTaskNum < taskinfo.VisitTaskNum&&x.VisitTaskId!=inDto.VisitTaskId).OrderByDescending(x => x.VisitTaskNum).FirstOrDefault(); + if (lastTask != null) + { + var thisTask = taskList.FirstOrDefault(); + if (thisTask.StudyTime != null && lastTask.StudyTime != null) + { + return (int)Math.Floor((thisTask.StudyTime.Value - lastTask.StudyTime.Value).TotalDays); + } + else + { + return -1; + } + + + } + else + { + return -1; + } + } + + #endregion + + + + #region 检查批次点肿瘤评估 + /// + /// 检查批次点肿瘤评估 + /// + /// + /// + public async Task GetSiteVisitForTumorEvaluation(ReadingCalculateDto inDto) + { + if(inDto.IsBaseLine) + { + return VisitTumorEvaluation.NA.GetEnumInt(); + } + + //如果日期未知,不需要计算肿瘤评估结果; + if (await GetNumberOfDaysBetween(inDto) == -1) + { + return string.Empty; + } + var isPD= await ChangeLastTaskSiteVisitForTumorEvaluation(inDto); + var newLesionsCount = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.NewLesionsCount).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + var baseLineLesionsCount = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.BaseLineLesionsCount).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + var alwaysNewLesionsCount = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.AlwaysNewLesionsCount).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + + if (isPD) + { + return VisitTumorEvaluation.PD.GetEnumInt(); + }else if (newLesionsCount == 0 && + baseLineLesionsCount == 0 && + alwaysNewLesionsCount == 0) + { + return VisitTumorEvaluation.ND.GetEnumInt(); + } + else + { + return VisitTumorEvaluation.NoPD.GetEnumInt(); + } + } + + /// + /// 修改上一次检查批次结果 + /// + /// + /// + public async Task ChangeLastTaskSiteVisitForTumorEvaluation(ReadingCalculateDto inDto) + { + var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + var taskList =await GetSiteVisitForTumorList(inDto); + var lastTask = taskList.Where(x => x.VisitTaskNum < taskinfo.VisitTaskNum&&x.VisitTaskId!=inDto.VisitTaskId).OrderByDescending(x => x.VisitTaskNum).FirstOrDefault(); + + var baseLineTask = taskList.OrderBy(x => x.VisitTaskNum).FirstOrDefault(); + var newLesionsCountQuestionId = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.NewLesionsCount).Select(x => x.QuestionId).FirstOrDefault(); + + var isPDResult = false; + + if (lastTask != null) + { + + #region 1、基线后第一个检查批次:新病灶计数≥ 2个;2、基线后第二个检查批次(应满足检查批次间隔6周以上,否则顺延)新病灶≥ 2个; + //1、基线后第一个检查批次:新病灶计数≥ 2个;2、基线后第二个检查批次(应满足检查批次间隔6周以上,否则顺延)新病灶≥ 2个; + + + var firstVisit = taskList.FirstOrDefault(); + if (baseLineTask.StudyTime != null) + { + //基线后第二个检查批次(应满足检查批次间隔6周以上,否则顺延) + var secondVisit = taskList.Where(x => x.VisitTaskNum >= 2 && x.StudyTime >= baseLineTask.StudyTime.Value.AddDays(42)).FirstOrDefault(); + if (secondVisit != null) + { + + var firstTaskNewLesionsCount = (await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == firstVisit.VisitTaskId && x.ReadingQuestionTrialId == newLesionsCountQuestionId).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + + // 第二检查批次数量 + var secondVisitLesionsCount = (await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == lastTask.VisitTaskId && x.ReadingQuestionTrialId == newLesionsCountQuestionId).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + + // 判断是否是当前检查批次 当前检查批次还未入库 + if (secondVisit.VisitTaskId == inDto.VisitTaskId) + { + secondVisitLesionsCount = (inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.NewLesionsCount).Select(x => x.Answer).FirstOrDefault()).IsNullOrEmptyReturn0(); + } + + if (firstTaskNewLesionsCount >= 2 && secondVisitLesionsCount >= 2) + { + isPDResult = true; + } + } + + #endregion + + + + if (lastTask.VisitTaskNum >= 2m) + { + + var lastTasknewLesionsCount = (await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == lastTask.VisitTaskId && x.ReadingQuestionTrialId == newLesionsCountQuestionId).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + var thisVisitTaskNewLesionsCount = (inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.NewLesionsCount).Select(x => x.Answer).FirstOrDefault()).IsNullOrEmptyReturn0(); + + var thisVisitTask = taskList.LastOrDefault(); + + if (thisVisitTask.StudyTime!=null&&lastTask.StudyTime!=null&& + + + lastTasknewLesionsCount >= 2 && thisVisitTaskNewLesionsCount >= 2 && lastTask.StudyTime.Value.AddDays(42) <= thisVisitTask.StudyTime) + { + isPDResult = true; + } + } + } + + } + if (isPDResult) + { + var visitForTumorEvaluationQuestionId = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SiteVisitForTumorEvaluation).Select(x => x.QuestionId).FirstOrDefault(); + + await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == lastTask.VisitTaskId && x.ReadingQuestionTrialId == visitForTumorEvaluationQuestionId,x=> new ReadingTaskQuestionAnswer + { + Answer= VisitTumorEvaluation.PD.GetEnumInt(), + }); + } + + + return isPDResult; + } + + #endregion + + /// + /// 获取检查批次日期信息 + /// + /// + /// + public async Task> GetSiteVisitForTumorList(ReadingCalculateDto inDto) + { + + + if (siteVisitForTumorList == null) + { + var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + siteVisitForTumorList = await _visitTaskRepository.Where(x => (x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == taskinfo.TrialReadingCriterionId && + x.DoctorUserId== taskinfo.DoctorUserId&& + x.IsAnalysisCreate==inDto.IsAnalysisCreate&& + x.SubjectId == taskinfo.SubjectId && x.ReadingTaskState == ReadingTaskState.HaveSigned && x.VisitTaskNum <= taskinfo.VisitTaskNum && x.TaskState == TaskState.Effect && x.ArmEnum == taskinfo.ArmEnum + )||x.Id== inDto.VisitTaskId).OrderByDescending(x => x.VisitTaskNum).Select(x => new SiteVisitForTumor() + { + VisitTaskId = x.Id, + VisitTaskNum = x.VisitTaskNum, + SubjectVisitId=x.SourceSubjectVisitId, + }).ToListAsync(); + + var visitIds = siteVisitForTumorList.Select(x => x.SubjectVisitId).ToList(); + // Dicom 用NM 非dicom 用骨扫描 (BoneScan) + List visitStudies = await _dicomStudyRepository.Where(x => visitIds.Contains(x.SubjectVisitId) && x.ModalityForEdit == "BoneScan").Select(x => new VisitStudyTime() + { + SubjectVisitId = x.SubjectVisitId, + StudyTime = x.StudyTime + + }).ToListAsync(); + + visitStudies.AddRange( + await _noneDicomStudyRepository.Where(x => visitIds.Contains(x.SubjectVisitId)&&x.Modality== "BoneScan").Select(x => new VisitStudyTime() { + SubjectVisitId = x.SubjectVisitId, + StudyTime = x.ImageDate + + }).ToListAsync() + ); + + visitStudies = visitStudies.Where(x => x.StudyTime != null).ToList(); + + foreach (var item in siteVisitForTumorList) + { + item.StudyTime = visitStudies.Where(x => x.SubjectVisitId == item.SubjectVisitId).Max(x => x.StudyTime); + } + + } + + return siteVisitForTumorList; + + + } + + + public async Task GetReportVerify(GetReportVerifyInDto inDto) + { + return new() { + + }; + } + + public async Task VerifyVisitTaskQuestions(VerifyVisitTaskQuestionsInDto inDto) + { + var tableAnswerList = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && (x.Answer == string.Empty || x.Answer == null) + && x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State + ) + .Select(x => new + { + x.ReadingQuestionTrial.OrderMark, + x.RowIndex, + QuestionMark = x.ReadingTableQuestionTrial.QuestionMark, + Answer = x.Answer, + + }).ToListAsync(); + + + string errorMassage = string.Empty; + + var rowAnswerList = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId &&x.ReadingQuestionTrial.LesionType!=LesionType.BaselineLesions&& (x.MeasureData == string.Empty || x.MeasureData == null)) + .Select(x => new + { + x.ReadingQuestionTrial.OrderMark, + x.RowIndex, + x.Id, + }).ToListAsync(); + + + var unableEvaluateRowIds = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && x.Answer == EvaluationOfState.UnableEvaluate.GetEnumInt() + && x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State + ) + .Select(x => x.RowId).Distinct().ToListAsync(); + + + List measureDataList = rowAnswerList.Where(x => !unableEvaluateRowIds.Contains(x.Id)).Select(x => x.OrderMark + x.RowIndex.GetLesionMark()).ToList(); + + List stateIsNullList = tableAnswerList.Select(x => x.OrderMark + x.RowIndex.GetLesionMark()).ToList(); + + + List allExists = measureDataList.Intersect(stateIsNullList).ToList(); + measureDataList = measureDataList.Except(allExists).ToList(); + stateIsNullList = stateIsNullList.Except(allExists).ToList(); + + + + if (measureDataList.Count() > 0) + { + errorMassage += _localizer["ReadingCalculate_NoMarker", string.Join(',', measureDataList)]+","; + } + + + if (tableAnswerList.Count > 0) + { + errorMassage += _localizer["ReadingCalculate_StatusIsEmpty", string.Join(',', stateIsNullList)] + ","; + } + + if (allExists.Count > 0) + { + errorMassage += _localizer["ReadingCalculate_NoMarkerEmpty", string.Join(',', stateIsNullList)] + ","; + } + + + if (errorMassage != string.Empty) + { + errorMassage = _localizer["ReadingCalculate_questionable"] + errorMassage; + throw new BusinessValidationFailedException(errorMassage); + } + } + } +} diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/RECIST1Point1CalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/RECIST1Point1CalculateService.cs new file mode 100644 index 0000000..9171730 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/RECIST1Point1CalculateService.cs @@ -0,0 +1,1816 @@ +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using Panda.DynamicWebApi.Attributes; +using IRaCIS.Core.Infra.EFCore.Common; +using Microsoft.Extensions.Caching.Memory; + +using IRaCIS.Core.Infrastructure; +using MassTransit; + +namespace IRaCIS.Core.Application.Service.ReadingCalculate +{ + + [ApiExplorerSettings(GroupName = "Reading")] + public class RECIST1Point1CalculateService : BaseService, ICriterionCalculateService + { + private readonly IRepository _readingTableQuestionAnswerRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _readingTableQuestionTrialRepository; + private readonly IRepository _readingTableAnswerRowInfoRepository; + private readonly IRepository _readingQuestionTrialRepository; + private readonly IRepository _organInfoRepository; + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _tumorAssessmentRepository; + private readonly IGeneralCalculateService _generalCalculateService; + private readonly IRepository _readingTaskQuestionAnswerRepository; + + public RECIST1Point1CalculateService( + IRepository readingTableQuestionAnswerRepository, + IRepository visitTaskRepository, + IRepository readingQuestionCriterionTrialRepository, + IRepository readingTableQuestionTrialRepository, + IRepository readingTableAnswerRowInfoRepository, + IRepository readingQuestionTrialRepository, + IRepository organInfoRepository, + IRepository subjectVisitRepository, + IRepository tumorAssessmentRepository, + IGeneralCalculateService generalCalculateService, + IRepository readingTaskQuestionAnswerRepository + ) + { + this._readingTableQuestionAnswerRepository = readingTableQuestionAnswerRepository; + this._visitTaskRepository = visitTaskRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this._readingTableQuestionTrialRepository = readingTableQuestionTrialRepository; + this._readingTableAnswerRowInfoRepository = readingTableAnswerRowInfoRepository; + this._readingQuestionTrialRepository = readingQuestionTrialRepository; + this._organInfoRepository = organInfoRepository; + this._subjectVisitRepository = subjectVisitRepository; + this._tumorAssessmentRepository = tumorAssessmentRepository; + this._generalCalculateService = generalCalculateService; + this._readingTaskQuestionAnswerRepository = readingTaskQuestionAnswerRepository; + } + + #region 临时对象 单个请求的生命周期 避免重复查询数据库 + + private List visitTaskAnswerList; + + /// + /// 获取Sod的值 + /// + private decimal? sODData; + + private string nAString = "NA"; + #endregion + + #region 删除病灶获取起始病灶序号 + /// + /// 删除病灶获取起始病灶序号(RECIST1Point1 固定是1) + /// + /// + public async Task GetDeleteLesionStatrIndex(DeleteReadingRowAnswerInDto inDto) + { + return 1; + } + #endregion + + #region 获取阅片报告 + /// + /// 获取阅片报告 + /// + /// + /// + [HttpPost] + public async Task GetReadingReportEvaluation(GetReadingReportEvaluationInDto indto) + { + GetReadingReportEvaluationOutDto result = new GetReadingReportEvaluationOutDto(); + + result.CalculateResult = await this.GetReportVerify(new GetReportVerifyInDto() + { + VisitTaskId = indto.VisitTaskId + }); + + + var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == indto.VisitTaskId).FirstNotNullAsync(); + + result.ReadingTaskState = visitTaskInfo.ReadingTaskState; + var taskInfoList = await _generalCalculateService.GetReadingReportTaskList(indto.VisitTaskId); + + result.VisitTaskList = taskInfoList; + + var visitTaskIds = taskInfoList.Select(x => x.VisitTaskId).ToList(); + + var criterionId = visitTaskInfo.TrialReadingCriterionId; + var questionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == criterionId).ToListAsync(); + var tableQuestionList = await _readingTableQuestionTrialRepository.Where(x => x.TrialCriterionId == criterionId).OrderBy(x => x.ShowOrder).ToListAsync(); + var tableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == indto.VisitTaskId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var answers = await _readingTaskQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync(); + var tableAnswers = await _readingTableQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync(); + + var alltableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync(); + var organIds = alltableAnsweRowInfos.Where(x => x.OrganInfoId != null).Select(x => x.OrganInfoId).Distinct().ToList(); + var organInfos = await _organInfoRepository.Where(x => organIds.Contains(x.Id)).ToListAsync(); + + var needChangeType = new List() { + QuestionMark.Organ, + QuestionMark.Location, + QuestionMark.Part, + }; + + // 第一级 + + #region 构造问题 + List questions = questionList.Where(x => x.Type == ReadingQestionType.Group).OrderBy(x => x.ShowOrder).Select(x => new ReadingReportDto() + { + QuestionId = x.Id, + GroupName = x.GroupName, + GroupEnName = x.GroupEnName, + IsShowInDicom = x.IsShowInDicom, + Type = x.Type, + GroupId = x.GroupId, + QuestionType = x.QuestionType, + LesionType = x.LesionType, + QuestionGenre = x.QuestionGenre, + DataSource = x.DataSource, + DictionaryCode = x.DictionaryCode, + TypeValue = x.TypeValue, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + ShowOrder = x.ShowOrder, + ValueType = x.ValueType, + Unit = x.Unit, + CustomUnit = x.CustomUnit, + ReportLayType = ReportLayType.Group, + }).ToList(); + + // 分组 + foreach (var item in questions) + { + item.Childrens = questionList.Where(x => x.GroupId == item.QuestionId).OrderBy(x => x.ShowOrder).Select(x => new ReadingReportDto() + { + GroupName = x.GroupName, + QuestionId = x.Id, + IsShowInDicom = x.IsShowInDicom, + GroupEnName = x.GroupEnName, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + LesionType = x.LesionType, + QuestionGenre = x.QuestionGenre, + DataSource = x.DataSource, + DictionaryCode = x.DictionaryCode, + Type = x.Type, + QuestionType = x.QuestionType, + TypeValue = x.TypeValue, + ShowOrder = x.ShowOrder, + OrderMark = x.OrderMark, + ValueType = x.ValueType, + Unit = x.Unit, + CustomUnit = x.CustomUnit, + ReportLayType = ReportLayType.Question, + }).ToList(); + + // 问题 + foreach (var question in item.Childrens) + { + + foreach (var task in taskInfoList) + { + + var answer = answers.Where(x => x.VisitTaskId == task.VisitTaskId && x.ReadingQuestionTrialId == question.QuestionId).FirstOrDefault(); + question.Answer.Add(new TaskQuestionAnswer() + { + Answer = answer == null ? string.Empty : answer.Answer, + IsGlobalChange = answer == null ? false : answer.IsGlobalChange, + GlobalChangeAnswer = answer == null ? string.Empty : answer.GlobalChangeAnswer, + TaskName = task.TaskName, + VisitTaskId = task.VisitTaskId, + + }); + } + + // 构造表格行数据 + + + var rowlist = tableAnsweRowInfos.Where(x => x.QuestionId == question.QuestionId).OrderBy(x => x.RowIndex).ToList(); + + + question.Childrens = rowlist.Select(x => new ReadingReportDto() + { + QuestionName = question.OrderMark + x.RowIndex.GetLesionMark(), + SplitOrMergeLesionName = x.MergeName.IsNullOrEmpty() ? x.SplitName : x.MergeName, + SplitOrMergeType = x.SplitOrMergeType, + LesionType = question.LesionType, + IsShowInDicom = question.IsShowInDicom, + IsCanEditPosition = x.IsCanEditPosition, + RowIndex = x.RowIndex, + BlindName = x.BlindName, + ReportLayType = ReportLayType.Lesions, + }).ToList(); + + + foreach (var row in question.Childrens) + { + // tableQuestion + row.Childrens = tableQuestionList.Where(x => x.ReadingQuestionId == question.QuestionId).Select(x => new ReadingReportDto() + { + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + QuestionId = x.ReadingQuestionId, + TableQuestionId = x.Id, + Type = x.Type, + LesionType = question.LesionType, + TableQuestionType = x.TableQuestionType, + RowId = row.RowId, + IsShowInDicom = question.IsShowInDicom, + DataSource = x.DataSource, + DictionaryCode = x.DictionaryCode, + QuestionMark = x.QuestionMark, + TypeValue = x.TypeValue, + RowIndex = row.RowIndex, + ShowOrder = x.ShowOrder, + ValueType = x.ValueType, + Unit = x.Unit, + ReportLayType = ReportLayType.TableQuestion, + }).ToList(); + + + foreach (var tableQuestion in row.Childrens) + { + foreach (var task in taskInfoList) + { + var rowinfo = alltableAnsweRowInfos.Where(x => x.VisitTaskId == task.VisitTaskId && x.QuestionId == tableQuestion.QuestionId && x.RowIndex == tableQuestion.RowIndex).FirstOrDefault(); + var taskQuestionAnswer = new TaskQuestionAnswer() + { + Answer = tableAnswers.Where(x => x.VisitTaskId == task.VisitTaskId && x.QuestionId == tableQuestion.QuestionId && x.RowIndex == tableQuestion.RowIndex && x.TableQuestionId == tableQuestion.TableQuestionId).Select(x => x.Answer).FirstIsNullReturnEmpty(), + TaskName = task.TaskName, + VisitTaskId = task.VisitTaskId, + }; + if (rowinfo != null && rowinfo.OrganInfoId != null) + { + var organInfo = organInfos.Where(x => x.Id == rowinfo.OrganInfoId).FirstOrDefault(); + + + if (organInfo != null && needChangeType.Contains(tableQuestion.QuestionMark)) + { + if (_userInfo.IsEn_Us) + { + switch (tableQuestion.QuestionMark) + { + case QuestionMark.Organ: + taskQuestionAnswer.Answer = organInfo.TULOCEN; + + break; + case QuestionMark.Location: + if (organInfo.IsCanEditPosition) + { + + } + else + { + taskQuestionAnswer.Answer = organInfo.TULATEN; + + } + break; + case QuestionMark.Part: + + taskQuestionAnswer.Answer = organInfo.PartEN; + + break; + + } + + } + else + { + switch (tableQuestion.QuestionMark) + { + case QuestionMark.Organ: + taskQuestionAnswer.Answer = organInfo.TULOC; + break; + case QuestionMark.Location: + if (organInfo.IsCanEditPosition) + { + + } + else + { + taskQuestionAnswer.Answer = organInfo.TULAT; + + } + break; + case QuestionMark.Part: + taskQuestionAnswer.Answer = organInfo.Part; + break; + + } + } + + } + } + tableQuestion.Answer.Add(taskQuestionAnswer); + } + } + + + } + + + }; + } + #endregion + + + + result.TaskQuestions = questions; + + + + return result; + + } + #endregion + + /// + /// 测试计算 + /// + /// + /// + /// + [HttpPost] + public async Task TestCalculate(Guid visitTaskId, QuestionType type) + { + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(visitTaskId); + await ReadingCalculate(readingData, new List() { type }); + } + + + + /// + /// 计算任务 + /// + /// + /// + [HttpPost] + public async Task CalculateTask(CalculateTaskInDto inDto) + { + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(inDto.VisitTaskId); + readingData.IsChangeOtherTask = inDto.IsChangeOtherTask; + await ReadingCalculate(readingData); + } + + /// + /// 获取报告验证的信息(这里每个标准可能不一样 返回用object) + /// + /// + /// + public async Task GetReportVerify(GetReportVerifyInDto inDto) + { + return new + { + TumorEvaluate = await this.GetReportTumor(inDto.VisitTaskId), + IsExistDisease = await this.GetReportIsExistDisease(inDto.VisitTaskId), + + }; + } + + + /// + /// 自动计算 + /// + /// + /// + /// + public async Task ReadingCalculate(ReadingCalculateDto inDto, List calculateType = null) + { + + #region 计算 这里顺序非常重要 后面计算的值要依赖前面计算的结果 + var needAddList = new List(); + + + List calculateList = new List() + { + //靶病灶径线之和(SOD) + new ReadingCalculateData (){QuestionType=QuestionType.SOD,GetDecimalNullFun=GetSODData}, + + //非淋巴结靶病灶长径之和 + new ReadingCalculateData (){QuestionType=QuestionType.SumOfDiameter,GetDecimalNullFun=GetSumOfDiameter}, + + //与基线SOD相比变化量(mm) + new ReadingCalculateData (){QuestionType=QuestionType.SODChange,GetDecimalNullFun=GetSODChange}, + + //与基线检查批次相比SOD变化百分比 + new ReadingCalculateData (){QuestionType=QuestionType.SODPercent,GetDecimalNullFun=GetSODPercent}, + + //与整个检查批次期间SOD最低点相比增加的值(mm) //其他任务需要改 + new ReadingCalculateData (){QuestionType=QuestionType.LowestIncrease,GetDecimalNullFun=GetLowestIncrease,/*ChangeAllTaskFun=ChangeAllLowestIncrease*/}, + + //与整个检查批次期间SOD最低点相比增加的百分比 //其他任务需要改 + new ReadingCalculateData (){QuestionType=QuestionType.LowPercent,GetDecimalNullFun=GetLowPercent,/*ChangeAllTaskFun=ChangeAllLowPercent*/}, + + //整个检查批次期间SOD最低点检查批次名称 //其他任务需要改 + new ReadingCalculateData (){QuestionType=QuestionType.LowVisit,GetStringFun=GetLowVisit,/*ChangeAllTaskFun=ChangeAllLowVisitName*/}, + + //是否存在非淋巴结靶病灶 + new ReadingCalculateData (){QuestionType=QuestionType.IsLymphTarget,GetStringFun=GetIsLymphTarget}, + + //是否存在淋巴结靶病灶且该病灶比上一检查批次短径增加5MM以上 + new ReadingCalculateData (){QuestionType=QuestionType.IsAddFive,GetStringFun=GetIsAddFive}, + + //被评估为NE的单个靶病灶 + new ReadingCalculateData (){QuestionType=QuestionType.NETarget,GetStringFun=GetNETarget}, + + + #region 疗效不自动计算 + // //靶病灶评估 + // new ReadingCalculateData (){QuestionType=QuestionType.TargetLesion,GetStringFun=GetTargetLesionEvaluate}, + + ////非靶病灶评估 + // new ReadingCalculateData (){QuestionType=QuestionType.NoTargetLesion,GetStringFun=GetNoTargetLesionEvaluate}, + + ////是否存在新病灶 + // new ReadingCalculateData (){QuestionType=QuestionType.NewLesions,GetStringFun=GetNewLesionEvaluate}, + + // //整体肿瘤评估 + // new ReadingCalculateData (){QuestionType=QuestionType.Tumor,GetStringFun=GetTumor}, + + // //是否存在疾病 + // new ReadingCalculateData (){QuestionType=QuestionType.ExistDisease,GetStringFun=GetIsExistDisease}, + #endregion + + + }; + + // 没有靶病灶只计算最后几个 + if (inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).Sum(x => x.TableRowInfoList.Count()) == 0) + { + + List questionTypes = new List() + { + QuestionType.TargetLesion, + QuestionType.NoTargetLesion, + QuestionType.NewLesions, + QuestionType.Tumor, + QuestionType.ExistDisease, + }; + + // 没有靶病灶就删除其他几个答案的值 + var isNeedDeleteTypes = calculateList.Where(x => !questionTypes.Contains(x.QuestionType)).Select(x => x.QuestionType).ToList(); + + + var isNeedDeleteIds = inDto.QuestionInfo.Where(x => x.QuestionType != null && isNeedDeleteTypes.Contains(x.QuestionType.Value)).Select(x => x.QuestionId).ToList(); + + await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == inDto.VisitTaskId && isNeedDeleteIds.Contains(x.ReadingQuestionTrialId), x => new ReadingTaskQuestionAnswer + { + Answer = string.Empty + }); + + + + calculateList = calculateList.Where(x => questionTypes.Contains(x.QuestionType)).ToList(); + + + + + + } + + if (calculateType != null) + { + calculateList = calculateList.Where(x => calculateType.Contains(x.QuestionType)).ToList(); + } + + var typeNAList = new List + { + QuestionType.SODChange, + QuestionType.SODPercent, + QuestionType.LowestIncrease, + QuestionType.LowPercent, + }; + + foreach (var calculate in calculateList) + { + var item = inDto.QuestionInfo.FirstOrDefault(x => x.QuestionType == calculate.QuestionType); + + if (item != null) + { + //计算答案 + if (inDto.IsOnlyChangeAllTask == false) + { + + #region 计算答案 + if (calculate.GetDecimalFun != null) + { + item.Answer = (await calculate.GetDecimalFun(inDto)).ToString(); + + } + else if (calculate.GetDecimalNullFun != null) + { + var value = await calculate.GetDecimalNullFun(inDto); + if (value == null) + { + if (typeNAList.Contains(item.QuestionType ?? QuestionType.SOD)) + { + item.Answer = nameof(YesOrNoOrNa.NA); + + } + else + { + item.Answer = this.nAString; + + } + + } + else + + { + item.Answer = value.ToString(); + } + } + else if (calculate.GetStringFun != null) + { + item.Answer = await calculate.GetStringFun(inDto); + } + #endregion + // 修改修约小数位数 + + List valueOfTypes = new List() { + + ValueOfType.Decimals, + ValueOfType.Percentage + }; + + if (inDto.DigitPlaces != null && inDto.DigitPlaces != -1) + { + try + { + + if (valueOfTypes.Contains(item.ValueType)) + { + item.Answer = decimal.Round(decimal.Parse(item.Answer ?? "0"), inDto.DigitPlaces).ToString(); + } + } + catch (Exception) + { + + + } + } + + + + needAddList.Add(new ReadingTaskQuestionAnswer() + { + Answer = item.Answer, + ReadingQuestionTrialId = item.QuestionId, + }); + } + + // 修改全局 + if (inDto.IsChangeOtherTask && calculate.ChangeAllTaskFun != null) + { + await calculate.ChangeAllTaskFun(new ChangeAllTaskDto() + { + calculateDto = inDto, + QuestionId = item.QuestionId, + }); + } + + } + } + + + + var questionIds = needAddList.Select(x => x.ReadingQuestionTrialId).ToList(); + + await _readingTaskQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => questionIds.Contains(x.ReadingQuestionTrialId) && x.VisitTaskId == inDto.VisitTaskId); + needAddList.ForEach(x => + { + x.SubjectId = inDto.SubjectId; + x.ReadingQuestionCriterionTrialId = inDto.CriterionId; + x.VisitTaskId = inDto.VisitTaskId; + x.TrialId = inDto.TrialId; + x.SubjectId = inDto.SubjectId; + }); + + await _readingTaskQuestionAnswerRepository.AddRangeAsync(needAddList); + + await _readingTaskQuestionAnswerRepository.SaveChangesAsync(); + #endregion + + + } + + + /// + /// 获取报告整体整体评估 + /// + /// + /// + public async Task GetReportTumor(Guid visitTaskId) + { + return await GetTumor(await _generalCalculateService.GetReadingCalculateDto(visitTaskId)); + } + + /// + /// 获取报告是否存在疾病 + /// + /// + /// + public async Task GetReportIsExistDisease(Guid visitTaskId) + { + return await GetIsExistDisease(await _generalCalculateService.GetReadingCalculateDto(visitTaskId)); + } + + /// + /// 验证检查批次提交 + /// + /// + /// + public async Task VerifyVisitTaskQuestions(VerifyVisitTaskQuestionsInDto inDto) + { + + var tableAnswerList = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && (x.Answer == string.Empty || x.Answer == null) + && x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State + ) + .Select(x => new + { + x.ReadingQuestionTrial.OrderMark, + x.RowIndex, + QuestionMark = x.ReadingTableQuestionTrial.QuestionMark, + Answer = x.Answer, + + }).ToListAsync(); + + + string errorMassage = string.Empty; + + var rowAnswerList = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && (x.MeasureData == string.Empty || x.MeasureData == null)) + .Select(x => new + { + x.ReadingQuestionTrial.OrderMark, + x.RowIndex, + x.Id, + }).ToListAsync(); + + + var unableEvaluateRowIds = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && x.Answer == TargetState.UnableEvaluate.GetEnumInt() + && x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State + ) + .Select(x => x.RowId).Distinct().ToListAsync(); + + + List measureDataList = rowAnswerList.Where(x => !unableEvaluateRowIds.Contains(x.Id)).Select(x => x.OrderMark + x.RowIndex.GetLesionMark()).ToList(); + + List stateIsNullList = tableAnswerList.Select(x => x.OrderMark + x.RowIndex.GetLesionMark()).ToList(); + + + List allExists = measureDataList.Intersect(stateIsNullList).ToList(); + measureDataList = measureDataList.Except(allExists).ToList(); + stateIsNullList = stateIsNullList.Except(allExists).ToList(); + + + if (measureDataList.Count() > 0) + { + errorMassage += _localizer["ReadingCalculate_NoMarker", string.Join(',', measureDataList)] + ","; + } + + + if (tableAnswerList.Count > 0) + { + errorMassage += _localizer["ReadingCalculate_StatusIsEmpty", string.Join(',', stateIsNullList)] + ","; + } + + if (allExists.Count > 0) + { + errorMassage += _localizer["ReadingCalculate_NoMarkerEmpty", string.Join(',', stateIsNullList)] + ","; + } + + + if (errorMassage != string.Empty) + { + errorMassage = _localizer["ReadingCalculate_Questionable"] + errorMassage; + throw new BusinessValidationFailedException(errorMassage); + } + + + } + + + + #region 将上一次的检查批次病灶添加到这一次 + + /// + /// 将上一次的检查批次病灶添加到这一次 + /// + /// + /// + public async Task AddTaskLesionAnswerFromLastTask(AddTaskLesionAnswerFromLastTaskInDto inDto) + { + var visitTaskId = inDto.VisitTaskId; + + var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + + var baseLineVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == taskinfo.SubjectId && x.IsBaseLine).Select(x => x.Id).FirstOrDefaultAsync(); + + // 判断当前任务是否是基线 + if (taskinfo.SourceSubjectVisitId != baseLineVisitId) + { + // 判断当前任务是是否有表格问题答案 + if (!(await _readingTableQuestionAnswerRepository.AnyAsync(x => x.VisitTaskId == visitTaskId))) + { + + var LastVisitTaskId = await _visitTaskRepository.Where(x => x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == taskinfo.TrialReadingCriterionId && + x.IsAnalysisCreate == taskinfo.IsAnalysisCreate && + x.DoctorUserId == taskinfo.DoctorUserId && + x.IsSelfAnalysis == taskinfo.IsSelfAnalysis && + x.SubjectId == taskinfo.SubjectId && x.ReadingTaskState == ReadingTaskState.HaveSigned && x.VisitTaskNum < taskinfo.VisitTaskNum && x.TaskState == TaskState.Effect && x.ArmEnum == taskinfo.ArmEnum + ).OrderByDescending(x => x.VisitTaskNum).Select(x => x.Id).FirstOrDefaultAsync(); + + + + var copyTableAnswers = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == LastVisitTaskId).Select(x => new CopyTableAnswerDto() + { + Answer = x.Answer, + QuestionId = x.QuestionId, + RowId = x.RowId, + QuestionMark = x.ReadingTableQuestionTrial.QuestionMark, + TableQuestionId = x.TableQuestionId, + RowIndex = x.RowIndex, + TrialId = x.TrialId + }).ToListAsync(); + + var tableRowAnswers = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == LastVisitTaskId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + tableRowAnswers.ForEach(x => + { + x.VisitTaskId = visitTaskId; + x.IsCurrentTaskAdd = false; + x.Id = NewId.NextGuid(); + x.SeriesId = null; + x.InstanceId = null; + x.MeasureData = string.Empty; + x.PicturePath = string.Empty; + }); + + tableRowAnswers.ForEach(x => + { + x.SplitRowId = tableRowAnswers.Where(y => y.OriginalId == x.SplitRowId).Select(y => y.Id).FirstOrDefault(); + x.MergeRowId = tableRowAnswers.Where(y => y.OriginalId == x.MergeRowId).Select(y => y.Id).FirstOrDefault(); + + }); + + List notNeedCopyMarks = new List() + { + QuestionMark.MajorAxis, + QuestionMark.ShortAxis, + QuestionMark.State, + }; + + var tableAnswers = copyTableAnswers.Select(x => new ReadingTableQuestionAnswer + { + Id = NewId.NextGuid(), + Answer = notNeedCopyMarks.Contains(x.QuestionMark) ? string.Empty : x.Answer, + QuestionId = x.QuestionId, + RowIndex = x.RowIndex, + RowId = tableRowAnswers.Where(y => y.OriginalId == x.RowId).Select(x => x.Id).FirstOrDefault(), + TableQuestionId = x.TableQuestionId, + TrialId = x.TrialId, + VisitTaskId = visitTaskId, + }); + + + + + var addList = _mapper.Map>(tableRowAnswers); + + await _readingTableAnswerRowInfoRepository.AddRangeAsync(addList); + await _readingTableQuestionAnswerRepository.AddRangeAsync(tableAnswers); + await _readingTableQuestionAnswerRepository.SaveChangesAsync(); + + } + } + + return new AddTaskLesionAnswerFromLastTaskOutDto() + { + + IsBaseLine = taskinfo.SourceSubjectVisitId == baseLineVisitId, + }; + + } + #endregion + + #region 获取SOD + + /// + /// 获取SOD + /// + /// + /// 靶病灶径线之和(SOD) + /// 非淋巴结的长径 和淋巴结的短径 + /// + /// + public async Task GetSODData(ReadingCalculateDto inDto) + { + if (sODData != null) + { + return sODData.Value; + } + + var tableQuestion = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + + if (tableQuestion.Count() == 0) + { + return null; + } + + decimal result = 0; + + foreach (var item in tableQuestion) + { + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.Yes))) + { + // 淋巴结的短径 + result += (item.TableQuestionList.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer).FirstOrDefault()).IsNullOrEmptyReturn0(); + } + + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.No))) + { + // 非淋巴结的长径 + result += item.TableQuestionList.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + } + } + + sODData = result; + + return sODData.Value; + + + + } + #endregion + + #region 非淋巴结靶病灶长径之和 + /// + /// 非淋巴结靶病灶长径之和 + /// + /// + /// + public async Task GetSumOfDiameter(ReadingCalculateDto inDto) + { + var tableQuestion = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + + if (tableQuestion.Count() == 0) + { + return null; + } + + decimal result = 0; + + foreach (var item in tableQuestion) + { + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.No))) + { + // 非淋巴结的长径 + result += item.TableQuestionList.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + } + } + + + return result; + } + #endregion + + #region 与基线SOD相比变化量(mm) + /// + /// 与基线SOD相比变化量(mm) + /// + /// + /// + public async Task GetSODChange(ReadingCalculateDto inDto) + { + + var value = await GetSODData(inDto); + + if (value == null || inDto.IsBaseLine) + { + return null; + } + return value.NullChange0() - await GetBaseLineSOD(inDto); + } + #endregion + + #region 与基线检查批次相比SOD变化百分比 + /// + /// 与基线检查批次相比SOD变化百分比 + /// + /// + /// + public async Task GetSODPercent(ReadingCalculateDto inDto) + { + var thisSOD = await GetSODData(inDto); + + if (thisSOD == null || inDto.IsBaseLine) + { + return null; + } + + var baseLineSOD = await GetBaseLineSOD(inDto); + + if (baseLineSOD == 0) + { + return 100; + } + else + { + return (thisSOD.NullChange0() - baseLineSOD) * 100 / baseLineSOD; + } + } + #endregion + + #region 与整个检查批次期间SOD最低点相比增加的值(mm) + /// + /// 与整个检查批次期间SOD最低点相比增加的值(mm) + /// + /// + /// + /// 要更新之前的 + /// + /// + public async Task GetLowestIncrease(ReadingCalculateDto inDto) + { + var value = await GetSODData(inDto); + if (value == null || inDto.IsBaseLine) + { + return null; + } + var decimalAnswerList = await GetLowSODVisit(inDto); + var minSOD = decimalAnswerList.OrderBy(x => x.SOD).Select(x => x.SOD).FirstOrDefault(); + return value.NullChange0() - minSOD; + } + #endregion + + #region 与整个检查批次期间SOD最低点相比增加的百分比 + /// + /// 与整个检查批次期间SOD最低点相比增加的百分比 + /// + /// + /// + /// 要更新之前的 + /// + /// + public async Task GetLowPercent(ReadingCalculateDto inDto) + { + var thisSOD = await GetSODData(inDto); + if (thisSOD == null || inDto.IsBaseLine) + { + return null; + } + var decimalAnswerList = await GetLowSODVisit(inDto); + var minSOD = decimalAnswerList.OrderBy(x => x.SOD).Select(x => x.SOD).FirstOrDefault(); + + if (minSOD == 0) + { + return 100; + } + else + { + return (thisSOD.NullChange0() - minSOD) * 100 / minSOD; + } + + + } + #endregion + + #region 整个检查批次期间SOD最低点检查批次名称 + /// + /// 整个检查批次期间SOD最低点检查批次名称 + /// + /// + /// + /// 要更新之前的 + /// + /// + public async Task GetLowVisit(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return nameof(YesOrNoOrNa.NA); + } + var targetCount = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).Count(); + if (targetCount == 0) + { + return nameof(YesOrNoOrNa.NA); + } + + var decimalAnswerList = await GetLowSODVisit(inDto); + return decimalAnswerList.OrderBy(x => x.SOD).Select(x => x.BlindName).FirstOrDefault() ?? string.Empty; + } + #endregion + + #region 是否存在非淋巴结靶病灶 + /// + /// 是否存在非淋巴结靶病灶 + /// + /// + /// + public async Task GetIsLymphTarget(ReadingCalculateDto inDto) + { + var result = IsLymph.No.GetEnumInt(); + var tableQuestion = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + + foreach (var item in tableQuestion) + { + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(IsLymph.No))) + { + result = IsLymph.Yes.GetEnumInt(); + } + } + + + + return result; + } + #endregion + + #region 是否存在淋巴结靶病灶且该病灶比上一检查批次短径增加5MM以上 + /// + /// 是否存在淋巴结靶病灶且该病灶比上一检查批次短径增加5MM以上 + /// + /// + /// + public async Task GetIsAddFive(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return YesOrNoOrNa.NA.GetEnumInt(); + } + var addFiveList = await GetIsAddFiveRowIndexs(inDto); + var isExists = addFiveList.Count() > 0 ? true : false; + + return isExists ? YesOrNoOrNa.Yes.GetEnumInt() : YesOrNoOrNa.No.GetEnumInt(); + + } + + /// + /// 获取存在淋巴结靶病灶且该病灶比上一检查批次短径增加5MM以上的病灶 + /// + /// + /// + public async Task> GetIsAddFiveRowIndexs(ReadingCalculateDto inDto) + { + List result = new List(); + var LastVisitTaskId = await this.GetLastVisitTaskId(inDto); + var questionIds = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).Select(x => x.QuestionId).ToList(); + var lastQuestionAsnwer = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == LastVisitTaskId && questionIds.Contains(x.QuestionId)).Include(x => x.ReadingQuestionTrial).Include(x => x.ReadingTableQuestionTrial).ToListAsync(); + var rowIndexs = lastQuestionAsnwer.Where(x => x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.Yes)).Select(x => x.RowIndex).Distinct().OrderBy(x => x).ToList(); + var thisQuestionAsnwer = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + foreach (var item in rowIndexs) + { + var lastValue = lastQuestionAsnwer.Where(x => x.RowIndex == item && x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + + var thisRowData = thisQuestionAsnwer.Where(x => x.RowIndex == item).SelectMany(x => x.TableQuestionList).ToList(); + var thisValue = thisRowData.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + + if (thisValue - lastValue >= 5) + { + result.Add(item); + } + } + + return result; + } + #endregion + + #region 被评估为NE的单个靶病灶 + /// + /// 被评估为NE的单个靶病灶 + /// + /// + /// + public async Task GetNETarget(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return ExistOrNA.NA.GetEnumInt(); + } + + var result = inDto.QuestionInfo.Any(x => x.QuestionType == QuestionType.TargetLesion && x.Answer.EqEnum(TargetAssessment.NE)); + + return result ? ExistOrNA.Exist.GetEnumInt() : ExistOrNA.NotExist.GetEnumInt(); + } + #endregion + + #region 整体肿瘤评估 + + /// + /// 整体肿瘤评估 + /// + /// + /// + public async Task GetTumor(ReadingCalculateDto inDto) + { + + if (inDto.IsBaseLine) + { + return OverallAssessment.NA.GetEnumInt(); + } + + var targetLesion = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.TargetLesion).Select(x => x.Answer).FirstOrDefault(); + var noTargetLesion = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.NoTargetLesion).Select(x => x.Answer).FirstOrDefault(); + var newLesions = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.NewLesions).Select(x => x.Answer).FirstOrDefault(); + var result = await _tumorAssessmentRepository.Where(x => + x.TargetLesion == (TargetAssessment)int.Parse(targetLesion.IsNullOrEmpty() ? TargetAssessment.NA.GetEnumInt() : targetLesion) && + x.NonTargetLesions == (NoTargetAssessment)int.Parse(noTargetLesion.IsNullOrEmpty() ? NoTargetAssessment.NA.GetEnumInt() : noTargetLesion) && + x.NewLesion == (NewLesionAssessment)int.Parse(newLesions.IsNullOrEmpty() ? NewLesionAssessment.NA.GetEnumInt() : newLesions)).Select(x => x.OverallEfficacy).ToListAsync(); + + return result.Count == 0 ? OverallAssessment.NE.GetEnumInt() : result[0].GetEnumInt(); + } + #endregion + + #region 是否存在疾病 + /// + /// 是否存在疾病 + /// + /// + /// + public async Task GetIsExistDisease(ReadingCalculateDto inDto) + { + if (!inDto.IsBaseLine) + { + return string.Empty; + } + + var lesionCount = inDto.QuestionInfo.SelectMany(x => x.TableRowInfoList).Count(); + + return lesionCount > 0 ? ExistDisease.Yes.GetEnumInt() : ExistDisease.No.GetEnumInt(); + } + #endregion + + + #region 修改其他标准 + + #region 修改与整个检查批次期间SOD最低点相比增加的值(mm) + + /// + /// 修改与整个检查批次期间SOD最低点相比增加的值(mm) + /// + /// + /// + public async Task ChangeAllLowestIncrease(ChangeAllTaskDto inDto) + { + var visitTaskList = await GetVisitTaskAnswerList(inDto.calculateDto); + + var lowSod = (await GetLowSODVisit(inDto.calculateDto)).Select(x => x.SOD).OrderBy(x => x).FirstOrDefault(); + + foreach (var item in visitTaskList.Where(x => x.VisitTaskId != inDto.calculateDto.BaseLineTaskId)) + { + await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == item.VisitTaskId && x.ReadingQuestionTrialId == inDto.QuestionId, x => new ReadingTaskQuestionAnswer() + { + Answer = (item.SOD - lowSod).ToString() + }); + } + } + + #endregion + + + #region 修改整个检查批次期间SOD最低点相比增加的百分比 + + /// + /// 修改整个检查批次期间SOD最低点相比增加的百分比 + /// + /// + /// + //public async Task ChangeAllLowPercent(ChangeAllTaskDto inDto) + //{ + // var visitTaskList = await GetVisitTaskAnswerList(inDto.calculateDto); + + // var lowSod = (await GetLowSODVisit(inDto.calculateDto)).Select(x => x.SOD).OrderBy(x => x).FirstOrDefault(); + + // foreach (var item in visitTaskList.Where(x => x.VisitTaskId != inDto.calculateDto.BaseLineTaskId)) + // { + // decimal percent = 0; + // if (lowSod == 0) + // { + // percent = 100; + // } + // else + // { + // percent = decimal.Round((item.SOD - lowSod) * 100 / lowSod, 2); + // } + + // await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == item.VisitTaskId && x.ReadingQuestionTrialId == inDto.QuestionId, x => new ReadingTaskQuestionAnswer() + // { + // Answer = percent.ToString() + // }); + // } + //} + + #endregion + + #region 修改最低方式点名称 + /// + /// 修改最低方式点名称 + /// + /// + /// + public async Task ChangeAllLowVisitName(ChangeAllTaskDto inDto) + { + // 找到所有检查批次任务的Id + + var visitTaskIds = await _visitTaskRepository.Where(x => x.IsAnalysisCreate == inDto.IsAnalysisCreate && + x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == inDto.calculateDto.TrialReadingCriterionId && + x.TaskState == TaskState.Effect && x.ReadingTaskState == ReadingTaskState.HaveSigned && x.ArmEnum == inDto.calculateDto.ArmEnum).Select(x => x.Id).ToListAsync(); + + var answer = (await GetLowSODVisit(inDto.calculateDto)).OrderBy(x => x.SOD).Select(x => x.BlindName).FirstOrDefault(); + visitTaskIds = visitTaskIds.Where(x => x != inDto.calculateDto.BaseLineTaskId).ToList(); + await this.ChangeAllVisitTaskAnswer(visitTaskIds, inDto.QuestionId, answer); + + } + #endregion + + + #endregion + + #region 通用方法 + + #region 修改所有检查批次任务的答案 + /// + /// 修改所有检查批次任务的答案 + /// + /// + /// + /// + /// + private async Task ChangeAllVisitTaskAnswer(List visitTaskGuids, Guid questionId, string answer) + { + await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => visitTaskGuids.Contains(x.VisitTaskId) && x.ReadingQuestionTrialId == questionId, x => new ReadingTaskQuestionAnswer() + { + Answer = answer + }); + } + #endregion + + + #region 获取基线SOD + /// + /// 获取基线SOD + /// + /// + /// + private async Task GetBaseLineSOD(ReadingCalculateDto inDto) + { + if (await _visitTaskRepository.AnyAsync(x => x.Id == inDto.VisitTaskId && x.SourceSubjectVisit.IsBaseLine && x.IsAnalysisCreate == inDto.IsAnalysisCreate && x.ArmEnum == inDto.ArmEnum)) + { + return 0; + } + + // 先找到基线的任务 + var baseLineTaskId = await _visitTaskRepository.Where(x => x.SubjectId == inDto.SubjectId && x.ReadingCategory == ReadingCategory.Visit + && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId && + + x.SourceSubjectVisit.IsBaseLine && x.TaskState == TaskState.Effect && + x.IsAnalysisCreate == inDto.IsAnalysisCreate + && x.DoctorUserId == inDto.DoctorUserId + && x.IsSelfAnalysis == inDto.IsSelfAnalysis && x.ArmEnum == inDto.ArmEnum) + .Select(x => x.Id).FirstOrDefaultAsync(); + + + var baseLineSOD = (await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == baseLineTaskId && x.ReadingQuestionTrial.QuestionType == QuestionType.SOD).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + return baseLineSOD; + } + #endregion + + + /// + /// 获取最低方式 + /// + /// + /// + public async Task> GetLowSODVisit(ReadingCalculateDto inDto) + { + var taskAnswerList = await GetVisitTaskAnswerList(inDto); + + taskAnswerList = taskAnswerList.Where(x => x.VisitTaskId != inDto.VisitTaskId).ToList(); + + var taskIds = taskAnswerList.Select(x => x.VisitTaskId).ToList(); + + // 排除无法评估 + var unableEvaluateTaskIds = await _readingTableQuestionAnswerRepository.Where(x => taskIds.Contains(x.VisitTaskId) && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State + && x.ReadingQuestionTrial.LesionType == LesionType.TargetLesion + && x.Answer == TargetState.UnableEvaluate.GetEnumInt() + ) + .Select(x => x.VisitTaskId).Distinct().ToListAsync(); + + taskAnswerList = taskAnswerList.Where(x => !unableEvaluateTaskIds.Contains(x.VisitTaskId)).ToList(); + return taskAnswerList.OrderBy(x => x.SOD).ToList(); + } + + #region 获取检查批次任务信息 + /// + /// 获取检查批次任务信息 + /// + /// + /// + public async Task> GetVisitTaskAnswerList(ReadingCalculateDto inDto) + { + if (visitTaskAnswerList == null) + { + // 查询的时候要把自己排除 因为查询出来的可能不是计算出的最新的 + visitTaskAnswerList = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId != inDto.VisitTaskId && x.VisitTask.ReadingCategory == ReadingCategory.Visit + && x.VisitTask.IsAnalysisCreate == inDto.IsAnalysisCreate + && x.VisitTask.IsSelfAnalysis == inDto.IsSelfAnalysis + && x.VisitTask.VisitTaskNum < inDto.VisitTaskNum + && x.SubjectId == inDto.SubjectId && x.VisitTask.ReadingTaskState == ReadingTaskState.HaveSigned && x.VisitTask.ArmEnum == inDto.ArmEnum && x.VisitTask.TaskState == TaskState.Effect && x.ReadingQuestionTrial.QuestionType == QuestionType.SOD) + .Select(x => new VisitTaskAnswerInfo + { + VisitTaskId = x.VisitTaskId, + QuestionId = x.ReadingQuestionTrialId, + VisitName = x.VisitTask.SourceSubjectVisit.VisitName, + BlindName = x.VisitTask.TaskBlindName, + + SOD = x.Answer.IsNullOrEmptyReturn0(), + }).ToListAsync(); + + // 这里是需要加上自己的 基线不用管 + if (visitTaskAnswerList.Count > 0) + { + visitTaskAnswerList.Add(new VisitTaskAnswerInfo() + { + VisitTaskId = inDto.VisitTaskId, + BlindName = inDto.TaskBlindName, + QuestionId = visitTaskAnswerList[0].QuestionId, + VisitName = inDto.VisitName, + SOD = (await GetSODData(inDto)).ToString().IsNullOrEmptyReturn0(), + }); + } + + + } + + return visitTaskAnswerList; + } + #endregion + + /// + /// 获取上一个检查批次任务Id + /// + /// + private async Task GetLastVisitTaskId(ReadingCalculateDto inDto) + { + // 拿到这一个检查批次 + var thisNum = await _subjectVisitRepository.Where(x => x.Id == inDto.SubjectVisitId).Select(x => x.VisitNum).FirstOrDefaultAsync(); + + // 先找到上一个检查批次 + var lastVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == inDto.SubjectId && !x.IsLostVisit && x.VisitNum < thisNum).OrderByDescending(x => x.VisitNum).Select(x => x.Id).FirstOrDefaultAsync(); + + // 找到检查批次任务Id + + var LastVisitTaskId = await _visitTaskRepository.Where(x => x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == inDto.TrialReadingCriterionId && + x.TaskState == TaskState.Effect && + x.IsAnalysisCreate == inDto.IsAnalysisCreate + && x.SourceSubjectVisitId == lastVisitId && x.ArmEnum == inDto.ArmEnum).Select(x => x.Id).FirstOrDefaultAsync(); + + return LastVisitTaskId; + } + #endregion + + #region 计算阅片问题 外层问题 + + #region 获取靶病灶评估 + /// + /// 获取靶病灶评估 + /// + /// + /// + /// 靶病灶疗效评估算法-20230306确认版本: + ///if(基线没有靶病灶) + ///{ + /// 靶病灶疗效为 ND + ///}else + ///{ + /// 初始化靶病灶疗效为 SD + /// + /// if (与基线期SOD相比减小≥30 %) + /// { + /// 靶病灶疗效为 PR + /// + /// } + /// + ///if (非淋巴结靶病灶长径之和 == 0 并且所有淋巴结靶病灶的短径 < 10且淋巴结非靶病灶全部消失) + /// { + /// 靶病灶疗效为 CR + /// + /// } + /// + ///if (有被评估为NE的单个靶病灶) + /// { + /// 靶病灶疗效为 NE + /// + /// } + /// + ///if (最低点SOD > 0) + ///{ + /// if(比整体检查批次期间最低点SOD增加≥20 % 且与整个检查批次期间最低点相比增加的值≥5 mm) + /// { + /// 靶病灶疗效为 PD + /// + /// } + ///} + ///else + ///{ + /// //进入该分支最低点SOD=0 + /// if (当前检查批次SOD > 0 且与整个检查批次期间最低点相比增加的值≥5 mm) + /// { + /// 靶病灶疗效为PD + /// + /// } + ///} + /// + ///if(上次检查批次点评估是CR) + /// { + /// if (当前检查批次点淋巴结病灶,至少一个淋巴结靶病灶短径≥10 mm 并且该淋巴结靶病灶短径绝对增加值≥5 mm) + /// { + /// 靶病灶疗效为 PD + /// + /// } + /// if (当前检查批次点非淋巴结病灶至少一个非淋巴结靶病灶的长径>0 mm。) + /// { + /// 靶病灶疗效为 PD + /// + /// } + ///} + ///} + /// + /// + public async Task GetTargetLesionEvaluate(ReadingCalculateDto inDto) + { + var tableQuestion = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + if (inDto.IsBaseLine) + { + return TargetAssessment.NA.GetEnumInt(); + } + + TargetLesionCalculateDto resultData = new TargetLesionCalculateDto() + { + + + // 是否存在靶病灶 + ExistsTargetLesion = tableQuestion.Count() > 0, + + // 最低SOD + LowSod = (await GetLowSODVisit(inDto)).Select(x => x.SOD).FirstOrDefault(), + + // 当前Sod + PresentSod = (await GetSODData(inDto)) ?? 0, + + //非淋巴结靶病灶长径之和 decimal + SumOfDiameter = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SumOfDiameter).Sum(x => x.Answer.IsNullOrEmptyReturn0()), + + //所有淋巴结靶病灶的短径小于10mm bool + DiameterLessThan10 = true, + + // SOD变化百分比 + SODPercent = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SODPercent).Sum(x => x.Answer.IsNullOrEmptyReturn0()), + + // SOD 百分比与基线期SOD相比减小≥30% bool + SODPercentBigger30 = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SODPercent).Sum(x => x.Answer.IsNullOrEmptyReturn0()) <= -30, + + // SOD 百分比 与基线期SOD相比减小<30% bool + SODPercentLess30 = 0 > inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SODPercent).Sum(x => x.Answer.IsNullOrEmptyReturn0()) && + + inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SODPercent).Sum(x => x.Answer.IsNullOrEmptyReturn0()) > -30, + + // SOD 百分比 整体检查批次期间SOD最低点SOD相比增加<20% + LowSODPercentLess20 = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.LowPercent).Sum(x => x.Answer.IsNullOrEmptyReturn0()) < 20, + + // SOD 百分比 比整体检查批次期间SOD最低点SOD增加≥20% + LowSODPercentBigger20 = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.LowPercent).Sum(x => x.Answer.IsNullOrEmptyReturn0()) >= 20, + + // SOD 变化值 比整体检查批次期间SOD最低点SOD绝对增加值<5 mm + LowSODChangeLess5 = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.LowestIncrease).Sum(x => x.Answer.IsNullOrEmptyReturn0()) < 5, + + // 比整体检查批次期间SOD最低点SOD绝对增加值≥5 mm + LowSODChangeBigger5 = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.LowestIncrease).Sum(x => x.Answer.IsNullOrEmptyReturn0()) >= 5, + + // 被评估为NE的单个靶病灶 是否存在状态为不可评估的靶病灶 + ExixtsNETargetLesion = tableQuestion.SelectMany(x => x.TableQuestionList).Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(TargetState.UnableEvaluate)), + + //// 上次检查批次点整体肿瘤评估 + LastTargetLesionEvaluate = string.Empty, + + // 当前检查批次点非淋巴结病灶至少一个非淋巴结靶病灶的长径>0 mm + CurrentMajoreBigger0 = false, + + // 当前检查批次点淋巴结病灶, 至少一个淋巴结靶病灶短径≥10 mm + CurrenShortBigger10 = false, + + // 靶病灶短径增加值有5mm的Index + AddFiveIndexs = await GetIsAddFiveRowIndexs(inDto), + + // 短径有10mm的Index + ShortBigger10Indexs = new List(), + + //淋巴结非靶病灶状态全部为消失 + NonTargetStateIsLoss = true, + + // 该淋巴结靶病灶短径绝对增加值≥5 mm + IsAddFive = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.IsAddFive && x.Answer.EqEnum(YesOrNoOrNa.Yes)).Count() > 0, + }; + + var nonTargetTableQuestion = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.NonTargetLesions).SelectMany(x => x.TableRowInfoList).ToList(); + + foreach (var item in nonTargetTableQuestion) + { + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.Yes))) + { + // 淋巴结非靶病灶状态全部为消失 + resultData.NonTargetStateIsLoss = resultData.NonTargetStateIsLoss && item.TableQuestionList.Where(x => x.QuestionMark == QuestionMark.State).Select(x => x.Answer).FirstOrDefault() == NoTargetState.Loss.GetEnumInt(); + } + } + + + foreach (var item in tableQuestion) + { + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.Yes))) + { + // 淋巴结的短径 + resultData.DiameterLessThan10 = resultData.DiameterLessThan10 && (item.TableQuestionList.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer).FirstOrDefault()).IsNullOrEmptyReturn0() < 10; + + var shortIsBigger10 = (item.TableQuestionList.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer).FirstOrDefault()).IsNullOrEmptyReturn0() >= 10; + resultData.CurrenShortBigger10 = resultData.CurrenShortBigger10 || shortIsBigger10; + + if (shortIsBigger10) + { + resultData.ShortBigger10Indexs.Add(item.RowIndex); + } + + + } + + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && !x.Answer.EqEnum(YesOrNoOrNa.Yes))) + { + // 当前检查批次点非淋巴结病灶至少一个非淋巴结靶病灶的长径 + resultData.CurrentMajoreBigger0 = resultData.CurrentMajoreBigger0 || (item.TableQuestionList.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Select(x => x.Answer).FirstOrDefault()).IsNullOrEmptyReturn0() > 0; + } + } + + + var lastVisitTaskId = await GetLastVisitTaskId(inDto); + var questionId = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.Tumor).Select(x => x.QuestionId).FirstOrDefault(); + resultData.LastTargetLesionEvaluate = (await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == lastVisitTaskId && x.ReadingQuestionTrialId == questionId) + .Select(x => x.Answer).FirstOrDefaultAsync()) ?? string.Empty; + + TargetAssessment result = TargetAssessment.SD; + //if(基线没有靶病灶) + if (!resultData.ExistsTargetLesion) + { + // 靶病灶疗效为 ND + result = TargetAssessment.ND; + } + else + { + //初始化靶病灶疗效为 SD + result = TargetAssessment.SD; + + //if (与基线期SOD相比减小≥30 %) + if (resultData.SODPercentBigger30) + { + //靶病灶疗效为 PR + result = TargetAssessment.PR; + } + //if (非淋巴结靶病灶长径之和 == 0 并且所有淋巴结靶病灶的短径 < 10且淋巴结非靶病灶全部消失) + if (resultData.SumOfDiameter == 0 && resultData.DiameterLessThan10 && resultData.NonTargetStateIsLoss) + { + //靶病灶疗效为 CR + result = TargetAssessment.CR; + } + // if (有被评估为NE的单个靶病灶) + if (resultData.ExixtsNETargetLesion) + { + // 靶病灶疗效为 NE + result = TargetAssessment.NE; + } + //if (最低点SOD > 0) + if (resultData.LowSod > 0) + { + // if(比整体检查批次期间最低点SOD增加≥20 % 且与整个检查批次期间最低点相比增加的值≥5 mm) + if (resultData.LowSODPercentBigger20 && resultData.LowSODChangeBigger5) + { + // 靶病灶疗效为 PD + result = TargetAssessment.PD; + } + } + else + { + //进入该分支最低点SOD=0 + // if (当前检查批次SOD > 0 且与整个检查批次期间最低点相比增加的值≥5 mm) + if (resultData.PresentSod > 0 && resultData.LowSODChangeBigger5) + { + // 靶病灶疗效为PD + result = TargetAssessment.PD; + } + } + //if(上次检查批次点评估是CR) + if (resultData.LastTargetLesionEvaluate.EqEnum(TargetAssessment.CR)) + { + //if (当前检查批次点淋巴结病灶,至少一个淋巴结靶病灶短径≥10 mm 并且该淋巴结靶病灶短径绝对增加值≥5 mm) + // 换句话说 就是 短径≥10 的病灶 和 短径增加值≥5的病灶 的交集数量大于0 + if (resultData.ShortBigger10Indexs.Intersect(resultData.AddFiveIndexs).ToList().Count() > 0) + { + //靶病灶疗效为 PD + result = TargetAssessment.PD; + } + //if (当前检查批次点非淋巴结病灶至少一个非淋巴结靶病灶的长径>0 mm。) + if (resultData.CurrentMajoreBigger0) + { + //靶病灶疗效为 PD + result = TargetAssessment.PD; + } + } + + + + #region 之前的逻辑 备注 + //if (resultData.LastTargetLesionEvaluate.EqEnum(TargetAssessment.CR)) + //{ + // if (resultData.ExixtsNETargetLesion) + // { + // result = TargetAssessment.NE; + // } + // if (resultData.CurrenShortBigger10 && resultData.IsAddFive) + // { + // result = TargetAssessment.PD; + // } + // if (resultData.CurrentMajoreBigger0) + // { + // result = TargetAssessment.PD; + // } + //} + + //else + //{ + // if (resultData.LowSod != 0 && resultData.LowPercentBigger20 && resultData.LowChangeBigger5) + // { + // result = TargetAssessment.PD; + // } + // else + // { + // if (resultData.ExixtsNETargetLesion) + // { + // result = TargetAssessment.NE; + // } + // else + // { + // if (resultData.SODPercentBigger30) + // { + // if (resultData.LowPercentLess20 || resultData.LowChangeLess5) + // { + // result = TargetAssessment.PR; + // } + + // if (resultData.SumOfDiameter == 0 && resultData.DiameterLessThan10 && resultData.NonTargetStateIsLoss) + // { + // result = TargetAssessment.CR; + // } + // } + // } + // } + + + //} + #endregion + + + } + + return result.GetEnumInt(); + } + #endregion + + #region 获取非靶病灶评估 + + /// + /// 获取非靶病灶评估 + /// + /// + /// + public async Task GetNoTargetLesionEvaluate(ReadingCalculateDto inDto) + { + + NoTargetAssessment result = NoTargetAssessment.NN; + + if (inDto.IsBaseLine) + { + return NoTargetAssessment.NA.GetEnumInt(); + } + + var tableRows = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.NonTargetLesions).SelectMany(x => x.TableRowInfoList).ToList(); + + var tableQuestions = tableRows.SelectMany(x => x.TableQuestionList).ToList(); + + //任意单个病灶 / 病灶组评估为“显著增大” + if (tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NoTargetState.Increase))) + { + result = NoTargetAssessment.PD; + } + // 任意单个病灶/病灶组评估为“无法评估”并且没有“显著增大” + else if (tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NoTargetState.UnableEvaluate)) && + !tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NoTargetState.Increase)) + ) + { + result = NoTargetAssessment.NE; + } + // 所有单个病灶/病灶组评估为”存在”或者有些评估为“消失”有些评估为“存在”,且没有“显著增大”和“无法评估”的病灶 + else if (tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NoTargetState.Exist)) + && !tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && (x.Answer.EqEnum(NoTargetState.Increase) || x.Answer.EqEnum(NoTargetState.UnableEvaluate))) + + ) + { + result = NoTargetAssessment.NN; + } + //所有单个病灶/病灶组状态评估状态为“消失” + else if (tableQuestions.Count(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NoTargetState.Loss)) == tableQuestions.Count(x => x.QuestionMark == QuestionMark.State) && tableQuestions.Count(x => x.QuestionMark == QuestionMark.State) > 0) + { + result = NoTargetAssessment.CR; + } + + // 基线时没有非靶病灶 + else if (tableQuestions.Count() == 0) + { + result = NoTargetAssessment.ND; + } + else + { + return string.Empty; + } + + return result.GetEnumInt(); + + } + + #endregion + + #region 获取新病灶评估 + /// + /// 获取新病灶评估 + /// + /// + /// + public async Task GetNewLesionEvaluate(ReadingCalculateDto inDto) + { + + NewLesionAssessment result = NewLesionAssessment.No; + if (inDto.IsBaseLine) + { + return NewLesionAssessment.NA.GetEnumInt(); + } + + var tableRows = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.NewLesions).SelectMany(x => x.TableRowInfoList).ToList(); + + var tableQuestions = tableRows.SelectMany(x => x.TableQuestionList).ToList(); + + + + // 当前检查批次存在至少一个明确新病灶 + if (tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NewLesionState.Exist))) + { + result = NewLesionAssessment.Yes; + } + //只要有任何一个新病灶状态为“无法评估” + else if (tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NewLesionState.UnableEvaluate))) + { + result = NewLesionAssessment.NE; + } + //当前检查批次不存在明确新病灶且存在至少一个疑似新病灶 + else if (!tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NewLesionState.Exist)) && + tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NewLesionState.Suspected)) + ) + { + result = NewLesionAssessment.Suspected; + } + + + else + { + result = NewLesionAssessment.No; + } + return result.GetEnumInt(); + + } + #endregion + + #endregion + + } + +} diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/ReadingCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/ReadingCalculateService.cs new file mode 100644 index 0000000..9a9311f --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/ReadingCalculateService.cs @@ -0,0 +1,231 @@ +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using Microsoft.AspNetCore.Mvc; + +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Application.ViewModel; + +namespace IRaCIS.Core.Application.Service.ReadingCalculate +{ + [ApiExplorerSettings(GroupName = "Image")] + public class ReadingCalculateService: BaseService, IReadingCalculateService + { + + /// + /// 标准和服务对应 + /// + Dictionary CalculateServiceDic = new Dictionary() + { + {CriterionType.RECIST1Pointt1,typeof(RECIST1Point1CalculateService) }, //RECIST1.1 + {CriterionType.PCWG3,typeof(PCWG3CalculateService) }, + {CriterionType.SelfDefine,typeof(SelfDefineCalculateService) } + }; + + + + private readonly IEnumerable _criterionServices; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private ICriterionCalculateService _useCriterion; + + + public ReadingCalculateService(IEnumerable criterionServices, + IRepository visitTaskRepository, + IRepository readingQuestionCriterionTrialRepository + + ) + { + + _criterionServices = criterionServices; + this._visitTaskRepository = visitTaskRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + } + + /// + /// 获取Service + /// + /// + /// + public async Task GetService(Guid visitTaskId) + { + if (_useCriterion == null) + { + var criterionId = await _visitTaskRepository.Where(x => x.Id == visitTaskId).Select(x => x.TrialReadingCriterionId).FirstNotNullAsync(); + + var criterionType = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == criterionId).Select(x => x.CriterionType).FirstOrDefaultAsync(); + + if (criterionType == null) + { + throw new BusinessValidationFailedException(_localizer["ReadingCalculate_NoDeveloped"]); + } + + try + { + CriterionType thisCriterionType = criterionType; + Type thisServiceType = CalculateServiceDic[thisCriterionType]; + _useCriterion = _criterionServices.FirstOrDefault(x => x.GetType().Name == thisServiceType.Name + "Proxy"); + + + + } + catch (Exception) + { + + _useCriterion = null; + } + + return _useCriterion; + + + } + else + { + return _useCriterion; + } + } + + /// + /// 自动计算 并修改值 + /// + /// + /// + public async Task CalculateTask(CalculateTaskInDto inDto) + { + + var service = await this.GetService(inDto.VisitTaskId); + if (service != null) + { + await service.CalculateTask(inDto); + } + + } + + /// + /// 验证检查批次提交 + /// + /// + /// + public async Task VerifyVisitTaskQuestions(VerifyVisitTaskQuestionsInDto inDto) + { + var service = await this.GetService(inDto.VisitTaskId); + if (service != null) + { + await service.VerifyVisitTaskQuestions(inDto); + } + + } + + /// + /// 将上一次的检查批次病灶添加到这一次 + /// + /// + /// + public async Task AddTaskLesionAnswerFromLastTask(AddTaskLesionAnswerFromLastTaskInDto inDto) + { + var service = await this.GetService(inDto.VisitTaskId); + var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + if (service != null&& visitTaskInfo.SourceSubjectVisitId!=null) + { + var readingTaskState = visitTaskInfo.ReadingTaskState; + var result = new AddTaskLesionAnswerFromLastTaskOutDto(); + + if (readingTaskState == ReadingTaskState.WaitReading) + { + result= await service.AddTaskLesionAnswerFromLastTask(inDto); + await service.CalculateTask(new CalculateTaskInDto() + { + IsChangeOtherTask = false, + VisitTaskId = inDto.VisitTaskId, + }); + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x=>x.Id==inDto.VisitTaskId, x => new VisitTask() + { + ReadingTaskState = ReadingTaskState.Reading, + + }); + + } + + return result; + } + else + { + return new AddTaskLesionAnswerFromLastTaskOutDto(); + } + + } + + /// + /// 获取报告验证的信息(这里每个标准可能不一样 返回用object) + /// + /// + /// + public async Task GetReportVerify(GetReportVerifyInDto inDto) + { + + var service = await this.GetService(inDto.VisitTaskId); + + if (service != null) + { + return await service.GetReportVerify(inDto); + } + else + { + return new { }; + } + + } + + /// + /// 获取阅片报告 + /// + /// + /// + public async Task GetReadingReportEvaluation(GetReadingReportEvaluationInDto inDto) + { + var service = await this.GetService(inDto.VisitTaskId); + + if (service != null) + { + + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + if (taskInfo.ReadingTaskState != ReadingTaskState.HaveSigned&&inDto.IsCalculate) + { + await service.CalculateTask(new CalculateTaskInDto() + { + + VisitTaskId = inDto.VisitTaskId + }); + } + + return await service.GetReadingReportEvaluation(inDto); + } + else + { + return new GetReadingReportEvaluationOutDto(); + } + } + + + /// + /// 删除病灶获取起始病灶序号 + /// + /// + public async Task GetDeleteLesionStatrIndex(DeleteReadingRowAnswerInDto inDto) + { + var service = await this.GetService(inDto.VisitTaskId); + + if (service != null) + { + return await service.GetDeleteLesionStatrIndex(inDto); + } + else + { + return 1; + } + } + } + + + +} diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/SelfDefineCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/SelfDefineCalculateService.cs new file mode 100644 index 0000000..f8c2809 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/SelfDefineCalculateService.cs @@ -0,0 +1,521 @@ +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using Panda.DynamicWebApi.Attributes; +using IRaCIS.Core.Infra.EFCore.Common; +using Microsoft.Extensions.Caching.Memory; + +using IRaCIS.Core.Infrastructure; +using MassTransit; + +namespace IRaCIS.Core.Application.Service.ReadingCalculate +{ + [ApiExplorerSettings(GroupName = "Reading")] + public class SelfDefineCalculateService : BaseService, ICriterionCalculateService + { + private readonly IRepository _readingTableQuestionAnswerRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _readingTableQuestionTrialRepository; + private readonly IRepository _readingTableAnswerRowInfoRepository; + private readonly IRepository _readingQuestionTrialRepository; + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _organInfoRepository; + private readonly IRepository _dicomStudyRepository; + private readonly IRepository _noneDicomStudyRepository; + private readonly IRepository _tumorAssessmentRepository; + private readonly IGeneralCalculateService _generalCalculateService; + private readonly IRepository _readingTaskQuestionAnswerRepository; + + public SelfDefineCalculateService( + IRepository readingTableQuestionAnswerRepository, + IRepository visitTaskRepository, + IRepository readingQuestionCriterionTrialRepository, + IRepository readingTableQuestionTrialRepository, + IRepository readingTableAnswerRowInfoRepository, + IRepository readingQuestionTrialRepository, + IRepository subjectVisitRepository, + IRepository organInfoRepository, + IRepository dicomStudyRepository, + IRepository noneDicomStudyRepository, + IRepository tumorAssessmentRepository, + IGeneralCalculateService generalCalculateService, + IRepository readingTaskQuestionAnswerRepository + ) + { + this._readingTableQuestionAnswerRepository = readingTableQuestionAnswerRepository; + this._visitTaskRepository = visitTaskRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this._readingTableQuestionTrialRepository = readingTableQuestionTrialRepository; + this._readingTableAnswerRowInfoRepository = readingTableAnswerRowInfoRepository; + this._readingQuestionTrialRepository = readingQuestionTrialRepository; + this._subjectVisitRepository = subjectVisitRepository; + this._organInfoRepository = organInfoRepository; + this._dicomStudyRepository = dicomStudyRepository; + this._noneDicomStudyRepository = noneDicomStudyRepository; + this._tumorAssessmentRepository = tumorAssessmentRepository; + this._generalCalculateService = generalCalculateService; + this._readingTaskQuestionAnswerRepository = readingTaskQuestionAnswerRepository; + } + + private List siteVisitForTumorList = null; + + + #region 删除病灶获取起始病灶序号 + /// + /// 删除病灶获取起始病灶序号 + /// + /// + public async Task GetDeleteLesionStatrIndex(DeleteReadingRowAnswerInDto inDto) + { + return 1; + + + } + #endregion + + #region 获取阅片报告 + /// + /// 获取阅片报告 + /// + /// + /// + [HttpPost] + public async Task GetReadingReportEvaluation(GetReadingReportEvaluationInDto indto) + { + GetReadingReportEvaluationOutDto result = new GetReadingReportEvaluationOutDto(); + + result.CalculateResult = await this.GetReportVerify(new GetReportVerifyInDto() + { + VisitTaskId = indto.VisitTaskId + }); + + var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == indto.VisitTaskId).FirstNotNullAsync(); + + + result.ReadingTaskState = visitTaskInfo.ReadingTaskState; + var taskInfoList = await _generalCalculateService.GetReadingReportTaskList(indto.VisitTaskId); + + result.VisitTaskList = taskInfoList; + + var visitTaskIds = taskInfoList.Select(x => x.VisitTaskId).ToList(); + + var criterionId = visitTaskInfo.TrialReadingCriterionId; + var questionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == criterionId).ToListAsync(); + var tableQuestionList = await _readingTableQuestionTrialRepository.Where(x => x.TrialCriterionId == criterionId).OrderBy(x => x.ShowOrder).ToListAsync(); + + var lesionsIndexs = await _readingTableAnswerRowInfoRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).GroupBy(x => new { x.QuestionId }).Select(x => new lesionsIndexDto() + { + QuestionId = x.Key.QuestionId, + Rowindexs = x.Select(x => x.RowIndex).Distinct().OrderBy(x => x).ToList() + + }).ToListAsync(); + + var tableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == indto.VisitTaskId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var answers = await _readingTaskQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync(); + var tableAnswers = await _readingTableQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync(); + + var alltableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync(); + var organIds = alltableAnsweRowInfos.Where(x => x.OrganInfoId != null).Select(x => x.OrganInfoId).Distinct().ToList(); + var organInfos = await _organInfoRepository.Where(x => organIds.Contains(x.Id)).ToListAsync(); + + var needChangeType = new List() { + QuestionMark.Organ, + QuestionMark.Location, + QuestionMark.Part, + }; + + // 第一级 + + #region 构造问题 + List questions = questionList.Where(x => x.Type == ReadingQestionType.Group).OrderBy(x => x.ShowOrder).Select(x => new ReadingReportDto() + { + QuestionId = x.Id, + GroupName = x.GroupName, + IsShowInDicom = x.IsShowInDicom, + Type = x.Type, + GroupId=x.GroupId, + GroupEnName=x.GroupEnName, + QuestionType = x.QuestionType, + DataSource= x.DataSource, + LesionType = x.LesionType, + QuestionGenre = x.QuestionGenre, + DictionaryCode = x.DictionaryCode, + LimitEdit= x.LimitEdit, + MaxAnswerLength=x.MaxAnswerLength, + FileType=x.FileType, + TypeValue = x.TypeValue, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + ShowOrder = x.ShowOrder, + ValueType = x.ValueType, + Unit = x.Unit, + CustomUnit=x.CustomUnit, + ReportLayType = ReportLayType.Group, + }).ToList(); + + // 分组 + foreach (var item in questions) + { + item.Childrens = questionList.Where(x => x.GroupId==item.QuestionId).OrderBy(x => x.ShowOrder).Select(x => new ReadingReportDto() + { + GroupName = x.GroupName, + QuestionId = x.Id, + IsShowInDicom = x.IsShowInDicom, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + LesionType = x.LesionType, + DataSource = x.DataSource, + QuestionGenre = x.QuestionGenre, + GroupEnName=x.GroupEnName, + LimitEdit = x.LimitEdit, + MaxAnswerLength=x.MaxAnswerLength, + FileType=x.FileType, + DictionaryCode = x.DictionaryCode, + Type = x.Type, + QuestionType = x.QuestionType, + TypeValue = x.TypeValue, + ShowOrder = x.ShowOrder, + OrderMark = x.OrderMark, + ValueType = x.ValueType, + Unit = x.Unit, + CustomUnit=x.CustomUnit, + ReportLayType = ReportLayType.Question, + }).ToList(); + + // 问题 + foreach (var question in item.Childrens) + { + + foreach (var task in taskInfoList) + { + + var answer = answers.Where(x => x.VisitTaskId == task.VisitTaskId && x.ReadingQuestionTrialId == question.QuestionId).FirstOrDefault(); + question.Answer.Add(new TaskQuestionAnswer() + { + Answer = answer == null ? string.Empty : answer.Answer, + IsGlobalChange = answer == null ? false : answer.IsGlobalChange, + GlobalChangeAnswer = answer == null ? string.Empty : answer.GlobalChangeAnswer, + TaskName = task.TaskName, + VisitTaskId = task.VisitTaskId, + + }); + } + + // 构造表格行数据 + + + var rowlist = tableAnsweRowInfos.Where(x => x.QuestionId == question.QuestionId).OrderBy(x => x.RowIndex).ToList(); + + question.Childrens = new List(); + + var rowoindexs = lesionsIndexs.Where(x => x.QuestionId == question.QuestionId).Select(x => x.Rowindexs.OrderBy(y => y).ToList()).FirstOrDefault(); + rowoindexs = rowoindexs == null ? new List() : rowoindexs; + foreach (var rowoindex in rowoindexs) + { + var rowinfo = rowlist.Where(x => x.RowIndex == rowoindex).FirstOrDefault(); + question.Childrens.Add(new ReadingReportDto() + { + QuestionName = question.OrderMark + rowoindex.GetLesionMark(), + RowId = rowinfo?.Id, + IsShowInDicom = question.IsShowInDicom, + SplitOrMergeLesionName = rowinfo!=null? (rowinfo.MergeName.IsNullOrEmpty() ? rowinfo.SplitName : rowinfo.MergeName):string.Empty, + SplitOrMergeType = rowinfo != null ? (rowinfo.SplitOrMergeType):null, + LesionType = question.LesionType, + IsCanEditPosition = rowinfo != null ? (rowinfo.IsCanEditPosition):false, + RowIndex = rowoindex, + BlindName = rowinfo != null ? rowinfo.BlindName : string.Empty, + ReportLayType = ReportLayType.Lesions, + }); + } + + + + + foreach (var row in question.Childrens) + { + // tableQuestion + row.Childrens = tableQuestionList.Where(x => x.ReadingQuestionId == question.QuestionId).Select(x => new ReadingReportDto() + { + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + QuestionId = x.ReadingQuestionId, + TableQuestionId = x.Id, + Type = x.Type, + IsShowInDicom = question.IsShowInDicom, + DataSource = x.DataSource, + LimitEdit = x.LimitEdit, + MaxAnswerLength=x.MaxAnswerLength, + FileType=x.FileType, + LesionType = question.LesionType, + TableQuestionType = x.TableQuestionType, + DictionaryCode = x.DictionaryCode, + QuestionMark = x.QuestionMark, + TypeValue = x.TypeValue, + RowIndex = row.RowIndex, + RowId=row.RowId, + ShowOrder = x.ShowOrder, + ValueType = x.ValueType, + CustomUnit=x.CustomUnit, + Unit = x.Unit, + ReportLayType = ReportLayType.TableQuestion, + }).ToList(); + + + foreach (var tableQuestion in row.Childrens) + { + foreach (var task in taskInfoList) + { + var rowinfo = alltableAnsweRowInfos.Where(x => x.VisitTaskId == task.VisitTaskId && x.QuestionId == tableQuestion.QuestionId && x.RowIndex == tableQuestion.RowIndex).FirstOrDefault(); + var taskQuestionAnswer = new TaskQuestionAnswer() + { + Answer = tableAnswers.Where(x => x.VisitTaskId == task.VisitTaskId && x.QuestionId == tableQuestion.QuestionId && x.RowIndex == tableQuestion.RowIndex && x.TableQuestionId == tableQuestion.TableQuestionId).Select(x => x.Answer).FirstIsNullReturnEmpty(), + TaskName = task.TaskName, + VisitTaskId = task.VisitTaskId, + }; + if (rowinfo != null && rowinfo.OrganInfoId != null) + { + var organInfo = organInfos.Where(x => x.Id == rowinfo.OrganInfoId).FirstOrDefault(); + + + if (organInfo != null && needChangeType.Contains(tableQuestion.QuestionMark)) + { + if (_userInfo.IsEn_Us) + { + switch (tableQuestion.QuestionMark) + { + case QuestionMark.Organ: + taskQuestionAnswer.Answer = organInfo.TULOCEN; + + break; + case QuestionMark.Location: + if (organInfo.IsCanEditPosition) + { + + } + else + { + taskQuestionAnswer.Answer = organInfo.TULATEN; + + } + break; + case QuestionMark.Part: + + taskQuestionAnswer.Answer = organInfo.PartEN; + + break; + + } + + } + else + { + switch (tableQuestion.QuestionMark) + { + case QuestionMark.Organ: + taskQuestionAnswer.Answer = organInfo.TULOC; + break; + case QuestionMark.Location: + if (organInfo.IsCanEditPosition) + { + + } + else + { + taskQuestionAnswer.Answer = organInfo.TULAT; + + } + break; + case QuestionMark.Part: + taskQuestionAnswer.Answer = organInfo.Part; + break; + + } + } + + } + } + tableQuestion.Answer.Add(taskQuestionAnswer); + } + } + + + } + + + }; + } + #endregion + + + + result.TaskQuestions = questions; + + + + return result; + + + } + #endregion + + /// + /// 将上一次的检查批次病灶添加到这一次 + /// + /// + /// + public async Task AddTaskLesionAnswerFromLastTask(AddTaskLesionAnswerFromLastTaskInDto inDto) + { + var visitTaskId = inDto.VisitTaskId; + var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + var isReadingTaskViewInOrder =await _readingQuestionCriterionTrialRepository.Where(x => x.Id == taskinfo.TrialReadingCriterionId).Select(x => x.IsReadingTaskViewInOrder).FirstOrDefaultAsync(); + var baseLineVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == taskinfo.SubjectId && x.IsBaseLine).Select(x => x.Id).FirstOrDefaultAsync(); + + // 判断当前任务是否是基线 + if (taskinfo.SourceSubjectVisitId != baseLineVisitId&& isReadingTaskViewInOrder) + { + // 判断当前任务是是否有表格问题答案 + if (!(await _readingTableQuestionAnswerRepository.AnyAsync(x => x.VisitTaskId == visitTaskId))) + { + + var LastVisitTaskId = await _visitTaskRepository.Where(x => x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == taskinfo.TrialReadingCriterionId && + x.IsAnalysisCreate == taskinfo.IsAnalysisCreate && + x.DoctorUserId == taskinfo.DoctorUserId && + x.IsSelfAnalysis == taskinfo.IsSelfAnalysis && + x.SubjectId == taskinfo.SubjectId && x.ReadingTaskState == ReadingTaskState.HaveSigned && x.VisitTaskNum < taskinfo.VisitTaskNum && x.TaskState == TaskState.Effect && x.ArmEnum == taskinfo.ArmEnum + ).OrderByDescending(x => x.VisitTaskNum).Select(x => x.Id).FirstOrDefaultAsync(); + + + + var copyTableAnswers = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == LastVisitTaskId&&x.ReadingQuestionTrial.IsCopyLesions).Select(x => new CopyTableAnswerDto() + { + Answer = x.Answer, + QuestionId = x.QuestionId, + RowId = x.RowId, + QuestionMark = x.ReadingTableQuestionTrial.QuestionMark, + TableQuestionId = x.TableQuestionId, + RowIndex = x.RowIndex, + IsCopy=x.ReadingTableQuestionTrial.IsCopy, + TrialId = x.TrialId, + }).ToListAsync(); + + var tableRowAnswers = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == LastVisitTaskId&&x.ReadingQuestionTrial.IsCopyLesions).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + tableRowAnswers.ForEach(x => + { + x.VisitTaskId = visitTaskId; + x.IsCurrentTaskAdd = false; + x.Id = NewId.NextGuid(); + x.SeriesId = null; + x.InstanceId = null; + x.MeasureData = string.Empty; + x.PicturePath = string.Empty; + }); + + tableRowAnswers.ForEach(x => + { + x.SplitRowId = tableRowAnswers.Where(y => y.OriginalId == x.SplitRowId).Select(y => y.Id).FirstOrDefault(); + x.MergeRowId = tableRowAnswers.Where(y => y.OriginalId == x.MergeRowId).Select(y => y.Id).FirstOrDefault(); + + }); + + + + var tableAnswers = copyTableAnswers.Select(x => new ReadingTableQuestionAnswer + { + Id = NewId.NextGuid(), + Answer = x.IsCopy ? x.Answer:string.Empty , + QuestionId = x.QuestionId, + RowIndex = x.RowIndex, + RowId = tableRowAnswers.Where(y => y.OriginalId == x.RowId).Select(x => x.Id).FirstOrDefault(), + TableQuestionId = x.TableQuestionId, + TrialId = x.TrialId, + VisitTaskId = visitTaskId, + }); + + + + var addList = _mapper.Map>(tableRowAnswers); + + await _readingTableAnswerRowInfoRepository.AddRangeAsync(addList); + await _readingTableQuestionAnswerRepository.AddRangeAsync(tableAnswers); + await _readingTableQuestionAnswerRepository.SaveChangesAsync(); + + } + } + + return new AddTaskLesionAnswerFromLastTaskOutDto() + { + + IsBaseLine = taskinfo.SourceSubjectVisitId == baseLineVisitId, + }; + + } + + /// + /// 测试计算 + /// + /// + /// + /// + [HttpPost] + public async Task TestCalculate(Guid visitTaskId, QuestionType type) + { + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(visitTaskId); + await ReadingCalculate(readingData, new List() { type }); + } + + /// + /// 计算任务 + /// + /// + /// + [HttpPost] + public async Task CalculateTask(CalculateTaskInDto inDto) + { + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(inDto.VisitTaskId); + readingData.IsChangeOtherTask = inDto.IsChangeOtherTask; + await ReadingCalculate(readingData); + } + + + /// + /// 自动计算 + /// + /// + /// + /// + public async Task ReadingCalculate(ReadingCalculateDto inDto, List calculateType = null) + { + //var questionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == inDto.CriterionId && x.CustomCalculateMark != null).ToListAsync(); + //var tableQuestionList = await _readingTableQuestionTrialRepository.Where(x => x.TrialCriterionId == inDto.CriterionId && x.CustomCalculateMark != null).ToListAsync(); + + + + + + } + + + + + + + + public async Task GetReportVerify(GetReportVerifyInDto inDto) + { + return new() { + + }; + } + + public async Task VerifyVisitTaskQuestions(VerifyVisitTaskQuestionsInDto inDto) + { + + } + } +} diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/DTO/TrialSiteEquipmentSurveyViewModel.cs b/IRaCIS.Core.Application/Service/SiteSurvey/DTO/TrialSiteEquipmentSurveyViewModel.cs new file mode 100644 index 0000000..e503d80 --- /dev/null +++ b/IRaCIS.Core.Application/Service/SiteSurvey/DTO/TrialSiteEquipmentSurveyViewModel.cs @@ -0,0 +1,64 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:21:04 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +namespace IRaCIS.Core.Application.Contracts +{ + /// TrialSiteEquipmentSurveyView 列表视图模型 + public class TrialSiteEquipmentSurveyView + { + public Guid Id { get; set; } + public Guid TrialSiteSurveyId { get; set; } + public string EquipmentType { get; set; } = string.Empty; + public Guid? EquipmentTypeId { get; set; } + + public string Parameters { get; set; } = string.Empty; + public string ManufacturerName { get; set; } = string.Empty; + public string ScannerType { get; set; } = string.Empty; + public string Note { get; set; } = string.Empty; + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + } + + ///TrialSiteEquipmentSurveyQuery 列表查询参数模型 + public class TrialSiteEquipmentSurveyQuery + { + + public Guid TrialSiteSurveyId { get; set; } + + public string ScannerType { get; set; } = string.Empty; + + ///// Parameters + //public string Parameters { get; set; } + + ///// ManufacturerName + //public string ManufacturerName { get; set; } + + ///// ScannerType + //public string ScannerType { get; set; } + + ///// Note + //public string Note { get; set; } + + } + + /// TrialSiteEquipmentSurveyAddOrEdit 列表查询参数模型 + public class TrialSiteEquipmentSurveyAddOrEdit + { + public Guid? Id { get; set; } + public Guid TrialSiteSurveyId { get; set; } + + public Guid? EquipmentTypeId { get; set; } + public string Parameters { get; set; } = string.Empty; + public string ManufacturerName { get; set; } = string.Empty; + public string ScannerType { get; set; } = string.Empty; + public string Note { get; set; } = string.Empty; + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/DTO/TrialSiteSurveyViewModel.cs b/IRaCIS.Core.Application/Service/SiteSurvey/DTO/TrialSiteSurveyViewModel.cs new file mode 100644 index 0000000..c6cecba --- /dev/null +++ b/IRaCIS.Core.Application/Service/SiteSurvey/DTO/TrialSiteSurveyViewModel.cs @@ -0,0 +1,349 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:21:04 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using System.ComponentModel.DataAnnotations; +using IRaCIS.Core.Domain.Share; +namespace IRaCIS.Core.Application.Contracts +{ + + public class TrialSurveyInitInfo + { + public Guid TrialId { get; set; } + public string Sponsor { get; set; } = string.Empty; + + //研究方案号 + public string ResearchProgramNo { get; set; } = string.Empty; + + //实验名称 + public string ExperimentName { get; set; } = string.Empty; + + public string TrialCode { get; set; } = string.Empty; + + public Guid IndicationTypeId { get; set; } + + public string IndicationType { get; set; } = string.Empty; + + public string TrialSiteSurveyUserRoles { get; set; } = string.Empty; + + public string TrialSiteSurveyEquipmentType { get; set; } = string.Empty; + + public List TrialSiteSelectList { get; set; } = new List(); + + } + + public class TrialSiteForSelect + { + public Guid Id { get; set; } + public Guid TrialId { get; set; } + + public Guid SiteId { get; set; } + + public string TrialSiteCode { get; set; } = string.Empty; + + public string TrialSiteAliasName { get; set; } = string.Empty; + + + public bool IsHaveSiteSurveyRecord { get; set; } + } + + + public class TrialSiteSimpleSelect + { + + public string TrialSiteCode { get; set; } = string.Empty; + + } + + public class LoginReturnDTO + { + public TrialSurveyInitInfo TrialInfo { get; set; } = new TrialSurveyInitInfo(); + + public TrialSiteSurveyView TrialSiteSurvey { get; set; } = new TrialSiteSurveyView(); + + public List TrialSiteEquipmentSurveyList { get; set; } = new List(); + + public List TrialSiteUserSurveyList { get; set; } = new List(); + } + + public class TrialSiteUserSurveyAllDTO : TrialSiteUserSurveyView + { + public TrialSiteSurveyView TrialSiteSurvey { get; set; } + } + + + public class TrialSiteUserSurveyExportQueryDto + { + [NotDefault] + public Guid TrialId { get; set; } + public Guid? SiteId { get; set; } + + public Guid? UserTypeId { get; set; } + + + public bool? IsGenerateAccount { get; set; } + public Guid? TrialRoleNameId { get; set; } + + public TrialSiteUserStateEnum? State { get; set; } + + public string FormWriterKeyInfo { get; set; } = string.Empty; + + + public string UserName { get; set; } = string.Empty; + + public string OrganizationName { get; set; } = string.Empty; + + } + + + public class TrialSiteUserSurveyAllQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + public Guid? SiteId { get; set; } + + public Guid? UserTypeId { get; set; } + + + public bool? IsGenerateAccount { get; set; } + public Guid? TrialRoleNameId { get; set; } + + public TrialSiteUserStateEnum? State { get; set; } + + public string FormWriterKeyInfo { get; set; } = string.Empty; + + + public string UserName { get; set; } = string.Empty; + + public string OrganizationName { get; set; } = string.Empty; + + + } + + + /// TrialSiteSurveyView 列表视图模型 + public class TrialSiteSurveyView + { + + public string TrialSiteCode { get; set; } = String.Empty; + public string TrialSiteAliasName { get; set; } = String.Empty; + + public string SiteName { get; set; } = string.Empty; + public bool IsDeleted { get; set; } + public Guid Id { get; set; } + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + public string UserName { get; set; } = string.Empty; + public string Phone { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public int AverageEngravingCycle { get; set; } + public bool IsConfirmImagingTechnologist { get; set; } + public string NotConfirmReson { get; set; } = string.Empty; + public int EfficacyEvaluatorType { get; set; } + public bool IsFollowStudyParameters { get; set; } + public string NotFollowReson { get; set; } = string.Empty; + + public TrialSiteSurveyEnum State { get; set; } + + public string LatestBackReason { get; set; } = string.Empty; + + public UserInfoBasic ReviewerUser { get; set; } + public UserInfoBasic PreliminaryUser { get; set; } + + public Guid? PreliminaryUserId { get; set; } + + public Guid? ReviewerUserId { get; set; } + + + public DateTime? PreliminaryTime { get; set; } + + public DateTime? ReviewerTime { get; set; } + + + + + } + + ///TrialSiteSurveyQuery 列表查询参数模型 + public class TrialSiteSurveyQuery + { + public Guid TrialId { get; set; } + + public Guid SiteId { get; set; } + + ///// UserName + //public string UserName { get; set; } + + ///// Phone + //public string Phone { get; set; } + + ///// Email + //public string Email { get; set; } + + ///// NotConfirmReson + //public string NotConfirmReson { get; set; } + + ///// NotFollowReson + //public string NotFollowReson { get; set; } + + } + + public class VerifyEmialGetDoctorInfoInDto + { + public string VerificationCode { get; set; } + + public string EmailOrPhone { get; set; } + } + + public class VerifyEmialGetDoctorInfoOutDto + { + public Guid? DoctorId { get; set; } + + public ReviewerInformationConfirmStatus? ReviewStatus { get; set; } + + public string Token { get; set; } + } + + public class SendEmialVerifyCodeInDto + { + public string Email { get; set; } = string.Empty; + } + + public class SiteSurveySendVerifyCode + { + //public VerifyType verificationType { get; set; } + public string Email { get; set; } = string.Empty; + + [NotDefault] + public Guid TrialId { get; set; } + + } + + public class LoginDto + { + public Guid TrialId { get; set; } + + public Guid SiteId { get; set; } + + + public bool IsUpdate { get; set; } + + + public string ReplaceUserEmailOrPhone { get; set; } = string.Empty; + + public VerifyType verificationType { get; set; } + public string EmailOrPhone { get; set; } = string.Empty; + + public string verificationCode { get; set; } = string.Empty; + } + + + /// TrialSiteSurveyAddOrEdit 列表查询参数模型 + public class TrialSiteSurveyAddOrEdit + { + public Guid? Id { get; set; } + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public string UserName { get; set; } = string.Empty; + public string Phone { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public int AverageEngravingCycle { get; set; } + public bool IsConfirmImagingTechnologist { get; set; } + public string NotConfirmReson { get; set; } = string.Empty; + public int EfficacyEvaluatorType { get; set; } + public bool IsFollowStudyParameters { get; set; } + public string NotFollowReson { get; set; } = string.Empty; + + + } + + + public class TrialSiteSubmitBackCommand + { + [NotDefault] + public Guid TrialId { get; set; } + [NotDefault] + public Guid TrialSiteSurveyId { get; set; } + + public string LatestBackReason { get; set; } = string.Empty; + + public string RouteUrl { get; set; } + } + + public class InviteEmailCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + + public string RouteUrl { get; set; } = string.Empty; + + public List UserList { get; set; } = new List(); + } + + + public class TrialSiteUserSurveyJoinCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + public Guid TrialSiteSurveyId { get; set; } + + public string BaseUrl { get; set; } = string.Empty; + + public string RouteUrl { get; set; } = string.Empty; + + public List UserList { get; set; } = new List(); + } + + + public class TrialSiteSurvyeSubmitDTO + { + [NotDefault] + public Guid TrialId { get; set; } + [NotDefault] + public Guid TrialSiteSurveyId { get; set; } + + + public string BaseUrl { get; set; } = string.Empty; + public string RouteUrl { get; set; } = string.Empty; + } + + public class TrialSiteSurveyQueryDTO : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public Guid? SiteId { get; set; } + + public string UserKeyInfo { get; set; } = string.Empty; + + + + public TrialSiteSurveyEnum? State { get; set; } + + public bool? IsDeleted { get; set; } + + public DateTime? UpdateTimeBegin { get; set; } + + public DateTime? UpdateTimeEnd { get; set; } + } + + public class CopyTrialSiteSurveyDTO + { + public Guid TrialSiteSurveyId { get; set; } + public string UserName { get; set; } = string.Empty; + public string Phone { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/DTO/TrialSiteUserSurveyViewModel.cs b/IRaCIS.Core.Application/Service/SiteSurvey/DTO/TrialSiteUserSurveyViewModel.cs new file mode 100644 index 0000000..a4fa5be --- /dev/null +++ b/IRaCIS.Core.Application/Service/SiteSurvey/DTO/TrialSiteUserSurveyViewModel.cs @@ -0,0 +1,108 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:21:04 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Application.Contracts +{ + /// TrialSiteUserSurveyView 列表视图模型 + public class TrialSiteUserSurveyView: TrialSiteUserSurveyAddOrEdit + { + public bool IsGenerateSuccess { get; set; } + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + public string UserType { get; set; } = string.Empty; + + [JsonIgnore] + public TrialSiteUserStateEnum InviteState { get; set; } + + public DateTime? ExpireTime { get; set; } + + public bool? IsJoin { get; set; } + + public DateTime? ConfirmTime { get; set; } + + public string RejectReason { get; set; } = string.Empty; + + + public TrialSiteUserStateEnum State + { + get + { + if (InviteState == TrialSiteUserStateEnum.HasSend && ExpireTime != null && ExpireTime < DateTime.Now) + { + return TrialSiteUserStateEnum.OverTime; + } + else + { + return InviteState; + } + } + } + + public string TrialRoleName { get; set; } + + public Guid? SystemUserId { get; set; } + + } + + + public class UserInfoBasic + { + public Guid Id { get; set; } + public string RealName { get; set; } = string.Empty; + public string UserName { get; set; } = string.Empty; + } + + + ///TrialSiteUserSurveyQuery 列表查询参数模型 + public class TrialSiteUserSurveyQuery + { + + public Guid TrialSiteSurveyId { get; set; } + + ///// UserName + //public string Name { get; set; } + + ///// Phone + //public string Phone { get; set; } + + ///// Email + //public string Email { get; set; } + + } + + /// TrialSiteUserSurveyAddOrEdit 列表查询参数模型 + public class TrialSiteUserSurveyAddOrEdit + { + public Guid? Id { get; set; } + + public Guid TrialSiteSurveyId { get; set; } + public Guid? UserTypeId { get; set; } + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string Phone { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public bool IsGenerateAccount { get; set; } + public Guid TrialRoleNameId { get; set; } + + public string OrganizationName { get; set; } = string.Empty; + } + + + public class TrialSiteUserSurveyVerfyResult + { + public Guid TrialSiteUserSurveyId { get; set; } + + public List ErroMsgList { get; set; } = new List(); + } + +} + + diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/Interface/ITrialSiteEquipmentSurveyService.cs b/IRaCIS.Core.Application/Service/SiteSurvey/Interface/ITrialSiteEquipmentSurveyService.cs new file mode 100644 index 0000000..6678045 --- /dev/null +++ b/IRaCIS.Core.Application/Service/SiteSurvey/Interface/ITrialSiteEquipmentSurveyService.cs @@ -0,0 +1,16 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:20:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + + +namespace IRaCIS.Core.Application.Contracts +{ + public interface ITrialSiteEquipmentSurveyService + { + Task AddOrUpdateTrialSiteEquipmentSurvey(TrialSiteEquipmentSurveyAddOrEdit addOrEditTrialSiteEquipmentSurvey); + Task DeleteTrialSiteEquipmentSurvey(Guid trialSiteEquipmentSurveyId); + Task> GetTrialSiteEquipmentSurveyList(Guid trialSiteSurveyId, string? scannerType); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/Interface/ITrialSiteSurveyService.cs b/IRaCIS.Core.Application/Service/SiteSurvey/Interface/ITrialSiteSurveyService.cs new file mode 100644 index 0000000..6bc2fc3 --- /dev/null +++ b/IRaCIS.Core.Application/Service/SiteSurvey/Interface/ITrialSiteSurveyService.cs @@ -0,0 +1,24 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:20:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Application.Services; +using IRaCIS.Core.Application.Auth; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface ITrialSiteSurveyService + { + Task AddOrUpdateTrialSiteSurvey(TrialSiteSurveyAddOrEdit addOrEditTrialSiteSurvey); + Task DeleteTrialSiteSurvey(Guid trialSiteSurveyId); + Task GetSiteSurveyInfo(Guid trialSiteSurveyId, Guid trialId); + Task> GetTrialSiteSurveyList(TrialSiteSurveyQueryDTO surveyQueryDTO); + Task GetTrialSurveyInitInfo(Guid trialId); + Task SendVerifyCode(SiteSurveySendVerifyCode userInfo); + //Task TrialSurveyLock(Guid trialSiteSurveyId, bool isLock); + //IResponseOutput TrialSurveySubmmit(Guid trialId, Guid trialSiteSurveyId); + Task VerifySendCode(LoginDto userInfo, [FromServices] ITokenService _tokenService); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/Interface/ITrialSiteUserSurveyService.cs b/IRaCIS.Core.Application/Service/SiteSurvey/Interface/ITrialSiteUserSurveyService.cs new file mode 100644 index 0000000..32942f1 --- /dev/null +++ b/IRaCIS.Core.Application/Service/SiteSurvey/Interface/ITrialSiteUserSurveyService.cs @@ -0,0 +1,15 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:20:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +namespace IRaCIS.Core.Application.Contracts +{ + public interface ITrialSiteUserSurveyService + { + Task AddOrUpdateTrialSiteUserSurvey(TrialSiteUserSurveyAddOrEdit addOrEditTrialSiteUserSurvey); + Task DeleteTrialSiteUserSurvey(Guid trialSiteUserSurveyId); + Task> GetTrialSiteUserSurveyList(Guid trialSiteSurveyId); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/Interface/JsonPatchUserRequestExample.cs b/IRaCIS.Core.Application/Service/SiteSurvey/Interface/JsonPatchUserRequestExample.cs new file mode 100644 index 0000000..0fa6ff8 --- /dev/null +++ b/IRaCIS.Core.Application/Service/SiteSurvey/Interface/JsonPatchUserRequestExample.cs @@ -0,0 +1,51 @@ +using Microsoft.AspNetCore.JsonPatch.Operations; +using Swashbuckle.AspNetCore.Filters; + +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// 实测 标注在服务方法上 没用 + /// + public class JsonPatchUserRequestExample : IExamplesProvider + { + public Operation[] GetExamples() + { + return new[] + { + new Operation + { + op = "replace", + path = "/name", + value = "Gordon" + }, + new Operation + { + op = "replace", + path = "/surname", + value = "Freeman" + } + }; + } + + object IExamplesProvider.GetExamples() + { + return new[] + { + new Operation + { + op = "replace", + path = "/name", + value = "Gordon" + }, + new Operation + { + op = "replace", + path = "/surname", + value = "Freeman" + } + }; + } + } + + +} diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteEquipmentSurveyService.cs b/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteEquipmentSurveyService.cs new file mode 100644 index 0000000..9cb9464 --- /dev/null +++ b/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteEquipmentSurveyService.cs @@ -0,0 +1,72 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:20:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// TrialSiteEquipmentSurveyService + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialSiteEquipmentSurveyService : BaseService, ITrialSiteEquipmentSurveyService + { + private readonly IRepository _trialSiteEquipmentSurveyRepository; + + public TrialSiteEquipmentSurveyService(IRepository trialSiteEquipmentSurveyRepository) + { + _trialSiteEquipmentSurveyRepository = trialSiteEquipmentSurveyRepository; + } + + + [HttpGet("{trialSiteSurveyId:guid}")] + public async Task> GetTrialSiteEquipmentSurveyList(Guid trialSiteSurveyId, string? scannerType) + { + var trialSiteEquipmentSurveyQueryable = _trialSiteEquipmentSurveyRepository.Where(t => t.TrialSiteSurveyId == trialSiteSurveyId) + .WhereIf(!string.IsNullOrEmpty(scannerType), t => t.ScannerType.Contains(scannerType!)) + .ProjectTo(_mapper.ConfigurationProvider); + + return await trialSiteEquipmentSurveyQueryable.ToListAsync(); + } + + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + [HttpPost("{trialId:guid}")] + public async Task AddOrUpdateTrialSiteEquipmentSurvey(TrialSiteEquipmentSurveyAddOrEdit addOrEditTrialSiteEquipmentSurvey) + { + + if (addOrEditTrialSiteEquipmentSurvey.Id != null) + { + if (await _trialSiteEquipmentSurveyRepository.Where(t => t.Id == addOrEditTrialSiteEquipmentSurvey.Id).AnyAsync(t => t.TrialSiteSurvey.State==TrialSiteSurveyEnum.PMCreatedAndLock)) + { + return ResponseOutput.NotOk("已锁定,不允许操作"); + } + } + + + var entity = await _trialSiteEquipmentSurveyRepository.InsertOrUpdateAsync(addOrEditTrialSiteEquipmentSurvey, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + [HttpDelete("{trialSiteEquipmentSurveyId:guid}/{trialId:guid}")] + public async Task DeleteTrialSiteEquipmentSurvey(Guid trialSiteEquipmentSurveyId) + { + if (await _trialSiteEquipmentSurveyRepository.Where(t => t.Id == trialSiteEquipmentSurveyId).AnyAsync(t => t.TrialSiteSurvey.State==TrialSiteSurveyEnum.PMCreatedAndLock)) + { + return ResponseOutput.NotOk("已锁定,不允许操作"); + } + var success = await _trialSiteEquipmentSurveyRepository.BatchDeleteNoTrackingAsync(t => t.Id == trialSiteEquipmentSurveyId); + + return ResponseOutput.Result(success); + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteSurveyService.cs b/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteSurveyService.cs new file mode 100644 index 0000000..edddbe0 --- /dev/null +++ b/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteSurveyService.cs @@ -0,0 +1,999 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:20:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Share; +using System.Text.RegularExpressions; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Application.Services; +using IRaCIS.Core.Application.Auth; +using IRaCIS.Application.Contracts; +using Microsoft.AspNetCore.Authorization; +using MailKit.Security; +using MimeKit; +using IRaCIS.Core.Application.Helper; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// TrialSiteSurveyService + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialSiteSurveyService : BaseService, ITrialSiteSurveyService + { + private readonly IRepository _trialSiteSurveyRepository; + private readonly IRepository _trialSiteUserSurveyRepository; + private readonly IRepository _userRepository; + private readonly IRepository _trialSiteRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _trialUserRepository; + private readonly ITokenService _tokenService; + private readonly IMailVerificationService _mailVerificationService; + + public TrialSiteSurveyService(IRepository trialSiteSurveyRepository, IRepository trialUserRepository, IRepository trialSiteUserSurveyRepository, + IRepository userRepository, IRepository trialSiteRepository, + IRepository doctorRepository, + ITokenService tokenService, + IMailVerificationService mailVerificationService) + { + _trialSiteSurveyRepository = trialSiteSurveyRepository; + _trialSiteUserSurveyRepository = trialSiteUserSurveyRepository; + _userRepository = userRepository; + _trialUserRepository = trialUserRepository; + _trialSiteRepository = trialSiteRepository; + this._doctorRepository = doctorRepository; + _tokenService = tokenService; + _mailVerificationService = mailVerificationService; + } + + private object lockObj { get; set; } = new object(); + + /// + /// 发送验证码 + /// + /// + /// + [AllowAnonymous] + public async Task SendEmialVerifyCode(SendEmialVerifyCodeInDto userInfo) + { + //检查手机或者邮箱是否有效 + if (!Regex.IsMatch(userInfo.Email, @"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$")) + { + throw new BusinessValidationFailedException("请输入正确的邮箱地址。"); + } + + //邮箱 + + //验证码 6位 + int verificationCode = new Random().Next(100000, 1000000); + await _mailVerificationService.SendEmailVerification(userInfo.Email, verificationCode); + return ResponseOutput.Ok(); + } + + /// + /// 验证邮箱验证码 获取医生信息Id + /// + /// + /// + [HttpPost] + [AllowAnonymous] + public async Task VerifyEmialGetDoctorInfo(VerifyEmialGetDoctorInfoInDto inDto) + { + var verificationRecord = await _repository.GetQueryable().OrderByDescending(x => x.ExpirationTime).Where(t => (t.EmailOrPhone == inDto.EmailOrPhone) && t.Code == inDto.VerificationCode && t.CodeType == VerifyType.Email).FirstOrDefaultAsync(); + VerifyEmialGetDoctorInfoOutDto result = new VerifyEmialGetDoctorInfoOutDto(); + + var doctorInfo = await _doctorRepository.Where(x => x.EMail == inDto.EmailOrPhone).FirstOrDefaultAsync(); + + result.DoctorId = doctorInfo == null ? null : doctorInfo.Id; + result.ReviewStatus = doctorInfo == null ? null : doctorInfo.ReviewStatus; + + //检查数据库是否存在该验证码 + if (verificationRecord == null) + { + throw new BusinessValidationFailedException("验证码错误。"); + } + else + { + //检查验证码是否失效 + if (verificationRecord.ExpirationTime < DateTime.Now) + { + throw new BusinessValidationFailedException("验证码已经过期。"); + } + else //验证码正确 并且 没有超时 + { + result.Token = _tokenService.GetToken(IRaCISClaims.Create(new UserBasicInfo())); + } + } + + return result; + } + + /// + /// 发送验证码 + /// + /// + /// + [AllowAnonymous] + public async Task SendVerifyCode(SiteSurveySendVerifyCode userInfo) + { + //检查手机或者邮箱是否有效 + if (!Regex.IsMatch(userInfo.Email, @"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$")) + { + throw new BusinessValidationFailedException("请输入正确的邮箱地址。"); + } + + //邮箱 + + //验证码 6位 + int verificationCode = new Random().Next(100000, 1000000); + + var trialInfo = await _repository.FirstOrDefaultAsync(t => t.Id == userInfo.TrialId); + + await _mailVerificationService.AnolymousSendEmail(trialInfo.ResearchProgramNo, userInfo.Email, verificationCode); + + + return ResponseOutput.Ok(); + + + } + + /// + /// 验证后 如果数据库该项目不存在该邮箱 那么就插入记录 存在 + /// + /// + /// + /// + /// + [HttpPost] + [AllowAnonymous] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task VerifySendCode(LoginDto userInfo, [FromServices] ITokenService _tokenService) + { + + var isReplaceUser = !string.IsNullOrEmpty(userInfo.ReplaceUserEmailOrPhone); + + + if (userInfo.IsUpdate && isReplaceUser && !await _trialSiteSurveyRepository.AnyAsync(t => (t.Email == userInfo.ReplaceUserEmailOrPhone || t.Phone == userInfo.ReplaceUserEmailOrPhone) && t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId)) + { + return ResponseOutput.NotOk("该中心不存在该交接人的中心调研记录表,不允许选择更新。"); + } + + + if (userInfo.IsUpdate && await _trialSiteSurveyRepository.AnyAsync(t => (t.Email == userInfo.EmailOrPhone || t.Phone == userInfo.EmailOrPhone) && t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId && t.State != TrialSiteSurveyEnum.PMCreatedAndLock)) + { + return ResponseOutput.NotOk("您的中心调研记录正在审核中,不允许进行更新操作。若需要更新,请在驳回后进行操作。"); + } + + + //自己的记录锁定了 只能更新自己的,不能更新别人的(但是别人能更新自己锁定的) + if (userInfo.IsUpdate && userInfo.ReplaceUserEmailOrPhone != userInfo.EmailOrPhone && await _trialSiteSurveyRepository.AnyAsync(t => (t.Email == userInfo.EmailOrPhone || t.Phone == userInfo.EmailOrPhone) && t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId && t.State == TrialSiteSurveyEnum.PMCreatedAndLock)) + { + //自己的锁了 想更新别人的 + return ResponseOutput.NotOk("当前中心中,您提交调研记录表已锁定,不允许更新其他人邮箱调研记录。"); + } + + //自己的锁定了 如果有其他未锁定的,也不能更新自己的 + if (userInfo.IsUpdate && userInfo.ReplaceUserEmailOrPhone == userInfo.EmailOrPhone && + await _trialSiteSurveyRepository.AnyAsync(t => (t.Email == userInfo.EmailOrPhone || t.Phone == userInfo.EmailOrPhone) && t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId && t.State == TrialSiteSurveyEnum.PMCreatedAndLock) + && await _trialSiteSurveyRepository.AnyAsync(t => (t.Email != userInfo.EmailOrPhone && t.Phone != userInfo.EmailOrPhone) && t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId && t.State != TrialSiteSurveyEnum.PMCreatedAndLock)) + { + + return ResponseOutput.NotOk("当前中心,您提交的调研记录表已锁定。当前存在其他人员提交的调研记录表未锁定,不允许更新您之前提交的调研记录。"); + } + + + ////存在未锁定的记录,却去更新已锁定的 + if (userInfo.IsUpdate && userInfo.ReplaceUserEmailOrPhone != userInfo.EmailOrPhone && await _trialSiteSurveyRepository.AnyAsync(t => t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId && t.State != TrialSiteSurveyEnum.PMCreatedAndLock) + && await _trialSiteSurveyRepository.AnyAsync(t => (t.Email == userInfo.ReplaceUserEmailOrPhone || t.Phone == userInfo.ReplaceUserEmailOrPhone) && t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId && t.State == TrialSiteSurveyEnum.PMCreatedAndLock) + && !await _trialSiteSurveyRepository.AnyAsync(t => (t.Email == userInfo.ReplaceUserEmailOrPhone || t.Phone == userInfo.ReplaceUserEmailOrPhone) && t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId && t.State != TrialSiteSurveyEnum.PMCreatedAndLock) + ) + { + return ResponseOutput.NotOk("当前中心存在未锁定的调研记录,不允许更新已锁定的调研记录。"); + } + + + + var verificationRecord = await _repository + .FirstOrDefaultAsync(t => (t.EmailOrPhone == userInfo.EmailOrPhone) && t.Code == userInfo.verificationCode && t.CodeType == userInfo.verificationType); + + //检查数据库是否存在该验证码 + if (verificationRecord == null) + { + return ResponseOutput.NotOk("验证码错误。"); + } + else + { + //检查验证码是否失效 + if (verificationRecord.ExpirationTime < DateTime.Now) + { + return ResponseOutput.NotOk("验证码已经过期。"); + } + else //验证码正确 并且 没有超时 + { + + TrialSiteSurvey? dbEntity = null; + + + //替换交接人 + if (isReplaceUser) + { + //该交接人的记录 是否有未锁定的 有就用未锁定的,没有就用 锁定的最后一条 + + var noLockedLastSurvey = await _trialSiteSurveyRepository.Where(t => (t.Email == userInfo.ReplaceUserEmailOrPhone || t.Phone == userInfo.ReplaceUserEmailOrPhone) && t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId && t.State == TrialSiteSurveyEnum.PMCreatedAndLock == false, true) + .Include(u => u.TrialSiteEquipmentSurveyList).Include(u => u.TrialSiteUserSurveyList).OrderByDescending(t => t.CreateTime).FirstOrDefaultAsync(); + + //都是锁定的 + if (noLockedLastSurvey == null) + { + + var latestLock = await _trialSiteSurveyRepository.Where(t => t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId).OrderByDescending(t => t.CreateTime).FirstOrDefaultAsync(); + + if (latestLock!.Email != userInfo.ReplaceUserEmailOrPhone) + { + return ResponseOutput.NotOk($"该邮箱{userInfo.ReplaceUserEmailOrPhone}对应的调查表不是最新锁定的记录,不允许更新!"); + } + + var lockedLastSurvey = await _trialSiteSurveyRepository.Where(t => (t.Email == userInfo.ReplaceUserEmailOrPhone || t.Phone == userInfo.ReplaceUserEmailOrPhone) && t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId && t.State == TrialSiteSurveyEnum.PMCreatedAndLock == true) + .Include(u => u.TrialSiteEquipmentSurveyList).Include(u => u.TrialSiteUserSurveyList).OrderByDescending(t => t.CreateTime).FirstOrDefaultAsync().IfNullThrowException(); + + //Copy 一份 更换邮箱 + + var copy = lockedLastSurvey.Clone(); + + copy.State = TrialSiteSurveyEnum.ToSubmit; + + copy.Email = userInfo.EmailOrPhone; + + if (userInfo.ReplaceUserEmailOrPhone != userInfo.EmailOrPhone) + { + copy.UserName = String.Empty; + copy.Phone = String.Empty; + } + + + copy.Id = Guid.Empty; + copy.TrialSiteEquipmentSurveyList.ForEach(t => t.Id = Guid.Empty); + copy.TrialSiteUserSurveyList.ForEach(t => t.Id = Guid.Empty); + + dbEntity = await _repository.AddAsync(copy); + + } + else + { + //有未锁定的 更新下邮箱 + noLockedLastSurvey.Email = userInfo.EmailOrPhone; + noLockedLastSurvey.UserName = String.Empty; + noLockedLastSurvey.Phone = String.Empty; + + dbEntity = noLockedLastSurvey; + } + + + + ////邮箱相同的话 就是同一个人进来 copy一份 + //if (userInfo.EmailOrPhone == userInfo.ReplaceUserEmailOrPhone) + //{ + // dbEntity = await _repository.Where(t => (t.Email == userInfo.EmailOrPhone || t.Phone == userInfo.EmailOrPhone) && t.SiteId == userInfo.SiteId && t.TrialId == userInfo.TrialId).Include(u => u.TrialSiteEquipmentSurveyList).Include(u => u.TrialSiteUserSurveyList).FirstOrDefaultAsync().IfNullThrowConvertException(); + + // var clone = dbEntity.Clone(); + // clone.Id = Guid.Empty; + // clone.TrialSiteEquipmentSurveyList.ForEach(t => t.Id = Guid.Empty); + // clone.TrialSiteUserSurveyList.ForEach(t => t.Id = Guid.Empty); + // clone.State = TrialSiteSurveyEnum.ToSubmit; + + // dbEntity = await _repository.AddAsync(clone); + + //} + + } + else + { + + + var dbEntityList = await _trialSiteSurveyRepository.Where(t => t.TrialId == userInfo.TrialId && t.SiteId == userInfo.SiteId).ToListAsync(); + + + //没有记录 new一份 + if (dbEntityList.Count == 0) + { + + dbEntity = await _repository.AddAsync(_mapper.Map(userInfo)); + + } + else + { + + + + //该site 下不存在该邮箱的记录 + if (!dbEntityList.Any(t => t.Email == userInfo.EmailOrPhone || t.Phone == userInfo.EmailOrPhone)) + { + return ResponseOutput.NotOk("该中心下已经有其他用户已填写的调研表,您不被允许继续填写"); + } + + + //有没有该邮箱 未锁定的 + var nolockEntity = dbEntityList.Where(t => t.Email == userInfo.EmailOrPhone || t.Phone == userInfo.EmailOrPhone).FirstOrDefault(t => t.State != TrialSiteSurveyEnum.PMCreatedAndLock); + + // 未锁定的 为空 + if (nolockEntity == null) + { + //查看最新锁定的 + dbEntity = dbEntityList.Where(t => t.Email == userInfo.EmailOrPhone || t.Phone == userInfo.EmailOrPhone).OrderByDescending(t => t.CreateTime).FirstOrDefault(t => t.State == TrialSiteSurveyEnum.PMCreatedAndLock).IfNullThrowException(); + + } + else //有未锁定的 直接用未锁定的 + { + dbEntity = nolockEntity; + } + + } + //_mapper.Map(userInfo, dbEntity); + + + + + } + + + //删除验证码历史记录 + await _repository.BatchDeleteAsync(t => t.EmailOrPhone == userInfo.EmailOrPhone && t.Code == userInfo.verificationCode && t.CodeType == userInfo.verificationType); + + await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(new + { + TrialSiteSurveyId = dbEntity!.Id, + Token = _tokenService.GetToken(IRaCISClaims.Create(new UserBasicInfo() + { + Id = Guid.NewGuid(), + IsReviewer = false, + IsAdmin = false, + RealName = "SiteSurvey", + UserName = "SiteSurvey", + Sex = 0, + //UserType = "ShareType", + UserTypeEnum = UserTypeEnum.Undefined, + Code = "SiteSurvey", + })) + }); + + } + } + + } + + + /// + /// 直接查询相关所有数据 + /// + /// + [HttpGet("{trialId:guid}/{trialSiteSurveyId:guid}")] + public async Task GetSiteSurveyInfo(Guid trialSiteSurveyId, Guid trialId) + { + var result = await _trialSiteSurveyRepository.Where(t => t.Id == trialSiteSurveyId && t.TrialId == trialId) + .ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + + return result; + } + + + /// + /// 实际这里只会是更新 添加在login的时候做了 + /// + /// + /// + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddOrUpdateTrialSiteSurvey(TrialSiteSurveyAddOrEdit addOrEditTrialSiteSurvey) + { + + if (addOrEditTrialSiteSurvey.Id != null) + { + if (await _trialSiteSurveyRepository.AnyAsync(t => t.Id == addOrEditTrialSiteSurvey.Id && t.State == TrialSiteSurveyEnum.PMCreatedAndLock)) + { + return ResponseOutput.NotOk("中心调研已锁定,不允许操作。"); + } + } + + + if (addOrEditTrialSiteSurvey.Id == null) + { + var entity = _mapper.Map(addOrEditTrialSiteSurvey); + await _trialSiteSurveyRepository.AddAsync(entity, true); + return ResponseOutput.Ok(entity.Id.ToString()); + } + else + { + + var entity = await _trialSiteSurveyRepository.Where(t => t.Id == addOrEditTrialSiteSurvey.Id, true).Include(x => x.ReviewerUser).Include(x => x.PreliminaryUser).FirstOrDefaultAsync(); + _mapper.Map(addOrEditTrialSiteSurvey, entity); + await _trialSiteSurveyRepository.SaveChangesAsync(); + + } + return ResponseOutput.Ok(true); + } + + + /// + /// 删除调研表 + /// + /// + /// + [HttpDelete("{trialSiteSurveyId:guid}/{trialId:guid}")] + public async Task DeleteTrialSiteSurvey(Guid trialSiteSurveyId) + { + + if (await _trialSiteSurveyRepository.AnyAsync(t => t.Id == trialSiteSurveyId && t.State == TrialSiteSurveyEnum.PMCreatedAndLock)) + { + return ResponseOutput.NotOk("中心调研表已锁定,不允许操作"); + } + + var success = await _trialSiteSurveyRepository.BatchDeleteNoTrackingAsync(t => t.Id == trialSiteSurveyId); + + return ResponseOutput.Result(success); + } + + + /// + /// 获取 项目 site的调研记录 New + /// + /// + [HttpPost] + public async Task> GetTrialSiteSurveyList(TrialSiteSurveyQueryDTO surveyQueryDTO) + { + var trialSiteSurveyQueryable = _trialSiteSurveyRepository.Where(t => t.TrialId == surveyQueryDTO.TrialId).IgnoreQueryFilters() + .WhereIf(surveyQueryDTO.SiteId != null, t => t.SiteId == surveyQueryDTO.SiteId) + .WhereIf(surveyQueryDTO.IsDeleted != null, t => t.IsDeleted == surveyQueryDTO.IsDeleted) + .WhereIf(!string.IsNullOrWhiteSpace(surveyQueryDTO.UserKeyInfo), t => t.UserName.Contains(surveyQueryDTO.UserKeyInfo) || t.Phone.Contains(surveyQueryDTO.UserKeyInfo) || t.Email.Contains(surveyQueryDTO.UserKeyInfo)) + .WhereIf(surveyQueryDTO.State != null, t => t.State == surveyQueryDTO.State) + .WhereIf(surveyQueryDTO.UpdateTimeBegin != null, t => t.UpdateTime >= surveyQueryDTO.UpdateTimeBegin) + .WhereIf(surveyQueryDTO.UpdateTimeEnd != null, t => t.UpdateTime <= surveyQueryDTO.UpdateTimeEnd) + + .ProjectTo(_mapper.ConfigurationProvider); + + return await trialSiteSurveyQueryable.ToPagedListAsync(surveyQueryDTO.PageIndex, surveyQueryDTO.PageSize, surveyQueryDTO.SortField, surveyQueryDTO.Asc); + } + + + /// + /// 项目Site调研用户列表 所有site的调研用户 最新的调研表的记录的用户 new + /// + /// + public async Task> TrialSiteSurveyUserList(TrialSiteUserSurveyAllQuery queryParam) + { + + + var groupSelectIdQuery = + _trialSiteSurveyRepository.Where(t => t.TrialId == queryParam.TrialId) + .WhereIf(queryParam.SiteId != null, t => t.SiteId == queryParam.SiteId) + .WhereIf(!string.IsNullOrEmpty(queryParam.FormWriterKeyInfo), t => (t.UserName).Contains(queryParam.FormWriterKeyInfo) || t.Email.Contains(queryParam.FormWriterKeyInfo) || t.Phone.Contains(queryParam.FormWriterKeyInfo)) + .GroupBy(t => t.SiteId) + .Select(g => g.OrderByDescending(u => u.CreateTime).Select(t => t.Id).First()); + + + var query = _trialSiteUserSurveyRepository + .Where(t => groupSelectIdQuery.Contains(t.TrialSiteSurveyId)) + .WhereIf(queryParam.UserTypeId != null, t => t.UserTypeId == queryParam.UserTypeId) + .WhereIf(queryParam.IsGenerateAccount != null, t => t.IsGenerateAccount == queryParam.IsGenerateAccount) + .WhereIf(queryParam.TrialRoleNameId != null, t => t.TrialRoleNameId == queryParam.TrialRoleNameId) + .WhereIf(queryParam.State != null && queryParam.State != TrialSiteUserStateEnum.OverTime, t => t.InviteState == queryParam.State) + .WhereIf(queryParam.State != null && queryParam.State == TrialSiteUserStateEnum.OverTime, t => t.InviteState == TrialSiteUserStateEnum.HasSend && t.ExpireTime < DateTime.Now) + .WhereIf(!string.IsNullOrEmpty(queryParam.UserName), t => (t.LastName + " / " + t.FirstName).Contains(queryParam.UserName)) + .WhereIf(!string.IsNullOrEmpty(queryParam.OrganizationName), t => t.OrganizationName.Contains(queryParam.OrganizationName)) + + .ProjectTo(_mapper.ConfigurationProvider); + + //var query = _trialSiteSurveyRepository.Where(t => t.TrialId == queryParam.TrialId && t.IsAbandon == false) + // .WhereIf(queryParam.SiteId != null, t => t.SiteId == queryParam.SiteId) + // .WhereIf(!string.IsNullOrEmpty(queryParam.FormWriterKeyInfo), t => (t.UserName).Contains(queryParam.FormWriterKeyInfo) || t.Email.Contains(queryParam.FormWriterKeyInfo) || t.Phone.Contains(queryParam.FormWriterKeyInfo)) + // .GroupBy(t => t.SiteId) + // .Select(g => g.OrderByDescending(u => u.CreateTime).FirstOrDefault()) + // .SelectMany(t => t.TrialSiteUserSurveyList) + //.WhereIf(queryParam.UserTypeId != null, t => t.UserTypeId == queryParam.UserTypeId) + //.WhereIf(queryParam.IsGenerateAccount != null, t => t.IsGenerateAccount == queryParam.IsGenerateAccount) + //.WhereIf(queryParam.TrialRoleNameId != null, t => t.TrialRoleNameId == queryParam.TrialRoleNameId) + //.WhereIf(queryParam.State != null && queryParam.State != TrialSiteUserStateEnum.OverTime, t => t.InviteState == queryParam.State) + //.WhereIf(queryParam.State != null && queryParam.State == TrialSiteUserStateEnum.OverTime, t => t.InviteState == TrialSiteUserStateEnum.HasSend && t.ExpireTime < DateTime.Now) + //.WhereIf(!string.IsNullOrEmpty(queryParam.UserKeyInfo), t => (t.LastName + " / " + t.FirstName).Contains(queryParam.UserKeyInfo) || t.Email.Contains(queryParam.UserKeyInfo) || t.Phone.Contains(queryParam.UserKeyInfo)) + //.ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize, queryParam.SortField, queryParam.Asc); + + + + + //return await query.ToPagedListAsync(queryParam.PageIndex, queryParam.PageSize, queryParam.SortField, queryParam.Asc); + } + + + + /// + /// 初始登陆界面 项目基本信息+下拉框数据 + /// + /// + /// + [AllowAnonymous] + [HttpGet("{trialId:guid}")] + public async Task GetTrialSurveyInitInfo(Guid trialId) + { + var info = await _repository.Where(t => t.Id == trialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + + return info; + } + + + /// + /// 驳回 New + /// + /// + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task SubmissionRejection(TrialSiteSubmitBackCommand trialSiteSubmitBackCommand, [FromServices] IMailVerificationService _IMailVerificationService) + { + var trialSiteSurveyId = trialSiteSubmitBackCommand.TrialSiteSurveyId; + + var survey = await _trialSiteSurveyRepository.FirstOrDefaultAsync(t => t.Id == trialSiteSurveyId); + + survey.LatestBackReason = trialSiteSubmitBackCommand.LatestBackReason; + + User? user = null; + + var messageToSend = new MimeMessage(); + + + + if (await _repository.AnyAsync(t => t.State == TrialSiteSurveyEnum.PMCreatedAndLock && t.Id == trialSiteSurveyId)) + { + return ResponseOutput.NotOk("中心调研表已锁定,不允许操作。"); + } + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM) + { + + //SPM 给填表人发 + messageToSend.To.Add(new MailboxAddress(String.Empty, survey.Email)); + + survey.State = TrialSiteSurveyEnum.ToSubmit; + } + else if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM) + { + var hasSPMOrCPM = await _trialSiteSurveyRepository.AnyAsync(t => t.TrialId == trialSiteSubmitBackCommand.TrialId && t.Trial.TrialUserList.Any(u => u.User.UserTypeEnum == UserTypeEnum.SPM || u.User.UserTypeEnum == UserTypeEnum.CPM)); + + if (hasSPMOrCPM) + { + + //PM 给SPM发 (初审人) + user = await _userRepository.FirstOrDefaultAsync(t => t.Id == survey.PreliminaryUserId); + + messageToSend.To.Add(new MailboxAddress(String.Empty, survey.PreliminaryUserId == null ? survey.Email : user.EMail)); + + survey.State = TrialSiteSurveyEnum.CRCSubmitted; + + survey.ReviewerUserId = null; + survey.ReviewerTime = null; + } + else + { + //没有SPM 给填表人发 + messageToSend.To.Add(new MailboxAddress(String.Empty, survey.Email)); + + survey.State = TrialSiteSurveyEnum.ToSubmit; + + survey.PreliminaryUserId = null; + survey.ReviewerUserId = null; + survey.PreliminaryTime = null; + survey.ReviewerTime = null; + + + } + } + + + var builder = new BodyBuilder(); + + var trialInfo = await _repository.FirstOrDefaultAsync(t => t.Id == trialSiteSubmitBackCommand.TrialId); + + var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialSiteSubmitBackCommand.TrialId && t.SiteId == survey.SiteId, true); + + + //主题 + messageToSend.Subject = $"[来自展影IRC] [{trialInfo.ResearchProgramNo}] 关于中心调研审批的提醒"; + + var pathToFile = _hostEnvironment.WebRootPath + + Path.DirectorySeparatorChar.ToString() + + "EmailTemplate" + + Path.DirectorySeparatorChar.ToString() + + "TrialSiteSurveyReject.html"; + + using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + { + var templateInfo = SourceReader.ReadToEnd(); + + + builder.HtmlBody = string.Format(templateInfo, + (user == null ? survey.UserName : user.LastName + "/ " + user.FirstName), + trialInfo.TrialCode, + trialInfo.ResearchProgramNo, + trialInfo.ExperimentName, + siteInfo.TrialSiteCode, + siteInfo.TrialSiteAliasName, + survey.LatestBackReason, + trialSiteSubmitBackCommand.RouteUrl, + (survey.State == TrialSiteSurveyEnum.ToSubmit ? "inline - block" : "none") + + ); + } + messageToSend.Body = builder.ToMessageBody(); + + + await _IMailVerificationService.SiteSurveyRejectEmail(messageToSend); + + + + + + await _trialSiteSurveyRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + /// + /// 驳回 + /// + /// + /// + /// + [HttpPut("{trialId:guid}/{trialSiteSurveyId:guid}")] + [Obsolete] + public async Task SubmissionRejection(Guid trialId, Guid trialSiteSurveyId) + { + if (await _repository.AnyAsync(t => t.State == TrialSiteSurveyEnum.PMCreatedAndLock && t.Id == trialSiteSurveyId)) + { + return ResponseOutput.NotOk("中心调研表已锁定,不允许操作。"); + } + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM) + { + await _repository.BatchUpdateAsync(t => t.Id == trialSiteSurveyId, u => new TrialSiteSurvey() { State = TrialSiteSurveyEnum.ToSubmit }); + } + else if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM) + { + var hasSPMOrCPM = await _trialSiteSurveyRepository.AnyAsync(t => t.TrialId == trialId && t.Trial.TrialUserList.Any(u => u.User.UserTypeEnum == UserTypeEnum.SPM || u.User.UserTypeEnum == UserTypeEnum.CPM)); + + if (hasSPMOrCPM) + { + await _trialSiteSurveyRepository.BatchUpdateNoTrackingAsync(t => t.Id == trialSiteSurveyId, u => new TrialSiteSurvey() { State = TrialSiteSurveyEnum.CRCSubmitted }); + + } + else + { + await _trialSiteSurveyRepository.BatchUpdateNoTrackingAsync(t => t.Id == trialSiteSurveyId, u => new TrialSiteSurvey() { State = TrialSiteSurveyEnum.ToSubmit }); + } + } + return ResponseOutput.Ok(); + + } + + + [HttpPut("{trialId:guid}/{trialSiteSurveyId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + public async Task AbandonSiteSurvey(Guid trialSiteSurveyId) + { + var survey = (await _trialSiteSurveyRepository.FirstOrDefaultAsync(t => t.Id == trialSiteSurveyId, true)).IfNullThrowException(); + + if (survey.State != TrialSiteSurveyEnum.ToSubmit) + { + return ResponseOutput.NotOk("只允许废除未提交的记录。"); + } + + survey.IsDeleted = true; + + await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + + } + + + + /// + /// 提交 后台自动识别是谁提交 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task TrialSurveySubmit(TrialSiteSurvyeSubmitDTO siteSurvyeSubmit) + { + + var trialId = siteSurvyeSubmit.TrialId; + var trialSiteSurveyId = siteSurvyeSubmit.TrialSiteSurveyId; + + if (_userInfo.IsAdmin) + { + return ResponseOutput.NotOk("不允许管理员操作。"); + } + + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.Undefined) + { + var hasSPMOrCPM = await _trialSiteSurveyRepository.AnyAsync(t => t.TrialId == trialId && t.Trial.TrialUserList.Any(u => u.User.UserTypeEnum == UserTypeEnum.SPM || u.User.UserTypeEnum == UserTypeEnum.CPM)); + + if (hasSPMOrCPM) + { + await _trialSiteSurveyRepository.UpdatePartialFromQueryAsync(t => t.Id == trialSiteSurveyId && t.State == TrialSiteSurveyEnum.ToSubmit, u => new TrialSiteSurvey() { State = TrialSiteSurveyEnum.CRCSubmitted }); + + } + else + { + await _trialSiteSurveyRepository.UpdatePartialFromQueryAsync(t => t.Id == trialSiteSurveyId && t.State == TrialSiteSurveyEnum.ToSubmit, u => new TrialSiteSurvey() { State = TrialSiteSurveyEnum.SPMApproved }); + + } + + } + else if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM) + { + + await _trialSiteSurveyRepository.UpdatePartialFromQueryAsync(t => t.Id == trialSiteSurveyId && t.State == TrialSiteSurveyEnum.CRCSubmitted, u => new TrialSiteSurvey() { State = TrialSiteSurveyEnum.SPMApproved, PreliminaryUserId = _userInfo.Id, PreliminaryTime = DateTime.Now }); + + } + else if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager) + { + + var trialSiteSurvey = (await _trialSiteSurveyRepository.Where(t => t.Id == trialSiteSurveyId).FirstOrDefaultAsync()).IfNullThrowException(); + + + + //已生成的不管 管的只需要是 生成失败的并且需要生成账号的 + var needGenerateList = _trialSiteUserSurveyRepository.Where(t => t.TrialSiteSurveyId == trialSiteSurveyId && t.IsGenerateAccount && t.IsJoin != true).ProjectTo(_mapper.ConfigurationProvider).ToList(); + + + //await SendInviteEmail(new InviteEmailCommand() { TrialId = trialId, RouteUrl = siteSurvyeSubmit.RouteUrl, UserList = needGenerateList }); + + + await GenerateAccountAsync(needGenerateList, trialId); + await SendSiteSurveyUserJoinEmail(new TrialSiteUserSurveyJoinCommand() { TrialId = trialId, TrialSiteSurveyId = trialSiteSurveyId, RouteUrl = siteSurvyeSubmit.RouteUrl, BaseUrl = siteSurvyeSubmit.BaseUrl, UserList = needGenerateList }); + + + } + await _trialSiteSurveyRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } + + + + private async Task GenerateAccountAsync(List needGenerateList, Guid trialId) + { + foreach (var item in needGenerateList) + { + + //找下系统中是否存在该用户类型的 并且邮箱 或者手机的账户 + var sysUserInfo = await _userRepository.Where(t => t.UserTypeId == item.UserTypeId && t.EMail == item.Email).Include(t => t.UserTypeRole).FirstOrDefaultAsync(); + + var trialType = _repository.Where(t => t.Id == trialId).Select(t => t.TrialType).FirstOrDefault(); + + if (sysUserInfo == null) + { + + lock (lockObj) + { + var saveItem = _mapper.Map(item); + + + if (trialType == TrialType.NoneOfficial) + { + saveItem.IsTestUser = true; + } + + // 中心调研生成账号 都是外部的 + saveItem.IsZhiZhun = false; + saveItem.Code = _userRepository.Select(t => t.Code).DefaultIfEmpty().Max() + 1; + + saveItem.UserCode = AppSettings.GetCodeStr(saveItem.Code, nameof(User)); + + saveItem.UserName = saveItem.UserCode; + + saveItem.UserTypeEnum = _repository.Where(t => t.Id == saveItem.UserTypeId).Select(t => t.UserTypeEnum).First(); + + //saveItem.Password = MD5Helper.Md5(verificationCode.ToString()); + + var newUser = _userRepository.AddAsync(saveItem).Result; + + + _ = _userRepository.SaveChangesAsync().Result; + + + sysUserInfo = newUser; + } + + } + + + //发送邮件的时候需要用到该字段 + item.SystemUserId = sysUserInfo.Id; + + await _trialSiteUserSurveyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Id, u => new TrialSiteUserSurvey() { IsGenerateSuccess = true, SystemUserId = sysUserInfo.Id }); + + + + + } + + await _trialSiteUserSurveyRepository.SaveChangesAsync(); + } + + + + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SendSiteSurveyUserJoinEmail(TrialSiteUserSurveyJoinCommand joinCommand) + { + var trialSiteSurvey = await _trialSiteSurveyRepository.FirstAsync(t => t.Id == joinCommand.TrialSiteSurveyId); + + + foreach (var userInfo in joinCommand.UserList) + { + + if (userInfo.SystemUserId == null) + { + throw new BusinessValidationFailedException("生成账户Id 未取到值,请排查"); + } + + var trialId = joinCommand.TrialId; + var userId = (Guid)userInfo.SystemUserId; + var siteId = trialSiteSurvey.SiteId; + + + + //判断TrialUser中是否存在 不存在就插入 + if (!await _trialUserRepository.AnyAsync(t => t.TrialId == trialId && t.UserId == userId, true)) + { + + await _repository.AddAsync(new TrialUser() { TrialId = trialId, UserId = userId, JoinTime = DateTime.Now }); + + await _repository.AddAsync(new TrialSiteUser() { TrialId = trialId, SiteId = siteId, UserId = userId }); + + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == userId, u => new User() { Status = UserStateEnum.Enable }); + + await _trialSiteUserSurveyRepository.UpdatePartialFromQueryAsync(t => t.Id == userInfo.Id, u => new TrialSiteUserSurvey() { IsJoin = true }); + + } + + await _mailVerificationService.SiteSurveyUserJoinEmail(trialId, userId, joinCommand.BaseUrl, joinCommand.RouteUrl); + + } + + await _trialSiteSurveyRepository.UpdatePartialFromQueryAsync(t => t.Id == trialSiteSurvey.Id && t.State == TrialSiteSurveyEnum.SPMApproved, u => new TrialSiteSurvey() { State = TrialSiteSurveyEnum.PMCreatedAndLock, ReviewerUserId = _userInfo.Id, ReviewerTime = DateTime.Now }); + await _userRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + + + } + + + + + + #region 废弃 + //Site 调研邀请 + public async Task SendInviteEmail(InviteEmailCommand inviteEmailCommand) + { + + var trialInfo = await _repository.FirstOrDefaultAsync(t => t.Id == inviteEmailCommand.TrialId); + + + foreach (var item in inviteEmailCommand.UserList) + { + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress("GRR", "iracis_grr@163.com")); + //收件地址 + messageToSend.To.Add(new MailboxAddress(String.Empty, item.Email)); + //主题 + messageToSend.Subject = $"[来自展影IRC] [{trialInfo.ResearchProgramNo}] 邀请信"; + + var builder = new BodyBuilder(); + + //找下系统中是否存在该用户类型的 并且邮箱 或者手机的账户 + var sysUserInfo = await _userRepository.Where(t => t.UserTypeId == item.UserTypeId && t.EMail == item.Email).Include(t => t.UserTypeRole).FirstOrDefaultAsync(); + + //int verificationCode = new Random().Next(100000, 1000000); + + //var baseApiUrl = baseUrl.Remove(baseUrl.IndexOf("#")) + "api"; + + + if (sysUserInfo == null) + { + + lock (lockObj) + { + var saveItem = _mapper.Map(item); + + saveItem.Code = _userRepository.Select(t => t.Code).DefaultIfEmpty().Max() + 1; + + saveItem.UserCode = AppSettings.GetCodeStr(saveItem.Code, nameof(User)); ; + + saveItem.UserName = saveItem.UserCode; + + saveItem.UserTypeEnum = _repository.Where(t => t.Id == saveItem.UserTypeId).Select(t => t.UserTypeEnum).First(); + + //saveItem.Password = MD5Helper.Md5(verificationCode.ToString()); + + _ = _repository.AddAsync(saveItem).Result; + + _ = _repository.SaveChangesAsync().Result; + + + sysUserInfo = saveItem; + } + + } + + + + builder.HtmlBody = @$" +
+
+
+ {sysUserInfo.LastName + "/" + sysUserInfo.FirstName}: +
+
+ 您好,展影医疗作为 实验方案号:{trialInfo.ResearchProgramNo} 项目的IRC供应商,诚邀您参加该项目IRC相关工作,欢迎您提供指导和建议,非常感谢! +
+ + + 查看并确认 + +
+
+ "; + + + + messageToSend.Body = builder.ToMessageBody(); + + using (var smtp = new MailKit.Net.Smtp.SmtpClient()) + { + + smtp.ServerCertificateValidationCallback = (s, c, h, e) => true; + + smtp.MessageSent += (sender, args) => + { + + _ = _trialSiteUserSurveyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Id, u => new TrialSiteUserSurvey() { IsGenerateSuccess = true, InviteState = TrialSiteUserStateEnum.HasSend, ConfirmTime = null, RejectReason = String.Empty, SystemUserId = sysUserInfo.Id, ExpireTime = DateTime.Now.AddDays(7) }).Result; + + }; + + + await smtp.ConnectAsync("smtp.163.com", 465, SecureSocketOptions.StartTls); + + + await smtp.AuthenticateAsync("iracis_grr@163.com", "XLWVQKZAEKLDWOAH"); + + + await smtp.SendAsync(messageToSend); + + + await smtp.DisconnectAsync(true); + } + + } + + + return ResponseOutput.Ok(); + } + + #endregion + + + + } +} diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteUserSurveyService.cs b/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteUserSurveyService.cs new file mode 100644 index 0000000..77c475c --- /dev/null +++ b/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteUserSurveyService.cs @@ -0,0 +1,131 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:20:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Infrastructure; + +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// TrialSiteUserSurveyService + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialSiteUserSurveyService : BaseService, ITrialSiteUserSurveyService + { + private readonly IRepository _trialSiteUserSurveyRepository; + + public TrialSiteUserSurveyService(IRepository trialSiteUserSurveyRepository) + { + _trialSiteUserSurveyRepository = trialSiteUserSurveyRepository; + } + + [HttpGet("{trialSiteSurveyId:guid}")] + public async Task> GetTrialSiteUserSurveyList(Guid trialSiteSurveyId) + { + + var trialSiteUserSurveyQueryable = _trialSiteUserSurveyRepository.Where(t => t.TrialSiteSurveyId == trialSiteSurveyId) + //.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM, t => t.TrialSiteSurvey.State >= TrialSiteSurveyEnum.CRCSubmitted) + //.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM|| _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM, t => t.TrialSiteSurvey.State >= TrialSiteSurveyEnum.SPMApproved) + .ProjectTo(_mapper.ConfigurationProvider); + + return await trialSiteUserSurveyQueryable.ToListAsync(); + } + + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + [HttpPost("{trialId:guid}")] + public async Task AddOrUpdateTrialSiteUserSurvey(TrialSiteUserSurveyAddOrEdit addOrEditTrialSiteUserSurvey) + { + + if (await _trialSiteUserSurveyRepository.Where(t => t.Id == addOrEditTrialSiteUserSurvey.Id).AnyAsync(t => t.TrialSiteSurvey.State == TrialSiteSurveyEnum.PMCreatedAndLock)) + { + return ResponseOutput.NotOk("已锁定,不允许操作"); + } + + if (addOrEditTrialSiteUserSurvey.UserTypeId != null && ( _userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM)) + { + var existSysUser = await _repository.FirstOrDefaultAsync(t => t.EMail == addOrEditTrialSiteUserSurvey.Email && t.UserTypeId == addOrEditTrialSiteUserSurvey.UserTypeId); + + + if (existSysUser != null) + { + if (existSysUser.LastName != addOrEditTrialSiteUserSurvey.LastName || existSysUser.FirstName != addOrEditTrialSiteUserSurvey.FirstName) + { + return ResponseOutput.NotOk($"该用户在系统中账户名为:{existSysUser.LastName + " / " + existSysUser.FirstName} ,与填写信息存在不一致项, 现将界面信息修改为与系统一致,可进行保存", + new { existSysUser.LastName, existSysUser.FirstName, existSysUser.Phone,existSysUser.IsTestUser,existSysUser.IsZhiZhun }, ApiResponseCodeEnum.NeedTips); + } + + } + + } + + + if (addOrEditTrialSiteUserSurvey.IsGenerateAccount ) + { + var trialId= _trialSiteUserSurveyRepository.Where(t=>t.TrialSiteSurveyId == addOrEditTrialSiteUserSurvey.TrialSiteSurveyId).Select(t=>t.TrialSiteSurvey.TrialId).FirstOrDefault(); + + var trialType = _repository.Where(t => t.Id == trialId).Select(t => t.TrialType).FirstOrDefault(); + + var item = addOrEditTrialSiteUserSurvey; + + //找下系统中是否存在该用户类型的 并且邮箱 或者手机的账户 + var sysUserInfo = await _repository.Where(t => t.UserTypeId == item.UserTypeId && t.EMail == item.Email).Include(t => t.UserTypeRole).FirstOrDefaultAsync(); + + if (sysUserInfo != null) + { + + if (trialType == TrialType.OfficialTrial || trialType == TrialType.Training) + { + + if (sysUserInfo.IsTestUser) + { + throw new BusinessValidationFailedException("正式类型 、培训类型的项目 不允许加入测试用户 "); + + } + } + + if (trialType == TrialType.NoneOfficial) + { + + if (sysUserInfo.IsTestUser == false) + { + throw new BusinessValidationFailedException("测试项目 不允许加入正式用户 "); + + } + } + } + + + } + + var entity = await _trialSiteUserSurveyRepository.InsertOrUpdateAsync(addOrEditTrialSiteUserSurvey, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + [HttpDelete("{trialSiteUserSurveyId:guid}/{trialId:guid}")] + public async Task DeleteTrialSiteUserSurvey(Guid trialSiteUserSurveyId) + { + + if (await _trialSiteUserSurveyRepository.Where(t => t.Id == trialSiteUserSurveyId).AnyAsync(t => t.TrialSiteSurvey.State == TrialSiteSurveyEnum.PMCreatedAndLock)) + { + return ResponseOutput.NotOk("已锁定,不允许操作"); + } + + var success = await _trialSiteUserSurveyRepository.BatchDeleteNoTrackingAsync(t => t.Id == trialSiteUserSurveyId); + + return ResponseOutput.Result(success); + } + + + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/_MapConfig.cs b/IRaCIS.Core.Application/Service/SiteSurvey/_MapConfig.cs new file mode 100644 index 0000000..99ab33b --- /dev/null +++ b/IRaCIS.Core.Application/Service/SiteSurvey/_MapConfig.cs @@ -0,0 +1,77 @@ +using AutoMapper; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.AutoMapper +{ + /// + /// 映射配置 + /// + public partial class SiteSurveyConfig : Profile + { + public SiteSurveyConfig() + { + //编辑 + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + + CreateMap().ForMember(d => d.Email, t => t.MapFrom(t => t.EmailOrPhone)); + + + //列表 + CreateMap() + .ForMember(t=>t.EquipmentType,u=>u.MapFrom(d=>d.EquipmentType.Value)); + + + + CreateMap() + .ForMember(d => d.TrialSiteAliasName, u => u.MapFrom(s => s.TrialSite.TrialSiteAliasName)) + .ForMember(d => d.SiteName, u => u.MapFrom(s => s.Site.SiteName)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)); + + CreateMap() + .ForMember(t => t.TrialRoleName, u => u.MapFrom(d => d.TrialRoleName.Value)) + .ForMember(d => d.UserType, u => u.MapFrom(s => s.UserTypeRole.UserTypeShortName)); + + + CreateMap() + .ForMember(d => d.Sponsor, u => u.MapFrom(s => s.Sponsor.SponsorName)) + .ForMember(d => d.IndicationType, u => u.MapFrom(s => s.IndicationType.Value)) + .ForMember(d => d.TrialSiteSelectList, u => u.MapFrom(s => s.TrialSiteList)) + .ForMember(d => d.TrialId, u => u.MapFrom(s => s.Id)); + + CreateMap() + .ForMember(t=>t.IsHaveSiteSurveyRecord,u=>u.MapFrom(t=>t.TrialSiteSurveyList.Any())); + + CreateMap() + .ForMember(d => d.TrialSiteSurvey, u => u.MapFrom(s => s)) + .ForMember(d => d.TrialInfo, u => u.MapFrom(s => s.Trial)) + .ForMember(d => d.TrialSiteUserSurveyList, u => u.MapFrom(s => s.TrialSiteUserSurveyList)); + + + + + CreateMap(); + + CreateMap(); + + + CreateMap().ForMember(d => d.RealName, u => u.MapFrom(s => s.LastName + " / " + s.FirstName)); + + + + CreateMap() + .ForMember(t=>t.TrialSiteSurvey,u=>u.MapFrom(c=>c.TrialSiteSurvey)) + .ForMember(t => t.TrialRoleName, u => u.MapFrom(d => d.TrialRoleName.Value)) + .ForMember(d => d.UserType, u => u.MapFrom(s => s.UserTypeRole.UserTypeShortName)); + + + + + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Stat/DTO/StatisticsViewModel.cs b/IRaCIS.Core.Application/Service/Stat/DTO/StatisticsViewModel.cs new file mode 100644 index 0000000..a08ddc9 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Stat/DTO/StatisticsViewModel.cs @@ -0,0 +1,293 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + + public class EnrollStatByReviewerDTO + { + public Guid Id { get; set; } + public string Hospital { get; set; } = string.Empty; + public string ChineseName { get; set; } = string.Empty; + + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + + public string FullName { get; set; } = string.Empty; + public string ReviewerCode { get; set; } = string.Empty; + + public int Pending { get; set; } + public int Approved { get; set; } + public int Reading { get; set; } + public int Finished { get; set; } + public int Total { get; set; } + public double EntryRate + { + get + { + if (Total == 0) + { + return 0; + } + return Math.Round(((Reading + Finished) / (double)Total) * 100, 1, MidpointRounding.AwayFromZero); + ; + } + } + + } + + + public class EnrollStatByTrialDTO + { + public string TrialCode { get; set; } = string.Empty; + public Guid TrialId { get; set; } + public string Indication { get; set; } = string.Empty; + //public Guid CroId { get; set; } + public string Cro { get; set; } = string.Empty; + + public DateTime CreateTime { get; set; } + + public int Expedited { get; set; } + + public int EnrollCount { get; set; } + + public List ReviewerNameCNList=new List(); + + public List ReviewerNameList=new List(); + } + + + #region 参与项目统计 + + + public class ParticipateQueryDto:PageInput + { + public string UserInfo { get; set; } = string.Empty; + public string OrganizationName { get; set; } = string.Empty; + + } + + public class UserParticipateTrialStat + { + public Guid UserId { get; set; } + public string OrganizationName { get; set; } = string.Empty; + + public string UserCode { get; set; } = string.Empty; + + public string Name { get; set; } = string.Empty; + + public string UserName { get; set; } = string.Empty; + + public string Phone { get; set; } = string.Empty; + + public string Email { get; set; } = string.Empty; + + public int TrialCount { get; set; } + + } + + public class UserParticipateTrialDetail + { + public Guid TrialId { get; set; } + public string Code { get; set; } = string.Empty; + public string Indication { get; set; } = string.Empty; + + public int Expedited { get; set; } + + public string CROName { get; set; } = string.Empty; + public string UserType { get; set; } = string.Empty; + } + + #endregion + + + + #region 统计 20200413 + public class WorkloadByTrialAndReviewerDTO + { + public Guid Id { get; set; } + public string TrialCode { get; set; } = string.Empty; + public Guid TrialId { get; set; } + public string Indication { get; set; } = string.Empty; + public Guid? CroId { get; set; } + public string Cro { get; set; } = string.Empty; + public string ChineseName { get; set; } = string.Empty; + + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + + public string FullName { get; set; } = string.Empty; + public string ReviewerCode { get; set; } = string.Empty; + + public int Training { get; set; } + public int Downtime { get; set; } + public int Timepoint { get; set; } + public int TimepointIn24H { get; set; } + public int TimepointIn48H { get; set; } + public int Adjudication { get; set; } + public int AdjudicationIn24H { get; set; } + public int AdjudicationIn48H { get; set; } + public int Global { get; set; } + + public int RefresherTraining { get; set; } + + public int PersonalTotal { get; set; } + } + + + public class StatisticsQueryDTO : PageInput + { + public Guid? CroId { get; set; } = Guid.Empty; + public string TrialCode { get; set; } = string.Empty; + public string Reviewer { get; set; } = string.Empty; + public DateTime BeginDate { get; set; } = DateTime.Now; + public DateTime EndDate { get; set; } = DateTime.Now; + public int StatType { get; set; } + + //医生付费类型 CN US + public int? Nation { get; set; } + public AttendedReviewerType? AttendedReviewerType { get; set; } + } + + public class StatisticsWorkloadQueryParam : PageInput + { + public Guid? CroId { get; set; } + public string TrialCode { get; set; } = string.Empty; + public string Reviewer { get; set; } = string.Empty; + public DateTime BeginDate { get; set; } = DateTime.Now; + public DateTime EndDate { get; set; } = DateTime.Now; + public int StatType { get; set; } + + public Guid? HospitalId { get; set; } + } + + public class RevenuesStatQueryDTO : PageInput + { + public Guid CroId { get; set; } = Guid.Empty; + + public string Reviewer { get; set; } = string.Empty; + public string TrialCode { get; set; } = string.Empty; + public DateTime BeginDate { get; set; } = DateTime.Now; + public DateTime EndDate { get; set; } = DateTime.Now; + + public int StatType { get; set; } + } + + + public class EnrollStatByReviewerQueryDTO : PageInput + { + public Guid? HospitalId { get; set; } = Guid.Empty; + public string Reviewer { get; set; } = string.Empty; + public DateTime BeginDate { get; set; } = DateTime.Now; + public DateTime EndDate { get; set; } = DateTime.Now; + } + + + public class EnrollStatByTrialQueryDTO : PageInput + { + public string TrialCode { get; set; } = string.Empty; + + public string Indication { get; set; } = string.Empty; + public DateTime? BeginDate { get; set; } = DateTime.Now; + public DateTime? EndDate { get; set; } = DateTime.Now; + + public Guid? CROId { get; set; } + + public int? Expedited { get; set; } + } + #endregion + + #region dashbord + + /// + /// 读片数量分类统计 + /// + public class ReadingDataDTO + { + public int Timepoint { get; set; } + public int Adjudication { get; set; } + public int Global { get; set; } + } + + public class ReadingDataMonthDTO : ReadingDataDTO + { + public string Month { get; set; } = String.Empty; + } + + public class ReadingDataRankDTO : ReadingDataDTO + { + public int TotalReadingCount { get; set; } + public Guid ReviewerId { get; set; } + public string ReviewerCode { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + public string ChineseName { get; set; } = String.Empty; + } + + public class RankReviewersDTO + { + public Guid? RankId { get; set; } + public string RankName { get; set; } = String.Empty; + public string RankNameAbbreviation + { + get + { + var arr = RankName.Split(' '); + return arr[0]; + } + } + public int ReviewerCount { get; set; } + } + + public class EnrollDataDTO + { + public int Year { get; set; } + public int Month { get; set; } + public string QuarterStr => Year + "-Q" + (Month / 3 + (Month % 3 == 0 ? 0 : 1)); + public string YearMonth => new DateTime(Year, Month, 1).ToString("yyyy-MM"); + public int EnrollCount { get; set; } + } + + + public class EnrollQuartDataDTO + { + //public int Year { get; set; } + + public string ViewStr { get; set; } = String.Empty; + + public int EnrollCount { get; set; } + } + + public class LatestWorkLoadDTO : ReadingDataDTO + { + public int TotalReadingCount => Timepoint + Adjudication + Global; + public Guid ReviewerId { get; set; } + public string ReviewerCode { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + public string ChineseName { get; set; } = String.Empty; + public string TrialCode { get; set; } = String.Empty; + } + + + + + public class TrialDataRankDTO + { + public Guid ReviewerId { get; set; } + public string ReviewerCode { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + public string ChineseName { get; set; } = String.Empty; + + public int TrialCount { get; set; } + } + + #endregion + + + + +} diff --git a/IRaCIS.Core.Application/Service/Stat/IStatisticsService.cs b/IRaCIS.Core.Application/Service/Stat/IStatisticsService.cs new file mode 100644 index 0000000..c597a32 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Stat/IStatisticsService.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface IStatisticsService + { + Task> GetWorkloadByTrialAndReviewer(StatisticsWorkloadQueryParam param); + + PageOutput GetEnrollStatByTrial(EnrollStatByTrialQueryDTO param); + + PageOutput GetParticipateTrialStat(ParticipateQueryDto param); + + List GetParticipateTrialList(Guid userId); + + #region Dashboard 数据统计 + /// 按类型统计读片数量 + ReadingDataDTO GetReadingDataByType(); + + /// 按月份统计读片数量 + List GetReadingDataByMonth(int monthCount); + + /// 读片数量排行 + List GetReadingDataRank(int topCount); + + /// 按Position统计 Reviewers 数量 + List GetReviewersByRank(); + + /// 每月入组人次 + List GetEnrollDataByQuarter(int quarterCount, int monthCount); + + /// 参与项目数排行 + List GetTrialCountRank(int topCount); + + /// 最新工作量 (已确定的) + List GetLatestWorkLoadList( int searchCount); + #endregion + + Task> GetEnrollStatByReviewer(EnrollStatByReviewerQueryDTO enrollTrialStatisticsQueryParam); + } +} diff --git a/IRaCIS.Core.Application/Service/Stat/StatisticsService.cs b/IRaCIS.Core.Application/Service/Stat/StatisticsService.cs new file mode 100644 index 0000000..c13ae83 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Stat/StatisticsService.cs @@ -0,0 +1,852 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using System.Linq.Expressions; +using System.Linq.Dynamic.Core; + +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Application.Services +{ /// + /// Dashboard统计、全局工作量统计、入组两个维度统计(按照项目、按照人) + /// + + [ApiExplorerSettings(GroupName = "Dashboard&Statistics")] + public class StatisticsService : BaseService, IStatisticsService + { + private readonly IRepository _doctorRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _enrollRepository; + private readonly IRepository _workloadRepository; + private readonly IRepository _croCompanyRepository; + private readonly IRepository _dictionaryRepository; + private readonly IRepository _hospitalRepository; + private readonly IRepository _enrollDetailRepository; + private readonly IRepository _userRepository; + private readonly IRepository _userTrialRepository; + + + public StatisticsService(IRepository doctorRepository, IRepository trialRepository, + IRepository intoGroupRepository, IRepository workloadRepository, + IRepository croCompanyRepository, + IRepository dictionaryRepository, + IRepository hospitalRepository, + IRepository enrollDetailRepository, IRepository userRepository, + IRepository userTrialRepository) + { + _doctorRepository = doctorRepository; + _trialRepository = trialRepository; + _enrollRepository = intoGroupRepository; + _workloadRepository = workloadRepository; + _croCompanyRepository = croCompanyRepository; + _dictionaryRepository = dictionaryRepository; + _hospitalRepository = hospitalRepository; + _enrollDetailRepository = enrollDetailRepository; + _userRepository = userRepository; + _userTrialRepository = userTrialRepository; + + } + + /// 根据项目和医生,分页获取工作量统计[New] + [HttpPost] + public async Task> GetWorkloadByTrialAndReviewer( + StatisticsWorkloadQueryParam param) + { + + + + var bDate = new DateTime(param.BeginDate.Year, param.BeginDate.Month, param.BeginDate.Day); + var eDate = new DateTime(param.EndDate.Year, param.EndDate.Month, param.EndDate.Day); + eDate = eDate.AddDays(1); + Expression> workloadLambda = x => x.DataFrom == (int)WorkLoadFromStatus.FinalConfirm; + workloadLambda = workloadLambda.And(x => x.WorkTime >= bDate && x.WorkTime < eDate); + + Expression> trialLambda = x => true; + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin) //超级管理员按照条件查询所有 + { + if (param.CroId != null) + { + trialLambda = trialLambda.And(u => u.CROId == param.CroId); + } + } + + else //不管是精鼎的pm还是我们的pm 还是运维人员 只能看到自己参与项目的统计 + { + List trialIdList = _userTrialRepository.Where(u => u.UserId == _userInfo.Id).Select(u => u.TrialId).ToList(); + trialLambda = trialLambda.And(u => trialIdList.Contains(u.Id)); + } + if (!string.IsNullOrWhiteSpace(param.TrialCode)) + { + trialLambda = trialLambda.And(u => u.TrialCode.Contains(param.TrialCode)); + } + + Expression> doctorLambda = x => true; + if (!string.IsNullOrWhiteSpace(param.Reviewer)) + { + + var reviewer = param.Reviewer.Trim(); + doctorLambda = doctorLambda.And(u => u.ChineseName.Contains(reviewer) + || u.FirstName.Contains(reviewer) + || u.LastName.Contains(reviewer) + || u.ReviewerCode.Contains(reviewer) + ); + + } + if (Guid.Empty != param.HospitalId && param.HospitalId != null) + { + doctorLambda = doctorLambda.And(u => u.HospitalId == param.HospitalId); + } + + var workloadQuery = from workLoad in _workloadRepository.Where(workloadLambda) + join trial in _trialRepository.Where(trialLambda) on workLoad.TrialId equals trial.Id + join doctor in _doctorRepository.Where(doctorLambda) on workLoad.DoctorId equals doctor.Id + group workLoad by new { workLoad.DoctorId, workLoad.TrialId } into gWorkLoad + + select new + { + DoctorId = gWorkLoad.Key.DoctorId, + TrialId = gWorkLoad.Key.TrialId, + Downtime = gWorkLoad.Sum(t => t.Downtime), + Training = gWorkLoad.Sum(t => t.Training), + Timepoint = gWorkLoad.Sum(t => t.Timepoint), + TimepointIn24H = gWorkLoad.Sum(t => t.TimepointIn24H), + TimepointIn48H = gWorkLoad.Sum(t => t.TimepointIn48H), + Global = gWorkLoad.Sum(t => t.Global), + Adjudication = gWorkLoad.Sum(t => t.Adjudication), + AdjudicationIn24H = gWorkLoad.Sum(t => t.AdjudicationIn24H), + AdjudicationIn48H = gWorkLoad.Sum(t => t.AdjudicationIn48H), + RefresherTraining = gWorkLoad.Sum(t => t.RefresherTraining), + + }; + var query = from w in workloadQuery + join trial in _trialRepository.Where(trialLambda) on w.TrialId equals trial.Id into t + from trialItem in t.DefaultIfEmpty() + join doctor in _doctorRepository.Where(doctorLambda) on w.DoctorId equals doctor.Id into tt + from doctorItem in tt.DefaultIfEmpty() + join cro in _croCompanyRepository.AsQueryable() on trialItem.CROId equals cro.Id into ttt + from croItem in ttt.DefaultIfEmpty() + select new WorkloadByTrialAndReviewerDTO + { + Id = Guid.NewGuid(), + TrialId = trialItem.Id, + Indication = trialItem.Indication, + TrialCode = trialItem.TrialCode, + CroId = trialItem.CROId, + Cro = croItem.CROName, + ReviewerCode = doctorItem.ReviewerCode, + ChineseName = doctorItem.ChineseName, + FirstName = doctorItem.FirstName, + LastName = doctorItem.LastName, + FullName = doctorItem.FullName, + Training = w.Training, + Timepoint = w.Timepoint, + TimepointIn24H = w.TimepointIn24H, + TimepointIn48H = w.TimepointIn48H, + Adjudication = w.Adjudication, + AdjudicationIn24H = w.AdjudicationIn24H, + AdjudicationIn48H = w.AdjudicationIn48H, + Global = w.Global, + Downtime = w.Downtime, + RefresherTraining = w.RefresherTraining, + + PersonalTotal = w.Timepoint + w.TimepointIn24H + w.TimepointIn48H + w.Adjudication + w.AdjudicationIn24H + + w.AdjudicationIn48H + w.Global + }; + + + return await query.ToPagedListAsync(param.PageIndex, param.PageSize, string.IsNullOrWhiteSpace(param.SortField) ? "ReviewerCode" : param.SortField, param.Asc); + + //var propName = param.SortField == "" ? "ReviewerCode" : param.SortField; + //query = param.Asc + // ? query.OrderBy(propName).ThenBy(t => t.TrialCode).ThenBy(u => u.ReviewerCode) + // : query.OrderByDescending(propName).ThenBy(t => t.TrialCode).ThenBy(u => u.ReviewerCode); + + + //if (propName == "FirstName" || propName == "LastName") + //{ + // query = param.Asc + // ? query.OrderBy(t => t.LastName).ThenBy(t => t.FirstName) + // : query.OrderByDescending(t => t.LastName).ThenBy(t => t.FirstName); + //} + //var count = query.Count(); + + //query = query + // .Skip((param.PageIndex - 1) * param.PageSize) + // .Take(param.PageSize); + //var doctorViewList = query.ToList(); + + //return new PageOutput(param.PageIndex, param.PageSize, count, doctorViewList); + } + + + + /// 项目入组 医生维度统计[New] + [HttpPost] + public async Task> GetEnrollStatByReviewer(EnrollStatByReviewerQueryDTO param) + { + var bDate = new DateTime(param.BeginDate.Year, param.BeginDate.Month, 1); + var eDate = new DateTime(param.EndDate.Year, param.EndDate.Month, 1); + eDate = eDate.AddMonths(1); + + Expression> enrollLambda = x => true; + enrollLambda = enrollLambda.And(x => x.EnrollTime >= bDate && x.EnrollTime < eDate); + + Expression> hospitalLambda = x => true; + if (Guid.Empty != param.HospitalId && param.HospitalId != null) + { + hospitalLambda = hospitalLambda.And(u => u.Id == param.HospitalId); + } + + Expression> doctorLambda = x => true; + if (!string.IsNullOrWhiteSpace(param.Reviewer)) + { + + var reviewer = param.Reviewer.Trim(); + doctorLambda = doctorLambda.And(u => u.ChineseName.Contains(reviewer) + || u.FirstName.Contains(reviewer) + || u.LastName.Contains(reviewer) + || u.ReviewerCode.Contains(reviewer) + ); + } + + var enrollQueryable = + from enroll in _enrollRepository.Where(enrollLambda) + group enroll by new { enroll.DoctorId } + into g + select new + { + DoctorId = g.Key.DoctorId, + Submitted = g.Sum(t => + t.EnrollStatus == EnrollStatus.HasCommittedToCRO ? 1 : 0), + Approved = g.Sum(t => + t.EnrollStatus == EnrollStatus.InviteIntoGroup ? 1 : 0), + Reading = g.Sum(t => + t.EnrollStatus == EnrollStatus.DoctorReading ? 1 : 0), + Finished = g.Sum(t => + t.EnrollStatus >= EnrollStatus.Finished ? 1 : 0), + }; + + + var query = from w in enrollQueryable + + join doctor in _doctorRepository.Where(doctorLambda) on w.DoctorId equals doctor.Id + join hospital in _hospitalRepository.Where(hospitalLambda) on doctor.HospitalId equals hospital.Id + select new EnrollStatByReviewerDTO + { + Id = Guid.NewGuid(), + Hospital = hospital.HospitalName, + ReviewerCode = doctor.ReviewerCode, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + FullName = doctor.FullName, + Pending = w.Submitted, + Approved = w.Approved, + Reading = w.Reading, + Finished = w.Finished, + + Total = w.Submitted + w.Approved + w.Reading + w.Finished + }; + + + var pageData = await query.ToPagedListAsync(param.PageIndex, param.PageIndex, param.SortField == "" ? "ReviewerCode" : param.SortField, param.Asc); + + if (param.SortField == "EntryRate") + { + pageData.CurrentPageData.OrderBy(t => t.EntryRate); + } + + return pageData; + + //var count = query.Count(); + //query = query + // .Skip((param.PageIndex - 1) * param.PageSize) + // .Take(param.PageSize); + //var list = query.ToList(); + + //if (param.SortField == "EntryRate") + //{ + // list = param.Asc + // ? list.OrderBy(t => t.EntryRate).ToList() + // : list.OrderByDescending(t => t.EntryRate).ToList(); + //} + + //return new PageOutput(param.PageIndex, param.PageSize, count, list); + } + + [HttpPost] + public PageOutput GetEnrollStatByTrial(EnrollStatByTrialQueryDTO param) + { + + #region 筛选条件 + + Expression> trialLambda = x => true; + + if (param.Expedited != null) + { + trialLambda = trialLambda.And(o => o.Expedited == param.Expedited); + } + + if (!string.IsNullOrEmpty(param.TrialCode)) + { + var code = param.TrialCode.Trim(); + trialLambda = trialLambda.And(o => o.TrialCode.Contains(code)); + } + + if (!string.IsNullOrWhiteSpace(param.Indication)) + { + var indication = param.Indication.Trim(); + trialLambda = trialLambda.And(o => o.Indication.Contains(indication)); + } + + if (param.CROId != null) + { + trialLambda = trialLambda.And(o => o.CROId == param.CROId); + } + + + if (param.BeginDate != null && param.EndDate != null) + { + var bDate = new DateTime(param.BeginDate.Value.Year, param.BeginDate.Value.Month, param.BeginDate.Value.Day); + var eDate = new DateTime(param.EndDate.Value.Year, param.EndDate.Value.Month, param.BeginDate.Value.Day); + eDate = eDate.AddMonths(1); + + trialLambda = trialLambda.And(o => o.CreateTime >= bDate && o.CreateTime < eDate); + + } + + #endregion + + var trialEnrollStatQuery = _enrollRepository.AsQueryable() + .Where(t => t.EnrollStatus >= EnrollStatus.ConfirmIntoGroup).GroupBy(t => t.TrialId).Select(g => new + { + TrialId = g.Key, + EnrollCount = g.Count() + }); + + var trialQuery = from trial in _trialRepository.Where(trialLambda) + join cro in _croCompanyRepository.AsQueryable() on trial.CROId equals cro.Id into t + from cro in t.DefaultIfEmpty() + + join trialEnroll in trialEnrollStatQuery on trial.Id equals trialEnroll.TrialId + select new EnrollStatByTrialDTO + { + TrialId = trial.Id, + Cro = cro != null ? cro.CROName : "", + Expedited = trial.Expedited, + Indication = trial.Indication, + TrialCode = trial.TrialCode, + EnrollCount = trialEnroll.EnrollCount, + CreateTime = trial.CreateTime + }; + + var propName = param.SortField == "" ? "TrialCode" : param.SortField; + trialQuery = param.Asc + ? trialQuery.OrderBy(propName) + : trialQuery.OrderBy(propName + " desc"); + + var count = trialQuery.Count(); + + trialQuery = trialQuery + .Skip((param.PageIndex - 1) * param.PageSize) + .Take(param.PageSize); + + var trialList = trialQuery.ToList(); + + var trialIds = trialList.Select(t => t.TrialId).ToList(); + + var enrollReviewerQuery = from enroll in _enrollRepository.AsQueryable() + .Where(t => t.EnrollStatus >= EnrollStatus.ConfirmIntoGroup && trialIds.Contains(t.TrialId)) + join reviewer in _doctorRepository.AsQueryable() on enroll.DoctorId equals reviewer.Id + + select new + { + TrialId = enroll.TrialId, + NameCN = reviewer.ChineseName, + Name = reviewer.LastName + " / " + reviewer.FirstName + }; + var enrollReviewerByTrialList = enrollReviewerQuery.ToList().GroupBy(t => t.TrialId).Select(g => new + { + TrialId = g.Key, + ReviewerNameCNList = g.Select(t => t.NameCN).ToList(), + ReviewerNameList = g.Select(t => t.Name).ToList() + }).ToList(); + + + trialList.ForEach(t => + { + var trialDoctors = enrollReviewerByTrialList.FirstOrDefault(u => u.TrialId == t.TrialId); + + if (trialDoctors != null) + { + t.ReviewerNameCNList = trialDoctors.ReviewerNameCNList; + t.ReviewerNameList = trialDoctors.ReviewerNameList; + } + }); + + + return new PageOutput(param.PageIndex, param.PageSize, count, trialList); + + } + + /// 用户参与项目 统计[New] + [HttpPost] + public PageOutput GetParticipateTrialStat(ParticipateQueryDto param) + { + + Expression> userLambda = x => true; + Expression> userTrialLambda = x => true; + if (!string.IsNullOrEmpty(param.UserInfo)) + { + var userInfo = param.UserInfo.Trim(); + userLambda = userLambda.And(o => o.UserCode.Contains(userInfo) || (o.LastName + ' ' + o.FirstName).Contains(userInfo) || o.UserName.Contains(userInfo)); + } + + if (!string.IsNullOrWhiteSpace(param.OrganizationName)) + { + var organizationName = param.OrganizationName.Trim(); + userLambda = userLambda.And(o => o.OrganizationName.Contains(organizationName)); + } + + var userTypeEnumStr = _userInfo.UserTypeEnumStr; + var userId = _userInfo.Id; + + + //PM 进来只能看到他负责的项目下的参与人员列表 + if (userTypeEnumStr == UserTypeEnum.ProjectManager.ToString()) + { + //参与到的项目 + var trialFilter = _userTrialRepository.Where(t => t.UserId == userId) + .Select(u => u.TrialId); + userTrialLambda = userTrialLambda.And(t => trialFilter.Contains(t.TrialId)); + } + //.Select(u => new { u.TrialId, u.UserId }).Distinct() + var trialStatQuery = _userTrialRepository.Where(userTrialLambda) + .GroupBy(t => t.UserId).Select( + g => new + { + UserId = g.Key, + TrialCount = g.Count() + }); + + var userQuery = from trialStat in trialStatQuery + join user in _userRepository.Where(userLambda) on trialStat.UserId equals user.Id + select new UserParticipateTrialStat + { + Email = user.EMail, + Name = user.LastName + ' ' + user.FirstName, + UserName = user.UserName, + OrganizationName = user.OrganizationName, + Phone = user.Phone, + TrialCount = trialStat.TrialCount, + UserCode = user.UserCode, + UserId = user.Id + }; + + var propName = param.SortField == "" ? "OrganizationName" : param.SortField; + userQuery = param.Asc + ? userQuery.OrderBy(propName) + : userQuery.OrderBy(propName + " desc"); + + var count = userQuery.Count(); + + userQuery = userQuery + .Skip((param.PageIndex - 1) * param.PageSize) + .Take(param.PageSize); + var list = userQuery.ToList(); + + return new PageOutput(param.PageIndex, param.PageSize, count, list); + } + /// 用户参与项目 列表[New] + [HttpGet("{userId:guid}")] + public List GetParticipateTrialList(Guid userId) + { + + Expression> userTrialLambda = x => x.UserId == userId; + + var userTypeEnum = _userInfo.UserTypeEnumStr; + var loginUserId = _userInfo.Id; + + + //PM 进来只能看到他负责的项目下的参与人员列表 + if (userTypeEnum == UserTypeEnum.ProjectManager.ToString()) + { + //参与到的项目 + var trialFilter = _userTrialRepository.Where(t => t.UserId == loginUserId) + .Select(u => u.TrialId); + userTrialLambda = userTrialLambda.And(t => trialFilter.Contains(t.TrialId)); + } + + var query = from userTrial in _userTrialRepository.Where(userTrialLambda) + join trial in _trialRepository.AsQueryable() on userTrial.TrialId equals trial.Id + join cro in _croCompanyRepository.AsQueryable() on trial.CROId equals cro.Id into cc + from cro in cc.DefaultIfEmpty() + select new UserParticipateTrialDetail() + { + Code = trial.TrialCode, + CROName = cro.CROName, + Expedited = trial.Expedited, + Indication = trial.Indication, + UserType = userTrial.User.UserTypeRole.UserTypeShortName, + TrialId = trial.Id + + }; + + return query.ToList(); + } + + + #region Dashboard 数据统计 + /// 读片数分类统计[New] + public ReadingDataDTO GetReadingDataByType() + { + int tp = 0; int ad = 0; int g = 0; + var finalConfirmWorkload = _workloadRepository.Where(u => u.DataFrom == (int)WorkLoadFromStatus.FinalConfirm).ToList(); + foreach (var item in finalConfirmWorkload) + { + tp += (item.Timepoint + item.TimepointIn24H + item.TimepointIn48H); + ad += (item.Adjudication + item.AdjudicationIn24H + item.AdjudicationIn48H); + g += item.Global; + } + return new ReadingDataDTO + { + Timepoint = tp, + Adjudication = ad, + Global = g + }; + } + /// 获取最近几个月份的数据[New] + [HttpGet, Route("{monthCount:int}")] + public List GetReadingDataByMonth(int monthCount) + { + DateTime now = DateTime.Now.AddMonths(-1); + DateTime eTime = new DateTime(now.Year, now.Month, 20); + + DateTime now6 = now.AddMonths(-1 * (monthCount - 1)); + DateTime bTime = new DateTime(now.AddMonths(-(monthCount - 1)).Year, + now.AddMonths(-(monthCount - 1)).Month, 1, 0, 0, 0, 0); + + var query = from workload in _workloadRepository + .Where(u => u.DataFrom == (int)WorkLoadFromStatus.FinalConfirm && + u.WorkTime >= bTime && u.WorkTime < eTime) + group workload by workload.YearMonth + into gWorkLoad + select new ReadingDataMonthDTO + { + Month = gWorkLoad.Key, + Timepoint = gWorkLoad.Sum(t => t.Timepoint) + gWorkLoad.Sum(t => t.TimepointIn24H) + + gWorkLoad.Sum(t => t.TimepointIn48H), + Adjudication = gWorkLoad.Sum(t => t.Adjudication) + gWorkLoad.Sum(t => t.AdjudicationIn24H) + + gWorkLoad.Sum(t => t.AdjudicationIn48H), + Global = gWorkLoad.Sum(t => t.Global) + }; + var workloadList = query.OrderByDescending(u => u.Month).ToList(); + List result = new List(); + + for (int i = 0; i < monthCount; i++) + { + var tempTime = now.AddMonths(-1 * i); + var existedItem = workloadList.Find(u => u.Month == + (tempTime.Year + "-" + tempTime.Month.ToString().PadLeft(2, '0'))); + if (existedItem != null) + { + result.Add(new ReadingDataMonthDTO + { + Month = existedItem.Month, + Timepoint = existedItem.Timepoint, + Adjudication = existedItem.Adjudication, + Global = existedItem.Global + }); + } + else + { + result.Add(new ReadingDataMonthDTO + { + Month = tempTime.Year + "-" + tempTime.Month.ToString().PadLeft(2, '0'), + Timepoint = 0, + Adjudication = 0, + Global = 0 + }); + } + } + return result; + } + /// 读片数量排行前几的数据[New] + [HttpGet("{topCount:int}")] + public List GetReadingDataRank(int topCount) + { + var query = from workload in _workloadRepository + .Where(u => u.DataFrom == (int)WorkLoadFromStatus.FinalConfirm) + group workload by workload.DoctorId + into gWorkLoad + + select new ReadingDataRankDTO + { + TotalReadingCount = gWorkLoad.Sum(t => t.Timepoint) + gWorkLoad.Sum(t => t.TimepointIn24H) + + gWorkLoad.Sum(t => t.TimepointIn48H) + gWorkLoad.Sum(t => t.Adjudication) + gWorkLoad.Sum(t => t.AdjudicationIn24H) + + gWorkLoad.Sum(t => t.AdjudicationIn48H) + gWorkLoad.Sum(t => t.Global), + ReviewerId = gWorkLoad.Key, + Timepoint = gWorkLoad.Sum(t => t.Timepoint) + gWorkLoad.Sum(t => t.TimepointIn24H) + + gWorkLoad.Sum(t => t.TimepointIn48H), + Adjudication = gWorkLoad.Sum(t => t.Adjudication) + gWorkLoad.Sum(t => t.AdjudicationIn24H) + + gWorkLoad.Sum(t => t.AdjudicationIn48H), + Global = gWorkLoad.Sum(t => t.Global) + }; + var workloadList = query.OrderByDescending(u => u.TotalReadingCount).Take(topCount).ToList(); + var reviewerList = from w in workloadList + join doctor in _doctorRepository.Where() on w.ReviewerId equals doctor.Id into tt + from doctorItem in tt.DefaultIfEmpty() + select new ReadingDataRankDTO + { + TotalReadingCount = w.TotalReadingCount, + ReviewerId = w.ReviewerId, + Timepoint = w.Timepoint, + Adjudication = w.Adjudication, + Global = w.Global, + ReviewerCode = doctorItem.ReviewerCode, + FirstName = doctorItem.FirstName, + LastName = doctorItem.LastName, + ChineseName = doctorItem.ChineseName + }; + return reviewerList.ToList(); + } + + /// 按Rank统计Reviewer 数量[New] + public List GetReviewersByRank() + { + var query = from q in (from reviewer in _doctorRepository.AsQueryable() + //.Find(u => u.ReviewStatus == 1 && u.CooperateStatus == 1) + group reviewer by reviewer.RankId + into gReviewer + select new + { + RankId = gReviewer.Key, + ReviewerCount = gReviewer.Count() + }) + + join dic in _dictionaryRepository.Where() on q.RankId equals dic.Id into tt + from dicItem in tt.DefaultIfEmpty() + select new RankReviewersDTO + { + RankId = q.RankId, + ReviewerCount = q.ReviewerCount, + RankName = dicItem == null ? "Other" : dicItem.Value + }; + + var rankList = query.ToList(); + var staffList = rankList.Where(u => u.RankName == "Staff" || u.RankName == "Other"); + int staffCount = 0; + foreach (var item in staffList) + { + staffCount += item.ReviewerCount; + } + rankList.RemoveAll(u => u.RankName == "Staff" || u.RankName == "Other"); + + rankList.Add(new RankReviewersDTO { RankId = Guid.NewGuid(), RankName = "Staff", ReviewerCount = staffCount }); + + return rankList; + } + /// 最近几个季度入组人次[New] type==0 按照月份 + [HttpGet("{type:int}/{count:int}")] + public List GetEnrollDataByQuarter(int type, int count) + { + //等于0按照月份 否则按照季度 + if (type != 0) + { + var quarterCount = count; + var year = DateTime.Now.AddMonths(-(quarterCount - 1) * 3 - DateTime.Now.Month % 3 + 1).Year; + var month = DateTime.Now.AddMonths(-(quarterCount - 1) * 3 - DateTime.Now.Month % 3 + 1).Month; + + var beginMonth = new DateTime(year, month, 1); + + var endMonth = DateTime.Now; + var querySql = from enrollDetail in _enrollDetailRepository.Where(t => + t.CreateTime > beginMonth && t.CreateTime < endMonth && + t.EnrollStatus == EnrollStatus.ConfirmIntoGroup) + select new + { + + OptTime = enrollDetail.CreateTime, + Year = enrollDetail.CreateTime.Year, + Month = enrollDetail.CreateTime.Month, + }; + + var result = querySql.GroupBy(t => new { t.Year, t.Month }).Select(s => new EnrollDataDTO + { + Year = s.Key.Year, + Month = s.Key.Month, + EnrollCount = s.Count() + }).ToList(); + + + #region 填充数据 + + List returnList = new List(); + for (int i = 0; i < DateTime.Now.Month % 3 + (quarterCount - 1) * 3; i++) + { + var tempTime = DateTime.Now.AddMonths(-1 * i); + var existedItem = result.FirstOrDefault(u => u.YearMonth == tempTime.ToString("yyyy-MM")); + if (existedItem != null) + { + returnList.Add(new EnrollDataDTO + { + Month = existedItem.Month, + Year = existedItem.Year, + EnrollCount = existedItem.EnrollCount + }); + } + else + { + returnList.Add(new EnrollDataDTO + { + Month = tempTime.Month, + Year = tempTime.Year, + EnrollCount = 0 + }); + } + } + + #endregion + + var returnResult = returnList.GroupBy(t => t.QuarterStr).Select(u => new EnrollQuartDataDTO() + { + ViewStr = u.Key, + EnrollCount = u.Sum(t => t.EnrollCount) + }).ToList(); + + return returnResult; + } + else + { + var year = DateTime.Now.AddMonths(-(count - 1)).Year; + var month = DateTime.Now.AddMonths(-(count - 1)).Month; + + var beginMonth = new DateTime(year, month, 1); + + var endMonth = DateTime.Now; + var querySql = from enrollDetail in _enrollDetailRepository.Where(t => + t.CreateTime > beginMonth && t.CreateTime < endMonth && + t.EnrollStatus == EnrollStatus.ConfirmIntoGroup) + select new + { + + OptTime = enrollDetail.CreateTime, + Year = enrollDetail.CreateTime.Year, + Month = enrollDetail.CreateTime.Month, + }; + + var result = querySql.GroupBy(t => new { t.Year, t.Month }).Select(s => new EnrollDataDTO + { + Year = s.Key.Year, + Month = s.Key.Month, + EnrollCount = s.Count() + }).ToList(); + + #region 填充数据 + + List returnList = new List(); + for (int i = 0; i < count; i++) + { + var tempTime = DateTime.Now.AddMonths(-1 * i); + var existedItem = result.FirstOrDefault(u => u.YearMonth == tempTime.ToString("yyyy-MM")); + if (existedItem != null) + { + returnList.Add(new EnrollDataDTO + { + Month = existedItem.Month, + Year = existedItem.Year, + EnrollCount = existedItem.EnrollCount + }); + } + else + { + returnList.Add(new EnrollDataDTO + { + Month = tempTime.Month, + Year = tempTime.Year, + EnrollCount = 0 + }); + } + } + + #endregion + + var returnResult = returnList.GroupBy(t => t.YearMonth).Select(u => new EnrollQuartDataDTO() + { + ViewStr = u.Key, + EnrollCount = u.Sum(t => t.EnrollCount) + }).ToList(); + + return returnResult; + } + + } + /// 参与项目数排行 [New] + [HttpGet("{topCount:int}")] + public List GetTrialCountRank(int topCount) + { + var queryList = (from enrollDetail in _enrollDetailRepository.Where(t => + t.EnrollStatus == EnrollStatus.ConfirmIntoGroup) + group enrollDetail by enrollDetail.DoctorId + into g + select new + { + ReviewerId = g.Key, + TrialCount = g.Count() + }).OrderByDescending(u => u.TrialCount).Take(topCount).ToList(); + + var trialDataRank = from stat in queryList + join doctor in _doctorRepository.AsQueryable() on stat.ReviewerId equals doctor.Id + select new TrialDataRankDTO() + { + TrialCount = stat.TrialCount, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + ReviewerId = doctor.Id, + ReviewerCode = doctor.ReviewerCode + }; + return trialDataRank.ToList(); + + } + /// 最新工作量 (已确定的)[New] + [HttpGet("{searchCount:int}")] + public List GetLatestWorkLoadList(int searchCount) + { + var workloadList = _workloadRepository.AsQueryable() + .Where(t => t.DataFrom == (int)WorkLoadFromStatus.FinalConfirm && + (t.Adjudication + t.AdjudicationIn24H + t.AdjudicationIn48H) > 0 && + (t.Timepoint + t.TimepointIn24H + t.TimepointIn48H) > 0) + .OrderByDescending(t => t.CreateTime).Take(searchCount).ToList(); + + + var reviewerList = from w in workloadList + join doctor in _doctorRepository.Where() on w.DoctorId equals doctor.Id into tt + from doctorItem in tt.DefaultIfEmpty() + join trial in _trialRepository.AsQueryable() on w.TrialId equals trial.Id into cc + from trialItem in cc.DefaultIfEmpty() + select new LatestWorkLoadDTO + { + + ReviewerId = w.DoctorId, + Timepoint = w.Timepoint + w.TimepointIn48H + w.TimepointIn24H, + Adjudication = w.Adjudication + w.AdjudicationIn48H + w.AdjudicationIn24H, + Global = w.Global, + ReviewerCode = doctorItem.ReviewerCode, + FirstName = doctorItem.FirstName, + LastName = doctorItem.LastName, + ChineseName = doctorItem.ChineseName, + + TrialCode = trialItem.TrialCode + }; + return reviewerList.ToList(); + + + } + + + + #endregion + + } +} diff --git a/IRaCIS.Core.Application/Service/Stat/_MapConfig.cs b/IRaCIS.Core.Application/Service/Stat/_MapConfig.cs new file mode 100644 index 0000000..38ef2f0 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Stat/_MapConfig.cs @@ -0,0 +1,15 @@ +using AutoMapper; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Application.Service +{ + public class StatConfig : Profile + { + public StatConfig() + { + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/InspectionViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/InspectionViewModel.cs new file mode 100644 index 0000000..b5fae16 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/InspectionViewModel.cs @@ -0,0 +1,31 @@ +using IRaCIS.Application.Contracts; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Core.Application.Contracts; + +public class SignDTO +{ + public string UserName { get; set; } = string.Empty; + + public string PassWord { get; set; } = string.Empty; + + public string SignText { get; set; } = string.Empty; + + + + + public Guid? TrialId { get; set; } + + public Guid? SignCodeId { get; set; } + + + public string SignCode { get; set; } = string.Empty; + + public Guid? SubjectVisitId { get; set; } + + + +} + + + diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/PersonalWorkstationViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/PersonalWorkstationViewModel.cs new file mode 100644 index 0000000..ad6e0cb --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/PersonalWorkstationViewModel.cs @@ -0,0 +1,78 @@ +using System; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Contracts +{ + + public class PersonalStataDTO + { + // 项目数量(排除 废除的) + public int? TrialCount { get; set; } + + //废除的 + public int? DeletedCount { get; set; } + + //项目数量 (包括废除的) + public int? TotalTrialCount => TrialCount + DeletedCount; + + + //总共需要签署项目文档的数量 (排除掉废除的 + 废除的但是签署了) + public int? TotalNeedSignTrialDocCount { get; set; } + + + //已签署的项目文档总数 + public int? HaveSignedTrialDocCount { get; set; } + + //现在需要签署的项目文档总数 + public int? WaitSignTrialDocCount => TotalNeedSignTrialDocCount - HaveSignedTrialDocCount; + + + + + public int? TotalNeedSignSystemDocCount { get; set; } + + public int? HaveSignedSystemDocCount { get; set; } + + public int? WaitSignSystemDocCount => TotalNeedSignSystemDocCount - HaveSignedSystemDocCount; + + + + //现在需要签署的总数(项目待签署 +系统待签署) + public int? TotalNeedSignDocCount => TotalNeedSignTrialDocCount + TotalNeedSignSystemDocCount; + + + public int? TotalApprovalRequiredCount { get; set; } + + + public int? TotalSystemNoticeCount { get; set; } + + public int? NeedReadSystemNoticeCount { get; set; } + + } + + public class TrialSiteSurveyStatQuery : PageInput + { + + } + + public class TrialSiteSurveyStat:TrialSelectDTO + { + public int? ApprovalRequiredCount { get; set; } + + + + public int? ApprovalRequiredSiteCount { get; set; } + } + + + public class DocSignStat : TrialSelectDTO + { + + public bool IsSystemDoc { get; set; } = false; + + public int? WaitSignCount { get; set; } + } + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs new file mode 100644 index 0000000..3a30814 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs @@ -0,0 +1,1007 @@ +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace IRaCIS.Core.Application.Contracts +{ + + public class BasicTrialConfig + { + public Guid TrialId { get; set; } + + /// + /// 患者编号具体规则 + /// + public string SubjectCodeRule { get; set; } = string.Empty; + + + + public bool IsSubjectSecondCodeView { get; set; } + + /// + /// 是否 验证拍片日期 + /// + public bool IsVerifyVisitImageDate { get; set; } = false; + + /// + /// 是否 提醒患者编号规则 + /// + public bool IsNoticeSubjectCodeRule { get; set; } = true; + + /// + /// 是否 有基准时间(首次给药时间) + /// + public bool IsHaveFirstGiveMedicineDate { get; set; } = true; + + /// + /// 是否有 患者年龄 + /// + public bool IsHaveSubjectAge { get; set; } = false; + + public bool IsTrialBasicLogicConfirmed { get; set; } = false; + + + /// + /// 出组后计划外检查批次名称 + /// + public string OutEnrollmentVisitName { get; set; } = "EOT"; + + public bool IsSubjectSexView { get; set; } = false; + + + public int ChangeDefalutDays { get; set; } = 5; + + + /// + /// 跨项目复制 + /// + public bool IsImageReplicationAcrossTrial { get; set; } = false; + + + public string BodyPartTypes { get; set; } = string.Empty; + + + public string Modalitys { get; set; } = string.Empty; + + public List ModalityList { get; set; } = new List(); + + + } + + public class TrialProcessConfig + { + + public List CriterionIds { get; set; } = new List(); + + public List TrialCriterionIds { get; set; } = new List(); + + + public Guid TrialId { get; set; } + + + /// + /// 临床信息传输 1:系统录入2:系统录入+PDF 0:无 + /// + public int ClinicalInformationTransmissionEnum { get; set; } = 1; + + + public List ClinicalDataTrialSetIds { get; set; } = new List(); + + /// + /// QC流程 0 不审,1 单审,2双审 + /// + public TrialQCProcess QCProcessEnum { get; set; } + + /// + /// 影像一致性核查 + /// + public bool IsImageConsistencyVerification { get; set; } = false; + + /// + /// 流程是否确认 + /// + public bool IsTrialProcessConfirmed { get; set; } = false; + + /// + /// 1 Mint、2 PACS + /// + + public int ImagePlatform { get; set; } = 1; + + //阅片方式 + public int ReadingMode { get; set; } + + ////阅片类型 + //public int ReadingType { get; set; } + + + //public bool IsGlobalReading { get; set; } = true; + + public bool? IsArbitrationReading { get; set; } + + public bool? IsClinicalReading { get; set; } + + + + + public int? DigitPlaces { get; set; } + + + + // ////读片任务显示是否顺序 + //// public bool IsReadingTaskViewInOrder { get; set; } = false; + + + /// + /// 仲裁规则 + /// + public ArbitrationRule ArbitrationRule { get; set; } + + + /// + /// 阅片是否显示患者信息 + /// + public bool IsReadingShowSubjectInfo { get; set; } = false; + + /// + /// 阅片是否显示既往结果 + /// + public bool IsReadingShowPreviousResults { get; set; } + + public ReadingTaskViewMethod ReadingTaskViewEnum { get; set; } + + + } + + + public class TrialTaskConfig + { + public Guid TrialId { get; set; } + public TaskAllocateObj TaskAllocateObjEnum { get; set; } + + public bool IsFollowVisitAutoAssign { get; set; } = true; + + public bool IsFollowGlobalVisitAutoAssign { get; set; } = true; + + public bool IsFollowJudgeTaskAutoAssign { get; set; } = true; + + + public TaskAllocateDefaultState FollowVisitAutoAssignDefaultState { get; set; } = TaskAllocateDefaultState.Allocated; + + public TaskAllocateDefaultState FollowGlobalVisitAutoAssignDefaultState { get; set; } = TaskAllocateDefaultState.Allocated; + + + public TaskAllocateDefaultState FollowJudgeTaskAutoAssignDefaultState { get; set; } = TaskAllocateDefaultState.Allocated; + + } + + public class TrialBasicConfigView : TrialTaskConfigView + { + + + /// + /// 流程是否确认 + /// + public bool IsTrialProcessConfirmed { get; set; } = false; + + + + //阅片方式 + public int ReadingMode { get; set; } + + + + + //public bool IsGlobalReading { get; set; } = true; + + public bool? IsArbitrationReading { get; set; } + + public bool? IsClinicalReading { get; set; } + + + public ArbitrationRule ArbitrationRule { get; set; } + + + } + + + public class TrialProcessConfigDTO + { + /// + /// QC流程 0 不审,1 单审,2双审 + /// + public TrialQCProcess QCProcessEnum { get; set; } = TrialQCProcess.DoubleAudit; + + public bool IsImageConsistencyVerification { get; set; } = true; + } + + public class TrialTaskConfigView : TrialTaskConfig + { + public ReadingTaskViewMethod ReadingTaskViewEnum { get; set; } + + public bool IsReadingTaskViewInOrder { get; set; } = true; + + //public ReadingMethod ReadingType { get; set; } + + /// + /// 阅片是否显示患者信息 + /// + public bool IsReadingShowSubjectInfo { get; set; } + + /// + /// 阅片是否显示既往结果 + /// + public bool IsReadingShowPreviousResults { get; set; } + + public ReadingTool ReadingTool { get; set; } + + + + + /// + /// QC流程 0 不审,1 单审,2双审 + /// + public TrialQCProcess QCProcessEnum { get; set; } = TrialQCProcess.DoubleAudit; + + /// + /// 影像一致性核查 + /// + public bool IsImageConsistencyVerification { get; set; } = true; + } + + public class TrialReadingTaskViewConfig + { + public Guid TrialId { get; set; } + + public ReadingTaskViewMethod ReadingTaskViewEnum { get; set; } + + public bool IsReadingTaskViewInOrder { get; set; } = true; + + /// + /// 阅片是否显示患者信息 + /// + public bool IsReadingShowSubjectInfo { get; set; } + + /// + /// 阅片是否显示既往结果 + /// + public bool IsReadingShowPreviousResults { get; set; } + } + + public class TrialJudgeTaskConfig + { + + public Guid TrialId { get; set; } + + public bool IsFollowJudgeTaskAutoAssign { get; set; } + + public TaskAllocateDefaultState FollowJudgeTaskAutoAssignDefaultState { get; set; } + } + + public class TrialUrgentConfig + { + public Guid TrialId { get; set; } + + public bool IsSubjectExpeditedView { get; set; } = false; + + /// + /// 是否有 入组评估确认 + /// + public bool IsEnrollementQualificationConfirm { get; set; } = false; + + /// + /// 流程配置是否确认 + /// + public bool IsTrialUrgentConfirmed { get; set; } = false; + + public bool IsUrgent { get; set; } + + public bool IsPDProgressView { get; set; } + + } + + public class TrialStateChangeDTO + { + public Guid Id { get; set; } + public Guid TrialId { get; set; } + + + public string OriginState { get; set; } = String.Empty; + + + public string NowState { get; set; } = String.Empty; + + + public string Reason { get; set; } = String.Empty; + + + public DateTime CreateTime { get; set; } + + + public Guid CreateUserId { get; set; } + + public string UserName { get; set; } = String.Empty; + + public string UserRealName { get; set; } = String.Empty; + + public string SignCode { get; set; } = string.Empty; + } + + public class GetTrialReadingInfoOutDto + { + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 是否有阅片期 + /// + public bool IsReadingPeriod { get; set; } = true; + + public bool IsSystemCriterion + { + get + { + return this.ReadingQuestionCriterionSystemId != null; + } + } + + /// + /// 阅片平台 + /// + public int ImagePlatform { get; set; } = 1; + + /// + /// 修约小数点 + /// + public int? DigitPlaces { get; set; } = 2; + + + /// + /// 系统标准ID + /// + public Guid? ReadingQuestionCriterionSystemId { get; set; } + + /// + /// 阅片模式 + /// + public ReadingMethod ReadingType { get; set; } + + public string GlobalUpdateType { get; set; } = string.Empty; + + /// + /// 任务组织级别 + /// + public ReadingTaskViewMethod ReadingTaskViewEnum { get; set; } + + /// + /// 影像是否有标注 + /// + public bool IsImageIabeled { get; set; } + + /// + /// IR阅片是否显示患者信息 + /// + public bool IsReadingShowSubjectInfo { get; set; } = false; + + /// + /// IR阅片是否显示既往结果 + /// + public bool IsReadingShowPreviousResults { get; set; } + + //读片任务显示是否顺序 + public bool IsReadingTaskViewInOrder { get; set; } = true; + + public List GlobalAssessTypeIds { get; set; } + + + /// + /// 全局阅片 + /// + public bool IsGlobalReading { get; set; } + + /// + /// 阅片工具 + /// + public ReadingTool? ReadingTool { get; set; } = IRaCIS.Core.Domain.Share.ReadingTool.Dicom; + + /// + /// 仲裁规则/对象 + /// + public ArbitrationRule ArbitrationRule { get; set; } + + + /// + /// 阅片信息签名时间 + /// + public DateTime? ReadingInfoSignTime { get; set; } + + public bool IsSign { + get { + return this.ReadingInfoSignTime != null; + } + } + + /// + /// eCRF报告是否显示在图像页面 + /// + public bool IseCRFShowInDicomReading { get; set; } = false; + + public bool IsClinicalReading { get; set; } + + /// + /// 仲裁阅片 + /// + public bool? IsArbitrationReading { get; set; } + + + /// + /// 是否必须全局阅片 + /// + public bool IsMustGlobalReading { get; set; } = false; + + + ///// + ///// 阅片模式 + ///// + //public ReadingMethod ReadingType { get; set; } + + ///// + ///// 全局阅片 + ///// + //public bool IsGlobalReading { get; set; } + + + + + /// + /// 肿瘤学阅片 原字段 IsClinicalReading + /// + public bool IsOncologyReading { get; set; } + + + + + + + + + } + + public class ReadingCriterionPageDto + { + /// + /// pageId + /// + public Guid Id { get; set; } + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 分页名称 + /// + public string PageName { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + + /// + /// 是否公共分页 + /// + public bool IsPublicPage { get; set; } + + + public int ShowOrder { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建用户ID + /// + public Guid CreateUserId { get; set; } + } + + public class TrialReadQuestion :ReadingQuestionTrial + { + /// + /// 分页名称 + /// + public string PageName { get; set; } + + /// + /// 父问题名称 + /// + public string ParentQuestionName { get; set; } + + /// + /// Parent字典code + /// + public string ParentDictionaryCode { get; set; } = string.Empty; + + /// + /// Parent问题类型 + /// + public TableQuestionType? ParentQuestionGenre { get; set; } + + + public int? RelevanceShowOrder { get; set; } + + + public int? ParentQuestionShowOrder { get; set; } + + } + + public class TrialJudgeQuestion + { + + + public Guid ReadingQuestionCriterionTrialId { get; set; } + + public Guid QuestionId { get; set; } + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + public string PageName { get; set; } + + /// + /// 答案分组 + /// + public string AnswerGroup { get; set; } = string.Empty; + + /// + /// 答案组合 + /// + public string AnswerCombination { get; set; } = string.Empty; + + /// + /// 裁判类型 + /// + public JudgeTypeEnum JudgeType { get; set; } + } + + public class TrialReadingCriterion + { + public Guid Id { get; set; } + + /// + /// 标准 + /// + public string CriterionName { get; set; } + } + + public class GetTrialConfirmCriterionListInDto + { + public Guid TrialId { get; set; } + } + + + public class GetTrialConfirmCriterionOutDto + { + public Guid TrialCriterionId { get; set; } + + + public string CriterionName { get; set; } + } + + public class TrialReadingInfoSignInDto + { + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + public class GetTrialReadingCriterionInfoOutDto + { + + /// + /// 阅片信息签名时间 + /// + public DateTime? ReadingInfoSignTime { get; set; } + + public bool IsSign + { + get + { + return this.ReadingInfoSignTime != null; + } + } + + + /// + /// 是否必须全局阅片 + /// + public bool IsMustGlobalReading { get; set; } = false; + + /// + /// 修约小数点 + /// + public int? DigitPlaces { get; set; } + + /// + /// eCRF报告是否显示在图像页面 + /// + public bool IseCRFShowInDicomReading { get; set; } = false; + + + public bool IsFromSystem { get; set; } = false; + + public FormType? FormType { get; set; } + + + public bool IsSystemCriterion { get; set; } + + /// + /// 项目标准Id + /// + public Guid? TrialCriterionId { get; set; } + + /// + /// 项目分页 + /// + public List ReadingCriterionPageList { get; set; } + + /// + /// 项目问题集合 + /// + public List TrialQuestionList { get; set; } + + + } + + + public class SetOncologySetInDto + { + /// + /// 项目标准ID + /// + public Guid TrialReadingCriterionId { get; set; } + + public List OncologyAssessIds { get; set; } + + /// + /// 评估结果 + /// + public string EvaluationResult { get; set; } = string.Empty; + + /// + /// 评估原因 + /// + public string EvaluationReason { get; set; } = string.Empty; + + /// + /// 是否显示详情 + /// + public bool IsShowDetail { get; set; } + } + + public class GetOncologySetOutDto + { + + public bool IsSign { get; set; } + + public List OncologyAssessIds { get; set; } + + public bool IsSystemCriterion { get; set; } + + /// + /// 评估原因 + /// + public string EvaluationReason { get; set; } = string.Empty; + + + + } + + public class GetGlobalReadingOutDto + { + public List GlobalAssessTypes { get; set; } + + + /// + /// 全局阅片 + /// + public bool IsGlobalReading { get; set; } + + public bool IsSystemCriterion { get; set; } + + public bool IsSign { get; set; } + } + + public class AsyncTrialCriterionDictionaryInDto + { + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + } + + public class SetGlobalReadingInfoInDto + { + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + public List GlobalAssessList { get; set; } + + + ///// + ///// 全局阅片 + ///// + //public bool IsGlobalReading { get; set; } + } + + + public class SetGlobalReading + { + /// + /// DictionaryId + /// + public Guid DictionaryId { get; set; } + + /// + /// IsBaseLineUse + /// + public bool IsBaseLineUse { get; set; } + + /// + /// IsBaseUse + /// + public bool IsFollowVisitUse { get; set; } + } + + public class GetOncologySetInDto + { + public Guid TrialReadingCriterionId { get; set; } + } + + public class GetTrialReadingInfoInDto + { + + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + + } + + public class SetTrialReadingCriterionInDto + { + public Guid TrialReadingCriterionId { get; set; } + + + + + /// + /// 表单类型 + /// + + public FormType FormType { get; set; } + + + ///// + ///// 修约小数点 + ///// + //public int? DigitPlaces { get; set; } + + + public bool IsSignSave { get; set; } = false; + } + + public class SetCriterionReadingInfoInDto + { + + /// + /// 项目标准ID + /// + public Guid TrialReadingCriterionId { get; set; } + + /// + /// eCRF报告是否显示在图像页面 + /// + public bool IseCRFShowInDicomReading { get; set; } + + /// + /// 是否有阅片期 + /// + public bool IsReadingPeriod { get; set; } = true; + + + public string GlobalUpdateType { get; set; } = string.Empty; + + /// + /// 修约小数点 + /// + public int? DigitPlaces { get; set; } = 2; + + /// + /// 阅片工具 + /// + public ReadingTool? ReadingTool { get; set; } + + + /// + /// 阅片平台 + /// + public ImagePlatform ImagePlatform { get; set; } = ImagePlatform.MINT; + + + /// + /// 任务组织级别 + /// + public ReadingTaskViewMethod ReadingTaskViewEnum { get; set; } + + + /// + /// 影像是否有标注 + /// + public bool IsImageIabeled { get; set; } + + //读片任务显示是否顺序 + public bool IsReadingTaskViewInOrder { get; set; } = true; + + /// + /// IR阅片是否显示患者信息 + /// + public bool IsReadingShowSubjectInfo { get; set; } = false; + + /// + /// IR阅片是否显示既往结果 + /// + public bool IsReadingShowPreviousResults { get; set; } + + + + /// + /// 仲裁规则/对象 + /// + public ArbitrationRule ArbitrationRule { get; set; } + + + + /// + /// 阅片模式 + /// + public ReadingMethod ReadingType { get; set; } = ReadingMethod.Double; + + /// + /// 全局阅片 + /// + public bool IsGlobalReading { get; set; } = true; + + /// + /// 仲裁阅片 + /// + public bool IsArbitrationReading { get; set; } = true; + + + /// + /// 肿瘤学阅片 原字段 IsClinicalReading + /// + public bool IsOncologyReading { get; set; } + + } + + public class SignConfirmDTO + { + [NotDefault] + public Guid TrialId { get; set; } + + //[NotDefault] + //public Guid SignId { get; set; } + + + [NotDefault] + public string SignCode { get; set; } = string.Empty; + } + + public class TrialConfigDTO : BasicTrialConfig + { + public List TrialCriterionIds { get; set; } = new List(); + + public List TrialCriterionNames { get; set; } = new List(); + + public List ClinicalDataTrialSetIds { get; set; } = new List(); + + public int ClinicalInformationTransmissionEnum { get; set; } + + public bool IsTrialProcessConfirmed { get; set; } + + + public bool IsTrialUrgentConfirmed { get; set; } + + + + /// + /// QC流程 0 不审,1 单审,2双审 + /// + public TrialQCProcess QCProcessEnum { get; set; } + + /// + /// 影像一致性核查 + /// + public bool IsImageConsistencyVerification { get; set; } = false; + + //阅片方式 + public int ReadingMode { get; set; } + + public bool IsSubjectExpeditedView { get; set; } = false; + + /// + /// 是否有 入组评估确认 + /// + public bool IsEnrollementQualificationConfirm { get; set; } = false; + + public bool IsUrgent { get; set; } + + public bool IsPDProgressView { get; set; } + + + public bool IsQCQuestionConfirmed { get; set; } + + public string TrialStatusStr { get; set; } = string.Empty; + + public List ClinicalDataSetNames { get; set; } = new List(); + + + //public bool IsTrialStart { get; set; } = false; + + + //public List CriterionIds { get; set; } = new List(); + + + //public bool IsGlobalReading { get; set; } = true; + + //public bool? IsArbitrationReading { get; set; } + + //public bool? IsClinicalReading { get; set; } + + + //public ArbitrationRule ArbitrationRule { get; set; } + + + //public int DigitPlaces { get; set; } + + ///// + ///// 1 Mint、2 PACS + ///// + + //public int ImagePlatform { get; set; } = 1; + + + + ////阅片类型 + //public int ReadingType { get; set; } + + //读片任务显示是否顺序 + //public bool IsReadingTaskViewInOrder { get; set; } + + + + //public string DocumentConfirmSignText { get; set; } = string.Empty; + //public string ImageQCSignText { get; set; } = string.Empty; + //public string PreliminaryAuditReuploadText { get; set; } = string.Empty; + //public string ReviewAuditReuploadText { get; set; } = string.Empty; + //public string CheckBackText { get; set; } = string.Empty; + //public string CheckCloseText { get; set; } = string.Empty; + //public string ChallengeTypes { get; set; } = string.Empty; + //public bool IsImageExport { get; set; } = false; + //public bool IsCRAAuditClinicalInformation { get; set; } = false; + //public string TrialSiteSurveyUserRoles { get; set; } = string.Empty; + //public string TrialSiteSurveyEquipmentType { get; set; } = string.Empty; + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialExternalUserViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialExternalUserViewModel.cs new file mode 100644 index 0000000..ca5d4e6 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialExternalUserViewModel.cs @@ -0,0 +1,189 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-04 13:33:45 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using IRaCIS.Core.Application.Contracts.DTO; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Application.ViewModel +{ + /// TrialExternalUserView 列表视图模型 + public class TrialExternalUserView : TrialExternalUserAddOrEdit + { + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + public bool IsSystemUser { get; set; } + + + public Guid SystemUserId { get; set; } + + + + [JsonIgnore] + public TrialExternalUserStateEnum InviteState { get; set; } + + + public TrialExternalUserStateEnum State + { + get + { + if (InviteState == TrialExternalUserStateEnum.HasSend && ExpireTime != null && ExpireTime < DateTime.Now) + { + return TrialExternalUserStateEnum.OverTime; + } + else + { + return InviteState; + } + } + } + + + + + + + public DateTime? ExpireTime { get; set; } + + public bool? IsJoin { get; set; } + + public DateTime? ConfirmTime { get; set; } + + public string RejectReason { get; set; } = string.Empty; + } + + ///TrialExternalUserQuery 列表查询参数模型 + public class TrialExternalUserQuery + { + public Guid TrialId { get; set; } + + public string Phone { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + + public string Name { get; set; } = string.Empty; + + + } + + /// TrialExternalUserAddOrEdit 列表查询参数模型 + public class TrialExternalUserAddOrEdit + { + public Guid? Id { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + + public string OrganizationName { get; set; } = String.Empty; + + + [NotDefault] + public Guid UserTypeId { get; set; } + + public string Email { get; set; } = string.Empty; + + public string Phone { get; set; } = string.Empty; + + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + + + + //public bool IsSendEmail { get; set; } + + //public string RouteUrl { get; set; } = string.Empty; + + + } + + + public class TrialExternalUserAddAndSendEmail: TrialExternalUserAddOrEdit + { + public bool IsSendEmail { get; set; } + + public string BaseUrl { get; set; } = string.Empty; + + public string RouteUrl { get; set; } = string.Empty; + } + + + public class TrialExternalUserConfirm + { + public string BaseUrl { get; set; } = string.Empty; + + [NotDefault] + public Guid Id { get; set; } + + public bool? IsJoin { get; set; } + + public DateTime? ConfirmTime { get; set; } + + public string RejectReason { get; set; } = string.Empty; + + } + + public class TrialSiteUserSurveyUserConfirm : TrialExternalUserConfirm + { + //[NotDefault] + //public Guid TrialId { get; set; } + + //[NotDefault] + //public Guid SiteId { get; set; } + } + + + public class TrialInfoWithPreparationInfo : TrialExternalUserConfirm + { + + public string ExperimentName { get; set; } = string.Empty; + + public string TrialCode { get; set; } = string.Empty; + + public string Indication { get; set; } = string.Empty; + + public string ResearchProgramNo { get; set; } = string.Empty; + + public Guid UserId { get; set; } + + public Guid TrialId { get; set; } + } + + public class TrialExternalUserSendEmail + { + [NotDefault] + public Guid TrialId { get; set; } + + public string BaseUrl { get; set; } = string.Empty; + + public string RouteUrl { get; set; } = string.Empty; + + public List SendUsers { get; set; } = new List(); + } + + public class UserEmail + { + public Guid Id { get; set; } + + public string Email { get; set; } = string.Empty; + + public bool IsSystemUser { get; set; } + + [NotDefault] + public Guid SystemUserId { get; set; } + + } + + + + +} + + diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialResearchCenter.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialResearchCenter.cs new file mode 100644 index 0000000..65d28aa --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialResearchCenter.cs @@ -0,0 +1,28 @@ +using System; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + public class TrialResearchCenterDTO + { + public Guid Id { get; set; } + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public string MainCenter { get; set; } = string.Empty; + public DateTime OptTime { get; set; } + public int State { get; set; } + } + + public class TrialResearchCenterDetailDTO : TrialResearchCenterDTO + { + public string TrialCode { get; set; } = string.Empty; + public string TrialName { get; set; } = string.Empty; + public string ResearchCenterName { get; set; } = string.Empty; + } + + public class TrialCenterQueryDTO : PageInput + { + public Guid TrialId { get; set; } + public Guid CenterId { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteViewModel.cs new file mode 100644 index 0000000..4d6f03f --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteViewModel.cs @@ -0,0 +1,128 @@ +using System; +using IRaCIS.Core.Infrastructure.Extention; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Core.Application.Contracts.DTO +{ + + + public class SiteCrcQueryDTO : PageInput + { + public Guid TrialId { get; set; } = Guid.Empty; + public string SiteName { get; set; } = String.Empty; + public string TrialSiteAliasName { get; set; } = String.Empty; + + public bool? IsDeleted { get; set; } + + public string TrialSiteCode { get; set; } = String.Empty; + + public string UserKeyInfo { get; set; } = String.Empty; + + + } + + public class SiteCRCExportQueryDTO + { + public Guid TrialId { get; set; } = Guid.Empty; + public string SiteName { get; set; } = String.Empty; + public string TrialSiteAliasName { get; set; } = String.Empty; + + public bool? IsDeleted { get; set; } + + public string TrialSiteCode { get; set; } = String.Empty; + + public string UserKeyInfo { get; set; } = String.Empty; + } + + public class TrialSiteCommand + { + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + + public string SiteName { get; set; } + + public bool IsDeleted { get; set; } = true; + + } + + public class EditTrialSiteCommand + { + + [NotDefault] + public Guid TrialId { get; set; } + + public Guid Id { get; set; } + + public string TrialSiteAliasName { get; set; } = String.Empty; + + public string TrialSiteCode { get; set; } = String.Empty; + + public bool IsDeleted { get; set; } + + } + + public class AssginSiteCRCCommand + { + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid UserId { get; set; } + public DateTime? CreateTime { get; set; } = DateTime.Now; + public string UserRealName { get; set; } = String.Empty; + + } + + public class AssginSiteCRCListDTO : AssginSiteCRCCommand + { + public Guid UserTypeId { get; set; } + public string UserType { get; set; } = String.Empty; + public UserTypeEnum UserTypeEnum { get; set; } + public string OrganizationName { get; set; } = String.Empty; + //public string UserRealName { get; set; } + public string UserName { get; set; } = String.Empty; + public DateTime UpdateTime { get; set; } = DateTime.Now; + + public bool IsSelect { get; set; } + + + public string Phone { get; set; } = String.Empty; + public string EMail { get; set; } = string.Empty; + + } + + + public class TrialSiteQuery : PageInput + { + public Guid TrialId { get; set; } + + public string SiteName { get; set; } = String.Empty; + + public string AliasName { get; set; } = string.Empty; + + public string City { get; set; } = string.Empty; + + + public string Country { get; set; } = string.Empty; + + } + + public class TrialSiteScreeningDTO + { + public Guid Id { get; set; } + public string SiteName { get; set; } = String.Empty; + + public string AliasName { get; set; } = string.Empty; + public string SiteCode { get; set; } = String.Empty; + public string City { get; set; } = String.Empty; + public string Country { get; set; } = String.Empty; + public Guid? HospitalId { get; set; } + + + public string HospitalName { get; set; } = String.Empty; + + public bool IsSelect { get; set; } + } + + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialUserPreparationViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialUserPreparationViewModel.cs new file mode 100644 index 0000000..503e64c --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialUserPreparationViewModel.cs @@ -0,0 +1,70 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-24 13:22:20 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Core.Application.ViewModel +{ + /// TrialUserPreparation View 列表视图模型 + public class TrialUserPreparationView + { + public Guid UserId { get; set; } + public Guid TrialId { get; set; } + public DateTime UpdateTime { get; set; } + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime ExpireTime { get; set; } + + + + public string UserName { get; set; } + + public string UserRealName { get; set; } + + public string UserTypeShortName { get; set; } + + } + + ///TrialUserPreparation Query 列表查询参数模型 + public class TrialUserPreparationQuery + { + public Guid? UserTypeId { get; set; } + } + + /// TrialUserPreparation AddOrEdit 列表查询参数模型 + + + + + + + public class JoinCommand + { + public UserJoinTrialCommand TrialUserInfo { get; set; } + + //public TrialUserPreparationEdit JoinInfo { get; set; } + } + + + public class UserJoinTrialCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid UserId { get; set; } + + public Guid? SiteId { get; set; } + + public bool IsExternal { get; set; } + } + +} + + diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialViewModel.cs new file mode 100644 index 0000000..09f402f --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialViewModel.cs @@ -0,0 +1,225 @@ +using IRaCIS.Core.Infrastructure.Extention; +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; + +namespace IRaCIS.Application.Contracts +{ + + + public class TrialBaseModel + { + public int? PlanSiteCount { get; set; } + public int? PlanVisitCount { get; set; } + public TrialType TrialType { get; set; } + public Guid? Id { get; set; } + public string TrialCode { get; set; } = string.Empty; + public string Indication { get; set; } = string.Empty; + + //public Guid? ReviewTypeId { get; set; } = Guid.Empty; + + //public Guid? CriterionId { get; set; } = Guid.Empty; + public Guid? CROId { get; set; } = Guid.Empty; + public Guid? SponsorId { get; set; } = Guid.Empty; + public Guid? ReviewModeId { get; set; } = Guid.Empty; + public string Note { get; set; } = string.Empty; + public string ProjectCycle { get; set; } = string.Empty; + public int ExpectedPatients { get; set; } + public decimal TimePointsPerPatient { get; set; } + public int? GRRReviewers { get; set; } + public int TotalReviewers { get; set; } + public int? Expedited { get; set; } + public int? AttendedReviewerType { get; set; } + + //研究方案号 + public string ResearchProgramNo { get; set; } = string.Empty; + + //实验名称 + public string ExperimentName { get; set; } = string.Empty; + + //主研单位 + public string MainResearchUnit { get; set; } = string.Empty; + + // 负责人PI + public string HeadPI { get; set; } = string.Empty; + + public Guid DeclarationTypeId { get; set; } + + public Guid IndicationTypeId { get; set; } + public Guid? PhaseId { get; set; } + + + + } + + + public class QuestionTypeDto + { + public Guid DictionaryId { get; set; } + + public string Value { get; set; } = string.Empty; + + public string ValueCN { get; set; } = string.Empty; + + public bool IsSelect { get; set; } + } + + public class TrialSelectDTO + { + public Guid Id { get; set; } + + public string ExperimentName { get; set; } = string.Empty; + + public string TrialCode { get; set; } = string.Empty; + + public string Indication { get; set; } = string.Empty; + + public string ResearchProgramNo { get; set; } = string.Empty; + + public string CriterionName { get; set;} = string.Empty; + + + } + + public class TrialModalitySelectDto + { + public Guid ModalityId { get; set; } + public string Modality { get; set; } = string.Empty; + } + + public class TrialDetailDTO : TrialBaseModel + { + + [JsonIgnore] + public List DictionaryList { get; set; } = new List(); + + public List ModalityList => DictionaryList.Where(t => t.ParentCode == StaticData.Modality).OrderBy(t => t.ShowOrder).Select(t => t.ValueCN).ToList(); + public List ModalityIds => DictionaryList.Where(t => t.ParentCode == StaticData.Modality).OrderBy(t => t.ShowOrder).Select(t => t.Id).ToList(); + + public List CriterionList { get; set; } + + + public string Modalitys { get; set; } + + + //public List CriterionIds => DictionaryList.Where(t => t.ParentCode == StaticData.ReadingStandard).OrderBy(t => t.ShowOrder).Select(t => t.Id).ToList(); + + public List ReviewTypeIds => DictionaryList.Where(t => t.ParentCode == StaticData.ReviewType).OrderBy(t => t.ShowOrder).Select(t => t.Id).ToList(); + + public List ReviewTypeList => DictionaryList.Where(t => t.ParentCode == StaticData.ReviewType).OrderBy(t => t.ShowOrder).Select(t => t.ValueCN ).ToList(); + + //public string ReviewType { get; set; } = string.Empty; + + + + //统计字段 + public string Phase { get; set; } = string.Empty; + + public string DeclarationType { get; set; } = string.Empty; + + public string IndicationType { get; set; } = string.Empty; + + + public int EnrollStatus { get; set; } + + + + public string ReviewMode { get; set; } = string.Empty; + + + public int ReadingType { get; set; } + + + public string CRO { get; set; } = string.Empty; + public string Sponsor { get; set; } = string.Empty; + public int TrialEnrollStatus { get; set; } + public string TrialStatusStr { get; set; } = string.Empty; + + public DateTime? CreateTime { get; set; } + public bool IsDeleted { get; set; } + + public bool IsLocked { get; set; } + //public int? SubjectCount { get; set; } + + //public int? StudyCount { get; set; } = 0; + //public int? SiteCount { get; set; } = 0; + + + } + + public class TrialAndTrialStateVieModel + { + public TrialDetailDTO TrialView { get; set; } = new TrialDetailDTO(); + + public int TrialMaxState { get; set; } + + + } + + + + public class TrialCommand : TrialBaseModel + { + + public List ModalityIds { get; set; } = new List(); + + public List CriterionIds { get; set; } = new List(); + + public List ReviewTypeIds { get; set; } = new List(); + + } + + + + public class TrialQueryDTO : PageInput + { + public Guid? DeclarationTypeId { get; set; } + + public Guid? IndicationTypeId { get; set; } + public Guid? SponsorId { get; set; } + public Guid? CROId { get; set; } + + +public string HeadPI { get; set; } = String.Empty; + public string ResearchProgramNo { get; set; } = String.Empty; + + public string ExperimentName { get; set; } = String.Empty; + public List ModalityIds { get; set; }=new List(); + public List CriterionIds { get; set; } = new List(); + + public List ReviewTypeIds { get; set; } = new List(); + //public Guid? ReviewTypeId { get; set; } + + public string Code { get; set; } = String.Empty; + + public string Indication { get; set; } = String.Empty; + public Guid? PhaseId { get; set; } + public string TrialStatusStr { get; set; } = String.Empty; + + public DateTime? BeginDate { get; set; } + public DateTime? EndDate { get; set; } + public int? Expedited { get; set; } + public AttendedReviewerType? AttendedReviewerType { get; set; } + } + + public class ReviewerTrialQueryDTO : PageInput + { + public string Code { get; set; } = string.Empty; + public string Indication { get; set; } = string.Empty; + public EnrollStatus? EnrollStatus { get; set; } + public int? Expedited { get; set; } + } + + + public class TrialByStatusQueryDTO : PageInput + { + public Guid DoctorId { get; set; } + public int Status { get; set; } + } + + + + + + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs new file mode 100644 index 0000000..64e91f9 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs @@ -0,0 +1,353 @@ +using System.ComponentModel.DataAnnotations; +using IRaCIS.Core.Infrastructure.Extention; +using IRaCIS.Core.Domain.Share; +using Magicodes.ExporterAndImporter.Core; +using MiniExcelLibs.Attributes; +using Newtonsoft.Json; +using IRaCIS.Core.Application.Contracts; + +namespace IRaCIS.Application.Contracts +{ + + + + + public class UserTrialDTO : UserTrialCommand + { + [JsonIgnore] + public string State => IsDeleted ? "退出" : "加入"; + + public bool IsDeleted { get; set; } + + [ExcelFormat("yyyy-MM-dd hh:mm:ss")] + public DateTime? DeletedTime { get; set; } + + public Guid? SiteId { get; set; } + public string Phone { get; set; } = String.Empty; + public DateTime UpdateTime { get; set; } + + [ExcelFormat("yyyy-MM-dd hh:mm:ss")] + public DateTime CreateTime { get; set; } + + + public string UserType { get; set; } = String.Empty; + public string UserRealName { get; set; } = String.Empty; + + public string OrganizationName { get; set; } = String.Empty; + + public string UserName { get; set; } = String.Empty; + + public string EMail { get; set; } = string.Empty; + + } + + public class TrialUserSelct: UserTrialCommand + { + public string Phone { get; set; } = String.Empty; + + public string Email { get; set; } = string.Empty; + + public string RealName { get; set; } = String.Empty; + public string UserName { get; set; } = String.Empty; + } + + public class TrialMaintenanceDTO : UserTrialCommand + { + [JsonIgnore] + public int No { get; set; } + //For MiniExcel ExcelFormat + public string State => IsDeleted ? "退出" : "加入"; + + + [ValueMapping(text: "退出", true)] + [ValueMapping(text: "加入", false)] + public bool IsDeleted { get; set; } + + [ExporterHeader(Format = "yyyy-mm-DD hh:mm:ss")] + [ExcelFormat("yyyy-MM-dd hh:mm:ss")] + public DateTime? DeletedTime { get; set; } + + + [ExporterHeader(Format = "yyyy-mm-DD")] + [ExcelFormat("yyyy-MM-dd")] + public DateTime? RemoveTime { get; set; } + + + [ExporterHeader(Format = "yyyy-mm-DD")] + [ExcelFormat("yyyy-MM-dd")] + public DateTime? JoinTime { get; set; } + + + [ExporterHeader(Format = "yyyy-mm-DD hh:mm:ss")] + [ExcelFormat("yyyy-MM-dd hh:mm:ss")] + public DateTime CreateTime { get; set; } + + + public string Phone { get; set; } = String.Empty; + + public string EMail { get; set; } = string.Empty; + + + public DateTime UpdateTime { get; set; } = DateTime.Now; + + public Guid UserTypeId { get; set; } + public string UserType { get; set; } = String.Empty; + + public UserTypeEnum UserTypeEnum { get; set; } + + public string OrganizationName { get; set; } = String.Empty; + + public string UserRealName { get; set; } = String.Empty; + public string UserName { get; set; } = String.Empty; + + + } + + + public class TrialCRCUploadImageList_Export: ExcelExportInfo + { + + + + } + + public class ExcelExportInfo : TrialSelectDTO + { + public DateTime CurrentTime { get; set; } = DateTime.Now; + + + public object List { get; set; } + + } + + public class TrialUserExportDTO : ExcelExportInfo + { + + public List TrialUserList { get; set; } = new List(); + } + + + public class TrialSiteUserExportDto : ExcelExportInfo + { + + public List TrialSiteUserList { get; set; } = new List(); + } + public class SiteUserExportDTO : UserTrialDTO + { + [JsonIgnore] + public int No { get; set; } + + public string TrialSiteCode { get; set; } = String.Empty; + public string TrialSiteAliasName { get; set; } = String.Empty; + } + + public class TrialSiteUserSummaryExportDto : ExcelExportInfo + { + + public List TrialSiteUserList { get; set; } = new List(); + + } + + public class TrialSiteUserSummaryDto: TrialSiteUserSurveyView + { + [JsonIgnore] + public int No { get; set; } + + public string TrialSiteCode { get; set; } = String.Empty; + public string TrialSiteAliasName { get; set; } = String.Empty; + + public string UserRealName => LastName + " / " + FirstName; + + public string IsGenerateAccountStr => IsGenerateAccount ? "是" : "否"; + + public string StateStr => State.GetDescription(); + + } + + + + public class SiteCRCCommand : UserTrialCommand + { + public Guid SiteId { get; set; } + + public Guid UserTypeId { get; set; } + public string UserType { get; set; } = String.Empty; + public string UserRealName { get; set; } = String.Empty; + + + public UserTypeEnum UserTypeEnum { get; set; } + + + public string OrganizationName { get; set; } = String.Empty; + + public string UserName { get; set; } = String.Empty; + } + + + + public class UserTrialCommand + { + public Guid? Id { get; set; } + public Guid UserId { get; set; } + public Guid TrialId { get; set; } + } + + + + + + public class SiteStatDTO : SiteStatSimpleDTO + { + + + + public int? VisitCount { get; set; } + public int? SubjectCount { get; set; } + + + } + + public class SiteStatSimpleDTO + { + public Guid Id { get; set; } + + public Guid SiteId { get; set; } + + public int? UserCount { get; set; } + + public string TrialSiteCode { get; set; } = String.Empty; + public string TrialSiteAliasName { get; set; } = String.Empty; + + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public DateTime UpdateTime { get; set; } + + public string Site { get; set; } = String.Empty; + + public string SiteCode { get; set; } = String.Empty; + + + public string Hospital { get; set; } = String.Empty; + public string City { get; set; } = String.Empty; + public string Country { get; set; } = String.Empty; + + public string DirectorName { get; set; } = String.Empty; + public string DirectorPhone { get; set; } = String.Empty; + public string ContactName { get; set; } = String.Empty; + public string ContactPhone { get; set; } = String.Empty; + + public string Address { get; set; } = String.Empty; + + + public List UserNameList { get; set; } = new List(); + } + + + + + public class TrialMaintenanceExportQuery + { + public Guid TrialId { get; set; } = Guid.Empty; + public string UserRealName { get; set; } = string.Empty; + + public Guid? UserTypeId { get; set; } + + public string UserName { get; set; } = string.Empty; + + public string OrganizationName { get; set; } = String.Empty; + + public bool? IsDeleted { get; set; } + } + + public class TrialMaintenanceQuery : PageInput + { + public Guid TrialId { get; set; } = Guid.Empty; + public string UserRealName { get; set; } = string.Empty; + + public Guid? UserTypeId { get; set; } + + public string UserName { get; set; } = string.Empty; + + public string OrganizationName { get; set; } = String.Empty; + + public bool? IsDeleted { get; set; } + + } + + public class SiteCRCQuery : TrialMaintenanceQuery + { + + public Guid SiteId { get; set; } + + } + + + + public class TrialUserQuery : PageInput + { + public Guid TrialId { get; set; } + public string UserName { get; set; } = string.Empty; + + public string UserRealName { get; set; } = string.Empty; + + public UserTypeEnum? UserTypeEnum { get; set; } + + public string OrganizationName { get; set; } = String.Empty; + } + + public class TrialUserScreeningDTO : TrialUserAddCommand + { + + public int Sex { get; set; } // 1-男 2-女 + + public string Phone { get; set; } = string.Empty; + public string EMail { get; set; } = string.Empty; + + public string DepartmentName { get; set; } = String.Empty; + public string PositionName { get; set; } = String.Empty; + + public string UserName { get; set; } = String.Empty; + + public bool IsSelect { get; set; } + + + + public Guid UserTypeId { get; set; } + public string UserType { get; set; } = string.Empty; + public UserTypeEnum UserTypeEnum { get; set; } + + public string OrganizationName { get; set; } = string.Empty; + public string UserRealName { get; set; } = string.Empty; + + + } + + + public class TrialUserAddCommand + { + public Guid UserId { get; set; } + + public Guid TrialId { get; set; } + + + + } + + + public class UpdateTrialUserCommand + { + [NotDefault] + public Guid Id { get; set; } + + public Guid TrialId { get; set; } + + public bool IsDeleted { get; set; } + + public DateTime? RemoveTime { get; set; } + + public DateTime? JoinTime { get; set; } + } + +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialConfigService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialConfigService.cs new file mode 100644 index 0000000..1512ffb --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialConfigService.cs @@ -0,0 +1,25 @@ +using IRaCIS.Core.Application.Contracts; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Infra.EFCore; + +namespace IRaCIS.Application.Interfaces +{ + public interface ITrialConfigService + { + Task TrialReadingInfoSign(TrialReadingInfoSignInDto inDto); + Task UpdateTrialState(Guid trialId, string trialStatusStr, string? reason); + Task AbandonTrial(Guid trialId, bool isAbandon); + Task GetTrialConfigInfo(Guid trialId); + + Task ConfigTrialBasicInfo(BasicTrialConfig trialConfig); + + Task ConfigTrialProcessInfo(TrialProcessConfig trialConfig); + + Task ConfigTrialUrgentInfo(TrialUrgentConfig trialConfig); + + + Task TrialConfigSignatureConfirm(SignConfirmDTO signConfirmDTO); + + Task AsyncTrialCriterionDictionary(AsyncTrialCriterionDictionaryInDto inDto); + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialExternalUserService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialExternalUserService.cs new file mode 100644 index 0000000..065aeea --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialExternalUserService.cs @@ -0,0 +1,27 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-04 13:33:52 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// ITrialExternalUserService + /// + public interface ITrialExternalUserService + { + + + Task> GetTrialExternalUserList(TrialExternalUserQuery queryTrialExternalUser); + + Task AddOrUpdateTrialExternalUser(TrialExternalUserAddAndSendEmail addOrEditTrialExternalUser); + + Task DeleteTrialExternalUser(Guid trialExternalUserId, bool isSystemUser, + Guid systemUserId); + + Task UserConfirmJoinTrial(Guid trialId, Guid trialExternalUserId); + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialMaintenanceService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialMaintenanceService.cs new file mode 100644 index 0000000..99668f0 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialMaintenanceService.cs @@ -0,0 +1,14 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts.DTO; + +namespace IRaCIS.Application.Interfaces +{ + public interface ITrialMaintenanceService + { + Task AddTrialUsers(TrialUserAddCommand[] userTrialCommands); + Task DeleteMaintenanceUser(Guid id, bool isDelete); + Task> GetMaintenanceUserList(TrialMaintenanceQuery param); + Task> GetSiteCRCScreeningList(SiteCRCQuery param); + Task> GetTrialUserScreeningList(TrialUserQuery trialUserQuery); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialService.cs new file mode 100644 index 0000000..a746f59 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialService.cs @@ -0,0 +1,23 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Application.Interfaces +{ + + public interface ITrialService + { + bool TrialExpeditedChange { get; set; } + + Task> AddOrUpdateTrial(TrialCommand trialAddModel, ITrialConfigService _ITrialConfigService); + Task DeleteTrial(Guid trialId); + Task> GetReviewerTrialListByEnrollmentStatus(TrialByStatusQueryDTO param); + Task> GetTrialEnrollmentReviewerIds(Guid trialId); + Task GetTrialExpeditedState(Guid trialId); + Task GetTrialInfoAndLockState(Guid projectId); + Task GetTrialInfoAndMaxTrialState(Guid trialId); + Task> GetTrialList(TrialQueryDTO searchParam); + Task> GetTrialListByReviewer(ReviewerTrialQueryDTO searchModel); + Task GetTrialMaxState(Guid trialId); + Task UpdateEnrollStatus(Guid trialId, EnrollStatus status); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteService.cs new file mode 100644 index 0000000..3065548 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteService.cs @@ -0,0 +1,18 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.DTO; + +namespace IRaCIS.Core.Application.Interfaces +{ + public interface ITrialSiteService + { + Task AddTrialSites(List trialSites); + Task AssignSiteCRC(List trialSiteCRCList); + Task DeleteSiteCRC(Guid id, bool isDelete); + Task DeleteTrialSite(Guid id); + Task> GetSiteCRCList(SiteCrcQueryDTO param); + Task> GetSiteCRCSimpleList(SiteCrcQueryDTO param); + Task> GetTrialSiteScreeningList(TrialSiteQuery trialSiteQuery); + Task> GetTrialSiteSelect(Guid trialId); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs new file mode 100644 index 0000000..d841dd3 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs @@ -0,0 +1,188 @@ +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Core.Application +{ + [ApiExplorerSettings(GroupName = "Trial")] + public class PersonalWorkstation : BaseService + { + private readonly IRepository _trialRepository; + private readonly IRepository _trialUserRepository; + private readonly IRepository _trialDocumentRepository; + private readonly IRepository _systemDocumentRepository; + private readonly IRepository _systemNoticeRepository; + + public PersonalWorkstation(IRepository trialRepository, IRepository trialUserRepository, IRepository trialDocumentRepository, + IRepository systemDocumentRepository,IRepository systemNoticeRepository) + { + _trialRepository = trialRepository; + _trialUserRepository = trialUserRepository; + _trialDocumentRepository = trialDocumentRepository; + _systemDocumentRepository = systemDocumentRepository; + _systemNoticeRepository = systemNoticeRepository; + } + + /// + /// 个人面板 统计值 + /// + /// + public async Task GetBasicStat() + { + + return new PersonalStataDTO() + { + //正参与的数量 + TrialCount = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin + ? await _trialRepository.CountAsync() + : await _trialUserRepository.Where(t => t.UserId == _userInfo.Id).CountAsync(), + + DeletedCount = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin + ? await _trialRepository.AsQueryable(true).CountAsync(t => t.IsDeleted) + : await _trialUserRepository.AsQueryable(true).Where(t => t.UserId == _userInfo.Id && t.IsDeleted) + .CountAsync(), + + + TotalNeedSignTrialDocCount = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin + ? 0 + : await _trialDocumentRepository.AsQueryable(true).Where(t => t.Trial.TrialStatusStr != StaticData.TrialState.TrialStopped) + .Where(t => t.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)) + .Where(t => t.IsDeleted == false || (t.IsDeleted == true && t.TrialDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id && t.ConfirmTime != null))) + + .SelectMany(t => t.NeedConfirmedUserTypeList) + .CountAsync(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId), + + HaveSignedTrialDocCount = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin + ? 0 + //废除了 已经签署了也要算进去 + : await _trialDocumentRepository.AsQueryable(true).Where(t => t.Trial.TrialStatusStr != StaticData.TrialState.TrialStopped) + .Where( t => t.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)) + .Where(t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + .SelectMany(t => t.TrialDocConfirmedUserList) + .CountAsync(t => t.ConfirmUserId == _userInfo.Id && t.ConfirmTime !=null), + + TotalNeedSignSystemDocCount = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin + ? 0 + : await _systemDocumentRepository + .Where(t => t.IsDeleted == false || (t.IsDeleted == true && t.SystemDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id && t.ConfirmTime !=null))) + .SelectMany(t => t.NeedConfirmedUserTypeList) + .CountAsync(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId), + + + HaveSignedSystemDocCount = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin + ? 0 + : await _systemDocumentRepository + .Where(t => t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId)) + .SelectMany(t => t.SystemDocConfirmedUserList) + .CountAsync(t => t.ConfirmUserId == _userInfo.Id && t.ConfirmTime != null), + + TotalApprovalRequiredCount= + _userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM ? + _trialRepository.Where(t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id)).SelectMany(t=>t.TrialSiteSurveyList).Where(t => t.State == TrialSiteSurveyEnum.SPMApproved).Count() + : _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM|| _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM + ? _trialRepository.Where(t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id)).SelectMany(t => t.TrialSiteSurveyList).Where(t => t.State == TrialSiteSurveyEnum.CRCSubmitted).Count() + :0, + + + TotalSystemNoticeCount= _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin + ? 0:await _systemNoticeRepository.Where(t => t.NoticeUserTypeList.Any(t => t.UserTypeId == _userInfo.UserTypeId) && t.NoticeStateEnum== Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished) + + .CountAsync(), + + NeedReadSystemNoticeCount = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin + ? 0 : await _systemNoticeRepository.Where(t => t.NoticeUserTypeList.Any(t => t.UserTypeId == _userInfo.UserTypeId) && t.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished + && !t.NoticeUserReadList.Any(t=>t.CreateUserId==_userInfo.Id)) + .Where(t => t.EndDate == null || t.EndDate != null && t.EndDate > DateTime.Now) + .CountAsync(), + + + }; + + } + + + /// + /// Site 调研 每个项目 需要处理的审批统计 + /// + /// + /// + [HttpPost] + public async Task> GetSiteSurveyApprovalList(TrialSiteSurveyStatQuery query) + { + + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || + _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM || + _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM || + _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM) + { + return await _trialRepository + .Where(t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id)) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM, c => c.TrialSiteSurveyList.Where(t => t.State == TrialSiteSurveyEnum.SPMApproved).Count() > 0) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM, c => c.TrialSiteSurveyList.Where(t => t.State == TrialSiteSurveyEnum.CRCSubmitted).Count() > 0) + .ProjectTo(_mapper.ConfigurationProvider, new { userTypeEnumInt = _userInfo.UserTypeEnumInt, userId = _userInfo.Id }) + .OrderByDescending(t => t.ApprovalRequiredCount).ToPagedListAsync(query.PageIndex, query.PageSize, query.SortField, query.Asc); + } + + else + { + return new PageOutput(query.PageIndex, query.PageSize, 0, new List()); + + } + + } + + + /// + /// 需要签署文件数量 系统级别的在第一行 + /// + /// + /// + [HttpPost] + public async Task> GetTrialDocStatList(TrialSiteSurveyStatQuery query) + { + if (_userInfo.IsAdmin) + { + return new PageOutput(query.PageIndex, query.PageSize, 0, new List()); + } + else + { + var trialDocStat = await _trialRepository/*.AsQueryable(true)*/.Where(t=>t.TrialStatusStr != StaticData.TrialState.TrialStopped) + .Where(t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id)) + .WhereIf(!_userInfo.IsAdmin, c => c.TrialDocumentList.Where(t => t.IsDeleted == false && t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId) && !t.TrialDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id && t.ConfirmTime!=null)) + .Count() > 0) + .ProjectTo(_mapper.ConfigurationProvider, new { userTypeEnumInt = _userInfo.UserTypeEnumInt, userId = _userInfo.Id, userTypeId = _userInfo.UserTypeId }) + .OrderByDescending(t => t.WaitSignCount) + .ToPagedListAsync(query.PageIndex, query.PageSize , query.SortField, query.Asc); + + //var sysDocStat = new DocSignStat() + //{ + // IsSystemDoc = true, + // WaitSignCount = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin + // ? 0 + // : await _systemDocumentRepository + // .Where(t => + // t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == _userInfo.UserTypeId) && + // !t.SystemDocConfirmedUserList.Any(t => t.ConfirmUserId == _userInfo.Id)) + // .CountAsync() + //}; + //var list = trialDocStat.CurrentPageData.ToList(); + //list.Insert(0, sysDocStat); + //trialDocStat.CurrentPageData = list; + //trialDocStat.TotalCount++; + + + return trialDocStat; + } + + + + } + + + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs new file mode 100644 index 0000000..c5538d8 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs @@ -0,0 +1,1019 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Core.Application.Contracts; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using IRaCIS.Core.Domain.Share; +using EasyCaching.Core; +using IRaCIS.Core.Infrastructure; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Auth; +using Panda.DynamicWebApi.Attributes; +using IRaCIS.Core.Infra.EFCore.Common; +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using MassTransit; +using IRaCIS.Core.Application.Filter; +using static IRaCIS.Core.Domain.Share.StaticData; + +namespace IRaCIS.Core.Application +{ + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialConfigService : BaseService, ITrialConfigService + { + private readonly IRepository _trialRepository; + private readonly IRepository _trialQCQuestionRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _readingQuestionTrialRepository; + private readonly IRepository _trialCriterionDictionaryCode; + private readonly IRepository _systemCriterionDictionaryCode; + private readonly IRepository _readingQuestionCriterionSystemRepository; + private readonly IRepository _clinicalDataTrialSetRepository; + private readonly IRepository _readingCriterionPageRepository; + private readonly IEasyCachingProvider _provider; + private readonly IOrganInfoService _iOrganInfoService; + private readonly IRepository _taskAllocationRuleRepository; + private readonly IRepository _readingCriterionDictionaryRepository; + private readonly IReadingQuestionService iReadingQuestionService; + + + private readonly IRepository _readingTaskQuestionAnswer; + + private readonly IRepository _visitTaskRepository; + private readonly IRepository _readingTableQuestionAnswerRepository; + private readonly IRepository _readingTableAnswerRowInfoRepository; + + private readonly IRepository _readingTableQuestionTrialRepository; + + public TrialConfigService(IRepository trialRepository, + IRepository trialQCQuestionRepository, + IRepository readingQuestionCriterionTrialRepository, + IRepository readingQuestionTrialRepository, + IRepository trialCriterionDictionaryCode, + IRepository systemCriterionDictionaryCode, + IRepository readingQuestionCriterionSystemRepository, + IRepository clinicalDataTrialSetRepository, + IRepository readingCriterionPageRepository, + IRepository taskAllocationRuleRepository, + IRepository readingCriterionDictionaryRepository, + IReadingQuestionService iReadingQuestionService, + IEasyCachingProvider provider, + IOrganInfoService iOrganInfoService, + + IRepository visitTaskRepository, + IRepository readingTableQuestionTrialRepository, + IRepository readingTableQuestionAnswerRepository, + IRepository readingTableAnswerRowInfoRepository, + IRepository readingTaskQuestionAnswer + + ) + { + _trialRepository = trialRepository; + _taskAllocationRuleRepository = taskAllocationRuleRepository; + this._readingCriterionDictionaryRepository = readingCriterionDictionaryRepository; + this.iReadingQuestionService = iReadingQuestionService; + this._trialQCQuestionRepository = trialQCQuestionRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this._readingQuestionTrialRepository = readingQuestionTrialRepository; + this._trialCriterionDictionaryCode = trialCriterionDictionaryCode; + this._systemCriterionDictionaryCode = systemCriterionDictionaryCode; + this._readingQuestionCriterionSystemRepository = readingQuestionCriterionSystemRepository; + this._clinicalDataTrialSetRepository = clinicalDataTrialSetRepository; + this._provider = provider; + this._iOrganInfoService = iOrganInfoService; + this._readingTableQuestionTrialRepository = readingTableQuestionTrialRepository; + this._readingCriterionPageRepository = readingCriterionPageRepository; + + this._visitTaskRepository = visitTaskRepository; + this._readingTableQuestionAnswerRepository = readingTableQuestionAnswerRepository; + this._readingTableAnswerRowInfoRepository = readingTableAnswerRowInfoRepository; + this._readingTaskQuestionAnswer = readingTaskQuestionAnswer; + } + + + /// + /// 阅片信息签名验证接口 + /// + /// + /// + /// + [HttpPost] + public async Task TrialReadingInfoSignVerify(TrialReadingInfoSignInDto inDto) + { + var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).FirstOrDefaultAsync(); + var existsJudge = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == trialCriterion.Id && x.IsJudgeQuestion && x.JudgeType == JudgeTypeEnum.None) + .WhereIf(trialCriterion.FormType == FormType.SinglePage, x => x.ReadingCriterionPageId == null) + .WhereIf(trialCriterion.FormType == FormType.MultiplePage, x => x.ReadingCriterionPageId != null) + .AnyAsync(); + + if (existsJudge && trialCriterion.IsArbitrationReading) + { + throw new BusinessValidationFailedException("有裁判问题未配置产生裁判阅片任务的条件,操作失败!"); + } + + return ResponseOutput.Ok(true); + } + + /// + /// 获取项目已经确认的标准 + /// + /// + [HttpPost] + public async Task> GetTrialConfirmCriterionList(GetTrialConfirmCriterionListInDto inDto) + { + List result = await _readingQuestionCriterionTrialRepository.Where(x => x.TrialId == inDto.TrialId && x.IsConfirm) + .Select(x => new GetTrialConfirmCriterionOutDto() + { + TrialCriterionId = x.Id, + CriterionName = x.CriterionName + }).ToListAsync(); + + return result; + } + + /// + /// 阅片信息签名 + /// + /// + /// + [NonDynamicMethod] + public async Task TrialReadingInfoSign(TrialReadingInfoSignInDto inDto) + { + var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).FirstOrDefaultAsync(); + + var existsJudge = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == trialCriterion.Id && x.IsJudgeQuestion && x.JudgeType == JudgeTypeEnum.None) + .WhereIf(trialCriterion.FormType == FormType.SinglePage, x => x.ReadingCriterionPageId == null) + .WhereIf(trialCriterion.FormType == FormType.MultiplePage, x => x.ReadingCriterionPageId != null) + .AnyAsync(); + + if (existsJudge && trialCriterion.IsArbitrationReading) + { + throw new BusinessValidationFailedException("有裁判问题未配置产生裁判阅片任务的条件,操作失败!"); + } + + await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(x => x.Id == inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() + { + IsSigned = true, + ReadingInfoSignTime = DateTime.Now + }); + + var result = await _trialRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(result); + } + + /// + /// 获取项目阅片信息 + /// + /// + /// + [HttpPost] + public async Task GetCriterionReadingInfo(GetTrialReadingInfoInDto inDto) + { + + + + GetTrialReadingInfoOutDto trialInfo = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).ProjectTo(_mapper.ConfigurationProvider).FirstNotNullAsync(); + + if (trialInfo.ReadingTool == null) + { + trialInfo.ReadingTool = ReadingTool.Dicom; + } + + return trialInfo; + } + + /// + /// 同步项目标准字典信息 + /// + /// + /// + [HttpPost] + public async Task AsyncTrialCriterionDictionary(AsyncTrialCriterionDictionaryInDto inDto) + { + var trialCriterion = await _readingQuestionCriterionTrialRepository.FindAsync(inDto.TrialReadingCriterionId); + + if (trialCriterion.ReadingQuestionCriterionSystemId != null) + { + + + if (trialCriterion.SynchronizeOriginalTime == null) + { + // 同步器官 + await _iOrganInfoService.SynchronizeSystemOrganToTrial(new SynchronizeSystemOrganToTrialInDto() + { + TrialReadingCriterionId = inDto.TrialReadingCriterionId, + SystemCriterionId = trialCriterion.ReadingQuestionCriterionSystemId + }); + + // 同步问题 + await iReadingQuestionService.SynchronizeCriterion(new SynchronizeCriterionInDto() + { + + TrialReadingCriterionId = inDto.TrialReadingCriterionId, + }); + + // 同步字典 + #region 同步字典 + + + + await _trialCriterionDictionaryCode.BatchDeleteNoTrackingAsync(x => x.TrialCriterionId == trialCriterion.Id); + await _readingCriterionDictionaryRepository.BatchDeleteNoTrackingAsync(x => x.CriterionId == trialCriterion.Id); + + var criterionDictionaryCodeList = await _systemCriterionDictionaryCode.Where(x => x.SystemCriterionId == trialCriterion.ReadingQuestionCriterionSystemId.Value) + .Select(x => new TrialCriterionDictionaryCode() + { + Code = x.Code, + TrialCriterionId = trialCriterion.Id, + }).ToListAsync(); + + criterionDictionaryCodeList.ForEach(x => + { + x.Id = NewId.NextGuid(); + }); + + + var criterionDictionaryList = await _readingCriterionDictionaryRepository.Where(x => x.CriterionId == trialCriterion.ReadingQuestionCriterionSystemId.Value) + .ToListAsync(); + criterionDictionaryList.ForEach(x => + { + x.Id = NewId.NextGuid(); + x.Dictionary = null; + x.IsSystemCriterion = false; + x.CriterionId = inDto.TrialReadingCriterionId; + }); + await _readingCriterionDictionaryRepository.AddRangeAsync(criterionDictionaryList); + + await _trialCriterionDictionaryCode.AddRangeAsync(criterionDictionaryCodeList); + #endregion + + + + var systemCriterion = await _readingQuestionCriterionSystemRepository.Where(x => x.Id == trialCriterion.ReadingQuestionCriterionSystemId).FirstNotNullAsync(); + + await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(x => x.Id == inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() + { + IsOncologyReading = systemCriterion.IsOncologyReading, + IseCRFShowInDicomReading = systemCriterion.IseCRFShowInDicomReading, + }); + + + await _readingCriterionDictionaryRepository.SaveChangesAsync(); + // 清除多余答案 -- + var questionQuery = _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == inDto.TrialReadingCriterionId).AsQueryable(); + var tableQuestionQuery = _readingTableQuestionTrialRepository.Where(x => x.TrialCriterionId == inDto.TrialReadingCriterionId).AsQueryable(); + + + await _readingTaskQuestionAnswer.BatchDeleteNoTrackingAsync(x => x.VisitTask.TrialReadingCriterionId == inDto.TrialReadingCriterionId && x.ReadingQuestionTrial.ReadingQuestionCriterionTrialId == inDto.TrialReadingCriterionId && questionQuery.Count(y => y.Id == x.ReadingQuestionTrialId) == 0); + await _readingTableAnswerRowInfoRepository.BatchDeleteNoTrackingAsync(x => x.VisitTask.TrialReadingCriterionId == inDto.TrialReadingCriterionId && x.ReadingQuestionTrial.ReadingQuestionCriterionTrialId == inDto.TrialReadingCriterionId && questionQuery.Count(y => y.Id == x.QuestionId) == 0); + await _readingTableQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => x.VisitTask.TrialReadingCriterionId == inDto.TrialReadingCriterionId && x.ReadingQuestionTrial.ReadingQuestionCriterionTrialId == inDto.TrialReadingCriterionId && x.ReadingTableQuestionTrial.TrialCriterionId == inDto.TrialReadingCriterionId && tableQuestionQuery.Count(y => y.Id == x.TableQuestionId) == 0); + + + } + + + await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(x => x.Id == inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() + { + SynchronizeOriginalTime = DateTime.Now + }); + + await _readingCriterionDictionaryRepository.SaveChangesAsync(); + + + + } + + return ResponseOutput.Ok(true); + } + + /// + /// 修改全局阅片配置信息 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetGlobalReadingInfo(SetGlobalReadingInfoInDto inDto) + { + + //await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() + //{ + // IsGlobalReading = inDto.IsGlobalReading + + //}); + await _readingCriterionDictionaryRepository.BatchDeleteNoTrackingAsync(x => x.CriterionId == inDto.TrialReadingCriterionId && x.ParentCode == ReadingCommon.CriterionDictionary.GlobalAssess); + await _readingCriterionDictionaryRepository.AddRangeAsync(inDto.GlobalAssessList.Select(x => new ReadingCriterionDictionary + { + CriterionId = inDto.TrialReadingCriterionId, + DictionaryId = x.DictionaryId, + IsBaseLineUse = x.IsBaseLineUse, + IsFollowVisitUse = x.IsFollowVisitUse, + IsSystemCriterion = false, + ParentCode = ReadingCommon.CriterionDictionary.GlobalAssess + })); + + var result = await _readingCriterionDictionaryRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(result); + } + + /// + /// 获取全局阅片信息 + /// + /// + /// + [HttpPost] + public async Task GetGlobalReadingInfo(GetOncologySetInDto inDto) + { + + var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).FirstNotNullAsync(); + GetGlobalReadingOutDto result = new GetGlobalReadingOutDto() + { + IsGlobalReading = trialCriterion.IsGlobalReading, + IsSystemCriterion = trialCriterion.ReadingQuestionCriterionSystemId != null, + IsSign = trialCriterion.IsSigned, + GlobalAssessTypes = await _readingCriterionDictionaryRepository.Where(x => x.CriterionId == inDto.TrialReadingCriterionId + && x.ParentCode == ReadingCommon.CriterionDictionary.GlobalAssess + ) + .Select(x => new CriterionDictionaryInfo() + { + Id = x.Id, + DictionaryId = x.DictionaryId, + ChildGroup = x.Dictionary.ChildGroup, + IsBaseLineUse = x.IsBaseLineUse, + IsFollowVisitUse = x.IsFollowVisitUse, + Code = x.Dictionary.Code, + Description = x.Dictionary.Description, + ShowOrder = x.Dictionary.ShowOrder, + ParentCode = x.Dictionary.Parent.Code, + Value = x.Dictionary.Value, + ValueCN = x.Dictionary.ValueCN + }).OrderBy(x => x.ParentCode).ThenBy(x => x.ShowOrder).ToListAsync() + }; + return result; + } + + /// + /// 获取项目肿瘤学配置 + /// + /// + /// + [HttpPost] + public async Task GetOncologySet(GetOncologySetInDto inDto) + { + + var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).FirstNotNullAsync(); + return new GetOncologySetOutDto + { + EvaluationReason = trialCriterion.EvaluationReason.IsNullOrEmpty() ? ReadingCommon.EvaluationReason : trialCriterion.EvaluationReason, + OncologyAssessIds = await _readingCriterionDictionaryRepository.Where(x => x.CriterionId == inDto.TrialReadingCriterionId && x.ParentCode == ReadingCommon.CriterionDictionary.OncologyAssess).Select(x => x.DictionaryId).ToListAsync(), + IsSystemCriterion = trialCriterion.ReadingQuestionCriterionSystemId != null, + IsSign = trialCriterion.IsSigned, + }; + } + + /// + /// 设置项目肿瘤学配置 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetOncologySet(SetOncologySetInDto inDto) + { + + + await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() + { + EvaluationResult = inDto.EvaluationResult, + EvaluationReason = inDto.EvaluationReason, + IsShowDetail = inDto.IsShowDetail, + + }); + + await _readingCriterionDictionaryRepository.BatchDeleteNoTrackingAsync(x => x.CriterionId == inDto.TrialReadingCriterionId && x.ParentCode == ReadingCommon.CriterionDictionary.OncologyAssess); + + await _readingCriterionDictionaryRepository.AddRangeAsync(inDto.OncologyAssessIds.Select(x => new ReadingCriterionDictionary + { + CriterionId = inDto.TrialReadingCriterionId, + DictionaryId = x, + IsSystemCriterion = false, + ParentCode = ReadingCommon.CriterionDictionary.OncologyAssess + })); + + var result = await _readingQuestionCriterionTrialRepository.SaveChangesAsync(); + return ResponseOutput.Ok(result); + } + + /// + /// 获取项目裁判信息 + /// + /// + /// + [HttpPost] + public async Task<(List, object)> GetTrialReadingJudgeList(GetTrialReadingInfoInDto inDto) + { + var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).FirstOrDefaultAsync(); + + var judgeQuestionList = await _readingQuestionTrialRepository + .WhereIf(trialCriterion.FormType == FormType.SinglePage, x => x.ReadingCriterionPageId == null) + .WhereIf(trialCriterion.FormType == FormType.MultiplePage, x => x.ReadingCriterionPageId != null) + .Where(x => x.ReadingQuestionCriterionTrial.IsConfirm && x.IsJudgeQuestion && x.TrialId == inDto.TrialId && x.ReadingQuestionCriterionTrialId == trialCriterion.Id) + .Select(x => new TrialJudgeQuestion() + { + AnswerCombination = x.AnswerCombination, + AnswerGroup = x.AnswerGroup, + JudgeType = x.JudgeType, + QuestionId = x.Id, + PageName = x.ReadingCriterionPage.PageName, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + ReadingQuestionCriterionTrialId = x.ReadingQuestionCriterionTrialId + }).ToListAsync(); + + + + return (judgeQuestionList, new + { + + IsSign = trialCriterion.IsSigned, + + }); + } + + /// + /// 获取项目标准分页信息 + /// + /// + /// + [HttpPost] + public async Task> GetReadingCriterionPageList(GetTrialReadingInfoInDto inDto) + { + return await _readingCriterionPageRepository.Where(x => x.TrialId == inDto.TrialId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + /// + /// 获取项目阅片标准信息 + /// + /// + /// + [HttpPost] + public async Task<(GetTrialReadingCriterionInfoOutDto, bool)> GetTrialReadingCriterionInfo(GetTrialReadingInfoInDto inDto) + { + GetTrialReadingCriterionInfoOutDto result = new GetTrialReadingCriterionInfoOutDto(); + result.ReadingCriterionPageList = await _readingCriterionPageRepository.Where(x => x.TrialId == inDto.TrialId).ProjectTo(_mapper.ConfigurationProvider).OrderBy(x => x.ShowOrder).ToListAsync(); + var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).FirstOrDefaultAsync(); + result.ReadingInfoSignTime = trialCriterion.ReadingInfoSignTime; + + result.DigitPlaces = trialCriterion.DigitPlaces; + result.TrialCriterionId = trialCriterion.Id; + result.FormType = trialCriterion.FormType; + result.IsFromSystem = trialCriterion.ReadingQuestionCriterionSystemId != null; + result.IsMustGlobalReading = trialCriterion.IsMustGlobalReading; + result.IsSystemCriterion = trialCriterion.ReadingQuestionCriterionSystemId != null; + + + result.TrialQuestionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrial.IsConfirm && x.TrialId == inDto.TrialId && x.ReadingQuestionCriterionTrialId == result.TrialCriterionId + && x.ReadingCriterionPageId == null) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(x => x.ShowOrder).ToListAsync(); + + return (result, true); + } + + + + + /// + /// 设置项目阅片标准 + /// + /// + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetTrialReadingCriterion(SetTrialReadingCriterionInDto inDto) + { + if (inDto.IsSignSave) + { + var criterionFormType = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == inDto.TrialReadingCriterionId).Select(x => x.FormType).FirstOrDefaultAsync(); + var count = _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == inDto.TrialReadingCriterionId) + .WhereIf(criterionFormType == FormType.SinglePage, x => x.ReadingCriterionPageId == null) + .WhereIf(criterionFormType == FormType.MultiplePage, x => x.ReadingCriterionPageId != null).Count(); + + if (count == 0) + { + throw new BusinessValidationFailedException("当前标准下未配置问题"); + } + } + + await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() + { + FormType = inDto.FormType, + }); + + + + + var result = await _trialRepository.SaveChangesAsync(); + + + return ResponseOutput.Ok(result); + } + + /// + /// 设置项目阅片信息 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetCriterionReadingInfo(SetCriterionReadingInfoInDto inDto) + { + ArbitrationRule arbitration = ArbitrationRule.NA; + + if (inDto.IsArbitrationReading) + { + arbitration = inDto.IsReadingPeriod ? ArbitrationRule.Reading : ArbitrationRule.Visit; + } + + await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() + { + ReadingTool = inDto.ReadingTool, + IsReadingPeriod = inDto.IsReadingPeriod, + //DigitPlaces=inDto.DigitPlaces, + IsReadingTaskViewInOrder = inDto.IsReadingTaskViewInOrder, + ReadingTaskViewEnum = inDto.ReadingTaskViewEnum, + DigitPlaces = inDto.DigitPlaces, + //IsImageIabeled = inDto.IsImageIabeled, + IsReadingShowSubjectInfo = inDto.IsReadingShowSubjectInfo, + IsReadingShowPreviousResults = inDto.IsReadingShowPreviousResults, + GlobalUpdateType = inDto.GlobalUpdateType, + ImagePlatform = inDto.ImagePlatform, + IseCRFShowInDicomReading = inDto.IseCRFShowInDicomReading, + ArbitrationRule = arbitration, + ReadingType = inDto.ReadingType, + IsGlobalReading = inDto.IsGlobalReading, + IsArbitrationReading = inDto.IsArbitrationReading, + IsOncologyReading = inDto.IsOncologyReading, + + + }); + + + + var result = await _readingQuestionCriterionTrialRepository.SaveChangesAsync(); + + + return ResponseOutput.Ok(result); + } + + /// + /// 获取签名文本 + /// + /// + /// + [HttpGet("{signCode}")] + public async Task GetSignText(string signCode) + { + var signRawText = await _repository.Where(t => t.Code == signCode).Select(t => new { t.Code, t.Value, t.ValueCN, t.Id, ParentValue = t.Parent.Value, ParentValueCN = t.Parent.ValueCN }).FirstOrDefaultAsync(); + + if (signRawText == null) + { + return ResponseOutput.NotOk("该操作需要电子签名确认,但未在系统中找到该场景的签名模板。"); + } + + return ResponseOutput.Ok(new + { + SignCodeId = signRawText.Id, + SignCode = signRawText.Code, + SignText = _userInfo.IsEn_Us ? signRawText.ParentValue.Replace("xxx", signRawText.Value) : signRawText.ParentValueCN.Replace("xxx", signRawText.ValueCN), + //SignTextCN = signRawText.ParentValueCN.Replace("xxx", signRawText.Value) + }); + } + + + //验证 仅仅在 Initializing/Ongoing 才可以操作 + private async Task VerifyOnlyInOngoingOrInitialIzingOptAsync(Guid trialId) + { + if (!await _trialRepository.AnyAsync(t => t.Id == trialId && (t.TrialStatusStr == StaticData.TrialState.TrialInitializing || t.TrialStatusStr == StaticData.TrialState.TrialOngoing))) + { + throw new BusinessValidationFailedException("该项目已结束或停止,不允许修改配置。"); + } + } + + + /// + /// 验证签名确认 + /// + /// + /// + /// + [HttpPost] + public async Task VerifyTrialConfigSignatureConfirm(SignConfirmDTO signConfirmDTO) + { + var trialConfig = (await _trialRepository + .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }) + .FirstOrDefaultAsync(t => t.TrialId == signConfirmDTO.TrialId)) + .IfNullThrowException(); + + + var showOrderList = await _repository.Where(t => t.TrialId == signConfirmDTO.TrialId).Select(t => + new { t.ShowOrder, ParentShowOrder = (int?)t.ParentQCQuestion.ShowOrder }).ToListAsync(); + + if (trialConfig.QCProcessEnum == + TrialQCProcess.DoubleAudit || trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) + { + if (showOrderList.Count == 0) + { + throw new BusinessValidationFailedException("当前未添加影像质控审核问题。请先添加影像质控审核问题,再进行确认。"); + } + } + + if (showOrderList.Count() != showOrderList.Select(t => t.ShowOrder).Distinct().Count()) + { + throw new BusinessValidationFailedException("影像质控审核问题显示序号不能重复。"); + } + + if (showOrderList.Where(t => t.ParentShowOrder != null).Any(t => t.ParentShowOrder > t.ShowOrder)) + { + throw new BusinessValidationFailedException("父问题的显示序号要比子问题的显示序号小,请确认。"); + } + + if (await _trialRepository.AnyAsync(t => t.Id == signConfirmDTO.TrialId && t.QCQuestionConfirmedUserId != null && t.QCQuestionConfirmedUserId != _userInfo.Id)) + { + throw new BusinessValidationFailedException("影像质控审核问题已被其他人员确认,不允许再次确认。"); + } + + return ResponseOutput.Ok(); + } + + + + /// + /// 签名确认 包括项目的三组配置 + QC问题确认 后修改状态 (适用于不会回退的,项目废除、状态修改, 存在回退 不在这里弄,提供单独接口修改状态) + /// + /// + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + public async Task TrialConfigSignatureConfirm(SignConfirmDTO signConfirmDTO) + { + await VerifyOnlyInOngoingOrInitialIzingOptAsync(signConfirmDTO.TrialId); + + + + if (signConfirmDTO.SignCode == ((int)SignEnum.TrialLogicConfim).ToString()) + { + await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == signConfirmDTO.TrialId, u => new Trial() { IsTrialBasicLogicConfirmed = true }); + } + else if (signConfirmDTO.SignCode == ((int)SignEnum.TrialProcessConfim).ToString()) + { + await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == signConfirmDTO.TrialId, u => new Trial() { IsTrialProcessConfirmed = true }); + } + else if (signConfirmDTO.SignCode == ((int)SignEnum.TrialUrgentConfim).ToString()) + { + await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == signConfirmDTO.TrialId, u => new Trial() { IsTrialUrgentConfirmed = true }); + } + else + + if (signConfirmDTO.SignCode == ((int)SignEnum.TrialQCQuestionConfirm).ToString()) + { + + var trialConfig = (await _trialRepository + .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }) + .FirstOrDefaultAsync(t => t.TrialId == signConfirmDTO.TrialId)) + .IfNullThrowException(); + + + var showOrderList = await _repository.Where(t => t.TrialId == signConfirmDTO.TrialId).Select(t => + new { t.ShowOrder, ParentShowOrder = (int?)t.ParentQCQuestion.ShowOrder }).ToListAsync(); + + if (trialConfig.QCProcessEnum == + TrialQCProcess.DoubleAudit || trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) + { + if (showOrderList.Count == 0) + { + throw new BusinessValidationFailedException("当前未添加影像质控审核问题。请先添加影像质控审核问题,再进行确认。"); + } + } + + if (showOrderList.Count() != showOrderList.Select(t => t.ShowOrder).Distinct().Count()) + { + throw new BusinessValidationFailedException("影像质控审核问题显示序号不能重复。"); + } + + if (showOrderList.Where(t => t.ParentShowOrder != null).Any(t => t.ParentShowOrder > t.ShowOrder)) + { + throw new BusinessValidationFailedException("父问题的显示序号要比子问题的显示序号小,请确认。"); + } + + if (await _trialRepository.AnyAsync(t => t.Id == signConfirmDTO.TrialId && t.QCQuestionConfirmedUserId != null && t.QCQuestionConfirmedUserId != _userInfo.Id)) + { + throw new BusinessValidationFailedException("影像质控审核问题已被其他人员确认,不允许再次确认。"); + } + + await _trialQCQuestionRepository.UpdatePartialFromQueryAsync(t => t.TrialId == signConfirmDTO.TrialId, x => new TrialQCQuestion + { + IsConfirm = true + }); + await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == signConfirmDTO.TrialId, u => new Trial() { QCQuestionConfirmedTime = DateTime.Now, QCQuestionConfirmedUserId = _userInfo.Id, IsQCQuestionConfirmed = true }); + await _trialRepository.SaveChangesAsync(); + } + + + + return ResponseOutput.Ok(); + } + + + + /// + /// 配置 基础逻辑信息 + /// + /// + /// + [HttpPut] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + public async Task ConfigTrialBasicInfo(BasicTrialConfig trialConfig) + { + await VerifyOnlyInOngoingOrInitialIzingOptAsync(trialConfig.TrialId); + + var trialInfo = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialConfig.TrialId)).IfNullThrowException(); + + trialConfig.Modalitys = $"|{String.Join('|', trialConfig.ModalityList)}|"; + _mapper.Map(trialConfig, trialInfo); + trialInfo.UpdateTime = DateTime.Now; + + + + + return ResponseOutput.Ok(await _repository.SaveChangesAsync()); + } + + /// + /// 配置流程 + /// + /// + /// + [HttpPut] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + public async Task ConfigTrialProcessInfo(TrialProcessConfig trialConfig) + { + if (!await _trialRepository.Where(t => t.Id == trialConfig.TrialId).IgnoreQueryFilters().AnyAsync(t => t.TrialStatusStr == StaticData.TrialState.TrialInitializing)) + { + return ResponseOutput.NotOk("该项目当前状态不是初始化,不允许进行该操作。"); + } + + + + + var trialInfo = await _trialRepository.Where(t => t.Id == trialConfig.TrialId, true).Include(t => t.TrialDicList.Where(u => u.KeyName == StaticData.Criterion)).FirstOrDefaultAsync(); + if (trialInfo == null) return Null404NotFound(trialInfo); + + + _mapper.Map(trialConfig, trialInfo); + + //if (trialInfo.IsGlobalReading && (trialInfo.IsArbitrationReading ?? false)) + //{ + // trialInfo.ArbitrationRule = ArbitrationRule.Reading; + //} + + //if (!trialInfo.IsGlobalReading && (trialInfo.IsArbitrationReading ?? false)) + //{ + // trialInfo.ArbitrationRule = ArbitrationRule.Visit; + //} + trialInfo.UpdateTime = DateTime.Now; + + // 修改临床数据 + var noconfirmids = await _clinicalDataTrialSetRepository.Where(x => x.TrialId == trialConfig.TrialId && !trialConfig.ClinicalDataTrialSetIds.Contains(x.Id)).Select(x => x.Id).ToListAsync(); + foreach (var item in trialConfig.ClinicalDataTrialSetIds) + { + await _clinicalDataTrialSetRepository.UpdatePartialFromQueryAsync(item, x => new ClinicalDataTrialSet() + { + + IsConfirm = true + }); + } + + foreach (var item in noconfirmids) + { + await _clinicalDataTrialSetRepository.UpdatePartialFromQueryAsync(item, x => new ClinicalDataTrialSet() + { + + IsConfirm = false + }); + } + + // 修改阅片标准 + var cancelConfirmCriterionIds = await _readingQuestionCriterionTrialRepository.Where(x => x.TrialId == trialConfig.TrialId && x.IsConfirm && !trialConfig.TrialCriterionIds.Contains(x.Id)).Select(x => x.Id).ToListAsync(); + + await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(x => cancelConfirmCriterionIds.Contains(x.Id), x => new ReadingQuestionCriterionTrial() + { + IsConfirm = false + }); + + await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(x => trialConfig.TrialCriterionIds.Contains(x.Id), x => new ReadingQuestionCriterionTrial() + { + IsConfirm = true + }); ; + + + + + return ResponseOutput.Ok(await _repository.SaveChangesAsync()); + } + + /// + /// 配置加急信息 + /// + /// + /// + [HttpPut] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + public async Task ConfigTrialUrgentInfo(TrialUrgentConfig trialConfig) + { + + await VerifyOnlyInOngoingOrInitialIzingOptAsync(trialConfig.TrialId); + + + var trialInfo = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialConfig.TrialId)).IfNullThrowException(); + + + //项目紧急 当前所有已提交,但未完成的检查批次,设置为加急。后续提交的检查批次也设置为加急 (在提交的时候,回去判断 项目加急,Subject加急,是否入组确认,是否Pd,从而设置检查批次是否加急) + if (trialConfig.IsUrgent) + { + + await _repository.BatchUpdateAsync(t => t.TrialId == trialInfo.Id && t.SubmitState == SubmitStateEnum.Submitted && t.ForwardState < ForwardStateEnum.Forwarded, + s => new SubjectVisit() { IsUrgent = trialConfig.IsUrgent }); + } + else //之前设置为加急的检查批次状态不变。后续提交的检查批次,为不加急。 + { + + } + + _mapper.Map(trialConfig, trialInfo); + + trialInfo.UpdateTime = DateTime.Now; + + return ResponseOutput.Ok(await _repository.SaveChangesAsync()); + } + + [HttpGet("{trialId:guid}")] + public async Task IfTrialCanOngoing(Guid trialId) + { + var canOPt = await _trialRepository.AnyAsync(trial => + trial.Id == trialId && trial.IsTrialBasicLogicConfirmed && trial.IsTrialProcessConfirmed && + trial.IsTrialUrgentConfirmed && trial.VisitPlanConfirmed); + return ResponseOutput.Ok(canOPt, msg: canOPt ? "" : "该项目的项目配置(基础配置、流程配置、加急配置) 、检查批次管理,有配置未确认,不能设置项目状态为启动。"); + } + + /// + /// 更新项目状态 + /// + /// + /// + /// + /// + [HttpPut("{trialId:guid}/{trialStatusStr}/{reason?}")] + [UnitOfWork] + //[Authorize(Policy = IRaCISPolicy.PM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task UpdateTrialState(Guid trialId, string trialStatusStr, string? reason) + { + + var trial = (await _trialRepository.Where(t => t.Id == trialId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); + + if (trialStatusStr == StaticData.TrialState.TrialOngoing) + { + if (trial.IsTrialBasicLogicConfirmed && trial.IsTrialProcessConfirmed && trial.IsTrialUrgentConfirmed && trial.VisitPlanConfirmed) + { + + } + else + { + return ResponseOutput.NotOk("无法变更项目状态。该项目的项目配置、检查批次管理中,有未确认项"); + } + } + + if ((trialStatusStr == StaticData.TrialState.TrialStopped || trialStatusStr == StaticData.TrialState.TrialCompleted) && trial.TrialStatusStr != StaticData.TrialState.TrialOngoing) + { + return ResponseOutput.NotOk("项目没有进入启动状态,不能设置为停止或完成状态"); + } + + //if (trialStatusStr != StaticData.TrialState.TrialOngoing) + //{ + // trial.TrialFinishTime = DateTime.Now; + //} + + trial.TrialStateChangeList.Add(new TrialStateChange() { OriginState = trial.TrialStatusStr, NowState = trialStatusStr, Reason = reason ?? String.Empty, TrialId = trialId }); + + trial.TrialStatusStr = trialStatusStr; + + + //Paused、 添加工总量 算医生读片中 + if (trialStatusStr.Contains(StaticData.TrialState.TrialCompleted)) + { + await _repository.BatchUpdateAsync(u => u.TrialId == trialId, e => new Enroll + { + EnrollStatus = EnrollStatus.Finished + }); + + await _trialRepository.BatchUpdateNoTrackingAsync(u => u.Id == trialId, s => new Trial { TrialFinishedTime = DateTime.Now }); + + } + + await _provider.SetAsync(trialId.ToString(), trialStatusStr, TimeSpan.FromDays(7)); + + await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + /// + /// 项目状态 变更历史 + /// + /// + /// + + [HttpGet("{trialId:guid}")] + public async Task> GetTrialStateChangeList(Guid trialId) + { + return await _repository.Where(t => t.TrialId == trialId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + + /// + /// 废除项目 + /// + /// + /// + /// + [HttpPut("{trialId:guid}/{isAbandon:bool}")] + //[Authorize(Policy = IRaCISPolicy.PM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + public async Task AbandonTrial(Guid trialId, /*Guid? signId,*/ bool isAbandon) + { + + await _trialRepository.UpdatePartialFromQueryAsync(trialId, u => new Trial() + { + IsDeleted = isAbandon, + TrialFinishTime = isAbandon ? DateTime.Now : null + }, true); + + + return ResponseOutput.Ok(); + } + + + + /// + /// 获取 配置的所有信息 没有分多个接口 + /// + /// + /// + [HttpGet("{trialId:guid}")] + public async Task GetTrialConfigInfo(Guid trialId) + { + return await _trialRepository.Where(t => t.Id == trialId).ProjectTo(_mapper.ConfigurationProvider) + .FirstOrDefaultAsync().IfNullThrowException(); + } + + + /// + /// 配置项目任务信息 + /// + /// + /// + [HttpPut] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + //[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ConfigTrialTaskInfo(TrialTaskConfig trialConfig) + { + var trialInfo = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialConfig.TrialId)).IfNullThrowException(); + + _mapper.Map(trialConfig, trialInfo); + + return ResponseOutput.Ok(await _trialRepository.SaveChangesAsync()); + } + + + /// + /// 配置项目读片查看规则 + /// + /// + /// + [HttpPut] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + //[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ConfigTrialReadingTaskViewRule(TrialReadingTaskViewConfig trialConfig) + { + var trialInfo = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialConfig.TrialId)).IfNullThrowException(); + + _mapper.Map(trialConfig, trialInfo); + + return ResponseOutput.Ok(await _trialRepository.SaveChangesAsync()); + } + + + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialExternalUserService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialExternalUserService.cs new file mode 100644 index 0000000..0b3bf32 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialExternalUserService.cs @@ -0,0 +1,767 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-04 13:33:56 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Domain.Share; +using MimeKit; +using MailKit.Security; +using Microsoft.AspNetCore.Authorization; +using Panda.DynamicWebApi.Attributes; +using IRaCIS.Core.Application.Auth; +using IRaCIS.Application.Services; +using IRaCIS.Core.Application.Filter; + +namespace IRaCIS.Core.Application.Service +{ + /// + /// 项目外部人员 录入流程相关 + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialExternalUserService : BaseService, ITrialExternalUserService + { + private readonly IRepository _trialExternalUseRepository; + private readonly IRepository _userRepository; + private readonly IRepository _trialUserRepository; + private readonly IRepository _trialSiteSurveyUserRepository; + private readonly IRepository _trialSiteUserRepository; + private readonly IMailVerificationService _mailVerificationService; + + public TrialExternalUserService(IRepository trialExternalUseRepository, IRepository userRepository, IRepository trialUserRepository, + IRepository trialSiteSurveyUserRepository, IRepository trialSiteUserRepository, + IMailVerificationService mailVerificationService) + { + _trialExternalUseRepository = trialExternalUseRepository; + _userRepository = userRepository; + _trialUserRepository = trialUserRepository; + _trialSiteSurveyUserRepository = trialSiteSurveyUserRepository; + _trialSiteUserRepository = trialSiteUserRepository; + + _mailVerificationService = mailVerificationService; + } + + + + [HttpPost] + public async Task> GetTrialExternalUserList(TrialExternalUserQuery queryTrialExternalUser) + { + + var trialExternalUserQueryable = _trialExternalUseRepository.Where(t => t.TrialId == queryTrialExternalUser.TrialId) + .WhereIf(!string.IsNullOrEmpty(queryTrialExternalUser.Phone), t => t.Phone.Contains(queryTrialExternalUser.Phone)) + .WhereIf(!string.IsNullOrEmpty(queryTrialExternalUser.Email), t => t.Email.Contains(queryTrialExternalUser.Email)) + .WhereIf(!string.IsNullOrEmpty(queryTrialExternalUser.Name), t => (t.LastName + " / " + t.FirstName).Contains(queryTrialExternalUser.Name)) + .ProjectTo(_mapper.ConfigurationProvider); + + return await trialExternalUserQueryable.ToListAsync(); + } + + + + /// + /// 添加和更新接口 已验证邮箱和账户类型不允许添加重复项 + /// + /// + /// + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddOrUpdateTrialExternalUser(TrialExternalUserAddAndSendEmail addOrEditTrialExternalUser) + { + + if (addOrEditTrialExternalUser.Id == null) + { + var existSysUser = await _userRepository.FirstOrDefaultAsync(t => t.EMail == addOrEditTrialExternalUser.Email && t.UserTypeId == addOrEditTrialExternalUser.UserTypeId); + + if (existSysUser != null) + { + if (existSysUser.LastName != addOrEditTrialExternalUser.LastName || existSysUser.FirstName != addOrEditTrialExternalUser.FirstName) + { + return ResponseOutput.NotOk($"该用户在系统中的用户名为:{existSysUser.LastName + " / " + existSysUser.FirstName} 电话:{existSysUser.Phone},与填写信息存在不一致项, 请将界面信息修改为与系统一致,再进行保存", new { existSysUser.LastName, existSysUser.FirstName, existSysUser.Phone, existSysUser.IsZhiZhun, existSysUser.IsTestUser }, ApiResponseCodeEnum.NeedTips); + } + + } + + //处理 生成账户 + + if (await _trialExternalUseRepository.AnyAsync(t => + t.Email == addOrEditTrialExternalUser.Email && + t.UserTypeId == addOrEditTrialExternalUser.UserTypeId && t.TrialId == addOrEditTrialExternalUser.TrialId)) + { + return ResponseOutput.NotOk("系统已经存在与列表中填写的邮箱和用户类型相同的账户,请确认。"); + } + + + + var addEntity = _mapper.Map(addOrEditTrialExternalUser); + + await _trialExternalUseRepository.AddAsync(addEntity); + + + var existUser = await _userRepository.FirstOrDefaultAsync(t => t.EMail == addOrEditTrialExternalUser.Email && t.UserTypeId == addOrEditTrialExternalUser.UserTypeId); + var trialType = await _repository.Where(t => t.Id == addOrEditTrialExternalUser.TrialId).Select(t => t.TrialType).FirstOrDefaultAsync(); + + if (existUser != null) + { + addEntity.IsSystemUser = true; + addEntity.SystemUserId = existUser.Id; + + } + else + { + + //生成账户 并插入 + + var generateUser = _mapper.Map(addOrEditTrialExternalUser); + + if (trialType == TrialType.NoneOfficial) + { + generateUser.IsTestUser = true; + } + + // 外部人员生成账号 都是外部的 + generateUser.IsZhiZhun = false; + + generateUser.Code = _userRepository.Select(t => t.Code).DefaultIfEmpty().Max() + 1; + + + generateUser.UserCode = AppSettings.GetCodeStr(generateUser.Code, nameof(User)); + + generateUser.UserName = generateUser.UserCode; + + generateUser.UserTypeEnum = _repository.Where(t => t.Id == generateUser.UserTypeId).Select(t => t.UserTypeEnum).First(); + + generateUser.Password = MD5Helper.Md5("123456"); + + generateUser.Status = UserStateEnum.Disable; + + var newAddUser = await _repository.AddAsync(generateUser); + + + addEntity.IsSystemUser = false; + addEntity.SystemUserId = newAddUser.Id; + + + existUser = newAddUser; + + } + + #region 验证用户 能否加入 + + if (trialType == TrialType.OfficialTrial || trialType == TrialType.Training) + { + + if (existUser.IsTestUser) + { + throw new BusinessValidationFailedException("正式类型 、培训类型的项目 不允许加入测试用户 "); + + } + } + + if (trialType == TrialType.NoneOfficial) + { + + if (existUser.IsTestUser == false ) + { + throw new BusinessValidationFailedException("测试项目 不允许加入正式用户 "); + } + } + + #endregion + + + await _trialExternalUseRepository.SaveChangesAsync(); + + + //添加的时候就发邮件 现在省略 + if (addOrEditTrialExternalUser.IsSendEmail) + { + await SendExternalUserJoinEmail(new TrialExternalUserSendEmail() + { + BaseUrl = addOrEditTrialExternalUser.BaseUrl, + RouteUrl = addOrEditTrialExternalUser.RouteUrl, + TrialId = addOrEditTrialExternalUser.TrialId, + SendUsers = new List() + { + new UserEmail() + { + Id = addEntity.Id, + Email=addEntity.Email, + IsSystemUser=addEntity.IsSystemUser, + SystemUserId=addEntity.SystemUserId + } + } + }); + } + + + return ResponseOutput.Ok(addEntity.Id.ToString()); + + } + else + { + return ResponseOutput.NotOk("人员信息不支持编辑,请删除后重新添加。"); + + } + } + + + + + [HttpDelete("{trialExternalUserId:guid}/{isSystemUser:bool}/{systemUserId}")] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + public async Task DeleteTrialExternalUser(Guid trialExternalUserId, bool isSystemUser, Guid systemUserId) + { + var trialExternalUser = await _trialExternalUseRepository.FirstOrDefaultAsync(t => t.Id == trialExternalUserId); + + if (await _trialUserRepository.AnyAsync(t => t.TrialId == trialExternalUser.TrialId && t.UserId == trialExternalUser.SystemUserId)) + { + return ResponseOutput.NotOk("当前用户已参与到项目,不允许删除"); + } + + + var success = await _trialExternalUseRepository.BatchDeleteNoTrackingAsync(t => t.Id == trialExternalUserId); + + if (isSystemUser == false) + { + await _userRepository.BatchDeleteNoTrackingAsync(t => t.Id == systemUserId); + } + + return ResponseOutput.Result(success); + } + + + //New 省掉邀请流程 + [HttpPost] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task SendExternalUserJoinEmail(TrialExternalUserSendEmail sendEmail) + { + var trialId = sendEmail.TrialId; + + foreach (var userInfo in sendEmail.SendUsers) + { + var userId = userInfo.SystemUserId; + + + + + //判断TrialUser中是否存在 不存在就插入 注意退出了,也不能再加进来 + if (!await _trialUserRepository.AnyAsync(t => t.TrialId == trialId && t.UserId == userId, true)) + { + + await _repository.AddAsync(new TrialUser() { TrialId = trialId, UserId = userId, JoinTime = DateTime.Now }); + + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == userId, u => new User() { Status = UserStateEnum.Enable }); + + await _trialExternalUseRepository.BatchUpdateNoTrackingAsync(t => t.Id == userInfo.Id, u => new TrialExternalUser() { IsJoin = true }); + + + await _userRepository.SaveChangesAsync(); + } + + await _mailVerificationService.ExternalUserJoinEmail(trialId, userId, sendEmail.BaseUrl, sendEmail.RouteUrl); + + } + + return ResponseOutput.Ok(); + + } + + + + + + + #region 老版本流程 现在废弃 + + /// + /// 勾选用户 批量发送邮件 + /// + /// + [HttpPost] + public async Task SendInviteEmail(TrialExternalUserSendEmail sendEmail) + { + + var trialInfo = await _repository.FirstOrDefaultAsync(t => t.Id == sendEmail.TrialId); + + foreach (var userInfo in sendEmail.SendUsers) + { + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress("GRR", "iracis_grr@163.com")); + //收件地址 + messageToSend.To.Add(new MailboxAddress(String.Empty, userInfo.Email)); + //主题 + messageToSend.Subject = $"[{trialInfo.ResearchProgramNo}] 邀请"; + + //var baseApiUrl = sendEmail.BaseUrl.Remove(sendEmail.BaseUrl.IndexOf("#")) + "api"; + + var builder = new BodyBuilder(); + + var sysUserInfo = (await _userRepository.Where(t => t.Id == userInfo.SystemUserId).FirstOrDefaultAsync()).IfNullThrowException(); + + + builder.HtmlBody = @$" +
+
+
+ {sysUserInfo.LastName + "/" + sysUserInfo.FirstName}: +
+
+ 您好,展影医疗作为 实验方案号:{trialInfo.ResearchProgramNo} 项目的IRC供应商,诚邀您参加该项目IRC相关工作,欢迎您提供指导和建议,非常感谢! +
+ + + + + 查看并确认 + +
櫭 +
+ "; + + + //< form action = '#' method = 'post' > + + // < button type = 'submit' style = 'margin-left:60px;font-size:14px;text-decoration: none;display: inline-block;height: 40px;width: 140px;background: #00D1B2;color:#fff;border-radius: 5px;line-height: 40px;text-align: center;border:none;margin-bottom: 100px;cursor: pointer' > 查看并确认 + + // + messageToSend.Body = builder.ToMessageBody(); + + using (var smtp = new MailKit.Net.Smtp.SmtpClient()) + { + smtp.MessageSent += (sender, args) => + { + + _ = _trialExternalUseRepository.BatchUpdateNoTrackingAsync(t => t.Id == userInfo.Id, u => new TrialExternalUser() { InviteState = TrialExternalUserStateEnum.HasSend, ConfirmTime = null, RejectReason = String.Empty, ExpireTime = DateTime.Now.AddDays(7) }).Result; + + }; + + smtp.ServerCertificateValidationCallback = (s, c, h, e) => true; + + await smtp.ConnectAsync("smtp.163.com", 465, SecureSocketOptions.StartTls); + + await smtp.AuthenticateAsync("iracis_grr@163.com", "XLWVQKZAEKLDWOAH"); + + await smtp.SendAsync(messageToSend); + + await smtp.DisconnectAsync(true); + } + + + } + + return ResponseOutput.Ok(); + + } + + + + + /// + /// 不带Token 访问 用户选择 参与 不参与 Id: TrialExternalUserId 加入发送邮件 + /// + /// + /// + [AllowAnonymous] + public async Task TrialExternalUserJoinTrial(TrialExternalUserConfirm editTrialUserPreparation) + { + + var needUpdate = await _trialExternalUseRepository.FirstOrDefaultAsync(t => t.Id == editTrialUserPreparation.Id); + + if (DateTime.Now > needUpdate.ExpireTime) + { + return ResponseOutput.NotOk("邀请加入时间已过期,重新被邀请后才可以进行确认操作"); + } + + _mapper.Map(editTrialUserPreparation, needUpdate); + + needUpdate.InviteState = editTrialUserPreparation.IsJoin == true ? TrialExternalUserStateEnum.UserConfirmed : TrialExternalUserStateEnum.UserReject; + + + var trialId = needUpdate.TrialId; + var userId = needUpdate.SystemUserId; + + //判断TrialUser中是否存在 不存在就插入 + if (!await _trialUserRepository.AnyAsync(t => t.TrialId == trialId && t.UserId == userId)) + { + + await _trialUserRepository.AddAsync(new TrialUser() { TrialId = trialId, UserId = userId, JoinTime = DateTime.Now }); + + } + + var success = await _trialExternalUseRepository.SaveChangesAsync(); + + + if (editTrialUserPreparation.IsJoin == true) + { + var trialInfo = await _repository.FirstOrDefaultAsync(t => t.Id == needUpdate.TrialId); + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress("GRR", "iracis_grr@163.com")); + //收件地址 + messageToSend.To.Add(new MailboxAddress(String.Empty, needUpdate.Email)); + //主题 + messageToSend.Subject = $"[来自展影IRC] [{trialInfo.ResearchProgramNo}] 账户信息"; + + var builder = new BodyBuilder(); + + + var sysUserInfo = (await _userRepository.Where(t => t.Id == needUpdate.SystemUserId).Include(t => t.UserTypeRole).FirstOrDefaultAsync()).IfNullThrowException(); + + int verificationCode = new Random().Next(100000, 1000000); + + if (sysUserInfo.IsFirstAdd) + { + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, + u => new User() { Password = MD5Helper.Md5(verificationCode.ToString()) }); + } + + builder.HtmlBody = @$" +
+
+
+ {sysUserInfo.LastName + "/" + sysUserInfo.FirstName}: +
+
+ 您好,欢迎您参加项目 实验方案号:{trialInfo.ResearchProgramNo}IRC相关工作。该项目采用电子化工作流,系统及您的账号信息如下: +
+
+
+ 项目编号: {trialInfo.TrialCode} +
+
+ 试验方案号: {trialInfo.ResearchProgramNo} +
+
+ 试验名称: {trialInfo.ExperimentName} +
+
+ 用户名: {sysUserInfo.UserName} +
+
+ 密码: {(sysUserInfo.IsFirstAdd ? verificationCode.ToString() + "(请在登录后进行修改)" : "***(您已有账号, 若忘记密码, 请通过邮箱找回)")} +
+
+ 角色: {sysUserInfo.UserTypeRole.UserTypeShortName} +
+
+ 系统登录地址: {editTrialUserPreparation.BaseUrl} +
+
+ +
+
+ "; + + messageToSend.Body = builder.ToMessageBody(); + + using (var smtp = new MailKit.Net.Smtp.SmtpClient()) + { + + smtp.ServerCertificateValidationCallback = (s, c, h, e) => true; + + await smtp.ConnectAsync("smtp.163.com", 465, SecureSocketOptions.StartTls); + + await smtp.AuthenticateAsync("iracis_grr@163.com", "XLWVQKZAEKLDWOAH"); + + await smtp.SendAsync(messageToSend); + + await smtp.DisconnectAsync(true); + } + + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == userId, u => new User() { Status = UserStateEnum.Enable }); + + + } + return ResponseOutput.Ok(); + + //else + //{ + // builder.HtmlBody = @$" + //
+ //
+ //
+ // {sysUserInfo.LastName + "/" + sysUserInfo.FirstName}: + //
+ //
+ // 您好,您拒绝了参加 {trialInfo.ResearchProgramNo} 项目IRC相关工作的邀请。详细信息如下: + //
+ //
+ //
+ // 项目编号: {trialInfo.TrialCode} + //
+ //
+ // 试验方案号: {trialInfo.ResearchProgramNo} + //
+ //
+ // 试验名称: {trialInfo.ExperimentName} + //
+ //
+ // 用户名: {sysUserInfo.UserName} + //
+ //
+ // 角色: {sysUserInfo.UserTypeRole.UserTypeShortName} + //
+ //
+ //
+ //
+ // "; + //} + + + + + + + } + + /// + /// 不带Token 访问 Site调研用户 加入项目 Id: TrialSiteSurveyUserId + /// + /// + /// + [AllowAnonymous] + public async Task TrialSiteSurveyUserJoinTrial(TrialExternalUserConfirm editInfo) + { + + var needUpdate = (await _trialSiteSurveyUserRepository.Where(t => t.Id == editInfo.Id, true).Include(t => t.TrialSiteSurvey).FirstOrDefaultAsync()).IfNullThrowException(); + + var revieweUser = await _userRepository.FirstOrDefaultAsync(t => t.Id == needUpdate.TrialSiteSurvey.ReviewerUserId); + + + if (DateTime.Now > needUpdate.ExpireTime) + { + return ResponseOutput.NotOk("邀请加入时间已过期,重新被邀请后才可以进行确认操作"); + } + + _mapper.Map(editInfo, needUpdate); + + needUpdate.InviteState = editInfo.IsJoin == true ? TrialSiteUserStateEnum.UserConfirmed : TrialSiteUserStateEnum.UserReject; + + + if (needUpdate.SystemUserId == null) + { + return ResponseOutput.NotOk("调研表系统用户Id 存储有问题"); + } + + var trialId = needUpdate.TrialSiteSurvey.TrialId; + var siteId = needUpdate.TrialSiteSurvey.SiteId; + var userId = (Guid)needUpdate.SystemUserId; + + + if (!await _trialUserRepository.AnyAsync(t => t.TrialId == trialId && t.UserId == userId)) + { + await _trialUserRepository.AddAsync(new TrialUser() { TrialId = trialId, UserId = userId, JoinTime = DateTime.Now }); + + await _trialSiteUserRepository.AddAsync(new TrialSiteUser() { TrialId = trialId, SiteId = siteId, UserId = userId }); + + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == needUpdate.SystemUserId, u => new User() { Status = UserStateEnum.Enable }); + + + } + + var success = await _trialExternalUseRepository.SaveChangesAsync(); + + var trialInfo = await _repository.FirstOrDefaultAsync(t => t.Id == needUpdate.TrialSiteSurvey.TrialId); + + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress("GRR", "iracis_grr@163.com")); + //收件地址 + messageToSend.To.Add(new MailboxAddress(String.Empty, editInfo.IsJoin == true ? needUpdate.Email : revieweUser.EMail)); + //主题 + messageToSend.Subject = $"[来自展影IRC] [{trialInfo.ResearchProgramNo}] 账户信息"; + + + var builder = new BodyBuilder(); + + + var sysUserInfo = await _userRepository.Where(t => t.Id == needUpdate.SystemUserId).Include(t => t.UserTypeRole).FirstOrDefaultAsync(); + + int verificationCode = new Random().Next(100000, 1000000); + + if (sysUserInfo.IsFirstAdd) + { + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, + u => new User() { Password = MD5Helper.Md5(verificationCode.ToString()) }); + } + + if (editInfo.IsJoin == true) + { + builder.HtmlBody = @$" +
+
+
+ {sysUserInfo.LastName + "/" + sysUserInfo.FirstName}: +
+
+ 您好,欢迎您参加项目 实验方案号: {trialInfo.ResearchProgramNo} IRC相关工作。该项目采用电子化工作流,系统及您的账号信息如下: +
+
+
+ 项目编号: {trialInfo.TrialCode} +
+
+ 试验方案号: {trialInfo.ResearchProgramNo} +
+
+ 试验名称: {trialInfo.ExperimentName} +
+
+ 用户名: {sysUserInfo.UserName} +
+
+ 密码: {(sysUserInfo.IsFirstAdd ? verificationCode.ToString() + "(请在登录后进行修改)" : "***(您已有账号, 若忘记密码, 请通过邮箱找回)")} +
+
+ 角色: {sysUserInfo.UserTypeRole.UserTypeShortName} +
+
+ 系统登录地址: {editInfo.BaseUrl} +
+
+ +
+
+ "; + + messageToSend.Body = builder.ToMessageBody(); + + using (var smtp = new MailKit.Net.Smtp.SmtpClient()) + { + + smtp.ServerCertificateValidationCallback = (s, c, h, e) => true; + + await smtp.ConnectAsync("smtp.163.com", 465, SecureSocketOptions.StartTls); + + await smtp.AuthenticateAsync("iracis_grr@163.com", "XLWVQKZAEKLDWOAH"); + + await smtp.SendAsync(messageToSend); + + await smtp.DisconnectAsync(true); + } + } + //else + //{ + + // builder.HtmlBody = @$" + //
+ //
+ //
+ // {revieweUser.LastName + "/" + revieweUser.FirstName}: + //
+ //
+ // 您好,{sysUserInfo.LastName + "/" + sysUserInfo.FirstName} 拒绝了参加 {trialInfo.ResearchProgramNo} 项目IRC相关工作的邀请。详细信息如下: + //
+ //
+ //
+ // 项目编号: {trialInfo.TrialCode} + //
+ //
+ // 试验方案号: {trialInfo.ResearchProgramNo} + //
+ //
+ // 试验名称: {trialInfo.ExperimentName} + //
+ //
+ // 用户名: {sysUserInfo.UserName} + //
+ //
+ // 角色: {sysUserInfo.UserTypeRole.UserTypeShortName} + //
+ //
+ // 拒绝原因: {editInfo.RejectReason} + //
+ //
+ //
+ //
+ // "; + + //} + + + + + + return ResponseOutput.Ok(); + + } + + + /// + /// 不带Token 访问 页面获取项目基本信息 和参与情况 (已经确认了 就不允许再次确认) Id: TrialExternalUserId/TrialSiteSurveyUserId + /// + /// + /// + /// + [AllowAnonymous] + public async Task JoinBasicInfo(Guid id, bool isExternalUser) + { + if (isExternalUser) + { + return (await _trialExternalUseRepository.Where(t => t.Id == id) + .ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + } + else + { + return (await _trialSiteSurveyUserRepository.Where(t => t.Id == id) + .ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + } + + } + + + + + + /// + /// 加入项目 + /// + /// + /// + /// + [HttpGet("{trialId:guid}/{trialExternalUserId:guid}")] + [NonDynamicMethod] + public async Task UserConfirmJoinTrial(Guid trialId, Guid trialExternalUserId) + { + + var externalUser = await _trialExternalUseRepository.FirstOrDefaultAsync(t => t.Id == trialExternalUserId); + + + //判断TrialUser中是否存在 不存在就插入 + if (!await _repository.AnyAsync(t => t.TrialId == trialId && t.UserId == externalUser.SystemUserId)) + { + await _repository.AddAsync(new TrialUser() { TrialId = trialId, UserId = (Guid)externalUser.SystemUserId, JoinTime = DateTime.Now }); + + await _trialExternalUseRepository.BatchUpdateNoTrackingAsync(t => t.Id == trialExternalUserId, + u => new TrialExternalUser() { InviteState = TrialExternalUserStateEnum.UserConfirmed }); + + await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == externalUser.SystemUserId, u => new User() { Status = UserStateEnum.Enable }); + + await _userRepository.SaveChangesAsync(); + } + + + + return ResponseOutput.Ok(); + + + } + + #endregion + + + + + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialMaintenanceService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialMaintenanceService.cs new file mode 100644 index 0000000..a764c1c --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialMaintenanceService.cs @@ -0,0 +1,281 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Application.Interfaces; +using IRaCIS.Core.Application.Service; + +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Auth; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialMaintenanceService : BaseService, ITrialMaintenanceService + { + private readonly IRepository _trialUseRepository; + private readonly IRepository _trialRepository; + + public TrialMaintenanceService(IRepository trialUseRepository, IRepository trialRepository) + { + _trialUseRepository = trialUseRepository; + _trialRepository = trialRepository; + } + + /// + /// 项目参与人员导出 + /// + /// + /// + /// + /// + [HttpPost] + [AllowAnonymous] + public async Task TrialUserListExport(TrialMaintenanceExportQuery param, [FromServices] IRepository _commonDocumentRepository) + { + + var exportInfo = (await _trialRepository.Where(t => t.Id == param.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.TrialUserList = await _trialUseRepository.Where(t => t.TrialId == param.TrialId).IgnoreQueryFilters() + .WhereIf(param.UserTypeId != null, t => t.User.UserTypeId == param.UserTypeId) + .WhereIf(!string.IsNullOrWhiteSpace(param.UserName), t => t.User.UserName.Contains(param.UserName)) + + .WhereIf(param.IsDeleted != null, t => t.IsDeleted == param.IsDeleted) + .WhereIf(!string.IsNullOrWhiteSpace(param.OrganizationName), + t => t.User.OrganizationName.Contains(param.OrganizationName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.UserRealName), + t => (t.User.FullName).Contains(param.UserRealName)) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var no = 1; + exportInfo.TrialUserList.ForEach(t => t.No = no++); + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialUserList_Export, exportInfo, exportInfo.TrialCode, _commonDocumentRepository, _hostEnvironment); + + } + + + + /// + /// Setting页面 获取项目参与人员列表 + /// + /// + /// + [HttpPost] + public async Task> GetMaintenanceUserList(TrialMaintenanceQuery param) + { + + var query = _trialUseRepository.Where(t => t.TrialId == param.TrialId).IgnoreQueryFilters() + .WhereIf(param.UserTypeId != null, t => t.User.UserTypeId == param.UserTypeId) + .WhereIf(!string.IsNullOrWhiteSpace(param.UserName), t => t.User.UserName.Contains(param.UserName)) + .WhereIf(param.IsDeleted != null, t => t.IsDeleted == param.IsDeleted) + .WhereIf(!string.IsNullOrWhiteSpace(param.OrganizationName), t => t.User.OrganizationName.Contains(param.OrganizationName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.UserRealName), t => (t.User.FullName).Contains(param.UserRealName)) + .ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToPagedListAsync(param.PageIndex, param.PageSize, string.IsNullOrWhiteSpace(param.SortField) ? "UpdateTime" : param.SortField, param.Asc); + + } + + + + + + /// Setting页面 为 site 勾选IC用户列表 + [HttpPost] + public async Task> GetSiteCRCScreeningList(SiteCRCQuery param) + { + // 最开始过滤site已经选择的用户 现在又改回去。。。 + + var query = _trialUseRepository.Where(t => t.TrialId == param.TrialId).IgnoreQueryFilters() + .Where(t => t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.User.UserTypeEnum == UserTypeEnum.CRA) + .WhereIf(param.UserTypeId != null, t => t.User.UserTypeId == param.UserTypeId) + .WhereIf(!string.IsNullOrWhiteSpace(param.UserName), t => t.User.UserName.Contains(param.UserName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.UserRealName), t => (t.User.FullName).Contains(param.UserRealName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.OrganizationName), t => t.User.OrganizationName.Contains(param.OrganizationName)) + .ProjectTo(_mapper.ConfigurationProvider, new { siteId = param.SiteId }); + + return await query.ToPagedListAsync(param.PageIndex, + param.PageSize, string.IsNullOrWhiteSpace(param.SortField) ? "UpdateTime" : param.SortField, param.Asc); + + } + + + /// Setting页面 项目参与人员勾选列表 + [HttpPost] + public async Task> GetTrialUserScreeningList(TrialUserQuery trialUserQuery) + { + var trialType = _trialRepository.Where(t => t.Id == trialUserQuery.TrialId).Select(t => t.TrialType).FirstOrDefault(); + + var userTypeEnums = new List() { UserTypeEnum.IQC, UserTypeEnum.APM, UserTypeEnum.MIM, UserTypeEnum.QA, UserTypeEnum.MW }; + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin) + { + userTypeEnums.Add(UserTypeEnum.ProjectManager); + } + + + //之前已经选择的用户 不放在列表中,现在又要改回去 废弃 + var query = _repository.Where(t => t.UserTypeEnum != UserTypeEnum.SuperAdmin) + + ////正式或者培训的项目 不能允许测试用户(必须正式用户) 同时必须是内部的用户 + //.WhereIf(trialType == TrialType.OfficialTrial || trialType == TrialType.Training, t => t.IsTestUser == false && t.IsZhiZhun) + + ////测试项目 可以加入 测试用户 或者内部正式用户 + //.WhereIf(trialType == TrialType.NoneOfficial, t => t.IsTestUser == true || (t.IsTestUser == false && t.IsZhiZhun)) + + + .WhereIf(!string.IsNullOrWhiteSpace(trialUserQuery.UserRealName), t => (t.FullName).Contains(trialUserQuery.UserRealName)) + .WhereIf(!string.IsNullOrWhiteSpace(trialUserQuery.UserName), t => t.UserName.Contains(trialUserQuery.UserName)) + .WhereIf(!string.IsNullOrWhiteSpace(trialUserQuery.OrganizationName), t => t.OrganizationName.Contains(trialUserQuery.OrganizationName)) + .WhereIf(trialUserQuery.UserTypeEnum != null, t => t.UserTypeEnum == trialUserQuery.UserTypeEnum) + //.WhereIf(_userInfo.IsAdmin, t => t.UserTypeRole.Type == UserTypeGroup.TrialUser) + //.WhereIf(!_userInfo.IsAdmin, t => t.UserTypeRole.Type == UserTypeGroup.TrialUser || t.UserTypeEnum != UserTypeEnum.ProjectManager) + .ProjectTo(_mapper.ConfigurationProvider, new { trialId = trialUserQuery.TrialId }); + + return await query.ToPagedListAsync(trialUserQuery.PageIndex, + trialUserQuery.PageSize, string.IsNullOrWhiteSpace(trialUserQuery.SortField) ? "UserRealName" : trialUserQuery.SortField, trialUserQuery.Asc); + + } + + + /// + /// Setting页面 批量添加项目参与人员 + /// + /// + /// + //[TrialAudit(AuditType.TrialAudit, AuditOptType.AddTrialStaff)] + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddTrialUsers(TrialUserAddCommand[] userTrialCommands) + { + + var addArray = _mapper.Map(userTrialCommands); + + var trialUsers = await _trialUseRepository.AddRangeAsync(addArray); + + + var trialReadingCriterionId = _repository.Where(t => t.CriterionType == CriterionType.RECIST1Pointt1 && t.TrialId == addArray.First().TrialId).Select(t => t.Id).FirstOrDefault(); + + foreach (var item in trialUsers) + { + + if(await _trialUseRepository.AnyAsync(t=>t.TrialId==item.TrialId && t.UserId == item.UserId, true)) + { + return ResponseOutput.NotOk("项目人员已存在该用户,不允许继续添加"); + } + + + item.JoinTime = DateTime.Now; + + + #region 添加IR + + + var trialId = item.TrialId; + var userId=item.UserId; + if (await _repository.AnyAsync(t => t.Id == item.UserId && t.UserTypeRole.UserTypeEnum == UserTypeEnum.IndependentReviewer)) + { + + var enroll = await _repository.AddAsync(new Enroll() { TrialId=trialId,DoctorUserId=userId,DoctorId= userId,EnrollStatus=EnrollStatus.ConfirmIntoGroup }); + + await _repository.AddAsync(new EnrollReadingCategory() { EnrollId = enroll.Id, ReadingCategory = ReadingCategory.Visit, TrialReadingCriterionId = trialReadingCriterionId }); + await _repository.AddAsync(new EnrollReadingCategory() { EnrollId = enroll.Id, ReadingCategory = ReadingCategory.Global, TrialReadingCriterionId = trialReadingCriterionId }); + + //if (!await _repository.AnyAsync(t => t.TrialId == trialId && t.DoctorUserId == userId && t.EnrollId == intoGroupItem.Id, true)) + { + await _repository.AddAsync(new TaskAllocationRule() { TrialId = trialId, DoctorUserId = userId, EnrollId = enroll.Id ,IsEnable=true}); + } + } + + #endregion + + + #region 自动添加CRC 到Site 里 + if (await _repository.AnyAsync(t => t.Id == item.UserId && t.UserTypeRole.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator)) + { + await _repository.AddAsync(new TrialSiteUser() { TrialId = trialId, SiteId = Guid.Parse("db83e2f5-1f2e-408f-a45b-08d8e1dcace0"), UserId =item.UserId }); + } + #endregion + + } + + + + + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Result(success); + } + + + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + [HttpPut] + public async Task UpdateTrialUser(UpdateTrialUserCommand updateTrialUserCommand) + { + var trialUser = await _trialUseRepository.Where(t => t.Id == updateTrialUserCommand.Id, true, true).FirstOrDefaultAsync(); + + if (trialUser == null) return Null404NotFound(trialUser); + + + if (updateTrialUserCommand.IsDeleted) + { + //if (await _repository.AnyAsync(t => t.UserId == trialUser.UserId && t.TrialId == trialUser.TrialId)) + //{ + // return ResponseOutput.NotOk("Participant has participated in site maintenance"); + //} + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IQC) + { + await _repository.BatchUpdateAsync(t => t.CurrentActionUserId == trialUser.UserId && t.TrialId == trialUser.TrialId && t.IsTake, u => new SubjectVisit() { CurrentActionUserId = null, CurrentActionUserExpireTime = null, IsTake = false }); + } + + } + + _mapper.Map(updateTrialUserCommand, trialUser); + + await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + + + } + + + /// 项目参与人员退出 其中IQC退出 回去释放工作量 + //[TrialAudit(AuditType.TrialAudit, AuditOptType.DeleteTrailStaff)] + [HttpDelete, Route("{id:guid}/{trialId:guid}/{isDelete:bool}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + [Obsolete] + public async Task DeleteMaintenanceUser(Guid id, bool isDelete) + { + + var trialUser = await _trialUseRepository.AsQueryable().IgnoreQueryFilters().FirstOrDefaultAsync(t => t.Id == id); + + if (trialUser == null) return Null404NotFound(trialUser); + + //if (await _repository.AnyAsync(t => t.UserId == trialUser.UserId && t.TrialId == trialUser.TrialId)) + //{ + // return ResponseOutput.NotOk("Participant has participated in site maintenance"); + //} + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IQC && isDelete) + { + await _repository.BatchUpdateAsync(t => t.CurrentActionUserId == trialUser.UserId && t.TrialId == trialUser.TrialId && t.IsTake, u => new SubjectVisit() { CurrentActionUserId = null, CurrentActionUserExpireTime = null, IsTake = false }); + } + + await _trialUseRepository.BatchUpdateNoTrackingAsync(t => t.Id == id, u => new TrialUser() { IsDeleted = isDelete, RemoveTime = isDelete ? DateTime.Now : null }); + + await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialService.cs new file mode 100644 index 0000000..02c95aa --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialService.cs @@ -0,0 +1,741 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using EasyCaching.Core; +using IRaCIS.Core.Application.Filter; +using Microsoft.AspNetCore.Mvc; +using Panda.DynamicWebApi.Attributes; +using IRaCIS.Core.Infrastructure; +using Microsoft.Extensions.Options; +using static IRaCIS.Core.Domain.Share.StaticData; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Domain.Models; +using MassTransit; + +namespace IRaCIS.Application.Services +{ + + [ApiExplorerSettings(GroupName = "Trial")] + + public class TrialService : BaseService, ITrialService + { + + private readonly IEasyCachingProvider _provider; + private readonly IRepository _trialRepository; + private readonly IRepository _trialUserRepository; + private readonly IOptionsMonitor _verifyConfig; + + public bool TrialExpeditedChange { get; set; } = false; + + + public TrialService(IEasyCachingProvider provider, IRepository trialRepository, + IRepository trialUserRepository, IOptionsMonitor verifyConfig + ) + { + _verifyConfig = verifyConfig; + _provider = provider; + _trialRepository = trialRepository; + this._trialUserRepository = trialUserRepository; + } + + /// + /// 分页获取临床项目列表 默认后台加急状态为3 查所有的 + /// + /// + /// + [HttpPost] + public async Task> GetTrialList(TrialQueryDTO searchParam) + { + var multiModalityIdSelectCount = searchParam.ModalityIds.Count; + var multiCriteriaSelectCount = searchParam.CriterionIds.Count; + var multiReviewTypeSelectCount = searchParam.ReviewTypeIds.Count; + + var query = _trialRepository.AsQueryable() + .WhereIf(!string.IsNullOrEmpty(searchParam.TrialStatusStr), o => o.TrialStatusStr.Contains(searchParam.TrialStatusStr)) + .WhereIf(searchParam.SponsorId != null, o => o.SponsorId == searchParam.SponsorId) + .WhereIf(searchParam.Expedited != null, o => o.Expedited == searchParam.Expedited) + .WhereIf(!string.IsNullOrEmpty(searchParam.Code), o => o.TrialCode.Contains(searchParam.Code)) + .WhereIf(!string.IsNullOrWhiteSpace(searchParam.Indication), o => o.Indication.Contains(searchParam.Indication)) + .WhereIf(!string.IsNullOrEmpty(searchParam.ResearchProgramNo), o => o.ResearchProgramNo.Contains(searchParam.ResearchProgramNo)) + .WhereIf(!string.IsNullOrWhiteSpace(searchParam.ExperimentName), o => o.ExperimentName.Contains(searchParam.ExperimentName)) + .WhereIf(!string.IsNullOrWhiteSpace(searchParam.HeadPI), o => o.HeadPI.Contains(searchParam.HeadPI)) + + .WhereIf(searchParam.PhaseId != null, o => o.PhaseId == searchParam.PhaseId) + .WhereIf(searchParam.DeclarationTypeId != null, o => o.DeclarationTypeId == searchParam.DeclarationTypeId) + .WhereIf(searchParam.IndicationTypeId != null, o => o.IndicationTypeId == searchParam.IndicationTypeId) + .WhereIf(searchParam.CROId != null, o => o.CROId == searchParam.CROId) + .WhereIf(searchParam.BeginDate != null, o => o.CreateTime >= searchParam.BeginDate) + .WhereIf(searchParam.EndDate != null, o => o.CreateTime <= searchParam.EndDate) + .WhereIf(searchParam.AttendedReviewerType != null, o => o.AttendedReviewerType == searchParam.AttendedReviewerType) + .WhereIf(multiModalityIdSelectCount > 0, t => t.TrialDicList.Count(t => t.KeyName == StaticData.Modality) == multiModalityIdSelectCount) + .WhereIf(multiCriteriaSelectCount > 0, t => t.TrialDicList.Count(t => t.KeyName == StaticData.Criterion) == multiCriteriaSelectCount) + .WhereIf(multiReviewTypeSelectCount > 0, t => t.TrialDicList.Count(t => t.KeyName == StaticData.ReviewType) == multiReviewTypeSelectCount) + .WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin, t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id) && t.IsDeleted == false) + .ProjectTo(_mapper.ConfigurationProvider, new { userTypeEnumInt = _userInfo.UserTypeEnumInt, userId = _userInfo.Id }); + + return await query.ToPagedListAsync(searchParam.PageIndex, searchParam.PageSize, string.IsNullOrWhiteSpace(searchParam.SortField) ? "CreateTime" : searchParam.SortField, searchParam.Asc); + + + } + + //过滤废除的项目 + public async Task> GetTrialSelect() + { + return await _trialRepository.AsQueryable().IgnoreQueryFilters() + .WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin, t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id) && t.IsDeleted == false) + + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + } + + + + /// + /// 获取项目基本信息 + /// + /// + /// + [HttpGet("{projectId:guid}")] + public async Task GetTrialInfoAndLockState(Guid projectId) + { + return (await _trialRepository.Where(o => o.Id == projectId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + } + + + [NonDynamicMethod] + public async Task GetTrialMaxState(Guid trialId) + { + return await _repository.Where(t => t.TrialId == trialId).MaxAsync(u => (int?)u.EnrollStatus) ?? 0; + } + + [HttpGet("{trialId:guid}")] + public async Task GetTrialInfoAndMaxTrialState(Guid trialId) + { + return new TrialAndTrialStateVieModel() + { + TrialView = await GetTrialInfoAndLockState(trialId), + TrialMaxState = await GetTrialMaxState(trialId) + }; + } + + + [HttpGet("{trialId:guid}")] + public async Task GetTrialExpeditedState(Guid trialId) + { + var trial = (await _trialRepository.FirstOrDefaultAsync(u => u.Id == trialId)).IfNullThrowException(); + + return trial.Expedited; + } + + + + /// + /// 添加项目 + /// + /// + /// + [NonDynamicMethod] + public virtual async Task> AddOrUpdateTrial(TrialCommand trialAddModel, ITrialConfigService _ITrialConfigService) + { + + //测试用户 只能创建非正式项目 + if (_userInfo.IsTestUser && trialAddModel.TrialType != TrialType.NoneOfficial) + { + throw new BusinessValidationFailedException("测试用户 只能创建非正式项目"); + } + + if (trialAddModel.Id == Guid.Empty || trialAddModel.Id == null) + { + if (await _trialRepository.AnyAsync(u => u.TrialCode == trialAddModel.TrialCode)) + { + throw new BusinessValidationFailedException("已经存在相同的项目编号。"); + } + + var dbMaxCode = await _trialRepository.Where(t => t.CreateTime.Year == DateTime.Now.Year && t.TrialType == trialAddModel.TrialType).Select(t => t.Code).DefaultIfEmpty().MaxAsync(); + + var currentYearMaxCodeNext = dbMaxCode + 1; + + + var trial = _mapper.Map(trialAddModel); + var yearStr = DateTime.Now.Year.ToString(); + + trial.Code = currentYearMaxCodeNext; + trial.TrialCode = (trial.TrialType == TrialType.NoneOfficial ? "T0" : yearStr.Substring(yearStr.Length - 2)) + "000" + currentYearMaxCodeNext.ToString("D3"); + + + //多选信息 + trialAddModel.ModalityIds.ForEach(modalityId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = modalityId, KeyName = StaticData.Modality, TrialId = trial.Id })); + trialAddModel.CriterionIds.ForEach(criterionId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = criterionId, KeyName = StaticData.Criterion, TrialId = trial.Id })); + trialAddModel.ReviewTypeIds.ForEach(ReviewTypeId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = ReviewTypeId, KeyName = StaticData.ReviewType, TrialId = trial.Id })); + + //添加项目后 项目状态变更为申请下载简历 + trial.TrialEnrollStatus = (int)TrialEnrollStatus.ChooseDoctor; + //trial.TrialStatusStr = StaticData.TrialInitializing; + + //状态变更详细表 + trial.ClinicalTrialProjectDetails.Add(new TrialStatusDetail() { TrialId = trial.Id, TrialStatus = (int)TrialEnrollStatus.ChooseDoctor }); + + #region 设置默认值 + + trial.IsNoticeSubjectCodeRule = false; + + //临床信息传输 + trial.ClinicalInformationTransmissionEnum = 0; + //影像质控流程 + trial.QCProcessEnum = TrialQCProcess.NotAudit; + + trial.IsImageConsistencyVerification = false; + trial.IsVerifyVisitImageDate = true; + trial.IsTrialProcessConfirmed = true; + trial.IsTrialBasicLogicConfirmed = true; + trial.IsTrialUrgentConfirmed = true; + + trial.VisitPlanConfirmed = true; + + trial.IsHaveFirstGiveMedicineDate = false; + + trial.TrialStatusStr = StaticData.TrialState.TrialInitializing; + + trial.BlindBaseLineName = "Batch 1"; + trial.BlindBaseLineName = "Batch "; + #endregion + + + + + trial = await _trialRepository.AddAsync(trial); + + + + List needAddCriterionList = + await _repository.Where(x => x.IsEnable && x.CriterionType == CriterionType.RECIST1Pointt1).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + needAddCriterionList.ForEach(x => + { + x.TrialId = trial.Id; + x.ReadingQuestionCriterionSystemId = x.Id; + x.Id = NewId.NextGuid(); + + //手动选择 + x.IsConfirm = true; + x.IsSigned = true; + x.ReadingInfoSignTime = DateTime.Now; + + //阅片平台 + x.ImagePlatform = ImagePlatform.PACS; + x.ReadingTool = ReadingTool.Dicom; + x.ReadingType = ReadingMethod.Single; + + x.IsReadingPeriod = false; + x.IsGlobalReading = false; + x.IsArbitrationReading = false; + x.IsOncologyReading = false; + x.IsReadingTaskViewInOrder = true; + x.ReadingTaskViewEnum = ReadingTaskViewMethod.Subject; + x.IsReadingShowSubjectInfo = true; + x.IsReadingShowPreviousResults = true; + x.DigitPlaces = 1; + x.IseCRFShowInDicomReading = false; + }); + + + var cretrion= await _repository.AddAsync(needAddCriterionList.FirstOrDefault()); + + //如果是PM, 则需要将该人员添加到 运维人员表 + //添加运维人员PM + await _repository.AddAsync(new TrialUser() { TrialId = trial.Id, UserId = _userInfo.Id, JoinTime = DateTime.Now }); + + // 添加扩展信息表记录 + await _repository.AddAsync(new TrialPaymentPrice() { TrialId = trial.Id }); + + //添加检查批次 + await _repository.AddAsync(new VisitStage { TrialId = trial.Id, VisitNum = 0, BlindName = "B" + 0.ToString("D3"), VisitDay = 0, VisitName = "Batch 1", IsBaseLine = true,IsConfirmed=true,IsHaveFirstConfirmed=true ,VisitWindowLeft=-3,VisitWindowRight=28 }); + await _repository.AddAsync(new VisitStage { TrialId = trial.Id, VisitNum = 1, BlindName = "B" + 10.ToString("D3"), VisitDay = 30, VisitName = "Batch 2", IsConfirmed = true, IsHaveFirstConfirmed = true, VisitWindowLeft = -3, VisitWindowRight = 3 }); + await _repository.AddAsync(new VisitStage { TrialId = trial.Id, VisitNum = 2, BlindName = "B" + 20.ToString("D3"), VisitDay = 60, VisitName = "Batch 3", IsConfirmed = true, IsHaveFirstConfirmed = true, VisitWindowLeft = -3, VisitWindowRight = 3 }); + await _repository.AddAsync(new VisitStage { TrialId = trial.Id, VisitNum = 3, BlindName = "B" + 30.ToString("D3"), VisitDay = 90, VisitName = "Batch 4", IsConfirmed = true, IsHaveFirstConfirmed = true, VisitWindowLeft = -3, VisitWindowRight = 3 }); + await _repository.AddAsync(new VisitStage { TrialId = trial.Id, VisitNum = 4, BlindName = "B" + 40.ToString("D3"), VisitDay = 120, VisitName = "Batch 5", IsConfirmed = true, IsHaveFirstConfirmed = true, VisitWindowLeft = -3, VisitWindowRight = 3 }); + + + //添加trialSite + await _repository.AddAsync(new TrialSite() { TrialId = trial.Id, TrialSiteAliasName = "MDT Site", TrialSiteCode = "01", SiteId = Guid.Parse("db83e2f5-1f2e-408f-a45b-08d8e1dcace0") }); + + + var success = await _repository.SaveChangesAsync(); + + _provider.Set(trial.Id.ToString(), StaticData.TrialState.TrialInitializing, TimeSpan.FromDays(7)); + + await _ITrialConfigService.AsyncTrialCriterionDictionary(new Core.Application.Contracts.AsyncTrialCriterionDictionaryInDto() { TrialReadingCriterionId = cretrion.Id }); + + + return ResponseOutput.Ok(trial); + } + else + { + var updateModel = trialAddModel; + + if (!await _repository.AnyAsync(u => u.Id == trialAddModel.Id && (u.TrialStatusStr == StaticData.TrialState.TrialInitializing || u.TrialStatusStr == StaticData.TrialState.TrialOngoing))) + { + throw new BusinessValidationFailedException("项目状态只有处于:初始化或者进行中时,才允许操作。"); + + } + // 判断项目Id 是否已经存在 + if (await _repository.AnyAsync(u => u.TrialCode == updateModel.TrialCode && u.Id != updateModel.Id)) + { + throw new BusinessValidationFailedException("已经存在相同的项目编号。"); + } + + + + var trial = await _repository.FirstOrDefaultAsync(t => t.Id == updateModel.Id); + + + //删除中间表 Title对应的记录 + await _repository.BatchDeleteAsync(t => t.TrialId == updateModel.Id); + + //重新插入新的 Title记录 + + updateModel.ModalityIds.ForEach(modalityId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = modalityId, KeyName = StaticData.Modality, TrialId = trial.Id })); + updateModel.CriterionIds.ForEach(criterionId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = criterionId, KeyName = StaticData.Criterion, TrialId = trial.Id })); + updateModel.ReviewTypeIds.ForEach(ReviewTypeId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = ReviewTypeId, KeyName = StaticData.ReviewType, TrialId = trial.Id })); + + + if (updateModel.Expedited != trial.Expedited && updateModel.Expedited != null) + { + TrialExpeditedChange = true; + await TrialExpeditedStatusChange(trial.Id, trial.Expedited, (int)updateModel.Expedited); + } + _mapper.Map(updateModel, trial); + + var success = await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(trial); + + } + } + + + // TODO: 需要优化,嵌套两层 switch case ? + [NonDynamicMethod] + private async Task TrialExpeditedStatusChange(Guid trialId, int oldState, int newState) + { + switch (oldState) + { + case (int)TrialExpedited.None: + switch (newState) + { + case (int)TrialExpedited.ExpeditedIn24H: + await _repository.BatchUpdateAsync(t => t.IsLock == false && t.TrialId == trialId, u => new Workload() + { + TimepointIn24H = u.Timepoint, + AdjudicationIn24H = u.Adjudication, + Timepoint = 0, + Adjudication = 0 + }); + break; + case (int)TrialExpedited.ExpeditedIn48H: + await _repository.BatchUpdateAsync(t => t.IsLock == false && t.TrialId == trialId, u => new Workload() + { + TimepointIn48H = u.Timepoint, + AdjudicationIn48H = u.Adjudication, + Timepoint = 0, + Adjudication = 0 + }); + break; + } + //_workloadRepository.Update(t => t.IsLock == false && t.TrialId == trialId, u => new Workload() + //{ + // Timepoint = 0, + // Adjudication = 0 + //}); + + break; + case (int)TrialExpedited.ExpeditedIn24H: + + switch (newState) + { + case (int)TrialExpedited.None: + await _repository.BatchUpdateAsync(t => t.IsLock == false && t.TrialId == trialId, u => new Workload() + { + Timepoint = u.TimepointIn24H, + Adjudication = u.AdjudicationIn24H, + TimepointIn24H = 0, + AdjudicationIn24H = 0 + }); + break; + case (int)TrialExpedited.ExpeditedIn48H: + await _repository.BatchUpdateAsync(t => t.IsLock == false && t.TrialId == trialId, u => new Workload() + { + TimepointIn48H = u.TimepointIn24H, + AdjudicationIn48H = u.AdjudicationIn24H, + TimepointIn24H = 0, + AdjudicationIn24H = 0 + }); + + break; + } + + //_workloadRepository.Update(t => t.IsLock == false && t.TrialId == trialId, u => new Workload() + //{ + // TimepointIn24H = 0, + // AdjudicationIn24H = 0 + //}); + + break; + case (int)TrialExpedited.ExpeditedIn48H: + switch (newState) + { + case (int)TrialExpedited.None: + await _repository.BatchUpdateAsync(t => t.IsLock == false && t.TrialId == trialId, u => new Workload() + { + Timepoint = u.TimepointIn48H, + Adjudication = u.AdjudicationIn48H, + TimepointIn48H = 0, + AdjudicationIn48H = 0 + }); + break; + case (int)TrialExpedited.ExpeditedIn24H: + await _repository.BatchUpdateAsync(t => t.IsLock == false && t.TrialId == trialId, u => new Workload() + { + TimepointIn24H = u.TimepointIn48H, + AdjudicationIn24H = u.AdjudicationIn48H, + TimepointIn48H = 0, + AdjudicationIn48H = 0 + }); + break; + } + + //_workloadRepository.Update(t => t.IsLock == false && t.TrialId == trialId, u => new Workload() + //{ + // TimepointIn48H = 0, + // AdjudicationIn48H = 0 + //}); + break; + } + } + + + + /// 真删除项目 方便清理测试数据 + /// 临床试验项目Id + + [HttpDelete, Route("{trialId:guid}")] + public async Task DeleteTrial(Guid trialId) + { + + + var trial = (await _trialRepository.FirstOrDefaultAsync(u => u.Id == trialId, true)).IfNullThrowException(); + + + if (_verifyConfig.CurrentValue.OpenTrialRelationDelete) + { + + #region 项目真删除废弃 + + //if (trial.VisitPlanConfirmed) + //{ + // return ResponseOutput.NotOk("Trial检查批次计划已经确认,无法删除"); + //} + + //if (await _repository.AnyAsync(u => u.TrialId == trialId)) + //{ + // return ResponseOutput.NotOk("该Trial有医生入组或在入组流程中,无法删除"); + //} + + //if (await _repository.AnyAsync(u => u.TrialId == trialId)) + //{ + // return ResponseOutput.NotOk("该Trial下面有Site,无法删除"); + //} + + ////PM 可以删除项目 仅仅在没有site 参与者只有他自己的时候 + //if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager) + //{ + // //参与者仅有他自己时,可以删除 + // if (await _trialUserRepository.CountAsync(t => t.TrialId == trialId) == 1) + // { + + // var success1 = await _repository.BatchDeleteAsync(o => o.Id == trialId) || + // await _repository.BatchDeleteAsync(t => t.TrialId == trialId) || + // await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + // return ResponseOutput.Result(success1); + // } + //} + + //if (await _repository.AnyAsync(u => u.TrialId == trialId)) + //{ + // return ResponseOutput.NotOk("该Trial下面有参与者,无法删除"); + //} + #endregion + + + + var success = await _repository.BatchDeleteAsync(o => o.SubjectVisit.TrialId == trialId); + await _repository.BatchDeleteAsync(o => o.TrialId == trialId); + await _repository.BatchDeleteAsync(o => o.TrialId == trialId); + + + await _repository.BatchDeleteAsync(o => o.TrialReadingCriterion.TrialId == trialId); + await _repository.BatchDeleteAsync(o => o.TrialReadingCriterion.TrialId == trialId); + + + await _repository.BatchDeleteAsync(o => o.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.Enroll.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.NoneDicomStudy.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + + + + await _repository.BatchDeleteAsync(t => t.SubjectVisit.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.SubjectVisit.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.SubjectVisit.TrialId == trialId); + + + await _repository.BatchDeleteAsync(t => t.QCChallenge.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + + + await _repository.BatchDeleteAsync(t => t.ReadingClinicalData.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + + await _repository.BatchDeleteAsync(t => t.TaskMedicalReview.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TaskMedicalReview.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + + await _repository.BatchDeleteAsync(t => t.VisitTask.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.ReadingPeriodSet.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.VisitTask.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.Subject.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.InfluenceTask.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + + await _repository.BatchDeleteAsync(o => o.Id == trialId); + await _repository.BatchDeleteAsync(t => t.TrialReadingCriterion.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialReadingCriterion.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialDocument.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialDocument.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialEmailNoticeConfig.TrialId == trialId); + + //await _repository.BatchDeleteAsync(t => t.TrialId == trialId) || + await _repository.BatchDeleteAsync(t => t.TrialEmailNoticeConfig.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialSiteSurvey.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialSiteSurvey.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + await _repository.BatchDeleteAsync(t => t.OriginalReReadingTask.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + + + + ; + + return ResponseOutput.Result(success); + + } + + else + { + return ResponseOutput.NotOk("当前运行环境下,不允许删除项目数据。"); + } + + + } + + [AllowAnonymous] + [HttpDelete, Route("{trialId:guid}")] + public async Task DeleteTrialTaskData(Guid trialId) + { + + //await _repository.BatchDeleteAsync(t => t.TrialId == trialId) || + // await _repository.BatchDeleteAsync(t => t.TrialId == trialId) || + // await _repository.BatchDeleteAsync(t => t.TrialId == trialId) || + + + + + //await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + // await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + + // await _repository.BatchDeleteAsync(t => t.TaskMedicalReview.TrialId == trialId); + // await _repository.BatchDeleteAsync(t => t.TaskMedicalReview.TrialId == trialId); + + + // await _repository.BatchDeleteAsync(t => t.VisitTask.TrialId == trialId); + + // await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + // await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + // await _repository.BatchDeleteAsync(t => t.TrialId == trialId) ; + + // await _repository.BatchDeleteAsync(t => t.VisitTask.TrialId == trialId); + // await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + // await _repository.BatchDeleteAsync(t => t.OriginalReReadingTask.TrialId == trialId); + // await _repository.BatchDeleteAsync(t => t.TrialId == trialId); + + return ResponseOutput.Ok(); + } + + [HttpPost] + public async Task> GetReviewerTrialListByEnrollmentStatus(TrialByStatusQueryDTO param) + { + + var query = _trialRepository.AsQueryable() + .WhereIf(param.Status == 5, t => t.EnrollList.Any(u => u.EnrollStatus == EnrollStatus.HasCommittedToCRO)) + .WhereIf(param.Status == 8, t => t.EnrollList.Any(u => u.EnrollStatus == EnrollStatus.InviteIntoGroup)) + .WhereIf(param.Status == 10, t => t.EnrollList.Any(u => u.EnrollStatus == EnrollStatus.DoctorReading)) + .WhereIf(param.Status == 14, t => t.EnrollList.Any(u => u.EnrollStatus == EnrollStatus.Finished)) + .ProjectTo(_mapper.ConfigurationProvider, new { userTypeEnumInt = _userInfo.UserTypeEnumInt, userId = _userInfo.Id }); + + return await query.ToPagedListAsync(param.PageIndex, param.PageSize, string.IsNullOrWhiteSpace(param.SortField) ? "CreateTime" : param.SortField, param.Asc); + + } + + + + /// + /// 根据项目Id 获取医生Id,用于触发计算费用 + /// + public async Task> GetTrialEnrollmentReviewerIds(Guid trialId) + { + return await _repository.Where(u => u.TrialId == trialId && + u.EnrollStatus >= EnrollStatus.DoctorReading).Select(u => u.DoctorId).Distinct().ToListAsync(); + } + + + + + #region 医生用户接口 + + /// 分页获取医生参与的临床实验项目列表(查询条件) + /// + [HttpPost] + public async Task> GetTrialListByReviewer(ReviewerTrialQueryDTO searchModel) + { + var query = _trialRepository + .WhereIf(searchModel.EnrollStatus != null, o => (int)searchModel.EnrollStatus! == 10 ? + o.EnrollList.Any(o => o.EnrollStatus >= EnrollStatus.ConfirmIntoGroup && o.EnrollStatus <= EnrollStatus.DoctorReading && o.DoctorId == _userInfo.Id) : + o.EnrollList.Any(o => o.EnrollStatus == searchModel.EnrollStatus && o.DoctorId == _userInfo.Id)) + .WhereIf(searchModel.Expedited != null, o => o.Expedited == searchModel.Expedited) + .WhereIf(!string.IsNullOrEmpty(searchModel.Code), o => o.TrialCode.Contains(searchModel.Code)) + .WhereIf(!string.IsNullOrWhiteSpace(searchModel.Indication), o => o.Indication.Contains(searchModel.Indication)) + .WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin, t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id)) + .ProjectTo(_mapper.ConfigurationProvider, new { userTypeEnumInt = _userInfo.UserTypeEnumInt, userId = _userInfo.Id }); + + + return await query.ToPagedListAsync(searchModel.PageIndex, searchModel.PageSize, string.IsNullOrWhiteSpace(searchModel.SortField) ? "CreateTime" : searchModel.SortField, searchModel.Asc); + + + + } + + + /// + /// 医生确认入组或拒绝入组 + /// + /// 项目Id + /// 9-拒绝入组,10-确认入组 + /// + [HttpPost("{trialId:guid}/{status:int}")] + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task UpdateEnrollStatus(Guid trialId, EnrollStatus status) + { + await _repository.AddAsync(new EnrollDetail() + { + DoctorId = _userInfo.Id, + TrialId = trialId, + EnrollStatus = status, + OptUserType = (int)SystemUserType.DoctorUser, + }); + return ResponseOutput.Result(await _repository.BatchUpdateAsync(u => u.TrialId == trialId && u.DoctorId == _userInfo.Id, e => new Enroll + { + EnrollStatus = status + })); + } + + #endregion + + + + + + + } + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteService.cs new file mode 100644 index 0000000..2621691 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteService.cs @@ -0,0 +1,382 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Filter; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.Service; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Auth; + +namespace IRaCIS.Core.Application.Services +{ + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialMaintenanceService : BaseService, ITrialSiteService + { + private readonly IRepository _trialSiteRepository; + private readonly IRepository _trialSiteUserRepository; + private readonly IRepository _siteRepository; + private readonly IRepository _trialRepository; + + public TrialMaintenanceService(IRepository trialSiteRepository, IRepository trialSiteUserRepository + , IRepository siteRepository, IRepository trialRepository) + { + _trialSiteRepository = trialSiteRepository; + _trialSiteUserRepository = trialSiteUserRepository; + _siteRepository = siteRepository; + _trialRepository = trialRepository; + } + + + #region 导出列表 + + /// + /// Site用户列表导出 + /// + /// + /// + /// + /// + [HttpPost] + [AllowAnonymous] + public async Task TrialSiteUserListExport(SiteCRCExportQueryDTO param, [FromServices] IRepository _commonDocumentRepository) + { + + var exportInfo = (await _trialRepository.Where(t => t.Id == param.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.TrialSiteUserList = await _trialSiteUserRepository.Where(t => t.TrialId == param.TrialId).IgnoreQueryFilters() + .WhereIf(param.IsDeleted != null, t => t.IsDeleted == param.IsDeleted) + .WhereIf(!string.IsNullOrWhiteSpace(param.SiteName), t => t.Site.SiteName.Contains(param.SiteName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.TrialSiteAliasName), + t => t.TrialSite.TrialSiteAliasName.Contains(param.TrialSiteAliasName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.TrialSiteCode), + t => t.TrialSite.TrialSiteCode.Contains(param.TrialSiteCode)) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, + t => t.UserId == _userInfo.Id) + .WhereIf(!string.IsNullOrWhiteSpace(param.UserKeyInfo), t => (t.User.FullName).Contains(param.UserKeyInfo) + || t.User.UserName.Contains(param.UserKeyInfo) || t.User.EMail.Contains(param.UserKeyInfo)) + + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + var no = 1; + exportInfo.TrialSiteUserList.ForEach(t => t.No = no++); + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSiteUserList_Export, exportInfo, exportInfo.TrialCode, _commonDocumentRepository, _hostEnvironment); + + } + + /// + /// Site用户汇总表导出 + /// + /// + /// + /// + /// + /// + /// + [HttpPost] + [AllowAnonymous] + public async Task TrialSiteUserSummaryListExport(TrialSiteUserSurveyExportQueryDto queryParam, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IRepository _trialSiteSurveyRepository, + [FromServices] IRepository _trialSiteUserSurveyRepository + ) + { + + var data = (await _trialRepository.Where(t => t.Id == queryParam.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + var groupSelectIdQuery = + _trialSiteSurveyRepository.Where(t => t.TrialId == queryParam.TrialId) + .WhereIf(queryParam.SiteId != null, t => t.SiteId == queryParam.SiteId) + .WhereIf(!string.IsNullOrEmpty(queryParam.FormWriterKeyInfo), t => (t.UserName).Contains(queryParam.FormWriterKeyInfo) || t.Email.Contains(queryParam.FormWriterKeyInfo) || t.Phone.Contains(queryParam.FormWriterKeyInfo)) + .GroupBy(t => t.SiteId) + .Select(g => g.OrderByDescending(u => u.CreateTime).Select(t => t.Id).First()); + + + var query = _trialSiteUserSurveyRepository + .Where(t => groupSelectIdQuery.Contains(t.TrialSiteSurveyId)) + .WhereIf(queryParam.UserTypeId != null, t => t.UserTypeId == queryParam.UserTypeId) + .WhereIf(queryParam.IsGenerateAccount != null, t => t.IsGenerateAccount == queryParam.IsGenerateAccount) + .WhereIf(queryParam.TrialRoleNameId != null, t => t.TrialRoleNameId == queryParam.TrialRoleNameId) + .WhereIf(queryParam.State != null && queryParam.State != TrialSiteUserStateEnum.OverTime, t => t.InviteState == queryParam.State) + .WhereIf(queryParam.State != null && queryParam.State == TrialSiteUserStateEnum.OverTime, t => t.InviteState == TrialSiteUserStateEnum.HasSend && t.ExpireTime < DateTime.Now) + .WhereIf(!string.IsNullOrEmpty(queryParam.UserName), t => (t.LastName + " / " + t.FirstName).Contains(queryParam.UserName)) + .WhereIf(!string.IsNullOrEmpty(queryParam.OrganizationName), t => t.OrganizationName.Contains(queryParam.OrganizationName)) + .ProjectTo(_mapper.ConfigurationProvider); + + data.TrialSiteUserList = await query.ToListAsync(); + + + + var exportInfo = data; + + //处理翻译 + var no = 1; + exportInfo.TrialSiteUserList.ForEach(t => t.No = no++); + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSiteUserSummary_Export, exportInfo, exportInfo.TrialCode, _commonDocumentRepository, _hostEnvironment); + + } + + + + + #endregion + + + + + + + + /// Pannel 进去 SiteTab + [HttpPost] + public async Task> GetSiteCRCList(SiteCrcQueryDTO param) + { + + var siteStatQuery = _trialSiteRepository.Where(t => t.TrialId == param.TrialId, ignoreQueryFilters: true) + .WhereIf(param.IsDeleted != null, t => t.IsDeleted == param.IsDeleted) + .WhereIf(!string.IsNullOrWhiteSpace(param.SiteName), t => t.Site.SiteName.Contains(param.SiteName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.TrialSiteAliasName), t => t.TrialSiteAliasName.Contains(param.TrialSiteAliasName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.TrialSiteCode), t => t.TrialSiteCode.Contains(param.TrialSiteCode)) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, t => t.CRCUserList.Any(k => k.UserId == _userInfo.Id)) + .WhereIf(!string.IsNullOrWhiteSpace(param.UserKeyInfo), t => t.CRCUserList.Any(k => (k.User.FullName).Contains(param.UserKeyInfo) + || k.User.UserName.Contains(param.UserKeyInfo) || k.User.EMail.Contains(param.UserKeyInfo))) + + .ProjectTo(_mapper.ConfigurationProvider); + + return await siteStatQuery.ToPagedListAsync(param.PageIndex, + param.PageSize, string.IsNullOrWhiteSpace(param.SortField) ? "SiteCode" : param.SortField, param.Asc); + + + } + + + /// [new] setting页面Site列表,和getSiteCRCList对比 没有统计数据,增加了一些site信息 + [HttpPost] + public async Task> GetSiteCRCSimpleList(SiteCrcQueryDTO param) + { + + var siteStatQuery = _trialSiteRepository.Where(t => t.TrialId == param.TrialId).IgnoreQueryFilters() + .WhereIf(param.IsDeleted != null, t => t.IsDeleted == param.IsDeleted) + .WhereIf(!string.IsNullOrWhiteSpace(param.SiteName), t => t.Site.SiteName.Contains(param.SiteName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.TrialSiteAliasName), t => t.TrialSiteAliasName.Contains(param.TrialSiteAliasName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.TrialSiteCode), t => t.TrialSiteCode.Contains(param.TrialSiteCode)) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, t => t.CRCUserList.Any(k => k.UserId == _userInfo.Id)) + .WhereIf(!string.IsNullOrWhiteSpace(param.UserKeyInfo), t => t.CRCUserList.Any(k => (k.User.FullName).Contains(param.UserKeyInfo) + || k.User.UserName.Contains(param.UserKeyInfo) || k.User.EMail.Contains(param.UserKeyInfo))) + + .ProjectTo(_mapper.ConfigurationProvider); + + + var result = await siteStatQuery.ToPagedListAsync(param.PageIndex, + param.PageSize, string.IsNullOrWhiteSpace(param.SortField) ? "Site" : param.SortField, param.Asc); + + return result; + } + + + /// 获取某一Site下面的负责的IC列表 + [HttpGet, Route("{trialId:guid}/{siteId:guid}")] + public async Task> GetTrialSiteCRCList(Guid trialId, Guid siteId) + { + var query = _trialSiteUserRepository.Where(t => t.TrialId == trialId && t.SiteId == siteId).IgnoreQueryFilters() + .ProjectTo(_mapper.ConfigurationProvider); + + return await query.ToListAsync(); + } + + + /// [new] Setting页面 Site勾选列表( + [HttpPost] + public async Task> GetTrialSiteScreeningList(TrialSiteQuery searchModel) + { + // 之前选择了的不能再次出现在列表,做的时候我就不建议这样搞,搞好了 现在又要改回去。。。 瞎折腾。。。。 + + + var siteQueryable = _siteRepository.AsQueryable(true) + .WhereIf(!string.IsNullOrWhiteSpace(searchModel.SiteName), t => t.SiteName.Contains(searchModel.SiteName)) + .WhereIf(!string.IsNullOrWhiteSpace(searchModel.AliasName), t => t.AliasName.Contains(searchModel.AliasName)) + .WhereIf(!string.IsNullOrWhiteSpace(searchModel.City), t => t.City.Contains(searchModel.City)) + .WhereIf(!string.IsNullOrWhiteSpace(searchModel.Country), t => t.Country.Contains(searchModel.Country)) + .ProjectTo(_mapper.ConfigurationProvider, new { trialId = searchModel.TrialId }); + + + return await siteQueryable.ToPagedListAsync(searchModel.PageIndex, + searchModel.PageSize, string.IsNullOrWhiteSpace(searchModel.SortField) ? "SiteName" : searchModel.SortField, searchModel.Asc); + + } + + + + + /// Setting页面 Site批量添加 + [HttpPost] + [UnitOfWork] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddTrialSites(List trialSites) + { + var addArray = _mapper.Map>(trialSites); + + await _repository.AddRangeAsync(addArray); + + + return ResponseOutput.Result(await _repository.SaveChangesAsync()); + } + + + + /// + /// 项目site 编辑接口 New 可以设置为启用不启用 不启用 不会验证Code 重复 + /// + /// + /// + [HttpPut] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + public async Task EditTrialSite(EditTrialSiteCommand editTrialSiteCommand) + { + + var dbEntity = await _trialSiteRepository.FirstOrDefaultAsync(t => t.Id == editTrialSiteCommand.Id, true); + + if (dbEntity == null) return Null404NotFound(dbEntity); + + var trialId = dbEntity.TrialId; + var siteId = dbEntity.SiteId; + + if (editTrialSiteCommand.IsDeleted) + { + + //if (await _repository.AnyAsync(t => t.TrialId == trialId && t.SiteId == siteId)) + //{ + // return ResponseOutput.NotOk("The site has been associated with IC, and couldn't be deleted."); + //} + + if (await _repository.AnyAsync(t => t.SiteId == siteId && t.TrialId == trialId)) + { + return ResponseOutput.NotOk("The subjects has been added to this site, and couldn't be disable."); + } + + //if (await _repository.AnyAsync(t => t.SiteId == siteId && t.TrialId == trialId)) + //{ + // return ResponseOutput.NotOk("The site has been uploaded study, and couldn't be deleted."); + //} + + + } + else + { + if (await _trialSiteRepository.AnyAsync(t => t.Id != editTrialSiteCommand.Id && t.TrialSiteCode == editTrialSiteCommand.TrialSiteCode && t.TrialId == editTrialSiteCommand.TrialId)) + { + return ResponseOutput.NotOk("Code is not allowed to be repeated"); + } + } + + + _mapper.Map(editTrialSiteCommand, dbEntity); + + await _trialSiteRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + + } + + + + /// 批量添加Site下 IC的负责人 + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + public async Task AssignSiteCRC(List trialSiteCRCList) + { + var addArray = _mapper.Map>(trialSiteCRCList); + + await _trialSiteUserRepository.AddRangeAsync(addArray); + + await _trialSiteUserRepository.SaveChangesAsync(); + return ResponseOutput.Result(true); + } + + /// 删除IC人员 + [HttpDelete, Route("{id:guid}/{trialId:guid}/{isDelete:bool}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + public async Task DeleteSiteCRC(Guid id, bool isDelete) + { + + + await _trialSiteUserRepository.UpdatePartialFromQueryAsync(t => t.Id == id, u => new TrialSiteUser() { IsDeleted = isDelete, DeletedTime = isDelete ? DateTime.Now : null }, true, true); + + return ResponseOutput.Ok(); + } + + /// + /// 获取项目下的 site 下拉框数据 IC只看到他负责的 + /// + /// + /// + [HttpGet("{trialId:guid}")] + public async Task> GetTrialSiteSelect(Guid trialId) + { + //IC只看到他负责的 + + var list = await _trialSiteRepository.Where(t => t.TrialId == trialId) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(t => t.TrialSiteCode).ToListAsync(); + + + return list; + } + + + public async Task> GetTrialSiteCodeSelect(Guid trialId, bool isIncludeVirtualSite = true) + { + //IC只看到他负责的 + + var list = await _trialSiteRepository.Where(t => t.TrialId == trialId) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + .Select(t => t.TrialSiteCode).ToListAsync(); + + var virtualList = await _repository.Where(t => t.IsSelfAnalysis == true && t.TrialId == trialId).Select(t => t.BlindTrialSiteCode).Distinct().ToListAsync(); + + return isIncludeVirtualSite ? list.Concat(virtualList) : list; + } + + + /// 删除 项目 下某一site + [HttpDelete("{id:guid}/{trialId:guid}")] + [TrialAudit(AuditType.TrialAudit, AuditOptType.DeleteTrialSite)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [Obsolete] + public async Task DeleteTrialSite(Guid id) + { + var relation = await _trialSiteRepository.FirstOrDefaultAsync(t => t.Id == id); + + if (relation == null) return Null404NotFound(relation); + + var trialId = relation.TrialId; + var siteId = relation.SiteId; + + if (await _repository.AnyAsync(t => t.TrialId == trialId && t.SiteId == siteId)) + { + return ResponseOutput.NotOk("The site has been associated with IC, and couldn't be deleted."); + } + + if (await _repository.AnyAsync(t => t.SiteId == siteId && t.TrialId == trialId)) + { + return ResponseOutput.NotOk("The subjects has been added to this site, and couldn't be deleted."); + } + if (await _repository.AnyAsync(t => t.SiteId == siteId && t.TrialId == trialId)) + { + return ResponseOutput.NotOk("The site has been uploaded study, and couldn't be deleted."); + } + + await _repository.DeleteAsync(relation); + + return ResponseOutput.Result(await _repository.SaveChangesAsync()); + } + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs new file mode 100644 index 0000000..55b688e --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs @@ -0,0 +1,310 @@ +using AutoMapper; +using AutoMapper.EquivalencyExpression; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.DTO; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Service +{ + public class TrialSiteUserConfig : Profile + { + public TrialSiteUserConfig() + { + //CreateMap(); + //CreateMap().ForMember(t => t.UserTypes, u => u.MapFrom(k => string.Join(',', k.UserTypeList))); + + + + //CreateMap().ForMember(d => d.Id, u => u.MapFrom(s => s.TrialId)).ReverseMap(); + + + CreateMap(); + + + CreateMap () + .ForMember(d => d.QCProcessEnum, u => u.MapFrom(s => s.Trial.QCProcessEnum)) + .ForMember(d => d.IsImageConsistencyVerification, u => u.MapFrom(s => s.Trial.IsImageConsistencyVerification)) + .ReverseMap(); + + + CreateMap().ForMember(d => d.Id, u => u.MapFrom(s => s.TrialId)).ReverseMap(); + + CreateMap().ForMember(d => d.Id, u => u.MapFrom(s => s.TrialId)).ReverseMap(); + + CreateMap().ForMember(d => d.Id, u => u.MapFrom(s => s.TrialId)); + + + + CreateMap(); + + CreateMap() + .ForMember(d => d.TrialSiteAliasName, u => u.MapFrom(s => s.SiteName)) + .ForMember(x => x.Id, x => x.Ignore()); + + CreateMap(); + + CreateMap(); + + + + CreateMap(); + + CreateMap(); + + CreateMap(); + + + //临床项目 + CreateMap().ForAllMembers(opt => opt.Condition((src, dest, srcMember) => srcMember != null)); + + CreateMap(); + + var userId = Guid.Empty; + var userTypeEnumInt = 0; + CreateMap() + .ForMember(x=>x.CriterionList,y=>y.MapFrom(z=>z.ReadingQuestionCriterionTrialList.Where(n=>n.IsConfirm).Select(m=>m.CriterionName))) + .ForMember(d => d.DictionaryList, u => u.MapFrom(s => s.TrialDicList.Select(t => t.Dictionary).OrderBy(t => t.ShowOrder))) + //.ForMember(d => d.Code, u => u.MapFrom(s => s.TrialCode)) + .ForMember(d => d.Sponsor, u => u.MapFrom(s => s.Sponsor.SponsorName)) + .ForMember(d => d.Phase, u => u.MapFrom(s => s.Phase.MappedValue)) + .ForMember(d => d.DeclarationType, u => u.MapFrom(s => s.DeclarationType.MappedValue)) + .ForMember(d => d.IndicationType, u => u.MapFrom(s => s.IndicationType.MappedValue)) + .ForMember(d => d.CRO, u => u.MapFrom(s => s.CRO.CROName)) + .ForMember(d => d.ReviewMode, u => u.MapFrom(s => s.ReviewMode.MappedValue)) + //.ForMember(d => d.ReviewType, u => u.MapFrom(s => s.ReviewType.Value)) + .ForMember(d => d.IsLocked, u => u.MapFrom(s => s.WorkloadList.Any(u => u.DataFrom == (int)WorkLoadFromStatus.FinalConfirm))) + //.ForMember(d => d.SiteCount, u => u.MapFrom(s => userTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator ? s.TrialSiteUserList.Count(k => k.UserId == userId) : s.TrialSiteList.Count())) + //.ForMember(d => d.StudyCount, u => u.MapFrom(s => userTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator ? s.StudyList.Count(t => t.TrialSite.CRCUserList.Any(t => t.UserId == userId)) : s.StudyList.Count())) + //.ForMember(d => d.SubjectCount, u => u.MapFrom(s => userTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator ? s.SubjectList.Count(t => t.TrialSite.CRCUserList.Any(t => t.UserId == userId)) : s.SubjectList.Count())) + ; + + + + + CreateMap().IncludeMembers(t => t.User) + .ForMember(d => d.UserRealName, u => u.MapFrom(s => s.User.FullName)) + .ForMember(t => t.UserType, u => u.MapFrom(t => t.User.UserTypeRole.UserTypeShortName)); + CreateMap(); + + var siteId = Guid.Empty; + CreateMap().IncludeMembers(t => t.User) + .ForMember(d => d.UserRealName, u => u.MapFrom(s => s.User.FullName)) + .ForMember(d => d.SiteId, u => u.MapFrom(t => siteId)) + .ForMember(d => d.UserType, u => u.MapFrom(t => t.User.UserTypeRole.UserTypeShortName)) + .ForMember(d => d.IsSelect, u => u.MapFrom(t => t.SiteList.Any(k => k.SiteId == siteId))); + CreateMap(); + + var trialId = Guid.Empty; + CreateMap() + .ForMember(d => d.UserRealName, u => u.MapFrom(s => s.FullName)) + .ForMember(d => d.UserType, u => u.MapFrom(s => s.UserTypeRole.UserTypeShortName)) + .ForMember(d => d.UserId, u => u.MapFrom(s => s.Id)) + .ForMember(d => d.TrialId, u => u.MapFrom(s => trialId)) + .ForMember(d => d.IsSelect, u => u.MapFrom(t => t.UserTrials.Any(t => t.TrialId == trialId))); + + + CreateMap() + .ForMember(d => d.HospitalName, u => u.MapFrom(s => s.Hospital.HospitalName)); + + //trial site 选择列表 subjectVisit pannel 模式添加的时候 + CreateMap() + .ForMember(d => d.IsSelect, u => u.MapFrom(s => s.TrialSiteList.Any(k => k.TrialId == trialId))); + + #region 项目 stie pannel + + #region site 也有country hospital 也有 注意区分 + CreateMap() + .ForMember(d => d.SiteCode, u => u.MapFrom(s => s.Site.SiteCode)) + .ForMember(d => d.City, u => u.MapFrom(s => s.Site.City)) + .ForMember(d => d.Country, u => u.MapFrom(s => s.Site.Country)) + .ForMember(d => d.Hospital, u => u.MapFrom(s => s.Site.Hospital.HospitalName)) + + + .ForMember(d => d.DirectorName, u => u.MapFrom(s => s.Site.DirectorName)) + .ForMember(d => d.DirectorPhone, u => u.MapFrom(s => s.Site.DirectorPhone)) + .ForMember(d => d.ContactPhone, u => u.MapFrom(s => s.Site.ContactPhone)) + .ForMember(d => d.Address, u => u.MapFrom(s => s.Site.Address)) + .ForMember(d => d.Site, u => u.MapFrom(s => s.Site.SiteName)) + .ForMember(d => d.VisitCount, u => u.MapFrom(s => s.SubjectVisitList.Count())) + .ForMember(d => d.SubjectCount, u => u.MapFrom(s => s.SubjectList.Count())) + + + + .ForMember(d => d.UserCount, u => u.MapFrom(s => s.CRCUserList.Count())) + .ForMember(d => d.UserNameList, u => u.MapFrom(s => s.CRCUserList.Where(t => t.IsDeleted == false).Select(u => u.User.FullName))); + #endregion + + + + + + CreateMap().IncludeMembers(t => t.Site) + .ForMember(d => d.Id, u => u.MapFrom(s => s.Id)) + .ForMember(d => d.UpdateTime, u => u.MapFrom(s => s.UpdateTime)) + .ForMember(d => d.Site, u => u.MapFrom(s => s.Site.SiteName)) + .ForMember(d => d.Hospital, u => u.MapFrom(s => s.Site.Hospital.HospitalName)) + .ForMember(d => d.UserCount, u => u.MapFrom(s => s.CRCUserList.Count())) + .ForMember(d => d.UserNameList, u => u.MapFrom(s => s.CRCUserList.Where(t => t.IsDeleted == false).Select(u => u.User.FullName))); + CreateMap(); + + + + CreateMap().IncludeMembers(t => t.User) + .ForMember(d => d.UserType, u => u.MapFrom(s => s.User.UserTypeRole.UserTypeShortName)) + .ForMember(d => d.UserRealName, u => u.MapFrom(s => s.User.FullName)); + CreateMap(); + #endregion + + + + CreateMap(); + CreateMap().ForMember(t => t.TrialId, u => u.MapFrom(c => c.Id)) + .ForMember(t => t.TrialCriterionIds, u => u.MapFrom(c => c.ReadingQuestionCriterionTrialList.Where(v =>v.IsConfirm).OrderBy(x=>x.ShowOrder).Select(r => r.Id))) + .ForMember(t => t.TrialCriterionNames, u => u.MapFrom(c => c.ReadingQuestionCriterionTrialList.Where(v => v.IsConfirm).OrderBy(x => x.ShowOrder).Select(r => r.CriterionName))) + .ForMember(t => t.ClinicalDataTrialSetIds, u => u.MapFrom(c => c.clinicalDataTrialSets.Where(v => v.IsConfirm).Select(r => r.Id))) + .ForMember(t => t.ClinicalDataSetNames, u => u.MapFrom(c => c.clinicalDataTrialSets.Where(v => v.IsConfirm).Select(r => r.ClinicalDataSetName))) + //.ForMember(t => t.CriterionIds, u => u.MapFrom(c => c.TrialDicList.Where(v => v.KeyName == StaticData.Criterion).Select(r => r.DictionaryId))) + ; + CreateMap(); + CreateMap(); + + + CreateMap(); + + CreateMap().ForMember(t => t.TrialDicList, u => u.MapFrom(k => k.CriterionIds)); + CreateMap().EqualityComparison((odto, o) => odto == o.DictionaryId) + .ForMember(t => t.DictionaryId, u => u.MapFrom(c => c)) + .ForMember(t => t.KeyName, u => u.MapFrom(c => StaticData.Criterion)); + + + CreateMap(); + + CreateMap(); + CreateMap ().ForMember(t=>t.TrialId,u=>u.MapFrom(c=>c.Id)); + + CreateMap(); + + + CreateMap(); + + CreateMap() + .ForMember(t => t.UserRealName, u => u.MapFrom(c => c.User.FullName)) + .ForMember(t => t.UserName, u => u.MapFrom(c => c.User.UserName)); + + + + CreateMap().ReverseMap(); + + CreateMap(); + + CreateMap().ReverseMap(); + CreateMap(); + + CreateMap(); + + + CreateMap().ReverseMap(); + + + CreateMap() + + .ForMember(t => t.UserRealName, u => u.MapFrom(c => c.User.FullName)) + .ForMember(t => t.UserName, u => u.MapFrom(c => c.User.UserName)) + .ForMember(t => t.UserTypeShortName, u => u.MapFrom(c => c.User.UserTypeRole.UserTypeShortName)); + + + CreateMap().IncludeMembers(t => t.Trial) + .ForMember(t => t.UserId, u => u.MapFrom(c => c.SystemUserId)); + CreateMap(); + + CreateMap().IncludeMembers(t => t.TrialSiteSurvey.Trial) + .ForMember(t => t.UserId, u => u.MapFrom(c => c.SystemUserId)) + .ForMember(t => t.TrialId, u => u.MapFrom(c => c.TrialSiteSurvey.TrialId)); + + + CreateMap(); + + CreateMap(); + + CreateMap(); + + CreateMap(); + + + CreateMap() + .ForMember(t => t.TrialUserList, u => u.Ignore()); + + + + + CreateMap(); + + + + CreateMap() + .ForMember(d => d.GroupName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupName : s.GroupInfo.GroupName)) + .ForMember(d => d.GroupEnName, u => u.MapFrom(s => s.GroupInfo == null ? s.GroupEnName : s.GroupInfo.GroupEnName)) + .ForMember(t => t.PageName, u => u.MapFrom(c => c.ReadingCriterionPage.PageName)) + .ForMember(d => d.ParentQuestionGenre, u => u.MapFrom(s => s.ParentReadingQuestionTrial.QuestionGenre)) + .ForMember(d => d.ParentDictionaryCode, u => u.MapFrom(s => s.ParentReadingQuestionTrial.DictionaryCode)) + .ForMember(t => t.ParentQuestionName, u => u.MapFrom(c => c.ParentReadingQuestionTrial.QuestionName)) + .ForMember(t => t.RelevanceShowOrder, u => u.MapFrom(c => c.RelevanceReadingQuestionTrial.ShowOrder)) + .ForMember(t => t.ParentQuestionShowOrder, u => u.MapFrom(c => c.ParentReadingQuestionTrial.ShowOrder)); + + + + CreateMap() + .ForMember(t => t.TrialSiteUserList, u => u.Ignore()); + + CreateMap().IncludeMembers(t => t.User) + .ForMember(t => t.TrialSiteCode, u => u.MapFrom(c => c.TrialSite.TrialSiteCode)) + .ForMember(t => t.TrialSiteAliasName, u => u.MapFrom(c => c.TrialSite.TrialSiteAliasName)) + .ForMember(t => t.UserRealName, u => u.MapFrom(c => c.User.FullName)) + .ForMember(t => t.UserType, u => u.MapFrom(c => c.User.UserTypeRole.UserTypeShortName)); + CreateMap(); + + + + + CreateMap() + .ForMember(t => t.TrialSiteUserList, u => u.Ignore()); + + CreateMap() + .ForMember(t => t.TrialRoleName, u => u.MapFrom(d => d.TrialRoleName.Value)) + .ForMember(d => d.UserType, u => u.MapFrom(s => s.UserTypeRole.UserTypeShortName)) + .ForMember(t => t.TrialSiteCode, u => u.MapFrom(d => d.TrialSiteSurvey.TrialSite.TrialSiteCode)) + .ForMember(d => d.TrialSiteAliasName, u => u.MapFrom(s => s.TrialSiteSurvey.TrialSite.TrialSiteAliasName)); + + + CreateMap() + .ForMember(t => t.ApprovalRequiredCount, u => + u.MapFrom(c => userTypeEnumInt == (int)UserTypeEnum.ProjectManager || userTypeEnumInt == (int)UserTypeEnum.APM ? + c.TrialSiteSurveyList.Where(t => t.State == TrialSiteSurveyEnum.SPMApproved).Count() + : c.TrialSiteSurveyList.Where(t => t.State == TrialSiteSurveyEnum.CRCSubmitted).Count()) + ) + + .ForMember(t => t.ApprovalRequiredSiteCount, u => + u.MapFrom(c => userTypeEnumInt == (int)UserTypeEnum.ProjectManager || userTypeEnumInt == (int)UserTypeEnum.APM ? + c.TrialSiteSurveyList.Where(t => t.State == TrialSiteSurveyEnum.SPMApproved).Select(t => t.SiteId).Distinct().Count() + : c.TrialSiteSurveyList.Where(t => t.State == TrialSiteSurveyEnum.CRCSubmitted).Select(t => t.SiteId).Distinct().Count()) + ) + ; + + var userTypeId = Guid.Empty; + CreateMap() + .ForMember(t => t.WaitSignCount, u => + u.MapFrom(c => userTypeEnumInt == (int)UserTypeEnum.SuperAdmin ? 0 + + : c.TrialDocumentList.Where(t => t.IsDeleted == false && t.NeedConfirmedUserTypeList.Any(t => t.NeedConfirmUserTypeId == userTypeId) && !t.TrialDocConfirmedUserList.Any(t => t.ConfirmUserId == userId && t.ConfirmTime!=null)).Count()) + ); + + + + + } + } + +} diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/ClinicalStudySubjects.cs b/IRaCIS.Core.Application/Service/Visit/DTO/ClinicalStudySubjects.cs new file mode 100644 index 0000000..5fc26f8 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/DTO/ClinicalStudySubjects.cs @@ -0,0 +1,168 @@ +using System; +using IRaCIS.Core.Infrastructure.Extention; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Application.Contracts +{ + public class SubjectCommand + { + public Guid? Id { get; set; } + public string Code { get; set; } = String.Empty; + + public int? Age { get; set; } + public string Sex { get; set; } = string.Empty; + public Guid SiteId { get; set; } + public Guid TrialId { get; set; } + public string MedicalNo { get; set; } = string.Empty; + + public string ShortName { get; set; } = string.Empty; + + public string Height { get; set; } = string.Empty; + + public string Weight { get; set; } = string.Empty; + + public DateTime? BirthDate { get; set; } + public DateTime? SignDate { get; set; } + + public bool IsUrgent { get; set; } + + public DateTime? FirstGiveMedicineTime { get; set; } + + + //public DateTime? OutEnrollmentTime { get; set; } + //public DateTime? VisitOverTime { get; set; } + //public SubjectStatus Status { get; set; } + //public string Reason { get; set; } = string.Empty; + + } + + public class SubjectStatusChangeCommand + { + [NotDefault] + public Guid SubjectId { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + [NotDefault] + public Guid SiteId { get; set; } + + public SubjectStatus Status { get; set; } + public DateTime? OutEnrollmentTime { get; set; } + public DateTime? VisitOverTime { get; set; } + public string Reason { get; set; } = string.Empty; + + public Guid? FinalSubjectVisitId { get; set; } + } + + + public class TrialSubjectConfig + { + public string SubjectCodeRule { get; set; } = string.Empty; + public bool IsNoticeSubjectCodeRule { get; set; } + public bool IsHaveFirstGiveMedicineDate { get; set; } + public bool IsEnrollementQualificationConfirm { get; set; } + public bool IsHaveSubjectAge { get; set; } + public string OutEnrollmentVisitName { get; set; } = string.Empty; + public bool VisitPlanConfirmed { get; set; } + + public bool IsSubjectSexView { get; set; } = false; + public bool IsSubjectExpeditedView { get; set; } = false; + + public bool IsPDProgressView { get; set; } + + public bool IsSubjectSecondCodeView { get; set; } = false; + } + + + public class SubjectQueryView: SubjectCommand + { + + public DateTime? OutEnrollmentTime { get; set; } + public DateTime? VisitOverTime { get; set; } + public SubjectStatus Status { get; set; } + public string Reason { get; set; } = string.Empty; + + + //public int? StudyCount { get; set; } + //public int? VisitCount { get; set; } + //public int? PlanVisitCount { get; set; } + + + //public int? InPlanDicomStudyUploadCount { get; set; } + //public int? OutPlanDicomStudyUploadCount { get; set; } + //public int? InPlanNoneDicomStudyUploadCount { get; set; } + //public int? OutPlanNoneDicomStudyUploadCount { get; set; } + + public Guid? FinalSubjectVisitId { get; set; } + + public int? TotalVisitCount => OutPlanVisitCount + InPlanVisitCount; + public int? TotalVisitSubmmitCount => OutPlanVisitSubmmitCount + InPlanVisitSubmmitCount; + + public int? OutPlanVisitCount { get; set; } + public int? OutPlanVisitSubmmitCount { get; set; } + + public int? InPlanVisitCount { get; set; } + public int? InPlanVisitSubmmitCount { get; set; } + + public string? FinalSubjectVisitName { get; set; } + + + public int? MissingSubmmitCount { get; set; } + + public int? LostVisitCount { get; set; } + + public DateTime? CreateTime { get; set; } + public DateTime? UpdateTime { get; set; } + public string SiteName { get; set; } = string.Empty; + public string TrialSiteCode { get; set; } = string.Empty; + + public string LatestVisitName { get; set; } = string.Empty; + + public string LatestBlindName { get; set; } = string.Empty; + + public bool IsMissingImages => MissingSubmmitCount > 0; + + public Guid LatestSubmitSubjectVisitId { get; set; } + + + + } + + + + public class SubjectQueryParam : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + public string Code { get; set; } = string.Empty; + + public string Name { get; set; } = string.Empty; + + public string Sex { get; set; } = string.Empty; + + public Guid? SiteId { get; set; } + public SubjectStatus? Status { get; set; } + } + + + public class SubjectSelect + { + public Guid SubjectId { get; set; } + + public string Name => LastName + " / " + FirstName; + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + + public string Code { get; set; } = string.Empty; + + public string Sex { get; set; } = string.Empty; + + public int? Age { get; set; } + public SubjectStatus Status { get; set; } + + public DateTime? FirstGiveMedicineTime { get; set; } + + public string MRN { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/VisitPlanViewModel.cs b/IRaCIS.Core.Application/Service/Visit/DTO/VisitPlanViewModel.cs new file mode 100644 index 0000000..0742b40 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/DTO/VisitPlanViewModel.cs @@ -0,0 +1,107 @@ +using IRaCIS.Core.Infrastructure.Extention; +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Application.Contracts +{ + public class VisitStageSelectDTO + { + public Guid Id { get; set; } + + public decimal VisitNum { get; set; } + + public string VisitName { get; set; } = String.Empty; + public int VisitDay { get; set; } + + + public bool IsBaseLine { get; set; } = false; + + public string AnonymousVisitName { get; set; } = string.Empty; + + public int VisitWindowLeft { get; set; } + public int VisitWindowRight { get; set; } + } + + public class VisitStageDTO : VisitPlanCommand + { + public DateTime CreateTime { get; set; } + + public bool IsHaveFirstConfirmed { get; set; } + + } + + public class VisitPlanView + { + public List VisitPlanList = new List(); + + public decimal TimePointsPerPatient { get; set; } + public bool VisitPlanConfirmed { get; set; } + + public bool IsHaveFirstGiveMedicineDate { get; set; } = true; + + public string BlindBaseLineName { get; set; } + + public string BlindFollowUpPrefix { get; set; } + + public bool IsHaveGeneratedTask { get; set; } + } + + + public class VisitBlindNameCommand + { + [NotDefault] + public Guid TrialId { get; set; } + public string BlindBaseLineName { get; set; } + + public string BlindFollowUpPrefix { get; set; } + } + + + + public class VisitPlanCommand + { + //public string BlindName => "B" + (VisitNum * 10).ToString("D3"); + + public string BlindName { get; set; } = String.Empty; + public bool IsConfirmed { get; set; } = false; + + public Guid? Id { get; set; } + public Guid TrialId { get; set; } + + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = String.Empty; + public int VisitDay { get; set; } + public string Description { get; set; } = String.Empty; + public bool NeedGlobal { get; set; } = false; + + public bool IsBaseLine { get; set; } = false; + + + public string AnonymousVisitName { get; set; } = string.Empty; + + public int VisitWindowLeft { get; set; } + public int VisitWindowRight { get; set; } + + public bool IsDeleted { get; set; } + } + + + public class VisitPlanQueryDTO : PageInput + { + public Guid TrialId { get; set; } = Guid.Empty; + public string Keyword { get; set; } = string.Empty; + } + + public class VisitPlanInfluenceSubjectVisitStatDTO + { + public Guid Id { get; set; } + public Guid CreateUserId { get; set; } + + public string CreateUser { get; set; } = String.Empty; + + public DateTime CreateTime { get; set; } + + public int InconsistentCount { get; set; } + } + + +} diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/VisitPointViewModel.cs b/IRaCIS.Core.Application/Service/Visit/DTO/VisitPointViewModel.cs new file mode 100644 index 0000000..084ed14 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/DTO/VisitPointViewModel.cs @@ -0,0 +1,486 @@ +using IRaCIS.Core.Infrastructure.Extention; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Contracts.Dicom.DTO; +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; +using IRaCIS.Core.Application.Service.Inspection.DTO; + +namespace IRaCIS.Core.Application.Contracts +{ + public class SubjectVisitCommand + { + public bool IsFinalVisit { get; set; } + + public string BlindName { get; set; } = string.Empty; + + public Guid? Id { get; set; } + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + public Guid SiteId { get; set; } + public bool InPlan { get; set; } = true; + public VisitExecutedEnum VisitExecuted { get; set; } + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + public int? VisitDay { get; set; } + public string SVUPDES { get; set; } = string.Empty; + + public bool IsBaseLine { get; set; } = false; + public int VisitWindowLeft { get; set; } + public int VisitWindowRight { get; set; } + + public bool IsLostVisit { get; set; } + public PDStateEnum? PDState { get; set; } + + public bool? IsEnrollmentConfirm { get; set; } + public DateTime? SubjectFirstGiveMedicineTime { get; set; } + + + + public Guid? OutPlanPreviousVisitId { get; set; } + + + + + } + + + public class IDDto + { + public Guid Id { get; set; } + + } + + public class DeleteSVCommand + { + public Guid SubjectVisitId { get; set; } + } + + public class SetSubjectVisitUrgentCommand + { + public Guid SubjectVisitId { get; set; } + + + public bool isUrgent { get; set; } + } + public class SubjectVisitDTO : SubjectVisitCommand + { + public SubjectStatus SubjectStatus { get; set; } + + } + + public class SubjectVisitNewDTO : SubjectVisitDTO + { + public TrialQCProcess QCProcessEnum { get; set; } + public CheckStateEnum CheckState { get; set; } + public SubmitStateEnum SubmitState { get; set; } + public AuditStateEnum AuditState { get; set; } + + public string CurrentActionUser { get; set; } = String.Empty; + public DateTime? CurrentActionUserExpireTime { get; set; } + public string PreliminaryAuitUser { get; set; } = String.Empty; + public string ReviewAuitUser { get; set; } = String.Empty; + public DateTime? ReviewAuitTime { get; set; } + public DateTime? PreliminaryAuditTime { get; set; } + + + public int? StudyCount { get; set; } + + public String TrialSiteCode { get; set; } = String.Empty; + public DateTime UpdateTime { get; set; } + + public string SubjectSex { get; set; } = String.Empty; + + public int SubjectAge { get; set; } + + public string SubjectCode { get; set; } = String.Empty; + + + [JsonIgnore] + public string SubjectName => LastName + " / " + FirstName; + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + + public string SiteName { get; set; } = String.Empty; + + public string SiteCode { get; set; } = String.Empty; + + + + + } + + public class UploadVisitCheckExcelDto + { + + public Guid trialId { get; set; } + + public string AuditInfo { get; set; } + } + public class ForwardSVDicomImageDto + { + public Guid[] subjectVisitIdList { get; set; } + } + public class SubjectVisitSearchDTO : PageInput + { + public string SubjectInfo { get; set; } = String.Empty; + + public string VisitPlanInfo { get; set; } = String.Empty; + + public Guid? SubjectId { get; set; } + public Guid TrialId { get; set; } + public Guid? SiteId { get; set; } + } + + public class SubjectVisitSelectItem + { + public Guid Id { get; set; } + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = String.Empty; + + public int VisitExecuted { get; set; } + + public bool IsUploadedImage { get; set; } + } + + public class SubjectVisitSelectDTO + { + public Guid GuidId { get; set; } = Guid.NewGuid(); + public Guid SubjectVisitId { get; set; } = Guid.Empty; + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + + public string SVUPDES { get; set; } = string.Empty; + + public int? VisitDay { get; set; } + + //public Guid VisitStageId { get; set; } = Guid.Empty; + + public DateTime? SVSTDTC { get; set; } + + public DateTime? SVENDTC { get; set; } + + + //public bool IsSubjectVisit { get; set; } = false; + + public VisitExecutedEnum VisitExecuted { get; set; } + + + + public List StudyList = new List(); + } + + + public class SubjectVisitStudyDTO + { + public Guid SubjectVisitId { get; set; } + public Guid StudyId { get; set; } + public string StudyCode { get; set; } = String.Empty; + + public string Modalities { get; set; } = String.Empty; + + public int Status { get; set; } + + } + + public class GetReadingVisitStudyListIndto + { + public Guid TrialId { get; set; } + public Guid SujectVisitId { get; set; } + public Guid? VisitTaskId { get; set; } + } + + + public class TempInstance + { + public Guid Id { get; set; } + public string Path { get; set; } + public int NumberOfFrames { get; set; } + public int InstanceNumber { get; set; } + + public int ShowOrder { get; set; } + + public decimal RowIndex { get; set; } + + } + + public class VisitStudyDTO + { + public Guid StudyId { get; set; } + public string StudyCode { get; set; } = String.Empty; + public string Modalities { get; set; } = String.Empty; + + public bool IsCriticalSequence { get; set; } = false; + + public int SeriesCount { get; set; } + public int InstanceCount { get; set; } + + public bool IsDicom { get; set; } = true; + + public List SeriesList { get; set; } = new List(); + } + + public class PreArchiveStudyCommand + { + public Guid SubjectVisitId { get; set; } + + public bool IsDicom { get; set; } + + + } + + public class ArchiveStudyCommand + { + //[NotDefault] + //public Guid StudyMonitorId { get; set; } + public Guid? AbandonStudyId { get; set; } + + + public Guid SubjectVisitId { get; set; } + + public string StudyInstanceUid { get; set; } = String.Empty; + + + + //public Guid TrialId { get; set; } + //public Guid SiteId { get; set; } + //public Guid SubjectId { get; set; } + //public DateTime? SVSTDTC { get; set; } + //public DateTime? SVENDTC { get; set; } + + //public string DTFPath { get; set; } + //public string FileRealName { get; set; } + + } + + + public class UploadDTFCommand + { + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + + } + + + public class StudyCommand + { + public Guid Id { get; set; } = Guid.Empty; + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + + } + + public class StudyEditCommand : StudyCommand + { + public Guid VisitStageId { get; set; } + public DateTime? SVSTDTC { get; set; } + public DateTime? SVENDTC { get; set; } + public string Comment { get; set; } = String.Empty; + } + + + + public class StudyStatDTO + { + + public int StudyCount { get; set; } + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = String.Empty; + public int VisitDay { get; set; } + public string Description { get; set; } = String.Empty; + public Guid TrialId { get; set; } + public Guid SubjectVisitId { get; set; } + + } + + public class DistributeReviewerStudyStatusDTO + { + public string NameCN { get; set; } = String.Empty; + public string Name { get; set; } = String.Empty; + public string ReviewerCode { get; set; } = String.Empty; + public string StudyCode { get; set; } = String.Empty; + + public int Status { get; set; } + + } + + public class StudyStatusQueryDTO : PageInput + { + public Guid TrialId { get; set; } + public Guid? ReviewerId { get; set; } + + public int? StudyStatus { get; set; } + } + + + + + public class VerifyStudyUploadResult + { + public string StudyInstanceUid { get; set; } = String.Empty; + + public VerifyStudyDto? StudyInfo { get; set; } + + public bool AllowUpload { get; set; } = false; + + public bool AllowReUpload { get; set; } = false; + + public string ErrorMesseage { get; set; } = String.Empty; + + } + + public class VerifyUploadOrReupload + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid SubjectId { get; set; } + + [NotDefault] + public Guid SubjectVisitId { get; set; } + + + public decimal VisitNum { get; set; } + + public List StudyInstanceUidList { get; set; } = new List(); + } + + public class VerifyUploadStudyBasicInfo + { + public string StudyInstanceUid { get; set; } = String.Empty; + + public DateTime? StudyDate { get; set; } + } + + + public class VerifyStudyDto + { + + public Guid Id { get; set; } + public string StudyCode { get; set; } = String.Empty; + + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + + public string SubjectCode { get; set; } = String.Empty; + public string VisitName { get; set; } = String.Empty; + public string SubjectName => LastName + " / " + FirstName; + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + + public DateTime? StudyTime { get; set; } + } + + public class StudyDTO + { + public Guid SubjectVisitId { get; set; } + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = String.Empty; + public int? VisitDay { get; set; } + + public string SVUPDES { get; set; } = String.Empty; + + public DateTime? SVSTDTC { get; set; } + + public DateTime? SVENDTC { get; set; } + + + public Guid Id { get; set; } + public Guid TrialId { get; set; } + public string StudyCode { get; set; } = String.Empty; + + + public int StudyStatus { get; set; } + public DateTime? StudyDate { get; set; } + public string Modalities { get; set; } = String.Empty; + public int SeriesCount { get; set; } + public int InstanceCount { get; set; } + + public bool IsDoubleReview { get; set; } + + public string StudyDescription { get; set; } = String.Empty; + + public string BodyPartExamined { get; set; } = String.Empty; + + public string Comment { get; set; } = String.Empty; + + public DateTime UpdateTime { get; set; } + + public DateTime? UploadedTime { get; set; } + + public DateTime? DeadlineTime { get; set; } + + //public List DistributeReviewers { get; set; }=new List(); + + + public Guid SiteId { get; set; } + public string SiteName { get; set; } = String.Empty; + public string InstitutionName { get; set; } = String.Empty; + public string StudyId { get; set; } = String.Empty;//这个Id是dicom文件中的Id + public Guid SubjectId { get; set; } + public string SubjectCode { get; set; } = String.Empty; + + public string SubjectName => LastName + " / " + FirstName; + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public int? SubjectAge { get; set; } + + public SubjectStatus SubjectStatus { get; set; } + public string SubjectSex { get; set; } = String.Empty; + + public string SubjectMedicalNo { get; set; } = string.Empty; + + public string PatientId { get; set; } = String.Empty; + public string PatientName { get; set; } = String.Empty; + public string PatientAge { get; set; } = String.Empty; + public string PatientSex { get; set; } = String.Empty; + public string AccessionNumber { get; set; } = string.Empty; + public string PatientBirthDate { get; set; } = string.Empty; + + + //public int? NeedDealMessageCount { get; set; } + //public int? TotalMessageCount { get; set; } + public int? DTFCount { get; set; } + + + public string UploaderFirstName { get; set; } = String.Empty; + public string UploaderLastName { get; set; } = String.Empty; + } + + public class DistributeReviewer + { + public Guid StudyId { get; set; } + + public Guid ReviewerId { get; set; } + public string NameCN { get; set; } = String.Empty; + public string Name { get; set; } = String.Empty; + + public int Status { get; set; } + } + + public class StudyQueryDTO : PageInput + { + public Guid? SubjectId { get; set; } + + public Guid? SiteId { get; set; } + public Guid TrialId { get; set; } + public string SubjectInfo { get; set; } = String.Empty; + + public string VisitPlanInfo { get; set; }=String.Empty; + + public Guid? SubjectVisitId { get; set; } + + + public DateTime? StudyTimeBegin { get; set; } + + public DateTime? StudyTimeEnd { get; set; } + + public DateTime? UpdateTimeBegin { get; set; } + + public DateTime? UpdateTimeEnd { get; set; } + } +} diff --git a/IRaCIS.Core.Application/Service/Visit/Interface/ISubjectService.cs b/IRaCIS.Core.Application/Service/Visit/Interface/ISubjectService.cs new file mode 100644 index 0000000..0db901b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/Interface/ISubjectService.cs @@ -0,0 +1,14 @@ +using IRaCIS.Application.Contracts; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Application.Interfaces +{ + public interface ISubjectService + { + Task> AddOrUpdateSubject([FromBody] SubjectCommand subjectCommand); + Task DeleteSubject(Guid id); + + Task UpdateSubjectStatus(SubjectStatusChangeCommand subjectStatusChangeCommand); + Task, TrialSubjectConfig>> GetSubjectList(SubjectQueryParam param); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Visit/Interface/ISubjectVisitService.cs b/IRaCIS.Core.Application/Service/Visit/Interface/ISubjectVisitService.cs new file mode 100644 index 0000000..67be25a --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/Interface/ISubjectVisitService.cs @@ -0,0 +1,14 @@ +using IRaCIS.Core.Application.Contracts; + +namespace IRaCIS.Core.Application.Interfaces +{ + public interface ISubjectVisitService + { + Task> AddOrUpdateSV(SubjectVisitCommand svCommand); + Task DeleteSV(Guid id); + Task> GetVisitStudyList(Guid trialId, Guid sujectVisitId, int isReading); + Task SetSVExecuted(Guid subjectVisitId); + + Task SetSubjectVisitUrgent(Guid subjectVisitId, bool isUrgent); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Visit/Interface/IVisitPlanService.cs b/IRaCIS.Core.Application/Service/Visit/Interface/IVisitPlanService.cs new file mode 100644 index 0000000..cad9d18 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/Interface/IVisitPlanService.cs @@ -0,0 +1,18 @@ +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Mvc; + +namespace IRaCIS.Application.Interfaces +{ + public interface IVisitPlanService + { + Task AddOrUpdateVisitStage(VisitPlanCommand visitPlan); + Task ConfirmTrialVisitPlan(Guid trialId); + Task DeleteVisitStage(Guid id); + Task DownloadInflunceStudyList(Guid visitPlanInfluenceStatId); + Task> GetInfluenceHistoryList(Guid trialId, [FromServices] IRepository _influnceStatRepository); + Task> GetTrialVisitStageList(VisitPlanQueryDTO param); + Task> GetTrialVisitStageSelect(Guid trialId); + Task GetVisitStageList(Guid trialId); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Visit/SubjectService.cs b/IRaCIS.Core.Application/Service/Visit/SubjectService.cs new file mode 100644 index 0000000..a4c83fd --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/SubjectService.cs @@ -0,0 +1,160 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Auth; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Trial")] + public class SubjectService : BaseService, ISubjectService + { + private readonly IRepository _subjectRepository; + private readonly IRepository _subjectVisitRepository; + + public SubjectService(IRepository subjectRepository, IRepository subjectVisitRepository) + { + _subjectRepository = subjectRepository; + _subjectVisitRepository = subjectVisitRepository; + } + + /// + /// 添加或更新患者信息[New] + /// + /// state:1-检查批次中,2-出组。0-全部 + /// + + [TrialAudit(AuditType.SubjectAudit, AuditOptType.AddOrUpdateSubject)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task> AddOrUpdateSubject([FromBody] SubjectCommand subjectCommand) + { + var svlist = new List(); + if (await _repository.AnyAsync(t => t.Id == subjectCommand.TrialId && !t.VisitPlanConfirmed)) + { + return ResponseOutput.NotOk("项目检查批次计划没有确认。请联系项目经理确认项目检查批次计划后,再添加患者。"); + } + + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = u => u.Code == subjectCommand.Code && u.TrialId == subjectCommand.TrialId, + VerifyMsg = "已存在具有相关患者编号的患者。" + }; + + + + + Subject? mapedSubject = null; + + if (subjectCommand.Id == null) //insert + { + + + mapedSubject = await _subjectRepository.InsertFromDTOAsync(subjectCommand, false, verifyExp1); + + + } + else //update + { + + mapedSubject = await _subjectRepository.UpdateFromDTOAsync(subjectCommand, false, false, verifyExp1/*, verifyExp2*/); + + } + + + + await _subjectRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(mapedSubject.Id.ToString()); + + } + + [HttpPut] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task UpdateSubjectStatus(SubjectStatusChangeCommand subjectStatusChangeCommand) + { + await _subjectRepository.UpdateFromDTOAsync(subjectStatusChangeCommand, true); + return ResponseOutput.Ok(); + } + + + [HttpDelete("{trialId:guid}/{id:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task DeleteSubject(Guid id) + { + + if (await _subjectVisitRepository.AnyAsync(u => u.SubjectId == id && u.VisitExecuted == VisitExecutedEnum.Executed)) + { + return ResponseOutput.NotOk("该患者已经有检查批次已经上传影像,不允许删除。"); + } + + await _subjectRepository.UpdatePartialFromQueryAsync(id, x => new Subject + { + IsDeleted = true, + }); + await _subjectVisitRepository.UpdatePartialFromQueryAsync(u => u.SubjectId == id, x => new SubjectVisit() + { + IsDeleted = true, + }); + + var isSuccess = await _subjectRepository.SaveChangesAsync(); + + return ResponseOutput.Result(isSuccess); + } + + /// 分页获取患者列表[New] + /// /// state:1-检查批次中,2-出组。0-全部 + [HttpPost] + public async Task, TrialSubjectConfig>> GetSubjectList(SubjectQueryParam param) + { + + var subjectQuery = _subjectRepository.Where(u => u.TrialId == param.TrialId) + .WhereIf(!string.IsNullOrWhiteSpace(param.Code), t => t.Code.Contains(param.Code)) + .WhereIf(!string.IsNullOrWhiteSpace(param.Name), t => t.ShortName.Contains(param.Name)) + .WhereIf(!string.IsNullOrWhiteSpace(param.Sex), t => t.Sex.Contains(param.Sex)) + .WhereIf(param.Status != null, t => t.Status == param.Status) + .WhereIf(param.SiteId != null, t => t.SiteId == param.SiteId) + // IC 只负责他管理site的患者 + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) + .ProjectTo(_mapper.ConfigurationProvider); + + + var pageList = await subjectQuery.ToPagedListAsync(param.PageIndex, param.PageSize, param.SortField == string.Empty ? "Code" : param.SortField, param.Asc); + + var trialConfig = await _repository.Where(t => t.Id == param.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + + return ResponseOutput.Ok(pageList, trialConfig); + + } + + + + /// + /// 计划外检查批次 获取患者选择下拉框列表 + /// + [HttpGet("{siteId:guid}/{trialId:guid}")] + public List GetSubjectListBySiteId(Guid siteId, Guid trialId) + { + var query = _subjectRepository.Where(t => t.SiteId == siteId && t.TrialId == trialId && t.Status == SubjectStatus.OnVisit).Select(u => new SubjectSelect() + { + Code = u.Code, + FirstName = u.FirstName, + LastName = u.LastName, + Sex = u.Sex, + SubjectId = u.Id, + Age = u.Age, + MRN = u.MedicalNo, + Status = u.Status, + + FirstGiveMedicineTime = u.FirstGiveMedicineTime + + }); + return query.ToList(); + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs b/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs new file mode 100644 index 0000000..9a2d27c --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs @@ -0,0 +1,554 @@ +using IRaCIS.Core.Application.Filter; + +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Contracts.Dicom.DTO; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Application.Auth; +using IRaCIS.Core.Infra.EFCore.Common; +using MassTransit; + +namespace IRaCIS.Core.Application.Services +{ + [ApiExplorerSettings(GroupName = "Trial")] + public class SubjectVisitService : BaseService, ISubjectVisitService + { + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _clinicalDataTrialSetRepository; + private readonly IRepository _readingClinicalDataRepository; + private readonly IRepository _readModuleRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _readingPeriodSetRepository; + private readonly IRepository _noneDicomStudyRepository; + private readonly IRepository _dicomInstanceRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _readingTableAnswerRowInfoRepository; + private readonly IRepository _noneDicomStudyFileRepository; + private readonly IRepository _readingPeriodPlanRepository; + private readonly IRepository _subjectRepository; + + public SubjectVisitService(IRepository subjectVisitRepository, + IRepository clinicalDataTrialSetRepository, + IRepository readingClinicalDataRepository, + IRepository readModuleRepository, + IRepository trialRepository, + IRepository readingPeriodSetRepository, + IRepository noneDicomStudyRepository, + IRepository dicomInstanceRepository, + IRepository visitTaskRepository, + IRepository readingTableAnswerRowInfoRepository, + IRepository noneDicomStudyFileRepository, + IRepository readingPeriodPlanRepository, + IRepository subjectRepository) + { + _subjectVisitRepository = subjectVisitRepository; + this._clinicalDataTrialSetRepository = clinicalDataTrialSetRepository; + this._readingClinicalDataRepository = readingClinicalDataRepository; + this._readModuleRepository = readModuleRepository; + this._trialRepository = trialRepository; + this._readingPeriodSetRepository = readingPeriodSetRepository; + this._noneDicomStudyRepository = noneDicomStudyRepository; + this._dicomInstanceRepository = dicomInstanceRepository; + this._visitTaskRepository = visitTaskRepository; + this._readingTableAnswerRowInfoRepository = readingTableAnswerRowInfoRepository; + this._noneDicomStudyFileRepository = noneDicomStudyFileRepository; + this._readingPeriodPlanRepository = readingPeriodPlanRepository; + _subjectRepository = subjectRepository; + } + + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + public async Task> AddOrUpdateSV(SubjectVisitCommand svCommand) + { + + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = t => t.VisitNum == svCommand.VisitNum && t.SubjectId == svCommand.SubjectId, + VerifyMsg = "该患者的检查批次中,在选择的上一检查后已存在计划外的的检查批次,请重新选择上一检查。" + }; + + var verifyExp2 = new EntityVerifyExp() + { + VerifyExp = t => t.SubjectId == svCommand.SubjectId && t.IsFinalVisit, + VerifyMsg = "该患者已经有检查批次设置为末次检查批次,不允许将当前检查批次设置为末次检查批次。", + IsVerify = svCommand.IsFinalVisit + }; + + var verifyExp3 = new EntityVerifyExp() + { + VerifyExp = t => t.SubjectId == svCommand.SubjectId && t.VisitName == svCommand.VisitName, + VerifyMsg = "该患者的检查批次中已经包含一个具有相同名称的检查批次。" + + }; + + svCommand.BlindName = "B" + ((int)(svCommand.VisitNum * 10)).ToString("D3"); + svCommand.VisitExecuted = svCommand.IsLostVisit ? VisitExecutedEnum.Executed : svCommand.VisitExecuted; + + + + + SubjectVisit? dbBeforeEntity = null; + //Add + if (svCommand.Id == null) + { + + //设置末次评估后,不允许添加计划外检查批次 + if (svCommand.InPlan == false) + { + if (await _subjectVisitRepository.AnyAsync(t => t.SubjectId == svCommand.SubjectId && t.IsFinalVisit)) + { + throw new BusinessValidationFailedException("设置末次评估后,不允许添加计划外检查批次。"); + } + + if (await _repository.AnyAsync(t => t.SubjectId == svCommand.SubjectId && t.TaskState == TaskState.Effect && t.VisitTaskNum > svCommand.VisitNum && t.SignTime != null && t.TrialReadingCriterion.IsReadingTaskViewInOrder)) + { + throw new BusinessValidationFailedException("该患者后续检查批次已有任务完成阅片(有序阅片标准),不允许在此添加,如果确实需要,请回退"); + } + + } + + dbBeforeEntity = await _subjectVisitRepository.InsertFromDTOAsync(svCommand, false, verifyExp2, verifyExp3,verifyExp1); + + //var cRCClinicalDataIds = await _clinicalDataTrialSetRepository.Where(x => x.TrialId == svCommand.TrialId && x.UploadRole == UploadRole.IC && x.IsConfirm && x.ClinicalDataLevel == ClinicalLevel.SubjectVisit) + + //.Select(x => x.Id).ToListAsync(); + + + + //List readingClinicals = cRCClinicalDataIds.Select(x => new ReadingClinicalData() + //{ + // ClinicalDataTrialSetId = x, + // IsVisit = true, + // SubjectId = svCommand.SubjectId, + // ReadingId = dbBeforeEntity.Id, + // TrialId = svCommand.TrialId + //}).ToList(); + + //await _readingClinicalDataRepository.AddRangeAsync(readingClinicals); + } + + else + { + dbBeforeEntity = await _subjectVisitRepository.UpdateFromDTOAsync(svCommand, false, false, verifyExp1, verifyExp2, verifyExp3); + + + if (svCommand.PDState != dbBeforeEntity.PDState && dbBeforeEntity.SubmitState == SubmitStateEnum.Submitted) + { + throw new BusinessValidationFailedException("当前检查批次影像提交后,不允许修改PD确认状态。"); + } + + if (svCommand.PDState != dbBeforeEntity.PDState && dbBeforeEntity.RequestBackState == RequestBackStateEnum.PM_AgressBack) + { + throw new BusinessValidationFailedException("当前检查批次为回退的检查批次,不允许修改PD确认状态。"); + } + + if (svCommand.IsLostVisit) + { + if (await _subjectVisitRepository.AnyAsync(t => t.Id == svCommand.Id && t.SubmitState == SubmitStateEnum.ToSubmit)) + { + throw new BusinessValidationFailedException("当前检查批次已经有有影像上传,不允许设置为失访。"); + } + } + + dbBeforeEntity = await _subjectVisitRepository.UpdateFromDTOAsync(svCommand, true, false, verifyExp1, verifyExp2, verifyExp3); + } + + + //更新患者 检查批次基准日期 是否入组确认 + if (svCommand.SubjectFirstGiveMedicineTime != null && svCommand.IsBaseLine) + { + + await _subjectRepository.UpdatePartialFromQueryAsync(svCommand.SubjectId, t => new Subject() + { + FirstGiveMedicineTime = svCommand.SubjectFirstGiveMedicineTime + }); + + } + + + await _subjectVisitRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(dbBeforeEntity.Id.ToString()); + + + } + + + + + [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{isUrgent:bool}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetSubjectVisitUrgent(Guid subjectVisitId, bool isUrgent) + { + await _subjectVisitRepository.UpdatePartialFromQueryAsync(subjectVisitId, u => new SubjectVisit() { IsUrgent = isUrgent }, true); + + return ResponseOutput.Ok(); + } + + + + [HttpDelete, Route("{trialId:guid}/{id:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task DeleteSV(Guid id) + { + if (await _repository.AnyAsync(t => t.SubjectVisitId == id)) + { + return ResponseOutput.NotOk("当前检查批次已经有影像上传,不允许删除。"); + } + if (await _subjectVisitRepository.AnyAsync(t => t.Id == id && t.InPlan)) + { + return ResponseOutput.NotOk("计划内的检查批次不允许删除。"); + } + if (await _subjectVisitRepository.AnyAsync(t => t.OutPlanPreviousVisitId == id)) + { + return ResponseOutput.NotOk("当前检查批次已经被设置为另一检查批次的上一检查批次,不允许删除。"); + } + + await _subjectVisitRepository.DeleteFromQueryAsync(s => s.Id == id, true); + + return ResponseOutput.Ok(); + } + + + + /// + /// 获取检查批次下的Dicom 检查信息 分所有的, 阅片的 不阅片 isReading : 0 查询所有 1 查询仅仅阅片的 + /// + /// + /// + /// + /// + [HttpGet, Route("{trialId:guid}/{sujectVisitId:guid}/{isReading}")] + [AllowAnonymous] + public async Task> GetVisitStudyList(Guid trialId, Guid sujectVisitId, int isReading) + { + var studyList = await _repository.Where(t => t.TrialId == trialId && t.SubjectVisitId == sujectVisitId).Select(k => new VisitStudyDTO() + { + InstanceCount = k.InstanceCount, + Modalities = k.Modalities, + SeriesCount = k.SeriesCount, + StudyCode = k.StudyCode, + StudyId = k.Id + }).ToListAsync(); + var studyIds = studyList.Select(t => t.StudyId).ToList(); + + var instanceList = await _repository.Where(t => studyIds.Contains(t.StudyId)) + .Select(t => new { t.SeriesId, t.Id, t.InstanceNumber, t.Path, t.NumberOfFrames }).ToListAsync(); + + foreach (var t in studyList) + { + t.SeriesList = await _repository.Where(s => s.StudyId == t.StudyId) + .WhereIf(isReading == 1, s => s.IsReading).OrderBy(s => s.SeriesNumber). + ThenBy(s => s.SeriesTime) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + t.SeriesList.ForEach(series => + { + series.InstanceList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Id).ToList(); + + //series.InstancePathList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Path).ToList(); + + //处理多帧 + series.InstancePathList = instanceList.OrderBy(t => t.InstanceNumber).Where(s => s.SeriesId == series.Id) + .SelectMany(u => + { + + if (u.NumberOfFrames > 1) + { + var pathList = new List(); + + for (int i = 1; i <= u.NumberOfFrames; i++) + { + pathList.Add(u.Path + "?frame=" + (i - 1)); + } + return pathList; + } + else + { + return new List { u.Path }; + + } + }) + .ToList(); + + } + + ); + + //设置为阅片与否 不更改数据库检查 的instance数量 和 SeriesCount 所以这里要实时统计 + t.SeriesCount = t.SeriesList.Count(); + t.InstanceCount = t.SeriesList.SelectMany(t => t.InstanceList).Count(); + } + + + + + return studyList; + + //return ResponseOutput.Ok(studyList.Where(t => t.SeriesList.Count > 0).ToList()); + + } + + /// + /// 获取检查批次下的Dicom 检查信息 分所有的, 阅片的 不阅片 isReading : 0 查询所有 1 查询仅仅阅片的 + /// + /// + /// + [HttpPost] + public async Task> GetReadingVisitStudyList(GetReadingVisitStudyListIndto indto) + { + var result = new List(); + + var thisRowinfo = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == indto.VisitTaskId && x.StudyId != null).OrderBy(x => x.ReadingQuestionTrial.ShowOrder).ThenBy(x => x.RowIndex).Select(x => new + { + x.ReadingQuestionTrial.ShowOrder, + x.RowIndex, + x.SeriesId, + x.StudyId, + x.InstanceId, + }).ToListAsync(); + + + var taskInfo = await _visitTaskRepository.Where(x => x.Id == indto.VisitTaskId).FirstNotNullAsync(); + + if (taskInfo.ReadingTaskState == ReadingTaskState.HaveSigned) + { + + var thisStudyIds = thisRowinfo.OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(x => x.StudyId).Distinct().ToList(); + var thisSeriesIdIds = thisRowinfo.Where(x => x.SeriesId != null).OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(x => x.SeriesId).Distinct().ToList(); + if (thisRowinfo.Count > 0) + { + var thisVisitTaskStudy = await _repository.Where(t => thisStudyIds.Contains(t.Id)).Select(k => new VisitStudyDTO() + { + InstanceCount = k.InstanceCount, + + SeriesCount = k.SeriesCount, + + StudyId = k.Id, + IsCriticalSequence = true, + + }).FirstOrDefaultAsync(); + + if (thisVisitTaskStudy != null) + { + thisVisitTaskStudy.StudyId = default(Guid); + var item = await _repository.Where(s => thisSeriesIdIds.Contains(s.Id)).OrderBy(s => s.SeriesNumber). + ThenBy(s => s.SeriesTime) + .ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + + if (item != null) + { + item.SeriesInstanceUid = string.Empty; + + item.InstanceList = thisRowinfo.Where(y => y.InstanceId != null).OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(y => y.InstanceId.Value).Distinct().ToList(); + + var tempInstanceList = await _repository.Where(t => item.InstanceList.Contains(t.Id)).OrderBy(t => t.InstanceNumber) + .Select(t => new TempInstance + { + + Id = t.Id, + Path = t.Path, + NumberOfFrames = t.NumberOfFrames, + InstanceNumber = t.InstanceNumber + }).ToListAsync(); + + + tempInstanceList.ForEach(x => + { + + var item = thisRowinfo.FirstOrDefault(y => y.InstanceId == x.Id); + if (item != null) + { + x.ShowOrder = item.ShowOrder; + x.RowIndex = item.RowIndex; + } + + }); + + + item.InstancePathList = tempInstanceList.OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).SelectMany(u => + { + + if (u.NumberOfFrames > 1) + { + var pathList = new List(); + + for (int i = 1; i <= u.NumberOfFrames; i++) + { + pathList.Add(u.Path + "?frame=" + (i - 1)); + } + return pathList; + } + else + { + return new List { u.Path }; + + } + }) + .ToList(); + + item.InstanceCount = item.InstanceList.Count(); + + thisVisitTaskStudy.SeriesList.Add(item); + thisVisitTaskStudy.SeriesCount = thisVisitTaskStudy.SeriesList.Count(); + + + + } + result.Add(thisVisitTaskStudy); + + } + + } + } + + + var studyList = await _repository.Where(t => t.TrialId == indto.TrialId && t.SubjectVisitId == indto.SujectVisitId).Select(k => new VisitStudyDTO() + { + InstanceCount = k.InstanceCount, + Modalities = k.Modalities, + SeriesCount = k.SeriesCount, + StudyCode = k.StudyCode, + StudyId = k.Id, + + }).ToListAsync(); + var studyIds = studyList.Select(t => t.StudyId).ToList(); + + var instanceList = await _repository.Where(t => studyIds.Contains(t.StudyId)) + .Select(t => new { t.SeriesId, t.Id, t.InstanceNumber, t.Path, t.NumberOfFrames,t.WindowCenter,t.WindowWidth }).ToListAsync(); + + + + List seriesLists = await _repository.Where(s => studyIds.Contains(s.StudyId) && s.IsReading).OrderBy(s => s.SeriesNumber). + ThenBy(s => s.SeriesTime) + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + foreach (var t in studyList) + { + t.SeriesList = seriesLists.Where(s => s.StudyId == t.StudyId).OrderBy(s => s.SeriesNumber). + ThenBy(s => s.SeriesTime).ToList(); + + t.SeriesList.ForEach(series => + { + series.InstanceList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Id).ToList(); + + + //series.InstancePathList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Path).ToList(); + + //处理多帧 + series.InstancePathList = instanceList.Where(s => s.SeriesId == series.Id).OrderBy(t => t.InstanceNumber) + .SelectMany(u => + { + + if (u.NumberOfFrames > 1) + { + var pathList = new List(); + + for (int i = 1; i <= u.NumberOfFrames; i++) + { + pathList.Add(u.Path + "?frame=" + (i - 1)); + } + return pathList; + } + else + { + return new List { u.Path }; + + } + }) + .ToList(); + + series.WindowWidth = instanceList.FirstOrDefault()?.WindowWidth; + series.WindowCenter = instanceList.FirstOrDefault()?.WindowCenter; + }); + + //设置为阅片与否 不更改数据库检查 的instance数量 和 SeriesCount 所以这里要实时统计 + t.SeriesCount = t.SeriesList.Count(); + t.InstanceCount = t.SeriesList.SelectMany(t => t.InstanceList).Count(); + } + + + // 非Dicom + + var noDicomList = await _noneDicomStudyRepository.Where(x => x.TrialId == indto.TrialId && x.SubjectVisitId == indto.SujectVisitId).ToListAsync(); + + + List noDicomStudyList = noDicomList.Select(x => new VisitStudyDTO() + { + InstanceCount = x.FileCount, + StudyId = x.Id, + Modalities = x.Modality, + SeriesCount = 1, + StudyCode = x.StudyCode, + IsDicom = false, + + }).ToList(); + + foreach (var item in noDicomStudyList) + { + var nodicom = noDicomList.Where(x => x.Id == item.StudyId).FirstOrDefault(); + item.SeriesList = new List() + { + new DicomSeriesDTO (){ + IsDicom=false, + Id=item.StudyId, + InstanceCount=await _noneDicomStudyFileRepository.Where(x=>x.NoneDicomStudyId==item.StudyId).CountAsync(), + Modality=item.Modalities, + StudyId=item.StudyId, + TrialId=nodicom.TrialId, + SiteId=nodicom.SiteId, + SubjectVisitId=nodicom.SubjectVisitId, + SubjectId=nodicom.SubjectId, + SeriesNumber=1, + NoneDicomFileFirstFile=await _noneDicomStudyFileRepository.Where(x=>x.NoneDicomStudyId==item.StudyId).Select(x=>x.Path).FirstOrDefaultAsync(), + } + + }; + } + + + if (studyList == null || studyList.Count == 0) + { + studyList = new List(); + } + + studyList.AddRange(noDicomStudyList); + + studyList.ForEach(x => + { + x.SeriesList.ForEach(y => + { + y.IsBeMark = thisRowinfo.Any(z => z.SeriesId == y.Id); + }); + + }); + result.AddRange(studyList); + result = result.Where(x => x.SeriesCount > 0).ToList(); + return result; + + //return ResponseOutput.Ok(studyList.Where(t => t.SeriesList.Count > 0).ToList()); + + } + + + + + /// + /// 设置患者检查批次已执行 也就是将studyUploaded状态置为true 为了那些没有影像 人工设置准备 + /// + /// + /// + [HttpPut("{trialId:guid}/{subjectVisitId:guid}")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [Obsolete] + public async Task SetSVExecuted(Guid subjectVisitId) + { + await _subjectVisitRepository.UpdatePartialFromQueryAsync(subjectVisitId, u => new SubjectVisit() { VisitExecuted = VisitExecutedEnum.Executed }, true); + return ResponseOutput.Ok(); + } + } +} diff --git a/IRaCIS.Core.Application/Service/Visit/VisitPlanService.cs b/IRaCIS.Core.Application/Service/Visit/VisitPlanService.cs new file mode 100644 index 0000000..c172a4e --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/VisitPlanService.cs @@ -0,0 +1,513 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Filter; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.MediatR.CommandAndQueries; +using Magicodes.ExporterAndImporter.Core; +using Magicodes.ExporterAndImporter.Excel; +using Magicodes.ExporterAndImporter.Excel.AspNetCore; +using IRaCIS.Core.Infrastructure; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Auth; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Trial")] + public class VisitPlanService : BaseService, IVisitPlanService + { + private readonly IRepository _visitStageRepository; + private readonly IRepository _trialRepository; + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _influnceRepository; + private readonly IRepository _subjectRepository; + private readonly IRepository _visitPlanInfluenceStatRepository; + + + + public VisitPlanService(IRepository visitStageRepository, IRepository trialRepository, IRepository subjectVisitRepository, + IRepository visitPlanInfluenceStudy, IRepository subjectRepository, IRepository visitPlanInfluenceStatRepository) + { + _visitStageRepository = visitStageRepository; + _trialRepository = trialRepository; + _subjectVisitRepository = subjectVisitRepository; + _influnceRepository = visitPlanInfluenceStudy; + _visitPlanInfluenceStatRepository = visitPlanInfluenceStatRepository; + _subjectRepository = subjectRepository; + } + + + ///暂时不用 + /// 获取项目检查批次计划 + [HttpPost] + public async Task> GetTrialVisitStageList(VisitPlanQueryDTO param) + { + var visitStageQuery = _visitStageRepository.AsQueryable(true).Where(u => u.TrialId == param.TrialId) + .WhereIf(!string.IsNullOrWhiteSpace(param.Keyword), t => t.VisitName.Contains(param.Keyword)) + .ProjectTo(_mapper.ConfigurationProvider); + + return await visitStageQuery.ToPagedListAsync(param.PageIndex, param.PageSize, "CreateTime", param.Asc); + + + } + + /// 根据项目Id,获取项目检查批次计划(不分页)[New] + [HttpGet("{trialId:guid}")] + public async Task GetVisitStageList(Guid trialId) + { + var query = _visitStageRepository.AsQueryable(true).Where(u => u.TrialId == trialId) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(t => t.VisitNum); + var list = await query.ToListAsync(); + var trial = (await _repository.FirstOrDefaultAsync(t => t.Id == trialId)).IfNullThrowException(); + + var isHaveGeneratedTask = (await _repository.AnyAsync(t => t.TrialId == trialId)); + return new VisitPlanView() + { + VisitPlanList = list, + TimePointsPerPatient = trial.TimePointsPerPatient, + VisitPlanConfirmed = trial.VisitPlanConfirmed, + IsHaveFirstGiveMedicineDate = trial.IsHaveFirstGiveMedicineDate, + BlindBaseLineName=trial.BlindBaseLineName, + BlindFollowUpPrefix=trial.BlindFollowUpPrefix, + IsHaveGeneratedTask= isHaveGeneratedTask, + //SubjectHasAdded = _subjectVisitRepository.Any(t => t.TrialId == trialId) + }; + } + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + public async Task UpdateVisitBlindName(VisitBlindNameCommand command) + { + + await _trialRepository.UpdatePartialFromQueryAsync(command.TrialId, t => new Trial() { BlindBaseLineName = command.BlindBaseLineName, BlindFollowUpPrefix = command.BlindFollowUpPrefix }); + + await _trialRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + /// + /// 获取检查批次计划下拉框列表 + /// + /// + /// + [HttpGet("{trialId:guid}")] + public async Task> GetTrialVisitStageSelect(Guid trialId) + { + var query = _visitStageRepository.Where(u => u.TrialId == trialId) + .ProjectTo(_mapper.ConfigurationProvider).OrderBy(t => t.VisitNum); + var list = await query.ToListAsync(); + return list; + } + + + /// 添加或更新检查批次计划某项 + [UnitOfWork] + [HttpPost] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + //[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task AddOrUpdateVisitStage(VisitPlanCommand visitPlan) + { + + if (!await _trialRepository.Where(t => t.Id == visitPlan.TrialId).IgnoreQueryFilters().AnyAsync(t => t.TrialStatusStr == StaticData.TrialState.TrialOngoing || t.TrialStatusStr == StaticData.TrialState.TrialInitializing)) + { + throw new BusinessValidationFailedException("只有当项目状态为:初始化或进行中时,可以操作。 "); + } + + var visitPlanList = await _visitStageRepository.Where(t => t.TrialId == visitPlan.TrialId, ignoreQueryFilters: true) + .Select(t => new { t.Id, t.VisitNum, t.VisitDay }).OrderBy(t => t.VisitNum).ToListAsync(); + + //更新的时候,需要排除自己 + if (visitPlan.Id != null) + { + visitPlanList = visitPlanList.Where(t => t.Id != visitPlan.Id).ToList(); + } + + if (visitPlanList.Any(t => t.VisitNum < visitPlan.VisitNum)) + { + //比当前 visitNum小的 visitDay的最大值 还小 不允许添加 + if (visitPlan.VisitDay <= visitPlanList.Where(t => t.VisitNum < visitPlan.VisitNum).Select(t => t.VisitDay).Max()) + { + throw new BusinessValidationFailedException("检查批次计划中,检查批次号大的检查批次,其检查批次间隔也应该比检查批次号小的检查批次大。"); + + } + } + + if (visitPlanList.Any(t => t.VisitNum > visitPlan.VisitNum)) + { + if (visitPlan.VisitDay >= visitPlanList.Where(t => t.VisitNum > visitPlan.VisitNum).Select(t => t.VisitDay).Min()) + { + throw new BusinessValidationFailedException("检查批次计划中,检查批次号大的计划检查批次,其检查批次间隔也应该比检查批次号小的计划检查批次大。"); + } + } + + if (visitPlan.Id == Guid.Empty || visitPlan.Id == null)//add + { + var trial = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == visitPlan.TrialId)).IfNullThrowException(); + + + if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && (t.VisitName == visitPlan.VisitName || t.VisitNum == visitPlan.VisitNum), true)) + { + throw new BusinessValidationFailedException("检查批次计划中已经存在具有项目检查批次名称或者检查批次号的计划检查批次模板。"); + } + + if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && t.IsBaseLine, true) && visitPlan.IsBaseLine) + { + throw new BusinessValidationFailedException("检查批次计划中已经存在基线。"); + } + + //不用前端传递的值 + visitPlan.BlindName = "B" + ((int)visitPlan.VisitNum * 10).ToString("D3"); + + var visitPlanItem = await _visitStageRepository.InsertFromDTOAsync(visitPlan, true); + + + return ResponseOutput.Ok(visitPlanItem.Id); + } + + else//update + { + + + if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && (t.VisitName == visitPlan.VisitName || t.VisitNum == visitPlan.VisitNum) && t.Id != visitPlan.Id, true)) + { + throw new BusinessValidationFailedException("检查批次计划中已经存在具有项目检查批次名称或者检查批次号的计划检查批次模板。"); + } + + if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && t.IsBaseLine && t.Id != visitPlan.Id, true) && visitPlan.IsBaseLine) + { + throw new BusinessValidationFailedException("检查批次计划中已经存在基线。"); + } + + visitPlan.IsConfirmed = false; + //不用前端传递的值 + visitPlan.BlindName = "B" + ((int)visitPlan.VisitNum * 10).ToString("D3"); + //返回的是数据库查询的数据 + var stage = await _visitStageRepository.UpdateFromDTOAsync(visitPlan, true); + + if (stage.IsBaseLine && stage.IsBaseLine != visitPlan.IsBaseLine) + { + if (await _repository.Where(t => t.TrialId == visitPlan.TrialId).AnyAsync(v => v.IsBaseLine && v.SubmitState >= SubmitStateEnum.ToSubmit)) + { + throw new BusinessValidationFailedException("有患者的基线已经上传了影像数据,不允许修改基线检查批次。"); + } + } + + + return ResponseOutput.Ok(); + } + + + + + } + + + + [UnitOfWork] + [HttpPost("{trialId:guid}")] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + //[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task ConfirmTrialVisitPlan(Guid trialId) + { + if (!await _trialRepository.AnyAsync(t => t.Id == trialId && (t.TrialStatusStr == StaticData.TrialState.TrialInitializing || t.TrialStatusStr == StaticData.TrialState.TrialOngoing))) + { + return ResponseOutput.NotOk("仅仅在项目初始化或者进行中时,才允许修改确认"); + } + + if (!await _visitStageRepository.AnyAsync(t => t.TrialId == trialId && t.IsBaseLine)) + { + return ResponseOutput.NotOk("没有基线,不允许确认"); + } + + if (!await _trialRepository.AnyAsync(t => t.Id == trialId && t.IsTrialBasicLogicConfirmed && t.IsTrialProcessConfirmed && t.IsTrialUrgentConfirmed)) + { + return ResponseOutput.NotOk("项目配置未确认,不允许确认检查批次计划"); + } + + var svList = await _visitStageRepository.Where(t => t.TrialId == trialId).Select(u => new { u.VisitDay, u.IsBaseLine }).ToListAsync(); + + if (svList.Min(t => t.VisitDay) != svList.Where(t => t.IsBaseLine).FirstOrDefault()?.VisitDay) + { + return ResponseOutput.NotOk("基线VisitDay 不是最小的, 不允许确认"); + } + + + //更新项目检查批次计划状态为已确认 必定生成更新的sql 通过状态改变 触发操作 + //await _trialRepository.UpdatePartialNowNoQueryAsync(trialId, t => new Trial() { VisitPlanConfirmed = true }); + + + var trial = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId)).IfNullThrowException(); + + //确认是否是首次确认 + var initTrialVisitPlanConfirmed = trial.VisitPlanConfirmed; + + trial.VisitPlanConfirmed = true; + + + #region 统一给Subject 增加检查批次任务 + + //首次确认时 IsHaveFirstConfirmed都为false 不需要给Subject 加检查批次 + if (initTrialVisitPlanConfirmed) + { + List subjectVisits = new List(); + + var addvisitStages = await _visitStageRepository.Where(x => !x.IsHaveFirstConfirmed && x.TrialId == trialId).ToListAsync(); + + foreach (var visitPlan in addvisitStages) + { + + var subjectSVS = await _subjectVisitRepository.Where(t => t.TrialId == visitPlan.TrialId).Select(t => new { t.SubjectId, t.SiteId, t.IsFinalVisit }).Distinct().ToListAsync(); + + var subjectList = subjectSVS.Select(t => new { t.SubjectId, t.SiteId }).Distinct().ToList(); + + foreach (var subject in subjectList) + { + + var svItem = _mapper.Map(visitPlan); + svItem.SiteId = subject.SiteId; + svItem.SubjectId = subject.SubjectId; + + //设置了末次检查批次,那么加检查批次计划的时候,设置为不可用 + if (subjectSVS.Any(t => t.SubjectId == svItem.SubjectId && t.IsFinalVisit)) + { + svItem.VisitExecuted = VisitExecutedEnum.Unavailable; + } + + //再加一层保险,防止重复 + if (!await _subjectVisitRepository.AnyAsync(t => t.VisitStageId == visitPlan.Id && t.SubjectId == subject.SubjectId)) + { + await _subjectVisitRepository.AddAsync(svItem); + + } + } + + + } + + await _subjectVisitRepository.AddRangeAsync(subjectVisits); + } + + + #endregion + + + + + #region 检查批次计划修改 影响检查 + + //找到检查批次计划修改的Item + var changedList = await _visitStageRepository.Where(t => t.TrialId == trial.Id && t.IsConfirmed == false) + .Select(t => new + { + t.Trial.IsHaveFirstGiveMedicineDate, + t.Id, + t.VisitName, + t.TrialId, + t.VisitWindowLeft, + t.VisitWindowRight, + t.VisitDay, + t.VisitNum, + t.IsBaseLine, + t.BlindName, + + t.Description, + IsConfirmed = true, + }).ToListAsync(); + + var visitPlanInfluenceStat = new VisitPlanInfluenceStat() { TrialId = trial.Id }; + + foreach (var changedItem in changedList) + { + //找到该项目 检查批次已经执行,并且配置了有首次给药日期 并且更新后超窗的检查批次,要把超窗之前的值也要查询出来 + var qcPassedVisitList = await _subjectVisitRepository.Where(t => t.TrialId == trialId + && t.VisitExecuted == VisitExecutedEnum.Executed + && t.AuditState == AuditStateEnum.QCPassed + && t.Trial.IsHaveFirstGiveMedicineDate == true + && t.VisitStageId == changedItem.Id + && t.Subject.FirstGiveMedicineTime != null + ).Select(k => new + { + SubjectVisitId = k.Id, + SelfWindowLeft = k.Subject.FirstGiveMedicineTime!.Value.AddDays(k.VisitDay + k.VisitWindowLeft), + SelfWindowRight = k.Subject.FirstGiveMedicineTime!.Value.AddDays(k.VisitDay + k.VisitWindowRight + 1).AddSeconds(-1), + + NowWindowLeft = k.Subject.FirstGiveMedicineTime!.Value.AddDays(changedItem.VisitDay + changedItem.VisitWindowLeft), + NowWindowRight = k.Subject.FirstGiveMedicineTime!.Value.AddDays(changedItem.VisitDay + changedItem.VisitWindowRight + 1).AddSeconds(-1), + + NoneDicomStudyList = + k.NoneDicomStudyList //之前是查询调整之后超窗的 现在调整前超窗 调整后 没超窗的也要记录 + //.Where(study => study.ImageDate k.Subject.FirstGiveMedicineTime.Value.AddDays(changedItem.VisitDay + changedItem.VisitWindowRight??0 + 1).AddSeconds(-1)) + .Select(t => new { NoneDicomStudyId = t.Id, t.Modality, StudyTime = t.ImageDate }), + + + DicomStudyList = k.StudyList + //.Where(study => study.StudyTime k.Subject.FirstGiveMedicineTime.Value.AddDays(changedItem.VisitDay + changedItem.VisitWindowRight??0 + 1).AddSeconds(-1)) + .Select(t => new { StudyId = t.Id, Modality = t.Modalities, t.StudyTime }) + + }).ToListAsync(); + + + foreach (var visit in qcPassedVisitList) + { + //找到本身没有超窗的数据 修改后超窗的 + visit.DicomStudyList.Where(t => (t.StudyTime > visit.SelfWindowLeft && t.StudyTime < visit.SelfWindowRight) && (t.StudyTime < visit.NowWindowLeft || t.StudyTime > visit.NowWindowRight)).ForEach(t => + { + visitPlanInfluenceStat.InconsistentCount++; + visitPlanInfluenceStat.InfluenceStudyList.Add(new VisitPlanInfluenceStudy() + { + IsOverWindowNowNotOverWindow = false, + Modality = t.Modality, + SubjectVisitId = visit.SubjectVisitId, + StudyId = t.StudyId, + IsDicomStudy = true, + StudyTime = t.StudyTime, + TrialId = trialId, + HistoryWindow = visit.SelfWindowLeft.ToString("yyyy-MM-dd") + " ~ " + visit.SelfWindowRight.ToString("yyyy-MM-dd"), + NowWindow = visit.NowWindowLeft.ToString("yyyy-MM-dd") + " ~ " + visit.NowWindowRight.ToString("yyyy-MM-dd") + + }); + + }); + + visit.NoneDicomStudyList.Where(t => (t.StudyTime > visit.SelfWindowLeft && t.StudyTime < visit.SelfWindowRight) && (t.StudyTime < visit.NowWindowLeft || t.StudyTime > visit.NowWindowRight)).ForEach(t => + { + visitPlanInfluenceStat.InconsistentCount++; + visitPlanInfluenceStat.InfluenceStudyList.Add(new VisitPlanInfluenceStudy() + { + IsOverWindowNowNotOverWindow = false, + Modality = t.Modality, + SubjectVisitId = visit.SubjectVisitId, + StudyId = t.NoneDicomStudyId, + IsDicomStudy = false, + StudyTime = t.StudyTime, + TrialId = trialId, + HistoryWindow = visit.SelfWindowLeft.ToString("yyyy-MM-dd") + " ~ " + visit.SelfWindowRight.ToString("yyyy-MM-dd"), + NowWindow = visit.NowWindowLeft.ToString("yyyy-MM-dd") + " ~ " + visit.NowWindowRight.ToString("yyyy-MM-dd") + + }); + }); + + //本身超窗 修改后没超窗的 + visit.DicomStudyList.Where(t => (t.StudyTime < visit.SelfWindowLeft || t.StudyTime > visit.SelfWindowRight) && (t.StudyTime > visit.NowWindowLeft && t.StudyTime < visit.NowWindowRight)).ForEach(t => + { + visitPlanInfluenceStat.InconsistentCount++; + visitPlanInfluenceStat.InfluenceStudyList.Add(new VisitPlanInfluenceStudy() + { + IsOverWindowNowNotOverWindow = true, + Modality = t.Modality, + SubjectVisitId = visit.SubjectVisitId, + StudyId = t.StudyId, + IsDicomStudy = true, + StudyTime = t.StudyTime, + TrialId = trialId, + HistoryWindow = visit.SelfWindowLeft.ToString("yyyy-MM-dd") + " ~ " + visit.SelfWindowRight.ToString("yyyy-MM-dd"), + NowWindow = visit.NowWindowLeft.ToString("yyyy-MM-dd") + " ~ " + visit.NowWindowRight.ToString("yyyy-MM-dd") + + }); + }); + + visit.NoneDicomStudyList.Where(t => (t.StudyTime < visit.SelfWindowLeft || t.StudyTime > visit.SelfWindowRight) && (t.StudyTime > visit.NowWindowLeft && t.StudyTime < visit.NowWindowRight)).ForEach(t => + { + visitPlanInfluenceStat.InconsistentCount++; + visitPlanInfluenceStat.InfluenceStudyList.Add(new VisitPlanInfluenceStudy() + { + IsOverWindowNowNotOverWindow = true, + Modality = t.Modality, + SubjectVisitId = visit.SubjectVisitId, + StudyId = t.NoneDicomStudyId, + IsDicomStudy = false, + StudyTime = t.StudyTime, + TrialId = trialId, + HistoryWindow = visit.SelfWindowLeft.ToString("yyyy-MM-dd") + " ~ " + visit.SelfWindowRight.ToString("yyyy-MM-dd"), + NowWindow = visit.NowWindowLeft.ToString("yyyy-MM-dd") + " ~ " + visit.NowWindowRight.ToString("yyyy-MM-dd") + + }); + }); + + } + + + + //变更某一检查批次计划Item 患者检查批次相关字段 + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trialId && t.VisitStageId == changedItem.Id, k => new SubjectVisit() + { + IsBaseLine = changedItem.IsBaseLine, + VisitName = changedItem.VisitName, + VisitNum = changedItem.VisitNum, + VisitDay = changedItem.VisitDay, + VisitWindowLeft = changedItem.VisitWindowLeft, + VisitWindowRight = changedItem.VisitWindowRight + }); + } + + + await _visitPlanInfluenceStatRepository.AddAsync(visitPlanInfluenceStat, true); + + #endregion + + + + //检查批次计划 整体状态变更为 确认 + await _visitStageRepository.UpdatePartialFromQueryAsync(u => u.TrialId == trialId && u.IsConfirmed == false, t => new VisitStage() { IsConfirmed = true, IsHaveFirstConfirmed = true }); + + await _visitStageRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + + } + + + + + [HttpGet("{trialId:guid}")] + public async Task> GetInfluenceHistoryList(Guid trialId, [FromServices] IRepository _influnceStatRepository) + { + var list = await _influnceStatRepository.Where(t => t.TrialId == trialId).ProjectTo(_mapper.ConfigurationProvider).OrderByDescending(t => t.CreateTime).ToListAsync(); + + return list; + } + + [HttpGet("{visitPlanInfluenceStatId:guid}")] + public async Task DownloadInflunceStudyList(Guid visitPlanInfluenceStatId) + { + var list = _influnceRepository.Where(t => t.VisitPlanInfluenceStatId == visitPlanInfluenceStatId) + .ProjectTo(_mapper.ConfigurationProvider).ToList(); + + IExporter exporter = new ExcelExporter(); + + var result = await exporter.ExportAsByteArray(list); + + return new XlsxFileResult(bytes: result, fileDownloadName: $"检查导出_{DateTime.Now.ToString("yyyy-MM-dd:hh:mm:ss")}.xlsx"); + + + } + + + + + /// 删除项目计划某一项 废弃 + [HttpDelete("{id:guid}/{trialId:guid}")] + [Obsolete] + public async Task DeleteVisitStage(Guid id) + { + + var visitPlan = await _visitStageRepository.FirstOrDefaultAsync(t => t.Id == id); + if (visitPlan == null) return Null404NotFound(visitPlan); + if (await _repository.AnyAsync(t => t.VisitName == visitPlan.VisitName && t.TrialId == visitPlan.TrialId && t.VisitExecuted == VisitExecutedEnum.Executed)) + { + return ResponseOutput.NotOk("The visit plan has been assigned to the subjects and executed."); + } + + await _repository.BatchDeleteAsync(t => t.TrialId == visitPlan.TrialId && t.VisitName == visitPlan.VisitName); + + var result = await _visitStageRepository.BatchDeleteNoTrackingAsync(u => u.Id == id); + + return ResponseOutput.Result(result); + } + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs b/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs new file mode 100644 index 0000000..48c3005 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs @@ -0,0 +1,107 @@ +using AutoMapper; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.MediatR.CommandAndQueries; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Service +{ + public class VisitMapConfig : Profile + { + public VisitMapConfig() + { + CreateMap(); + CreateMap(); + CreateMap(); + + CreateMap(); + + CreateMap(); + CreateMap() + .ForMember(d => d.Id, t => t.Ignore()) + .ForMember(d => d.VisitStageId, t =>t.MapFrom(u=>u.Id)); + + + + + + CreateMap() + .ForMember(d => d.CreateUser, u => u.MapFrom(g => g.CreateUser.LastName + " / " + g.CreateUser.FirstName)); + + + + CreateMap() + .ForMember(d => d.Id, t => t.MapFrom(u=>u.SubjectId)); + CreateMap(); + + CreateMap() + .ForMember(d => d.SiteName, u => u.MapFrom(s => s.TrialSite.Site.SiteName)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + .ForMember(d => d.LatestBlindName, u => u.MapFrom(s => s.LatestSubjectVisit.BlindName)) + .ForMember(d => d.LatestVisitName, u => u.MapFrom(s => s.LatestSubjectVisit.VisitName)) + + + //.ForMember(d => d.FinalSubjectVisitId, u => u.MapFrom(s => s.SubjectVisitList.Where(t => t.IsFinalVisit).Select(c => (Guid?)c.Id).FirstOrDefault())) + //.ForMember(d => d.FinalSubjectVisitName, u => u.MapFrom(s => s.SubjectVisitList.Where(t => t.IsFinalVisit).Select(c => c.VisitName).FirstOrDefault())) + + .ForMember(d => d.FinalSubjectVisitId, u => u.MapFrom(s => s.FinalSubjectVisitId)) + .ForMember(d => d.FinalSubjectVisitName, u => u.MapFrom(s => s.FinalSubjectVisit.VisitName)) + + //.ForMember(d => d.IsSubjectSexView, u => u.MapFrom(s => s.Trial.IsSubjectSexView)) + //.ForMember(d => d.IsSubjectExpeditedView, u => u.MapFrom(s => s.Trial.IsSubjectExpeditedView)) + + //不能对包含聚合或子查询的表达式执行聚合函数 + //.ForMember(d => d.InPlanStudyCount, u => u.MapFrom(s => s.SubjectVisitList.Where(t => t.InPlan).Sum(k => k.StudyList.Count()))) + //.ForMember(d => d.OutPlanStudyCount, u => u.MapFrom(s => s.SubjectVisitList.Where(t => t.InPlan==false).Sum(k => k.StudyList.Count()))) + + //.ForMember(d => d.InPlanDicomStudyUploadCount, u => u.MapFrom(s => s.SubjectVisitList.Where(t => t.InPlan).SelectMany(k => k.StudyList).Count())) + //.ForMember(d => d.OutPlanDicomStudyUploadCount, u => u.MapFrom(s => s.SubjectVisitList.Where(t => t.InPlan == false).SelectMany(k => k.StudyList).Count())) + + //.ForMember(d => d.InPlanNoneDicomStudyUploadCount, u => u.MapFrom(s => s.SubjectVisitList.Where(t => t.InPlan).SelectMany(k => k.NoneDicomStudyList).Count())) + //.ForMember(d => d.OutPlanNoneDicomStudyUploadCount, u => u.MapFrom(s => s.SubjectVisitList.Where(t => t.InPlan == false).SelectMany(k => k.NoneDicomStudyList).Count())) + + .ForMember(d => d.InPlanVisitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.InPlan))) + .ForMember(d => d.OutPlanVisitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.InPlan == false))) + //执行不一定上传了 可能是失访 实际执行过了 + + + .ForMember(d => d.MissingSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.VisitNum d.InPlanVisitSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.SubmitState == SubmitStateEnum.Submitted && t.InPlan == true))) + .ForMember(d => d.LostVisitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.IsLostVisit))) + .ForMember(d => d.InPlanVisitSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.SubmitState == SubmitStateEnum.Submitted && t.InPlan == true))) + .ForMember(d => d.OutPlanVisitSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.SubmitState == SubmitStateEnum.Submitted && t.InPlan == false))); + // .ForMember(d => d.InPlanVisitSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.SubmitState>=SubmitStateEnum.ToSubmit && t.InPlan == true))) + //.ForMember(d => d.OutPlanVisitSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.SubmitState >= SubmitStateEnum.ToSubmit && t.InPlan == false))); + //.ForMember(d => d.InPlanVisitUploadCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.VisitExecuted == VisitExecutedEnum.Executed && t.InPlan))) + //.ForMember(d => d.OutPlanVisitUploadCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.VisitExecuted == VisitExecutedEnum.Executed && t.InPlan == false))); + + + //审计信息 这里不用IncludeMembers 也可以识别 是以导航属性名称开头 + // 还有 外键? COALESCE([t0].[SubjectId], '00000000-0000-0000-0000-000000000000') 因为destination 是Guid + CreateMap() + .ForMember(d => d.SubjectName, u => u.MapFrom(s => s.Subject.LastName + " / " + s.Subject.FirstName)) + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)); + + + CreateMap().ForAllMembers(opt => opt.Condition((src, dest, srcMember) => srcMember != null)); + + + + CreateMap().IncludeMembers(t=>t.Trial) + .ForMember(d => d.SiteName, u => u.MapFrom(s => s.Site.SiteName)) + .ForMember(d => d.SiteCode, u => u.MapFrom(s => s.Site.SiteCode)) + .ForMember(d => d.SubjectVisitId, u => u.MapFrom(s => s.Id)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + .ForMember(d => d.TrialCode, u => u.MapFrom(s => s.Trial.TrialCode)) + .ForMember(d => d.Sponsor, u => u.MapFrom(s => s.Trial.Sponsor.SponsorName)); + CreateMap(); + + CreateMap() + .ForMember(d => d.SubjectVisitId, u => u.MapFrom(s => s.Id)) + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.SubjectVisit.Subject.Code)) + .ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)) + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.SubjectVisit.TrialSite.TrialSiteCode)); + } + } + +} diff --git a/IRaCIS.Core.Application/Service/WorkLoad/DTO/DoctorWorkLoadViewModel.cs b/IRaCIS.Core.Application/Service/WorkLoad/DTO/DoctorWorkLoadViewModel.cs new file mode 100644 index 0000000..f5d4509 --- /dev/null +++ b/IRaCIS.Core.Application/Service/WorkLoad/DTO/DoctorWorkLoadViewModel.cs @@ -0,0 +1,422 @@ +using System.ComponentModel.DataAnnotations; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + /// + ///后台 工作量审核视图模型 + /// + public class WorkLoadDetailDTO : WorkloadDTO + { + public DateTime CreateTime { get; set; } + public string ChineseName { get; set; } = String.Empty; + + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + public string Code { get; set; } = String.Empty; + public string Indication { get; set; } = String.Empty; + public int RowIntId { get; set; } + public Guid RowGuid { get; set; } + public string YearMonthStr { get; set; } = String.Empty; + + public string WorkTimeStr => YearMonthStr; + + public bool IsLock { get; set; } = false; + } + + public class WorkLoadDetailViewModel : WorkloadDTO + { + public DateTime CreateTime { get; set; } + public string ChineseName { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + public string Code { get; set; } = String.Empty; + public string Indication { get; set; } = String.Empty; + + public Guid RowGuid { get; set; } + public string YearMonthStr { get; set; } = String.Empty; + + public bool IsLock { get; set; } = false; + public string DeclareTimeStr => CreateTime.ToString("yyyy-MM-dd"); + + public string WorkTimeStr => WorkTime.ToString("yyyy-MM-dd"); + } + + + + public class CalculatePaymentDTO : WorkloadDTO + { + public string TrialCode { get; set; } = String.Empty; + public decimal? TrialAdditional { get; set; } + public decimal AdjustmentMultiple { get; set; } + //public double RankPrice { get; set; } + public decimal PersonalAdditional { get; set; } + public decimal TimepointPrice { get; set; } + public decimal TimepointIn24HPrice { get; set; } + public decimal TimepointIn48HPrice { get; set; } + public decimal AdjudicationPrice { get; set; } + public decimal AdjudicationIn24HPrice { get; set; } + public decimal AdjudicationIn48HPrice { get; set; } + public decimal GlobalPrice { get; set; } + public decimal TrainingPrice { get; set; } + public decimal DowntimePrice { get; set; } + public decimal RefresherTrainingPrice { get; set; } + } + + /// + /// 工作量审核数据库查询模型 + /// + public class WorkloadDTO + { + public Guid? Id { get; set; } + + public Guid TrialId { get; set; } + + public Guid DoctorId { get; set; } + + public int DataFrom { get; set; } + + public DateTime WorkTime { get; set; } + + public int Training { get; set; } + + public int Downtime { get; set; } + + public int Timepoint { get; set; } + + public int TimepointIn24H { get; set; } + + public int TimepointIn48H { get; set; } + + public int Adjudication { get; set; } + + public int AdjudicationIn24H { get; set; } + + public int AdjudicationIn48H { get; set; } + + public int Global { get; set; } + public int RefresherTraining { get; set; } + + + } + + /// + /// 工作量分页列表模型 医生端查询模型 + /// + public class WorkLoadQueryDTO : PageInput + { + public Guid TrialId { get; set; } + public int WorkLoadFromStatus { get; set; } // 关联枚举 + public DateTime? SearchBeginDateTime { get; set; } + public DateTime? SearchEndDateTime { get; set; } + } + + /// + /// 后台查询模型 + /// + public class WorkLoadStatsQueryDTO : PageInput + { + public Guid? TrialId { get; set; } + public Guid? DoctorId { get; set; } = Guid.Empty; + public List WorkLoadFromStatus { get; set; } = new List(); // 关联枚举 + public DateTime? SearchBeginDateTime { get; set; } + public DateTime? SearchEndDateTime { get; set; } + } + + + public class WorkLoadDetailQueryDTO + { + public Guid TrialId { get; set; } = Guid.Empty; + public Guid DoctorId { get; set; } = Guid.Empty; + public string YearMonthStr { get; set; } = String.Empty; + } + + public class CalculateDoctorAndMonthDTO + { + [Required(ErrorMessage = "需要有效的时间")] + public DateTime CalculateMonth { get; set; } + + [Required(ErrorMessage = "需要有效的医生列表")] + public List NeedCalculateReviewers { get; set; } = new List(); + } + public class SetEnrollReadingCategoryInDto + { + [NotDefault] + public Guid EnrollId { get; set; } + + public Guid TrialId { get; set; } + + [NotDefault] + public Guid TrialReadingCriterionId { get; set; } + + public List ReadingCategorys { get; set; } + } + + + public class SetEnrollEnableCommand + { + [NotDefault] + public Guid EnrollId { get; set; } + + + public Guid TrialId { get; set; } + + public bool IsEnable { get; set; } + + } + + public class WorkLoadDoctorQueryDTO : PageInput + { + public Guid TrialId { get; set; } = Guid.Empty; + } + + public class WorkLoadCommand : WorkloadDTO + { + public Guid CreateUserId { get; set; } + public int CreateUserType { get; set; } + public Guid UpdateUserId { get; set; } + } + + + public class ExistWorkloadViewModel + { + public bool IsExist { get; set; } + public WorkloadDTO WorkLoad { get; set; } = new WorkloadDTO(); + } + + public class WorkloadCommand : WorkloadDTO + { + } + + public class WorkloadExistQueryDTO + { + public Guid TrialId { get; set; } + public Guid DoctorId { get; set; } + public DateTime WorkDate { get; set; } + } + + + + public class WorkLoadAndTrainingDTO + { + + public Guid DoctorId { get; set; }/*=Guid.Empty;*/ + public string Code { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + public string LastName { get; set; } = String.Empty; + + public string FullName { get; set; } = String.Empty; + public string ChineseName { get; set; } = String.Empty; + + public DateTime? EnrollTime { get; set; } + + public int ReviewerReadingType { get; set; } + + public string? EnrollTimeStr => EnrollTime?.ToString("yyyy-MM-dd HH:mm:ss"); + + public DateTime? UpdateTime { get; set; } + + public string? UpdateTimeStr => UpdateTime?.ToString("yyyy-MM-dd HH:mm:ss"); + + + + + + //public int? TrainingTimes { get; set; } + //public int? Downtime { get; set; } + + ////读片点 + //public int? Timepoint { get; set; } + //public int? TimepointIn24H { get; set; } + //public int? TimepointIn48H { get; set; } + + ////裁判阅片 + //public int? Adjudication { get; set; } + //public int? AdjudicationIn24H { get; set; } + //public int? AdjudicationIn48H { get; set; } + + ////全局阅片 + //public int? Global { get; set; } + + //public int? RefresherTraining { get; set; } + + + + } + + + public class WorkLoadAndAgreementDTO : WorkLoadAndTrainingDTO + { + public bool IsEnable { get; set; } + + public Guid EnrollId { get; set; } + public DateTime? OutEnrollTime { get; set; } + public Guid AgreementId { get; set; } + + public string AgreementPath { get; set; } = String.Empty; + + public string? AgreementFileName => AgreementPath?.Split('/').Last(); + + public string AgreementFullPath => AgreementPath; + + + //public List ReadingCategorys { get; set; } + + public string UserName { get; set; } + + + public List TrialReadingCriterionList { get; set; } + + public List CriterionReadingCategoryList { get; set; } + + //任务阅片状态 + public List ReadingTaskStateList { get; set; }=new List(); + + public List CriterionFileList { get; set; }=new List(); + + + public List CriterionCategoryList => + TrialReadingCriterionList.Select(t => + new CriterionReadingCategory() + { + EnrollId = EnrollId, + PendingCount= ReadingTaskStateList.Where(x=>x.ReadingTaskState != ReadingTaskState.HaveSigned&& x.TrialReadingCriterionId == t.TrialReadingCriterionId).Count(), + + ComplectedCount = ReadingTaskStateList.Where(x => x.ReadingTaskState == ReadingTaskState.HaveSigned && x.TrialReadingCriterionId == t.TrialReadingCriterionId).Count(), + + TotalCount = ReadingTaskStateList.Where(x => x.TrialReadingCriterionId == t.TrialReadingCriterionId) .Count(), + + StatementCriterionFileList = CriterionFileList.Where(x=>x.CriterionType==t.CriterionType&&x.FileType==CriterionFileType.Statement) + .WhereIf(t.CriterionType == CriterionType.SelfDefine, x => x.TrialReadingCriterionId == t.TrialReadingCriterionId).ToList(), + AcknowledgementCriterionFileList = CriterionFileList.Where(x => x.CriterionType == t.CriterionType && x.FileType == CriterionFileType.Acknowledgement) + .WhereIf(t.CriterionType == CriterionType.SelfDefine, x => x.TrialReadingCriterionId == t.TrialReadingCriterionId).ToList(), + TrialReadingCriterionId = t.TrialReadingCriterionId, + ReadingCategorys = CriterionReadingCategoryList.Where(c => c.TrialReadingCriterionId == t.TrialReadingCriterionId).Select(t => t.ReadingCategory).OrderBy(c => c).ToList() + }).ToList(); + + + //CriterionReadingCategoryList.Count==0? TrialReadingCriterionList.Select(t=>new CriterionReadingCategory() { EnrollId= EnrollId ,TrialReadingCriterionId=t.TrialReadingCriterionId}).ToList(): + // CriterionReadingCategoryList + //.GroupBy(t => new { t.TrialReadingCriterionId, t.EnrollId }) + //.Select(g => new CriterionReadingCategory() { EnrollId = g.Key.EnrollId, TrialReadingCriterionId = g.Key.TrialReadingCriterionId, ReadingCategorys = g.Select(t => t.ReadingCategory).OrderBy(t=>t).ToList() }).ToList(); + + + } + + public class SetTaskUrgentInDto + { + public Guid VisitTaskId { get; set; } + + public bool IsUrgent { get; set; } + + /// + /// 加急类型 + /// + public TaskUrgentType? TaskUrgentType { get; set; } + + + /// + /// 任务加急备注 + /// + public string TaskUrgentRemake { get; set; } = string.Empty; + } + public class DoctorUserTask + { + public ReadingTaskState ReadingTaskState { get; set; } + + public CriterionType? CriterionType { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + } + + public class TrialReadingCriterionDto + { + public Guid TrialReadingCriterionId { get; set; } + public string TrialReadingCriterionName { get; set; } + + public ReadingMethod ReadingType { get; set; } + + public CriterionType? CriterionType { get; set; } + + public DateTime? ReadingInfoSignTime { get; set; } + + public bool IsGlobalReading { get; set; } = true; + + + public bool IsArbitrationReading { get; set; } = true; + + + public bool IsOncologyReading { get; set; } + } + + public class TrialCriterionReadingCategory + { + public Guid EnrollId { get; set; } + public Guid TrialReadingCriterionId { get; set; } + public ReadingCategory ReadingCategory { get; set; } + } + + + public class CriterionFile + { + + public Guid Id { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + /// + /// 文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件路径 + /// + public string FilePath { get; set; } + + /// + /// 标准类型 + /// + public CriterionType CriterionType { get; set; } + + /// + /// 医生Id + /// + public Guid DoctorId { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 文件类型 + /// + public CriterionFileType FileType { get; set; } + } + + + public class CriterionReadingCategory + { + public Guid EnrollId { get; set; } + public Guid TrialReadingCriterionId { get; set; } + + /// + /// 待办数量 + /// + public int PendingCount { get; set; } + + public int ComplectedCount { get; set; } + + public int TotalCount { get; set; } + + public List StatementCriterionFileList { get; set; } + + public List AcknowledgementCriterionFileList { get; set; } + + + public List ReadingCategorys { get; set; } = new List(); + } + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/WorkLoad/DTO/EnrollViewModel.cs b/IRaCIS.Core.Application/Service/WorkLoad/DTO/EnrollViewModel.cs new file mode 100644 index 0000000..9ffb53c --- /dev/null +++ b/IRaCIS.Core.Application/Service/WorkLoad/DTO/EnrollViewModel.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.WorkLoad.DTO +{ + public class EnrollViewModel + { + [StringLength(50)] + public string ChineseName { get; set; } = string.Empty; + + + [StringLength(100)] + public string FirstName { get; set; } = string.Empty; + + + [StringLength(100)] + public string LastName { get; set; } = string.Empty; + + public decimal? AdjustmentMultiple { get; set; } = 0; + + public Guid DoctorId { get; set; } + + public Guid TrialId { get; set; } + + public Guid Id { get; set; } + + public int? Training { get; set; } + + public int? RefresherTraining { get; set; } + + public int? Timepoint { get; set; } + + public int? Timepoint48H { get; set; } + + public int? Timepoint24H { get; set; } + + public int? Adjudication { get; set; } + + public int? Adjudication48H { get; set; } + + public int? Adjudication24H { get; set; } + + public int? Global { get; set; } + + + public int? Downtime { get; set; } + + public string ReviewerCode { get; set; } = string.Empty; + + public int? Code { get; set; } + + } + + + public class EnrollGetQuery:PageInput + { + public Guid TrialId { get; set; } + + + + + + + } + + public class ConfirmReviewerCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + public Guid[] DoctorIdArray { get; set; } = new Guid [0]; + + public int ConfirmState { get; set; } + + public string BaseUrl { get; set; } = string.Empty; + + public string RouteUrl { get; set; } = string.Empty; + } + + + public class EnrollCommand + { + public Guid Id { get; set; } + + + public decimal? Training { get; set; } + + public decimal? RefresherTraining { get; set; } + + public decimal? Timepoint { get; set; } + + public decimal? Timepoint48H { get; set; } + + public decimal? Timepoint24H { get; set; } + + public decimal? Adjudication { get; set; } + + public decimal? Adjudication48H { get; set; } + + public decimal? Adjudication24H { get; set; } + + public decimal? Global { get; set; } + + + public decimal? Downtime { get; set; } + + + } + } diff --git a/IRaCIS.Core.Application/Service/WorkLoad/DTO/WorkloadDistribution.cs b/IRaCIS.Core.Application/Service/WorkLoad/DTO/WorkloadDistribution.cs new file mode 100644 index 0000000..c577802 --- /dev/null +++ b/IRaCIS.Core.Application/Service/WorkLoad/DTO/WorkloadDistribution.cs @@ -0,0 +1,143 @@ +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Contracts +{ + #region TP + public class WorkloadTPDTO + { + public Guid Id { get; set; } + public Guid SiteId { get; set; } + public string SiteName { get; set; } = String.Empty; + public Guid SubjectId { get; set; } + public string SubjectCode { get; set; } = String.Empty; + public Guid SubjectVisitId { get; set; } + public string VisitName { get; set; } = String.Empty; + public decimal VisitNum { get; set; } + public Guid StudyId { get; set; } + public string StudyCode { get; set; } = String.Empty; + public string TimepointCode { get; set; } = String.Empty; + public Guid ReviewerId { get; set; } + public string ReviewerCode { get; set; } = String.Empty; + public string ReviewerFirstName { get; set; } = String.Empty; + public string ReviewerLastName { get; set; } = String.Empty; + public string ReviewerChineseName { get; set; } = String.Empty; + public int Status { get; set; } + public DateTime UpdateTime { get; set; } + } + #endregion + + #region Global + public class WorkloadGlobalDTO + { + public Guid Id { get; set; } + public Guid SiteId { get; set; } + public string SiteName { get; set; } = String.Empty; + public Guid VisitId { get; set; } + public string VisitName { get; set; } = String.Empty; + + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + public string SubjectCode { get; set; } = String.Empty; + public decimal VisitNum { get; set; } + + public string GlobalCode { get; set; } = String.Empty; + public Guid ReviewerId { get; set; } + public string ReviewerCode { get; set; } = String.Empty; + public string ReviewerFirstName { get; set; } = String.Empty; + public string ReviewerLastName { get; set; } = String.Empty; + public string ReviewerChineseName { get; set; } = String.Empty; + public int Status { get; set; } + public DateTime UpdateTime { get; set; } + } + #endregion + + #region AD + public class WorkloadADDTO + { + public Guid Id { get; set; } + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public string SiteName { get; set; }=String.Empty; + public Guid SubjectId { get; set; } + public string SubjectCode { get; set; } = String.Empty; + public string ADCode { get; set; } = String.Empty; + public Guid ReviewerId { get; set; } + public string ReviewerCode { get; set; } = String.Empty; + public string ReviewerFirstName { get; set; } = String.Empty; + public string ReviewerLastName { get; set; } = String.Empty; + public string ReviewerChineseName { get; set; } = String.Empty; + public int Status { get; set; } + public DateTime UpdateTime { get; set; } + public Guid Global1Id { get; set; } + public Guid Global2Id { get; set; } + } + #endregion + + public class WorkloadDistributionQueryParam : PageInput + { + public Guid TrialId { get; set; } = Guid.Empty; + public Guid? SiteId { get; set; } = Guid.Empty; + public string SubjectCode { get; set; } = string.Empty; + public string WorkloadCode { get; set; } = string.Empty; + public string Reviewer { get; set; } = string.Empty; + public int? Status { get; set; } + public int? GroupId { get; set; } = 0; + } + + public class WorkloadTPInfo + { + public Guid Id { get; set; } + public Guid StudyId { get; set; } + } + public class WorkloadTPCommand + { + public List TpList { get; set; } = new List(); + public Guid ReviewerId { get; set; } + public string ReviewerName { get; set; } = String.Empty; + } + + public class WorkloadGlobalInfo + { + public Guid Id { get; set; } + public Guid SubjectId { get; set; } + public decimal VisitNum { get; set; } + } + public class WorkloadGlobalCommand + { + public List GlobalList { get; set; } = new List(); + public Guid ReviewerId { get; set; } + public string ReviewerName { get; set; } = String.Empty; + } + public class WorkloadAdCommand + { + public List IdList { get; set; } = new List(); + public Guid ReviewerId { get; set; } + public string ReviewerName { get; set; } = String.Empty; + } + + //public class WorkloadDistributionUpdateCommand + //{ + // public Guid Id { get; set; } + // public Guid? ReviewerId { get; set; } + //} + + public class WorkloadDetailDTO + { + public Guid Id { get; set; } + public Guid WorkloadId { get; set; } + public string OptUserName { get; set; } = String.Empty; + public DateTime OptTime { get; set; } + public string ReviewerFirstName { get; set; } = String.Empty; + public string ReviewerLastName { get; set; } = String.Empty; + public string ReviewerChineseName { get; set; } = String.Empty; + public int Status { get; set; } + } + + //public class WorkloadDetailQueryParam + //{ + // public int WorkloadType { get; set; } //1-tp,2-global,3-ad + // public Guid WorkloadId { get; set; } + + //} + +} diff --git a/IRaCIS.Core.Application/Service/WorkLoad/DoctorWorkloadService.cs b/IRaCIS.Core.Application/Service/WorkLoad/DoctorWorkloadService.cs new file mode 100644 index 0000000..0096200 --- /dev/null +++ b/IRaCIS.Core.Application/Service/WorkLoad/DoctorWorkloadService.cs @@ -0,0 +1,827 @@ +using AutoMapper; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using System.Linq.Expressions; +using IRaCIS.Core.Application.Filter; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Auth; +using System.Linq.Dynamic.Core; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Trial")] + public class DoctorWorkloadService : BaseService, IDoctorWorkloadService + { + private readonly IRepository _trialRepository; + private readonly IRepository _enrollRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _doctorWorkloadRepository; + private readonly IRepository _enrollReadingCategoryRepository; + private readonly IRepository _attachmentRepository; + private readonly IRepository _costStatisticsRepository; + private readonly IRepository _trialRevenuesPriceRepository; + private readonly IRepository _trialRevenuesPriceVerificationRepository; + + private readonly IRepository _taskAllocationRuleRepository; + public DoctorWorkloadService(IRepository clinicalTrialProjectRepository, + IRepository intoGroupRepository, + IRepository doctorInfoRepository, + IRepository doctorWorkloadRepository, + IRepository enrollReadingCategoryRepository, + IRepository attachmentRepository, + IRepository costStatisticsRepository, + IRepository trialRevenuesPriceRepository, + IRepository taskAllocationRuleRepository, + IRepository trialRevenuesPriceVerificationRepository, + IMapper mapper) + { + _taskAllocationRuleRepository = taskAllocationRuleRepository; + _trialRepository = clinicalTrialProjectRepository; + _enrollRepository = intoGroupRepository; + _doctorRepository = doctorInfoRepository; + _doctorWorkloadRepository = doctorWorkloadRepository; + this._enrollReadingCategoryRepository = enrollReadingCategoryRepository; + _attachmentRepository = attachmentRepository; + _costStatisticsRepository = costStatisticsRepository; + _trialRevenuesPriceRepository = trialRevenuesPriceRepository; + _trialRevenuesPriceVerificationRepository = trialRevenuesPriceVerificationRepository; + + } + + #region 入组工作量统计列表 具体详情 增删改查相关 上传\删除协议 + + + #region 协议废弃 + ///// + ///// 保存协议- ack Sow [AUTH] + ///// + //[HttpPost("{trialId}")] + + //[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + //public async Task UploadReviewerAckSOW(Guid trialId, + // ReviewerAckDTO attachmentViewModel) + //{ + + // var intoGroupItem = (await _enrollRepository.Where(t => t.TrialId == trialId && t.DoctorId == attachmentViewModel.DoctorId).FirstOrDefaultAsync()).IfNullThrowException(); + + + // if (attachmentViewModel.Id != Guid.Empty) + // { + // await _attachmentRepository.BatchDeleteNoTrackingAsync(t => t.Id == attachmentViewModel.Id); + // } + + // var attachment = await _attachmentRepository.InsertFromDTOAsync(attachmentViewModel); + + // //intoGroupItem.AttachmentId = attachment.Id; + // await _enrollRepository.UpdatePartialFromQueryAsync(intoGroupItem.Id, u => new Enroll() { AttachmentId = attachment.Id }); + + + // var success = await _enrollRepository.SaveChangesAsync(); + + // return ResponseOutput.Result(success, attachment.Id.ToString()); + //} + + ///// + ///// 删除协议 + ///// + //[HttpDelete, Route("{trialId}/{doctorId}/{attachmentId}")] + + //[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + //public async Task DeleteReviewerAckSOW(Guid trialId, Guid doctorId, Guid attachmentId) + //{ + // var success1 = await _attachmentRepository.BatchDeleteNoTrackingAsync(a => a.Id == attachmentId); + // await _enrollRepository.UpdatePartialFromQueryAsync(t => t.TrialId == trialId && t.DoctorId == doctorId, u => + // new Enroll() + // { + // AttachmentId = Guid.Empty + // }, true); + // return ResponseOutput.Ok(success1); + //} + + + + #endregion + + + + + /// + /// 修改项目医生的阅片类型 + /// + /// + /// + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SetEnrollReadingCategory(SetEnrollReadingCategoryInDto inDto) + { + + var enroll = await _enrollRepository.FirstAsync(t => t.Id == inDto.EnrollId); + + + if (_repository.Where(t => t.TrialId == enroll.TrialId && t.DoctorUserId == enroll.DoctorUserId && t.TaskAllocationState == TaskAllocationState.Allocated && t.TrialReadingCriterionId==inDto.TrialReadingCriterionId).Any()) + { + + var readingCategoryList = await _enrollReadingCategoryRepository.Where(t => t.EnrollId == inDto.EnrollId && t.TrialReadingCriterionId == inDto.TrialReadingCriterionId).Select(t => t.ReadingCategory).ToListAsync(); + + if (readingCategoryList.Except(inDto.ReadingCategorys).Count() > 0) + { + return ResponseOutput.NotOk("已分配任务,不允许减少阅片类型"); + + } + + } + + //if (inDto.ReadingCategorys.Count > 0) + //{ + // await _enrollReadingCategoryRepository.BatchDeleteNoTrackingAsync(x => x.EnrollId == inDto.EnrollId); + + //} + //else + //{ + // await _enrollReadingCategoryRepository.DeleteFromQueryAsync(x => x.EnrollId == inDto.EnrollId); + //} + + await _enrollReadingCategoryRepository.DeleteFromQueryAsync(x => x.EnrollId == inDto.EnrollId && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId); + + + List enrollReadings = inDto.ReadingCategorys.Select(x => new EnrollReadingCategory() + { + TrialReadingCriterionId= inDto.TrialReadingCriterionId, + EnrollId = inDto.EnrollId, + ReadingCategory = x + }).ToList(); + + await _enrollReadingCategoryRepository.AddRangeAsync(enrollReadings); + var result = await _enrollReadingCategoryRepository.SaveChangesAsync(); + + + return ResponseOutput.Ok(result); + } + + /// + /// 修改项目医生启用禁用状态 + /// + /// + /// + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task UpdateTrialReviewerState(SetEnrollEnableCommand inCommand) + { + await _taskAllocationRuleRepository.UpdatePartialFromQueryAsync(t => t.TrialId == inCommand.TrialId && t.EnrollId == inCommand.EnrollId, u => new TaskAllocationRule() { IsEnable = inCommand.IsEnable },true); + + return ResponseOutput.Ok(); + } + + + /// + /// 0代表裁判和Tp 都可以 1、代表Tp 2 代表裁判 + /// + /// + [HttpPost("{trialId}/{doctorId}/{type}")] + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + public async Task UpdateReviewerReadingType(Guid trialId, Guid doctorId, int type) + { + var success2 = await _enrollRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trialId && t.DoctorId == doctorId, u => + new Enroll() + { + ReviewerReadingType = type + }); + return ResponseOutput.Result(success2); + } + + /// + /// 获取某个项目入组的医生工作量统计列表 + /// + [HttpPost] + public async Task> GetTrialEnrollmentWorkloadStats(WorkLoadDoctorQueryDTO doctorSearchModel) + { + //https://blog.csdn.net/sunshineblog/article/details/78636389 + + var trialId = doctorSearchModel.TrialId; + + var doctorIntoGroupQueryable = + + from intoGroup in _enrollRepository.Where(x => x.TrialId == trialId && x.EnrollStatus >= EnrollStatus.ConfirmIntoGroup) + join allocateRule in _taskAllocationRuleRepository.AsQueryable() on intoGroup.Id equals allocateRule.EnrollId + + + join attachmentItem in _attachmentRepository.AsQueryable() on intoGroup.AttachmentId equals attachmentItem.Id into cc + from attachment in cc.DefaultIfEmpty() + + select new WorkLoadAndAgreementDTO() + { + EnrollId = intoGroup.Id, + IsEnable = allocateRule.IsEnable, + + TrialReadingCriterionList = intoGroup.Trial.ReadingQuestionCriterionTrialList.Where(t=>t.IsConfirm).Select(t=>new TrialReadingCriterionDto() { TrialReadingCriterionId=t.Id,TrialReadingCriterionName=t.CriterionName,CriterionType=t.CriterionType,IsOncologyReading=t.IsOncologyReading,IsArbitrationReading=t.IsArbitrationReading,IsGlobalReading=t.IsGlobalReading,ReadingInfoSignTime=t.ReadingInfoSignTime,ReadingType=t.ReadingType}).ToList(), + + + + CriterionReadingCategoryList =intoGroup.EnrollReadingCategoryList.Select(t=>new TrialCriterionReadingCategory() { EnrollId=t.EnrollId,ReadingCategory=t.ReadingCategory, TrialReadingCriterionId = t.TrialReadingCriterionId }).ToList(), + + + UserName= intoGroup.DoctorUser.UserName, + + OutEnrollTime = intoGroup.OutEnrollTime, + EnrollTime = intoGroup.EnrollTime, + AgreementId = intoGroup.AttachmentId, + ReviewerReadingType = intoGroup.ReviewerReadingType, + AgreementPath = attachment.Path, + + + + }; + + return await doctorIntoGroupQueryable.ToPagedListAsync(doctorSearchModel.PageIndex, doctorSearchModel.PageSize, doctorSearchModel.SortField == "" ? "EnrollTime" : doctorSearchModel.SortField, doctorSearchModel.Asc); + + + } + + /// + /// 获取入组某个项目的医生的最近几个月的工作量详情(带有填充数据) + /// + [HttpPost] + public PageOutput GetEnrollmentWorkloadStatsDetail(WorkLoadStatsQueryDTO workLoadSearch) + { + #region 条件查询 + Expression> workloadLambda = x => true; + if (workLoadSearch.TrialId != Guid.Empty && workLoadSearch.TrialId != null) + { + workloadLambda = workloadLambda.And(x => x.TrialId == workLoadSearch.TrialId); + } + if (workLoadSearch.DoctorId != Guid.Empty && workLoadSearch.DoctorId != null) + { + workloadLambda = workloadLambda.And(t => t.DoctorId == workLoadSearch.DoctorId); + } + if (workLoadSearch.SearchBeginDateTime != null) + { + var bDate = workLoadSearch.SearchBeginDateTime; + + var beginDate = new DateTime(bDate.Value.Year, bDate.Value.Month, 1); + + workloadLambda = workloadLambda.And(t => + t.WorkTime >= beginDate); + } + if (workLoadSearch.SearchEndDateTime != null) + { + var eDate = workLoadSearch.SearchEndDateTime.Value.AddMonths(1); + + DateTime endDate = new DateTime(eDate.Year, eDate.Month, 1); + + workloadLambda = workloadLambda.And(t => + t.WorkTime < endDate); + } + + + #endregion + + IQueryable? workLoadQueryable = default; + + var tempWorkload = new List(); + if (workLoadSearch.WorkLoadFromStatus.Contains(1)) + { + workLoadQueryable = from workLoad in _doctorWorkloadRepository.Where(workloadLambda.And(t => t.DataFrom == (int)WorkLoadFromStatus.CRO)) + join doctor in _doctorRepository.AsQueryable() on workLoad.DoctorId equals doctor.Id + join trial in _trialRepository.AsQueryable() on workLoad.TrialId equals trial.Id + + select new WorkLoadDetailDTO() + { + Id = workLoad.Id, + DoctorId = workLoad.DoctorId, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + + YearMonthStr = workLoad.YearMonth, + CreateTime = workLoad.CreateTime, + WorkTime = workLoad.WorkTime, + + DataFrom = workLoad.DataFrom, + TrialId = workLoad.TrialId, + Indication = trial.Indication, + Code = trial.TrialCode, + + Timepoint = workLoad.Timepoint, + TimepointIn24H = workLoad.TimepointIn24H, + TimepointIn48H = workLoad.TimepointIn48H, + Global = workLoad.Global, + RefresherTraining = workLoad.RefresherTraining, + Adjudication = workLoad.Adjudication, + AdjudicationIn24H = workLoad.AdjudicationIn24H, + AdjudicationIn48H = workLoad.AdjudicationIn48H, + + + + Training = workLoad.Training, + Downtime = workLoad.Downtime, + IsLock = workLoad.IsLock + }; + tempWorkload.AddRange(workLoadQueryable.ToList()); + } + + + if (workLoadSearch.WorkLoadFromStatus.Contains(2)) + { + workLoadQueryable = from workLoad in _doctorWorkloadRepository.Where(workloadLambda.And(t => t.DataFrom == (int)WorkLoadFromStatus.FinalConfirm)) + join doctor in _doctorRepository.AsQueryable() on workLoad.DoctorId equals doctor.Id + join trial in _trialRepository.AsQueryable() on workLoad.TrialId equals trial.Id + + select new WorkLoadDetailDTO() + { + Id = workLoad.Id, + DoctorId = workLoad.DoctorId, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + + YearMonthStr = workLoad.YearMonth, + CreateTime = workLoad.CreateTime, + WorkTime = workLoad.WorkTime, + + DataFrom = workLoad.DataFrom, + TrialId = workLoad.TrialId, + Indication = trial.Indication, + Code = trial.TrialCode, + + Timepoint = workLoad.Timepoint, + TimepointIn24H = workLoad.TimepointIn24H, + TimepointIn48H = workLoad.TimepointIn48H, + Global = workLoad.Global, + RefresherTraining = workLoad.RefresherTraining, + Adjudication = workLoad.Adjudication, + AdjudicationIn24H = workLoad.AdjudicationIn24H, + AdjudicationIn48H = workLoad.AdjudicationIn48H, + + Training = workLoad.Training, + Downtime = workLoad.Downtime, + IsLock = workLoad.IsLock + }; + tempWorkload.AddRange(workLoadQueryable.ToList()); + } + + if (workLoadSearch.WorkLoadFromStatus.Contains(0)) + { + workLoadQueryable = + from workLoad in (from workLoadItem in _doctorWorkloadRepository.Where(workloadLambda.And(t => t.DataFrom == (int)WorkLoadFromStatus.Doctor)) + group workLoadItem by new { workLoadItem.YearMonth, workLoadItem.DoctorId, workLoadItem.TrialId, workLoadItem.IsLock } into g + select new + { + Id = Guid.NewGuid(), + DoctorId = g.Key.DoctorId, + DataFrom = 0, + TrialId = g.Key.TrialId, + IsLock = g.Key.IsLock, + YearMonthStr = g.Key.YearMonth, + Timepoint = g.Sum(workLoad => workLoad.Timepoint), + TimepointIn24H = g.Sum(workLoad => workLoad.TimepointIn24H), + TimepointIn48H = g.Sum(workLoad => workLoad.TimepointIn48H), + Global = g.Sum(workLoad => workLoad.Global), + RefresherTraining = g.Sum(workLoad => workLoad.RefresherTraining), + Adjudication = g.Sum(workLoad => workLoad.Adjudication), + AdjudicationIn24H = g.Sum(workLoad => workLoad.AdjudicationIn24H), + AdjudicationIn48H = g.Sum(workLoad => workLoad.AdjudicationIn48H), + + Training = g.Sum(workLoad => workLoad.Training), + Downtime = g.Sum(workLoad => workLoad.Downtime) + }) + join doctor in _doctorRepository.AsQueryable() on workLoad.DoctorId equals doctor.Id + join trial in _trialRepository.AsQueryable() on workLoad.TrialId equals trial.Id + select new WorkLoadDetailDTO() + { + Id = workLoad.Id, + DoctorId = workLoad.DoctorId, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + YearMonthStr = workLoad.YearMonthStr, + + DataFrom = workLoad.DataFrom, + TrialId = workLoad.TrialId, + Indication = trial.Indication, + Code = trial.TrialCode, + + Timepoint = workLoad.Timepoint, + TimepointIn24H = workLoad.TimepointIn24H, + TimepointIn48H = workLoad.TimepointIn48H, + Global = workLoad.Global, + RefresherTraining = workLoad.RefresherTraining, + Adjudication = workLoad.Adjudication, + AdjudicationIn24H = workLoad.AdjudicationIn24H, + AdjudicationIn48H = workLoad.AdjudicationIn48H, + + Training = workLoad.Training, + Downtime = workLoad.Downtime, + IsLock = workLoad.IsLock + }; + tempWorkload.AddRange(workLoadQueryable.ToList()); + } + + //workLoadQueryable = workLoadQueryable.OrderByDescending(t => t.YearMonthStr).ThenBy(t => t.DataFrom); + + //取选取月份的数据 + var month = (workLoadSearch.SearchEndDateTime?.Year - workLoadSearch.SearchBeginDateTime?.Year) * 12 + (workLoadSearch.SearchEndDateTime?.Month - workLoadSearch.SearchBeginDateTime?.Month) + 1; + var count = (month ?? 1) * workLoadSearch.WorkLoadFromStatus.Count; + //tempWorkload = tempWorkload.Take(count); + tempWorkload.OrderByDescending(t => t.YearMonthStr).ThenBy(t => t.DataFrom).Take(count); + //var workLoadList = workLoadQueryable.ToList(); + return BuildFullData(tempWorkload, workLoadSearch); + } + + /// + /// 获取来自Reviewer自己的数据,某个月添加的多条 + /// + [HttpPost] + public async Task> GetReviewerWorkLoadListDetail( + WorkLoadDetailQueryDTO workLoadSearch) + { + Expression> workloadLambda = t => t.DataFrom == (int)WorkLoadFromStatus.Doctor && t.YearMonth == workLoadSearch.YearMonthStr; + + if (workLoadSearch.TrialId != Guid.Empty) + { + workloadLambda = workloadLambda.And(x => x.TrialId == workLoadSearch.TrialId); + } + + if (workLoadSearch.DoctorId != Guid.Empty) + { + workloadLambda = workloadLambda.And(t => t.DoctorId == workLoadSearch.DoctorId); + } + + var workLoadQueryable = from workLoad in _doctorWorkloadRepository.Where(workloadLambda) + join doctor in _doctorRepository.AsQueryable() on workLoad.DoctorId equals doctor.Id + join trial in _trialRepository.AsQueryable() on workLoad.TrialId equals trial.Id + + select new WorkLoadDetailViewModel() + { + Id = workLoad.Id, + DoctorId = workLoad.DoctorId, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + YearMonthStr = workLoad.YearMonth, + CreateTime = workLoad.CreateTime, + WorkTime = workLoad.WorkTime, + + DataFrom = workLoad.DataFrom, + TrialId = workLoad.TrialId, + Indication = trial.Indication, + Code = trial.TrialCode, + + Timepoint = workLoad.Timepoint, + TimepointIn24H = workLoad.TimepointIn24H, + TimepointIn48H = workLoad.TimepointIn48H, + Global = workLoad.Global, + Adjudication = workLoad.Adjudication, + AdjudicationIn24H = workLoad.AdjudicationIn24H, + AdjudicationIn48H = workLoad.AdjudicationIn48H, + RefresherTraining = workLoad.RefresherTraining, + + Training = workLoad.Training, + Downtime = workLoad.Downtime, + IsLock = workLoad.IsLock + }; + + workLoadQueryable = workLoadQueryable.OrderBy("WorkTime"); + + var workLoadList = await workLoadQueryable.ToListAsync(); + + workLoadList.ForEach(t => t.RowGuid = Guid.NewGuid()); + + return workLoadList; + + } + + /// + /// 工作量是否存在,用于判断只能添加一条的工作量记录 + /// + /// + /// 查询某个医生是否在某天有某个项目的工作量数据,处理添加来自医生自己的工作量数据 + /// + [HttpPost] + public async Task> WorkloadExist(WorkloadExistQueryDTO param) + { + var isExist = false; + var exist = await _doctorWorkloadRepository.FirstOrDefaultAsync(u => u.TrialId == param.TrialId && u.DoctorId == param.DoctorId + && u.WorkTime == param.WorkDate && u.DataFrom == 0); + if (exist != null) + { + isExist = true; + } + return ResponseOutput.Ok(new ExistWorkloadViewModel + { + IsExist = isExist, + WorkLoad = _mapper.Map(exist), + }); + } + + /// + /// 添加或更新工作量 + /// + //[Authorize(Policy = IRaCISPolicy.PM_APM)] + public async Task AddOrUpdateWorkload(WorkloadCommand workLoadAddOrUpdateModel, + Guid userId) + { + var yearMonthStr = workLoadAddOrUpdateModel.WorkTime.ToString("yyyy-MM"); + + + if (workLoadAddOrUpdateModel.Id == Guid.Empty || workLoadAddOrUpdateModel.Id == null) + { + //确定是那个医生 那个项目 那个月份做的 + Expression> workloadLambda = x => x.DoctorId == workLoadAddOrUpdateModel.DoctorId && x.TrialId == workLoadAddOrUpdateModel.TrialId && x.YearMonth == yearMonthStr; + + //只要是来自CRO或者最终确认的 只能有一条 + switch (workLoadAddOrUpdateModel.DataFrom) + { + case (int)WorkLoadFromStatus.CRO: //1 来自CRO的 + workloadLambda = workloadLambda.And(t => t.DataFrom == (int)WorkLoadFromStatus.CRO); + break; + + case (int)WorkLoadFromStatus.FinalConfirm: //2 最终确认的 + workloadLambda = workloadLambda.And(t => t.DataFrom == (int)WorkLoadFromStatus.FinalConfirm); + + var isExist = await _costStatisticsRepository.AnyAsync(u => u.YearMonth == yearMonthStr && u.DoctorId == workLoadAddOrUpdateModel.DoctorId); + if (!isExist) + { + await _costStatisticsRepository.AddAsync(new Payment + { + DoctorId = workLoadAddOrUpdateModel.DoctorId, + YearMonth = yearMonthStr, + YearMonthDate = DateTime.Parse(yearMonthStr), + IsLock = false + }); + } + + //处理 验证项目收入价格是否都填写了 + + var revenuesPriceExist = await _trialRevenuesPriceRepository.AsQueryable() + .FirstOrDefaultAsync(t => t.TrialId == workLoadAddOrUpdateModel.TrialId); + + //没填写收入价格 插入记录 所有的都是false + if (revenuesPriceExist == null) + { + await _trialRevenuesPriceVerificationRepository.AddAsync(new TrialRevenuesPriceVerification() + { + TrialId = workLoadAddOrUpdateModel.TrialId, + ReviewerId = workLoadAddOrUpdateModel.DoctorId, + YearMonth = yearMonthStr, + WorkLoadDate = workLoadAddOrUpdateModel.WorkTime + }); + } + else //有价格 校验 看全不全 + { + + var tpNeedSetPrice = workLoadAddOrUpdateModel.Timepoint > 0 && + revenuesPriceExist.Timepoint <= 0; + var tp24HNeedSetPrice = workLoadAddOrUpdateModel.TimepointIn24H > 0 && + revenuesPriceExist.TimepointIn24H <= 0; + var tp48NeedSetPrice = workLoadAddOrUpdateModel.TimepointIn48H > 0 && + revenuesPriceExist.TimepointIn48H <= 0; + var adNeedSetPrice = workLoadAddOrUpdateModel.Adjudication > 0 && + revenuesPriceExist.Adjudication <= 0; + var ad24NeedSetPrice = workLoadAddOrUpdateModel.AdjudicationIn24H > 0 && + revenuesPriceExist.AdjudicationIn24H <= 0; + var ad48NeedSetPrice = workLoadAddOrUpdateModel.AdjudicationIn48H > 0 && + revenuesPriceExist.AdjudicationIn48H <= 0; + var downtimeNeedSetPrice = workLoadAddOrUpdateModel.Downtime > 0 && + revenuesPriceExist.Downtime <= 0; + var globalNeedSetPrice = workLoadAddOrUpdateModel.Global > 0 && + revenuesPriceExist.Global <= 0; + var trainingNeedSetPrice = workLoadAddOrUpdateModel.Training > 0 && + revenuesPriceExist.Training <= 0; + + var refresherTrainingTrainingNeedSetPrice = workLoadAddOrUpdateModel.RefresherTraining > 0 && + revenuesPriceExist.RefresherTraining <= 0; + + + + + var needAdd = tpNeedSetPrice || tp24HNeedSetPrice || tp48NeedSetPrice || adNeedSetPrice || ad24NeedSetPrice || + ad48NeedSetPrice || downtimeNeedSetPrice || globalNeedSetPrice && trainingNeedSetPrice || refresherTrainingTrainingNeedSetPrice; + + //验证不通过 增加 + if (needAdd) + { + await _trialRevenuesPriceVerificationRepository.AddAsync(new TrialRevenuesPriceVerification() + { + TrialId = workLoadAddOrUpdateModel.TrialId, + ReviewerId = workLoadAddOrUpdateModel.DoctorId, + YearMonth = yearMonthStr, + WorkLoadDate = workLoadAddOrUpdateModel.WorkTime, + //需要设置价格和 有价格是相反的 数据库此字段存储的时候对应项是否有价格 + Timepoint = !tpNeedSetPrice, + TimepointIn24H = !tp24HNeedSetPrice, + TimepointIn48H = !tp48NeedSetPrice, + Adjudication = !adNeedSetPrice, + AdjudicationIn24H = !ad24NeedSetPrice, + AdjudicationIn48H = !ad48NeedSetPrice, + Downtime = !downtimeNeedSetPrice, + Global = !globalNeedSetPrice, + Training = !trainingNeedSetPrice, + RefresherTraining = !refresherTrainingTrainingNeedSetPrice + }); + } + + } + + + break; + } + if (workLoadAddOrUpdateModel.DataFrom != (int)WorkLoadFromStatus.Doctor) + { + if (await _doctorWorkloadRepository.AnyAsync(workloadLambda)) + { + + return ResponseOutput.NotOk("This type of data can only have one"); + + } + } + if (workLoadAddOrUpdateModel.DataFrom == (int)WorkLoadFromStatus.Doctor) + { + Expression> doctorworkloadLambda = x => x.DoctorId == workLoadAddOrUpdateModel.DoctorId && x.TrialId == workLoadAddOrUpdateModel.TrialId && x.YearMonth == yearMonthStr && workLoadAddOrUpdateModel.WorkTime == x.WorkTime; + + doctorworkloadLambda = doctorworkloadLambda.And(t => t.DataFrom == (int)WorkLoadFromStatus.Doctor); + if (await _doctorWorkloadRepository.AnyAsync(doctorworkloadLambda)) + { + return ResponseOutput.NotOk("This type of data can only have one"); + } + } + + var workLoad = _mapper.Map(workLoadAddOrUpdateModel); + + workLoad.YearMonth = yearMonthStr; + + await _doctorWorkloadRepository.AddAsync(workLoad); + + await _enrollRepository.BatchUpdateNoTrackingAsync( + t => t.DoctorId == workLoadAddOrUpdateModel.DoctorId && t.TrialId == workLoadAddOrUpdateModel.TrialId, u => new Enroll() + { + EnrollStatus = EnrollStatus.DoctorReading, + }); + + var success = await _doctorWorkloadRepository.SaveChangesAsync(); + + return ResponseOutput.Result(success, workLoad.Id); + + } + else + { + //判断是否已经被锁定 + var isLocked = await _costStatisticsRepository.AnyAsync(u => u.DoctorId == workLoadAddOrUpdateModel.DoctorId && + u.YearMonth == yearMonthStr && u.IsLock); + if (isLocked) + { + + return ResponseOutput.NotOk("Expenses have been settled and workload cannot be modified."); + + } + + var success = await _doctorWorkloadRepository.BatchUpdateNoTrackingAsync(t => t.Id == workLoadAddOrUpdateModel.Id, + u => new Workload() + { + Timepoint = workLoadAddOrUpdateModel.Timepoint, + TimepointIn24H = workLoadAddOrUpdateModel.TimepointIn24H, + TimepointIn48H = workLoadAddOrUpdateModel.TimepointIn48H, + + Adjudication = workLoadAddOrUpdateModel.Adjudication, + AdjudicationIn24H = workLoadAddOrUpdateModel.AdjudicationIn24H, + AdjudicationIn48H = workLoadAddOrUpdateModel.AdjudicationIn48H, + + RefresherTraining = workLoadAddOrUpdateModel.RefresherTraining, + + Downtime = workLoadAddOrUpdateModel.Downtime, + Training = workLoadAddOrUpdateModel.Training, + Global = workLoadAddOrUpdateModel.Global, + + WorkTime = workLoadAddOrUpdateModel.WorkTime, + YearMonth = yearMonthStr + }); + return ResponseOutput.Result(success); + + } + } + + /// + /// 删除工作量 + /// + public async Task DeleteWorkload(Guid workloadId) + { + return ResponseOutput.Result(await _doctorWorkloadRepository.BatchDeleteNoTrackingAsync(t => t.Id == workloadId)); + } + + /// + /// 获取工作量详情(用于判断工作量锁定时,调用) + /// + public async Task GetWorkloadDetailById(Guid id) + { + var workload = await _doctorWorkloadRepository.FirstOrDefaultAsync(u => u.Id == id); + return _mapper.Map(workload); + } + + #endregion + + private PageOutput BuildFullData( + List workLoadList, WorkLoadStatsQueryDTO workLoadSearch) + { + var doctor = _doctorRepository.Where(t => t.Id == workLoadSearch.DoctorId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault().IfNullThrowException(); + //查询 1-3月的 共3个月 + var month = (workLoadSearch.SearchEndDateTime?.Year - workLoadSearch.SearchBeginDateTime?.Year) * 12 + + (workLoadSearch.SearchEndDateTime?.Month - workLoadSearch.SearchBeginDateTime?.Month) + 1; + + var needCount = month * workLoadSearch.WorkLoadFromStatus.Count ?? 1; + + if (workLoadSearch.WorkLoadFromStatus.Contains((int)WorkLoadFromStatus.FinalConfirm)) + { + if (workLoadList.Count != needCount) + { + for (int i = 0; i < month; i++) + { + var yearMonthStr = workLoadSearch.SearchBeginDateTime?.AddMonths(i).ToString("yyyy-MM"); + if (!workLoadList.Any(t => t.YearMonthStr == yearMonthStr && + t.DataFrom == (int)WorkLoadFromStatus.FinalConfirm)) + { + workLoadList.Add(new WorkLoadDetailDTO() + { + Id = Guid.Empty, + DataFrom = (int)WorkLoadFromStatus.FinalConfirm, + YearMonthStr = yearMonthStr, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + Code = doctor.ReviewerCode + }); + } + } + } + } + if (workLoadSearch.WorkLoadFromStatus.Contains((int)WorkLoadFromStatus.CRO)) + { + if (workLoadList.Count != needCount) + { + for (int i = 0; i < month; i++) + { + var yearMonthStr = workLoadSearch.SearchBeginDateTime?.AddMonths(i).ToString("yyyy-MM"); + if (!workLoadList.Any(t => t.YearMonthStr == yearMonthStr && + t.DataFrom == (int)WorkLoadFromStatus.CRO)) + { + workLoadList.Add(new WorkLoadDetailDTO() + { + Id = Guid.Empty, + DataFrom = (int)WorkLoadFromStatus.CRO, + YearMonthStr = yearMonthStr, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + Code = doctor.ReviewerCode + }); + } + } + } + } + if (workLoadSearch.WorkLoadFromStatus.Contains((int)WorkLoadFromStatus.Doctor)) + { + if (workLoadList.Count != needCount) + { + for (int i = 0; i < month; i++) + { + var yearMonthStr = workLoadSearch.SearchBeginDateTime?.AddMonths(i).ToString("yyyy-MM"); + if (!workLoadList.Any(t => t.YearMonthStr == yearMonthStr && + t.DataFrom == (int)WorkLoadFromStatus.Doctor)) + { + workLoadList.Add(new WorkLoadDetailDTO() + { + Id = Guid.Empty, + DataFrom = (int)WorkLoadFromStatus.Doctor, + YearMonthStr = yearMonthStr, + ChineseName = doctor.ChineseName, + FirstName = doctor.FirstName, + LastName = doctor.LastName, + Code = doctor.ReviewerCode + }); + } + } + } + + } + + workLoadList = workLoadList.OrderByDescending(t => t.YearMonthStr).ThenBy(t => t.DataFrom) + .ToList(); + int tempRowId = 0; + foreach (var workLoad in workLoadList) + { + workLoad.RowGuid = Guid.NewGuid(); + workLoad.RowIntId = tempRowId + 1; + tempRowId++; + } + + return new PageOutput(1, workLoadList.Count, + workLoadList.Count, workLoadList); + + + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/WorkLoad/EnrollService.cs b/IRaCIS.Core.Application/Service/WorkLoad/EnrollService.cs new file mode 100644 index 0000000..3270491 --- /dev/null +++ b/IRaCIS.Core.Application/Service/WorkLoad/EnrollService.cs @@ -0,0 +1,512 @@ +using IRaCIS.Application.Interfaces; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Application.Filter; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Service.WorkLoad.DTO; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Auth; +using System.Text.RegularExpressions; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Enroll")] + public class EnrollService : BaseService, IEnrollService + { + private readonly IRepository _trialRepository; + private readonly IRepository _trialDetailRepository; + + private readonly IRepository _TrialPaymentPriceRepository; + private readonly IRepository _enrollRepository; + private readonly IRepository _doctorRepository; + private readonly IRepository _enrollDetailRepository; + private readonly IRepository _workloadRepository; + private readonly IMailVerificationService _mailVerificationService; + + //private readonly IRepository _trialUserRepository; + + + public EnrollService(IRepository clinicalTrialProjectRepository, + IRepository clinicalProjectDetailRepository, + IRepository intoGroupRepository, + IRepository doctorRepository, + IRepository TrialPaymentPriceRepository, + IRepository intoGroupDetailRepository, + IRepository workloadRepository, + IMailVerificationService mailVerificationService) + { + _trialRepository = clinicalTrialProjectRepository; + _TrialPaymentPriceRepository = TrialPaymentPriceRepository; + _doctorRepository = doctorRepository; + + _trialDetailRepository = clinicalProjectDetailRepository; + _enrollRepository = intoGroupRepository; + _enrollDetailRepository = intoGroupDetailRepository; + _workloadRepository = workloadRepository; + + _mailVerificationService = mailVerificationService; + //_trialUserRepository = trialUserRepository; + + } + + + + /// + /// 添加或更新项目医生项目价格 + /// + [HttpPost] + public async Task AddOrUpdateEnroll(EnrollCommand addOrUpdateModel) + { + + var trialDoctoritem = await _enrollRepository.FirstOrDefaultAsync(u => u.Id == addOrUpdateModel.Id); + if (trialDoctoritem == null)//insert + { + await _enrollRepository.InsertFromDTOAsync(addOrUpdateModel); + } + else//update + { + await _enrollRepository.UpdateFromDTOAsync(addOrUpdateModel); + } + var success = await _enrollRepository.SaveChangesAsync(); + return ResponseOutput.Result(success); + } + + + + /// + /// 获取医生项目列表 + /// + /// + /// + [HttpPost] + public async Task> GetTrialDoctorList(EnrollGetQuery challengeQuery) + { + var costStatisticsQueryable = from enroll in _enrollRepository.Where(t => t.TrialId == challengeQuery.TrialId) + join dociorc in _doctorRepository.Where() on enroll.DoctorId equals dociorc.Id + join price in _TrialPaymentPriceRepository.Where() on enroll.TrialId equals price.TrialId + select new EnrollViewModel() + { + ChineseName = dociorc.ChineseName, + AdjustmentMultiple = enroll.AdjustmentMultiple, + FirstName = dociorc.FirstName, + LastName = dociorc.LastName, + DoctorId = dociorc.Id, + TrialId = challengeQuery.TrialId, + Id = enroll.Id, + Training = enroll.Training, + RefresherTraining = enroll.RefresherTraining, + Timepoint = enroll.Timepoint, + Timepoint48H = enroll.Timepoint48H, + Timepoint24H = enroll.Timepoint24H, + Adjudication = enroll.Adjudication, + Adjudication48H = enroll.Adjudication48H, + Adjudication24H = enroll.Adjudication24H, + Global = enroll.Global, + Code = dociorc.Code, + ReviewerCode = dociorc.ReviewerCode, + Downtime = enroll.Downtime, + }; + return await costStatisticsQueryable.ToPagedListAsync(challengeQuery.PageIndex, challengeQuery.PageSize, "ChineseName"); + + + + //var query2 = _repository.Where(x => x.TrialId == challengeQuery.TrialId) + // .WhereIf(challengeQuery.TrialId != null, t => t.TrialId == challengeQuery.TrialId) + // .WhereIf(challengeQuery.DoctorId != null, t => t.DoctorId == challengeQuery.DoctorId) + + // .ProjectTo(_mapper.ConfigurationProvider); + + //var pageList = await query2.ToListAsync(); + + + // return new PageOutput(challengeQuery.pageIndex, challengeQuery.pageSize, + //costStatisticsQueryable); + + + } + + + #region Reviewer 入组审核流程(select-submit-approve-confirm) + + /// 为项目筛选医生 提交 【select】 + /// 项目Id + /// 医生Id数组 + /// + + [HttpPost("{trialId:guid}")] + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM_SPM_CPM_SMM_CMM)] + public async Task SelectReviewers(Guid trialId, Guid[] doctorIdArray) + { + var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); + + if (trial == null) return Null404NotFound(trial); + + //_trialRepository.Attach(trial); + + //更新项目状态 + trial.TrialEnrollStatus = (int)TrialEnrollStatus.HasApplyDownLoadResume; + + //_trialRepository.Update(trial); + + //添加项目状态变化记录 + var trialDetail = new TrialStatusDetail() + { + TrialId = trial.Id, + TrialStatus = (int)TrialEnrollStatus.HasApplyDownLoadResume, + }; + await _trialDetailRepository.AddAsync(trialDetail); + + // 入组表 入组状态跟踪表 + foreach (var doctorId in doctorIdArray) + { + await _enrollRepository.AddAsync(new Enroll() + { + DoctorId = doctorId, + TrialId = trialId, + EnrollStatus = EnrollStatus.HasApplyDownloadResume, + + DoctorUserId = _repository.Where(t => t.DoctorId == doctorId).Select(t => t.Id).FirstOrDefault() + }); ; + + await _enrollDetailRepository.AddAsync(new EnrollDetail() + { + DoctorId = doctorId, + TrialId = trialId, + EnrollStatus = EnrollStatus.HasApplyDownloadResume, + OptUserType = (int)SystemUserType.AdminUser, + }); + } + + return ResponseOutput.Result(await _enrollRepository.SaveChangesAsync()); + + } + + /// + /// 入组流程-向CRO提交医生[Submit] + /// + + [HttpPost("{trialId:guid}/{commitState:int}")] + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM_SPM_CPM_SMM_CMM)] + public async Task SubmitReviewer(Guid trialId, Guid[] doctorIdArray, int commitState) + { + + var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); + + + if (trial != null) + { + if (commitState == 1) //确认提交CRO + { + //更新项目状态 + trial.TrialEnrollStatus = (int)TrialEnrollStatus.HasCommitCRO; + + //添加项目详细记录 + var trialDetail = new TrialStatusDetail() + { + TrialId = trial.Id, + TrialStatus = (int)TrialEnrollStatus.HasCommitCRO + }; + await _trialDetailRepository.AddAsync(trialDetail); + + //更新入组表 跟踪了 所以不用下面的_enrollRepository.Update(intoGroupItem); + var intoGroupList = await _enrollRepository.Where(t => t.TrialId == trialId, true).ToListAsync(); + + foreach (var intoGroupItem in intoGroupList) + { + if (doctorIdArray.Contains(intoGroupItem.DoctorId)) + { + intoGroupItem.EnrollStatus = EnrollStatus.HasCommittedToCRO; + //_enrollRepository.Update(intoGroupItem); + + await _enrollDetailRepository.AddAsync(new EnrollDetail() + { + TrialDetailId = trialDetail.Id, + DoctorId = intoGroupItem.DoctorId, + TrialId = trialId, + EnrollStatus = EnrollStatus.HasCommittedToCRO, + OptUserType = (int)SystemUserType.AdminUser, //后台用户 + }); + } + } + + + } + else if (commitState == 0)//回退上一步 + { + //更新入组表 + var intoGroupList = await _enrollRepository.Where(t => t.TrialId == trialId, true).ToListAsync(); + + foreach (var intoGroupItem in intoGroupList) + { + if (doctorIdArray.Contains(intoGroupItem.DoctorId)) + { + intoGroupItem.EnrollStatus = EnrollStatus.HasApplyDownloadResume; + + //_enrollRepository.Update(intoGroupItem); + + + var deleteItem = await _enrollDetailRepository.FirstOrDefaultAsync(t => + t.TrialId == trialId && t.DoctorId == intoGroupItem.DoctorId && + t.EnrollStatus == EnrollStatus.HasCommittedToCRO); + + await _enrollDetailRepository.DeleteAsync(deleteItem); + } + } + + + } + return ResponseOutput.Result(await _enrollRepository.SaveChangesAsync()); + } + return ResponseOutput.NotOk($"Cannot find trial {trialId}"); + } + + /// + /// 入组流程-CRO确定医生名单 [ Approve] + /// + + [HttpPost("{trialId:guid}/{auditState:int}")] + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM_SPM_CPM_SMM_CMM)] + public async Task ApproveReviewer(Guid trialId, Guid[] doctorIdArray, int auditState) + { + + var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); + + if (trial == null) return Null404NotFound(trial); + + if (auditState == 1) //确认入组 + { + //var existItem = _trialRepository.FindSingleOrDefault(u => u.Id == trialId && u.TrialStatus >= (int)TrialEnrollStatus.HasConfirmedDoctorNames); + //if (existItem == null) + //{ + // trial.TrialStatus = (int)TrialEnrollStatus.HasConfirmedDoctorNames; + // trial.TrialStatusStr = "Approved"; + //} + //_trialRepository.Update(trial); + + //更新项目状态 + trial.TrialEnrollStatus = (int)TrialEnrollStatus.HasConfirmedDoctorNames; + + //添加项目详细记录 + var trialDetail = new TrialStatusDetail() + { + TrialId = trialId, + TrialStatus = (int)TrialEnrollStatus.HasConfirmedDoctorNames + }; + await _trialDetailRepository.AddAsync(trialDetail); + + //更新入组表 跟踪方式,不用下面的_enrollRepository.Update(intoGroupItem); + var intoGroupList = _enrollRepository.Where(t => t.TrialId == trialId, true).ToList(); + + + foreach (var intoGroupItem in intoGroupList) + { + if (doctorIdArray.Contains(intoGroupItem.DoctorId)) + { + intoGroupItem.EnrollStatus = EnrollStatus.InviteIntoGroup; + //_enrollRepository.Update(intoGroupItem); + + await _enrollDetailRepository.AddAsync(new EnrollDetail() + { + DoctorId = intoGroupItem.DoctorId, + TrialId = trialId, + EnrollStatus = EnrollStatus.InviteIntoGroup, + OptUserType = (int)SystemUserType.AdminUser, //后台用户 + }); + } + } + + } + else if (auditState == 0)//回退上一步 + { + //更新入组表 + var intoGroupList = _enrollRepository.Where(t => t.TrialId == trialId, true).ToList(); + + foreach (var intoGroupItem in intoGroupList) + { + if (doctorIdArray.Contains(intoGroupItem.DoctorId)) + { + intoGroupItem.EnrollStatus = EnrollStatus.HasCommittedToCRO; + + //_enrollRepository.Update(intoGroupItem); + + var deleteItem = await _enrollDetailRepository.FirstOrDefaultAsync(t => + t.TrialId == trialId && t.DoctorId == intoGroupItem.DoctorId && + t.EnrollStatus == EnrollStatus.InviteIntoGroup); + + await _enrollDetailRepository.DeleteAsync(deleteItem); + } + } + + + } + return ResponseOutput.Result(await _enrollRepository.SaveChangesAsync()); + + + + } + + /// + /// 入组流程-后台确认医生入组[Confirm] + /// + + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM_SPM_CPM_SMM_CMM)] + [UnitOfWork] + public async Task ConfirmReviewer(ConfirmReviewerCommand confirmReviewerCommand, + [FromServices] IRepository _trialUserRepository, + [FromServices] IRepository _taskAllocationRuleRepository) + { + //var trial = _trialRepository.FirstOrDefault(t => t.Id == trialId); + //var existItem = _trialRepository.FindSingleOrDefault(u => u.Id == trialId && u.TrialStatus >= (int)TrialEnrollStatus.HasConfirmedDoctorNames); + + //trial.TrialStatusStr = "Reading"; + ////trial.TrialStatus = (int)TrialStatus.HasConfirmedDoctorNames; + //_trialRepository.Update(trial); + + var trialId = confirmReviewerCommand.TrialId; + + var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); + + if (trial == null) return Null404NotFound(trial); + + + //更新入组表 + var intoGroupList = await _enrollRepository.Where(t => t.TrialId == trialId,true).ToListAsync(); + + //验证邮件 + var emaiList = await _doctorRepository.Where(t => intoGroupList.Select(t => t.DoctorId).Contains(t.Id)) + .Select(t => new { t.EMail, t.FirstName, t.LastName }).ToListAsync(); + + + var errorList = emaiList.Where(t => !Regex.IsMatch(t.EMail, @"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$")) + .ToList(); + + if (errorList.Count() > 0) + { + return ResponseOutput.NotOk(string.Join(',', errorList.Select(c => c.LastName+" / "+c.FirstName)) +"邮箱格式存在问题") ; + } + + if (confirmReviewerCommand.ConfirmState == 1) //确认入组 + { + foreach (var intoGroupItem in intoGroupList) + { + if (confirmReviewerCommand.DoctorIdArray.Contains(intoGroupItem.DoctorId)) + { + + //当邮件发送没有问题的时候,才修改状态 如果有问题,就当前不做处理 + try + { + var userId = await _mailVerificationService.DoctorJoinTrialEmail(trialId, intoGroupItem.DoctorId, confirmReviewerCommand.BaseUrl, confirmReviewerCommand.RouteUrl); + + if (!await _trialUserRepository.AnyAsync(t => t.TrialId == trialId && t.UserId == userId, true)) + { + await _trialUserRepository.AddAsync(new TrialUser() { TrialId = trialId, UserId = userId, JoinTime = DateTime.Now }); + } + + await _enrollRepository.BatchUpdateNoTrackingAsync(t => t.Id == intoGroupItem.Id, u => new Enroll() { DoctorUserId = userId }); + + if (!await _taskAllocationRuleRepository.AnyAsync(t => t.TrialId == trialId && t.DoctorUserId == userId && t.EnrollId == intoGroupItem.Id, true)) + { + await _taskAllocationRuleRepository.AddAsync(new TaskAllocationRule() { TrialId = trialId, DoctorUserId = userId, EnrollId = intoGroupItem.Id }); + + } + + await _enrollDetailRepository.AddAsync(new EnrollDetail() + { + DoctorId = intoGroupItem.DoctorId, + TrialId = trialId, + EnrollStatus = EnrollStatus.ConfirmIntoGroup, + OptUserType = (int)SystemUserType.AdminUser, //后台用户 + }); + + intoGroupItem.EnrollStatus = EnrollStatus.ConfirmIntoGroup; + intoGroupItem.EnrollTime = DateTime.Now; + } + catch (Exception ) + { + intoGroupItem.EnrollStatus = EnrollStatus.ConfirmIntoGroupFailed; + + } + + } + } + + } + else if (confirmReviewerCommand.ConfirmState == 0)//回退上一步 + { + foreach (var intoGroupItem in intoGroupList) + { + if (confirmReviewerCommand.DoctorIdArray.Contains(intoGroupItem.DoctorId)) + { + intoGroupItem.EnrollStatus = EnrollStatus.InviteIntoGroup; + intoGroupItem.EnrollTime = null; + + + var deleteItem = await _enrollDetailRepository.FirstOrDefaultAsync(t => + t.TrialId == trialId && t.DoctorId == intoGroupItem.DoctorId && + t.EnrollStatus == EnrollStatus.ConfirmIntoGroup); + + await _enrollDetailRepository.DeleteAsync(deleteItem); + } + } + + + } + return ResponseOutput.Result(await _enrollRepository.SaveChangesAsync()); + + } + + + + /// + /// optType 0表示入组,列表没这条数据了, 1表示出组,需要填写出组时间 废弃 + /// + /// + /// + /// + /// + /// + [HttpPost("{trialId:guid}/{doctorId:guid}/{optType:int}")] + [TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })] + //[Authorize(Policy = IRaCISPolicy.PM_APM_SPM_CPM)] + [Obsolete] + public async Task EnrollBackOrOut(Guid trialId, Guid doctorId, int optType, DateTime? outEnrollTime) + { + var intoGroupItem = await _enrollRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.DoctorId == doctorId); + if (optType == 0) + { + var sum = _workloadRepository.Where(t => + t.TrialId == trialId && t.DoctorId == doctorId && + t.DataFrom == (int)WorkLoadFromStatus.FinalConfirm) + .Sum(u => u.Adjudication + u.AdjudicationIn24H + u.AdjudicationIn48H + u.Timepoint + + u.TimepointIn24H + u.TimepointIn48H + u.RefresherTraining + u.Training + u.Global + + u.Downtime); + if (sum != 0) + { + return ResponseOutput.NotOk("Reviewers with workload cannot go back"); + } + + intoGroupItem.EnrollStatus = EnrollStatus.InviteIntoGroup; + intoGroupItem.EnrollTime = null; + + var deleteItem = await _enrollDetailRepository.FirstOrDefaultAsync(t => + t.TrialId == trialId && t.DoctorId == intoGroupItem.DoctorId && + t.EnrollStatus == EnrollStatus.ConfirmIntoGroup); + + await _enrollDetailRepository.DeleteAsync(deleteItem); + + } + else if (optType == 1) + { + intoGroupItem.OutEnrollTime = outEnrollTime; + } + return ResponseOutput.Result(await _enrollRepository.SaveChangesAsync()); + } + #endregion + + } +} diff --git a/IRaCIS.Core.Application/Service/WorkLoad/Interface/IDoctorWorkloadService.cs b/IRaCIS.Core.Application/Service/WorkLoad/Interface/IDoctorWorkloadService.cs new file mode 100644 index 0000000..b3972e4 --- /dev/null +++ b/IRaCIS.Core.Application/Service/WorkLoad/Interface/IDoctorWorkloadService.cs @@ -0,0 +1,18 @@ +using IRaCIS.Application.Contracts; + +namespace IRaCIS.Application.Services +{ + public interface IDoctorWorkloadService + { + Task AddOrUpdateWorkload(WorkloadCommand workLoadAddOrUpdateModel, Guid userId); + //Task DeleteReviewerAckSOW(Guid trialId, Guid doctorId, Guid attachmentId); + Task DeleteWorkload(Guid workloadId); + PageOutput GetEnrollmentWorkloadStatsDetail(WorkLoadStatsQueryDTO workLoadSearch); + Task> GetReviewerWorkLoadListDetail(WorkLoadDetailQueryDTO workLoadSearch); + Task> GetTrialEnrollmentWorkloadStats(WorkLoadDoctorQueryDTO doctorSearchModel); + Task GetWorkloadDetailById(Guid id); + Task UpdateReviewerReadingType(Guid trialId, Guid doctorId, int type); + //Task UploadReviewerAckSOW(Guid trialId, ReviewerAckDTO attachmentViewModel); + Task> WorkloadExist(WorkloadExistQueryDTO param); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/WorkLoad/Interface/IEnrollService.cs b/IRaCIS.Core.Application/Service/WorkLoad/Interface/IEnrollService.cs new file mode 100644 index 0000000..bc34d31 --- /dev/null +++ b/IRaCIS.Core.Application/Service/WorkLoad/Interface/IEnrollService.cs @@ -0,0 +1,15 @@ +using IRaCIS.Core.Application.Service.WorkLoad.DTO; + +namespace IRaCIS.Application.Services +{ + public interface IEnrollService + { + Task AddOrUpdateEnroll(EnrollCommand addOrUpdateModel); + Task ApproveReviewer(Guid trialId, Guid[] doctorIdArray, int auditState); + //Task ConfirmReviewer(Guid trialId, Guid[] doctorIdArray, int confirmState); + Task EnrollBackOrOut(Guid trialId, Guid doctorId, int optType, DateTime? outEnrollTime); + Task> GetTrialDoctorList(EnrollGetQuery challengeQuery); + Task SelectReviewers(Guid trialId, Guid[] doctorIdArray); + Task SubmitReviewer(Guid trialId, Guid[] doctorIdArray, int commitState); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/WorkLoad/Interface/ITrialEnrollmentService.cs b/IRaCIS.Core.Application/Service/WorkLoad/Interface/ITrialEnrollmentService.cs new file mode 100644 index 0000000..e0e5374 --- /dev/null +++ b/IRaCIS.Core.Application/Service/WorkLoad/Interface/ITrialEnrollmentService.cs @@ -0,0 +1,29 @@ +using System; +using IRaCIS.Core.Application.Service.WorkLoad.DTO; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Interfaces +{ + public interface ITrialEnrollmentService + { + IResponseOutput AddOrUpdateEnroll(EnrollCommand addOrUpdateModel); + + Task< PageOutput> GetTrialDoctorList(EnrollGetQuery challengeQuery); + + /// 入组流程-筛选医生 [select] + IResponseOutput SelectReviewers( Guid trialId, Guid[] doctorIdArray); + + /// 入组流程-向CRO提交医生[Submit] + IResponseOutput SubmitReviewer( Guid trialId, Guid[] doctorIdArray, int commitState); + + /// 入组流程-CRO确定医生名单 [ Approve] + IResponseOutput ApproveReviewer( Guid trialId, Guid[] doctorIdArray, int auditState); + + /// 入组流程-向CRO提交医生[Submit] + IResponseOutput ConfirmReviewer(Guid trialId, Guid[] doctorIdArray, int confirmState); + + + IResponseOutput EnrollBackOrOut( Guid trialId, Guid doctorId, int optType,DateTime? outEnrollTime); + + } +} diff --git a/IRaCIS.Core.Application/Service/WorkLoad/Interface/IWorkloadDistributionService.cs b/IRaCIS.Core.Application/Service/WorkLoad/Interface/IWorkloadDistributionService.cs new file mode 100644 index 0000000..7043447 --- /dev/null +++ b/IRaCIS.Core.Application/Service/WorkLoad/Interface/IWorkloadDistributionService.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Core.Application.Contracts +{ + public interface IWorkloadDistributionService + { + PageOutput GetWorkloadTPList(WorkloadDistributionQueryParam param); + IResponseOutput DistributeTP(WorkloadTPCommand workloadTPCommand); + IResponseOutput UpdateDistributeTP(Guid tpId, Guid ReviewerId, Guid studyId); + + PageOutput GetWorkloadGlobalList(WorkloadDistributionQueryParam param); + IResponseOutput DistributeGlobal(WorkloadGlobalCommand workloadGlobalCommand); + IResponseOutput UpdateDistributeGlobal(Guid tpId, Guid ReviewerId,Guid subjectId, decimal visitNum); + + PageOutput GetWorkloadADList(WorkloadDistributionQueryParam param); + IResponseOutput DistributeAD(WorkloadAdCommand workloadTPCommand); + IResponseOutput UpdateDistributeAD(Guid tpId, Guid ReviewerId); + + IResponseOutput> GetWorkloadDetail(Guid WorkloadId); + + IResponseOutput UpdateGlobalStatus(Guid globalId); + + } +} diff --git a/IRaCIS.Core.Application/Service/WorkLoad/WorkloadDistributionService.cs b/IRaCIS.Core.Application/Service/WorkLoad/WorkloadDistributionService.cs new file mode 100644 index 0000000..ddc0735 --- /dev/null +++ b/IRaCIS.Core.Application/Service/WorkLoad/WorkloadDistributionService.cs @@ -0,0 +1,573 @@ +//using IRaCIS.Application.Contracts; +//using IRaCIS.Core.Application.Contracts; +//using System.Linq.Expressions; +//using IRaCIS.Core.Infrastructure.ExpressionExtend; +//using IRaCIS.Core.Infra.EFCore; +//using IRaCIS.Core.Domain.Models; +//using IRaCIS.Core.Domain.Share; +//using IRaCIS.Core.Infrastructure.Extention; +//using Microsoft.AspNetCore.Mvc; + +//namespace IRaCIS.Core.Application.Services +//{ +// /// +// /// TP、Global、AD 工作量分配查看 +// /// +// [ ApiExplorerSettings(GroupName = "Trial")] +//#pragma warning disable +// public class WorkloadDistributionService : BaseService, IWorkloadDistributionService +// { +// private readonly IWorkloadTPRepository _workloadTpRepository; +// private readonly IWorkloadGlobalRepository _workloadGlobalRepository; +// private readonly IWorkloadADRepository _workloadAdRepository; +// private readonly IRepository _subjectVisitRepository; +// private readonly IRepository _studyRepository; +// private readonly IRepository _doctorRepository; +// private readonly IRepository _subjectRepository; +// private readonly IRepository _siteRepository; +// private readonly IWorkloadDetailRepository _workloadDetailRepository; + + +// public WorkloadDistributionService(IWorkloadTPRepository workloadTpRepository, +// IWorkloadGlobalRepository workloadGlobalRepository, +// IWorkloadADRepository workloadAdRepository, +// IRepository subjectVisitRepository, IRepository studyRepository, +// IRepository doctorRepository, IRepository subjectRepository, +// IRepository siteRepository, +// IWorkloadDetailRepository workloadDetailRepository, +// IUserInfo userInfo) +// { +// _workloadTpRepository = workloadTpRepository; +// _workloadGlobalRepository = workloadGlobalRepository; +// _workloadAdRepository = workloadAdRepository; +// _subjectVisitRepository = subjectVisitRepository; +// _studyRepository = studyRepository; +// _doctorRepository = doctorRepository; +// _subjectRepository = subjectRepository; +// _siteRepository = siteRepository; +// _workloadDetailRepository = workloadDetailRepository; + +// } + +// /// +// /// 批量分配Tp +// /// +// /// +// /// +// [HttpPost] +// public IResponseOutput DistributeTP(WorkloadTPCommand workloadTPCommand) +// { +// //当前采用的是没有提示到具体的TP,如有需要在修改下 +// var studyIdList = workloadTPCommand.TpList.Select(u => u.StudyId); +// var temp = _workloadTpRepository.Where(u => studyIdList.Contains(u.StudyId) && u.ReviewerId == workloadTPCommand.ReviewerId); +// if (temp.Any() ) +// { +// return ResponseOutput.NotOk("The TPs of different Arms of the same subject couldn't be assigned to the same reviewer."); +// } +// var success = false; +// workloadTPCommand.TpList.ForEach(t => +// { +// _workloadDetailRepository.Add(new WorkloadDetail +// { +// WorkloadId = t.Id, +// OptUserName = _userInfo.RealName, +// OptTime = DateTime.Now, +// Status = (int)WorkloadStatus.Distributed, +// ReviewerId = workloadTPCommand.ReviewerId +// }); +// _workloadDetailRepository.SaveChanges(); +// success = _workloadTpRepository.Update(u => u.Id == t.Id, k => new WorkloadTP() +// { +// ReviewerId = workloadTPCommand.ReviewerId, +// Status = (int)WorkloadStatus.Distributed +// }); +// }); +// return ResponseOutput.Result(success); +// } + + +// /// +// /// 批量分配AD +// /// +// /// +// /// +// [HttpPost] +// public IResponseOutput DistributeAD(WorkloadAdCommand workloadTPCommand) +// { +// var success = false; +// workloadTPCommand.IdList.ForEach(t => +// { +// _workloadDetailRepository.Add(new WorkloadDetail +// { +// WorkloadId = t, +// OptUserName = _userInfo.RealName, +// OptTime = DateTime.Now, +// Status = (int)WorkloadStatus.Distributed, +// ReviewerId = workloadTPCommand.ReviewerId +// }); +// _workloadDetailRepository.SaveChanges(); +// success = _workloadAdRepository.Update(u => u.Id == t, k => new WorkloadAD() +// { +// ReviewerId = workloadTPCommand.ReviewerId, +// Status = (int)WorkloadStatus.Distributed +// }); +// }); +// return ResponseOutput.Result(success); +// } + +// /// +// /// 批量分配Global +// /// +// /// +// /// +// [HttpPost] +// public IResponseOutput DistributeGlobal(WorkloadGlobalCommand workloadGCommand) +// { +// var temp = _workloadGlobalRepository.Where(u => workloadGCommand.GlobalList.Select(s => s.SubjectId).Contains(u.SubjectId) +// && workloadGCommand.GlobalList.Select(s => s.VisitNum).Contains(u.VisitNum) && +// u.ReviewerId == workloadGCommand.ReviewerId); +// if (temp.Any()) +// { +// return ResponseOutput.NotOk("The Globals of different Arms of the same subject couldn't be assigned to the same reviewer."); +// } + +// var success = false; +// workloadGCommand.GlobalList.ForEach(t => +// { +// _workloadDetailRepository.Add(new WorkloadDetail +// { +// WorkloadId = t.Id, +// OptUserName = _userInfo.RealName, +// OptTime = DateTime.Now, +// Status = (int)WorkloadStatus.Distributed, +// ReviewerId = workloadGCommand.ReviewerId +// }); +// _workloadDetailRepository.SaveChanges(); +// success = _workloadGlobalRepository.Update(u => u.Id == t.Id, k => new WorkloadGlobal() +// { +// ReviewerId = workloadGCommand.ReviewerId, +// Status = (int)WorkloadStatus.Distributed +// }); +// }); +// return ResponseOutput.Result(success); +// } + +// [HttpPost] +// public PageOutput GetWorkloadGlobalList(WorkloadDistributionQueryParam param) +// { +// IQueryable query = null; +// Expression> workloadTPLambda = x => x.TrialId == param.TrialId; +// if (param.SiteId != null) +// { +// workloadTPLambda = workloadTPLambda.And(t => t.SiteId == param.SiteId); +// } + +// if (param.Status != null) +// { +// workloadTPLambda = workloadTPLambda.And(t => t.Status == param.Status); +// } +// if (!string.IsNullOrEmpty(param.WorkloadCode)) +// { +// var globalCode = param.WorkloadCode.Trim(); +// workloadTPLambda = workloadTPLambda.And(t => t.GlobalCode.Contains(globalCode)); +// } +// if (param.GroupId != null && param.GroupId > 0) +// { +// var groupCode = "G0" + param.GroupId; +// workloadTPLambda = workloadTPLambda.And(t => t.GlobalCode.Contains(groupCode)); +// } + +// Expression> subjectLambda = x => x.TrialId == param.TrialId; +// if (!string.IsNullOrEmpty(param.SubjectCode)) +// { +// var subjectCode = param.SubjectCode.Trim(); +// subjectLambda = subjectLambda.And(t => t.Code.Contains(subjectCode)); +// } +// Expression> doctorLambda = x => true; +// if (!string.IsNullOrEmpty(param.Reviewer)) +// { +// var reviewer = param.Reviewer.Trim(); + +// doctorLambda = doctorLambda.And(t => t.ChineseName.Contains(reviewer) +// || t.FirstName.Contains(reviewer) || t.LastName.Contains(reviewer)); +// query = from workloadG in _workloadGlobalRepository.Where(workloadTPLambda) +// join subject in _subjectRepository.Where(subjectLambda) on workloadG.SubjectId equals subject.Id +// join site in _siteRepository.AsQueryable() on workloadG.SiteId equals site.Id +// join doctor in _doctorRepository.Where(doctorLambda) on workloadG.ReviewerId equals doctor.Id +// select new WorkloadGlobalDTO() +// { +// Id = workloadG.Id, +// ReviewerCode = doctor.ReviewerCode, +// ReviewerChineseName = doctor.ChineseName, +// ReviewerFirstName = doctor.FirstName, +// ReviewerId = doctor.Id, +// ReviewerLastName = doctor.LastName, +// SiteId = workloadG.SiteId, +// SiteName = site.SiteName, +// Status = workloadG.Status, +// GlobalCode = workloadG.GlobalCode, +// SubjectCode = subject.Code, +// VisitId = workloadG.VisitId, +// UpdateTime = workloadG.UpdateTime, +// SubjectId = workloadG.SubjectId, +// VisitNum = workloadG.VisitNum, +// VisitName = workloadG.VisitName +// }; +// } +// else +// { +// query = from workloadG in _workloadGlobalRepository.Where(workloadTPLambda) +// join subject in _subjectRepository.Where(subjectLambda) on workloadG.SubjectId equals subject.Id +// join site in _siteRepository.AsQueryable() on workloadG.SiteId equals site.Id +// join doctor in _doctorRepository.Where(doctorLambda) on workloadG.ReviewerId equals doctor.Id into cc +// from doctor in cc.DefaultIfEmpty() +// select new WorkloadGlobalDTO() +// { +// Id = workloadG.Id, +// ReviewerCode = doctor.ReviewerCode, +// ReviewerChineseName = doctor.ChineseName, +// ReviewerFirstName = doctor.FirstName, +// ReviewerId = doctor.Id, +// ReviewerLastName = doctor.LastName, +// SiteId = workloadG.SiteId, +// SiteName = site.SiteName, +// Status = workloadG.Status, +// GlobalCode = workloadG.GlobalCode, +// SubjectCode = subject.Code, +// VisitId = workloadG.VisitId, +// UpdateTime = workloadG.UpdateTime, +// SubjectId = workloadG.SubjectId, +// VisitNum = workloadG.VisitNum, +// VisitName = workloadG.VisitName +// }; +// } + +// var count = query.Count(); + +// var propName = param.SortField == string.Empty ? "GlobalCode" : param.SortField; + +// query = param.Asc +// ? query.OrderBy(propName).ThenBy(t => t.SiteName).ThenBy(t => t.SubjectCode) +// : query.OrderByDescending(propName).ThenBy(t => t.SiteName).ThenBy(t => t.SubjectCode); + +// query = query.Skip((param.PageIndex - 1) * param.PageSize).Take(param.PageSize); +// var list = query.ToList(); + +// return new PageOutput(param.PageIndex, +// param.PageSize, count, list); +// } + +// [HttpPost] +// public PageOutput GetWorkloadADList(WorkloadDistributionQueryParam param) +// { +// IQueryable query = null; +// Expression> workloadAdLambda = x => x.TrialId == param.TrialId; +// if (param.SiteId != null) +// { +// workloadAdLambda = workloadAdLambda.And(t => t.SiteId == param.SiteId); +// } + +// if (param.Status != null) +// { +// workloadAdLambda = workloadAdLambda.And(t => t.Status == param.Status); +// } +// if (!string.IsNullOrEmpty(param.WorkloadCode)) +// { +// var adCode = param.WorkloadCode.Trim(); +// workloadAdLambda = workloadAdLambda.And(t => t.ADCode.Contains(adCode)); +// } + +// Expression> subjectLambda = x => x.TrialId == param.TrialId; +// if (!string.IsNullOrEmpty(param.SubjectCode)) +// { +// var subjectCode = param.SubjectCode.Trim(); +// subjectLambda = subjectLambda.And(t => t.Code.Contains(subjectCode)); + +// } +// Expression> doctorLambda = x => true; +// if (!string.IsNullOrEmpty(param.Reviewer)) +// { +// var reviewer = param.Reviewer.Trim(); +// doctorLambda = doctorLambda.And(t => t.ChineseName.Contains(reviewer) +// || t.FirstName.Contains(reviewer) || t.LastName.Contains(reviewer)); +// query = from workloadAd in _workloadAdRepository.Where(workloadAdLambda) +// join subject in _subjectRepository.Where(subjectLambda) on workloadAd.SubjectId equals subject.Id +// join site in _siteRepository.AsQueryable() on workloadAd.SiteId equals site.Id +// join doctor in _doctorRepository.Where(doctorLambda) on workloadAd.ReviewerId equals doctor.Id +// select new WorkloadADDTO() +// { +// Id = workloadAd.Id, +// ReviewerCode = doctor.ReviewerCode, +// ReviewerChineseName = doctor.ChineseName, +// ReviewerFirstName = doctor.FirstName, +// ReviewerId = doctor.Id, +// ReviewerLastName = doctor.LastName, +// SiteId = workloadAd.SiteId, +// SiteName = site.SiteName, +// Status = workloadAd.Status, +// ADCode = workloadAd.ADCode, +// SubjectCode = subject.Code, +// SubjectId = workloadAd.SubjectId, +// UpdateTime = workloadAd.UpdateTime +// }; +// } +// else +// { +// query = from workloadAd in _workloadAdRepository.Where(workloadAdLambda) +// join subject in _subjectRepository.Where(subjectLambda) on workloadAd.SubjectId equals subject.Id + +// join site in _siteRepository.AsQueryable() on workloadAd.SiteId equals site.Id +// join doctor in _doctorRepository.Where(doctorLambda) on workloadAd.ReviewerId equals doctor.Id into cc +// from doctor in cc.DefaultIfEmpty() +// select new WorkloadADDTO() +// { +// Id = workloadAd.Id, +// ReviewerCode = doctor.ReviewerCode, +// ReviewerChineseName = doctor.ChineseName, +// ReviewerFirstName = doctor.FirstName, +// ReviewerId = doctor.Id, +// ReviewerLastName = doctor.LastName, +// SiteId = workloadAd.SiteId, +// SiteName = site.SiteName, +// Status = workloadAd.Status, +// ADCode = workloadAd.ADCode, +// SubjectCode = subject.Code, +// SubjectId = workloadAd.SubjectId, +// UpdateTime = workloadAd.UpdateTime +// }; + +// } +// var count = query.Count(); + +// var propName = param.SortField == string.Empty ? "ADCode" : param.SortField; + +// query = param.Asc +// ? query.OrderBy(propName).ThenBy(t => t.SiteName).ThenBy(t => t.SubjectCode) +// : query.OrderByDescending(propName).ThenBy(t => t.SiteName).ThenBy(t => t.SubjectCode); + +// query = query.Skip((param.PageIndex - 1) * param.PageSize).Take(param.PageSize); +// var list = query.ToList(); + +// return new PageOutput(param.PageIndex, +// param.PageSize, count, list); +// } + +// [HttpPost] +// public PageOutput GetWorkloadTPList(WorkloadDistributionQueryParam param) +// { +// IQueryable query = null; +// Expression> workloadTPLambda = x => x.TrialId == param.TrialId; +// if (param.SiteId != null) +// { +// workloadTPLambda = workloadTPLambda.And(t => t.SiteId == param.SiteId); +// } + +// if (param.Status != null) +// { +// workloadTPLambda = workloadTPLambda.And(t => t.Status == param.Status); +// } +// if (!string.IsNullOrEmpty(param.WorkloadCode)) +// { +// var timepoint = param.WorkloadCode.Trim(); + +// workloadTPLambda = workloadTPLambda.And(t => t.TimepointCode.Contains(timepoint)); +// } +// if (param.GroupId != null && param.GroupId > 0) +// { +// var groupCode = "T0" + param.GroupId; +// workloadTPLambda = workloadTPLambda.And(t => t.TimepointCode.Contains(groupCode)); +// } + +// Expression> subjectLambda = x => x.TrialId == param.TrialId; +// if (!string.IsNullOrEmpty(param.SubjectCode)) +// { +// var subjectCode = param.SubjectCode.Trim(); +// subjectLambda = subjectLambda.And(t => t.Code.Contains(subjectCode)); +// } +// Expression> doctorLambda = x => true; +// if (!string.IsNullOrEmpty(param.Reviewer)) +// { +// var reviewer = param.Reviewer.Trim(); +// doctorLambda = doctorLambda.And(t => t.ChineseName.Contains(reviewer) +// || t.FirstName.Contains(reviewer) || t.LastName.Contains(reviewer)); +// query = from workloadTp in _workloadTpRepository.Where(workloadTPLambda) +// join subject in _subjectRepository.Where(subjectLambda) on workloadTp.SubjectId equals subject.Id +// join subjectVisit in _subjectVisitRepository.AsQueryable() on workloadTp.SubjectVisitId equals subjectVisit.Id +// join study in _studyRepository.AsQueryable() on workloadTp.StudyId equals study.Id +// join site in _siteRepository.AsQueryable() on workloadTp.SiteId equals site.Id +// join doctor in _doctorRepository.Where(doctorLambda) on workloadTp.ReviewerId equals doctor.Id + +// select new WorkloadTPDTO() +// { +// Id = workloadTp.Id, +// ReviewerCode = doctor.ReviewerCode, +// ReviewerChineseName = doctor.ChineseName, +// ReviewerFirstName = doctor.FirstName, +// ReviewerId = doctor.Id, +// ReviewerLastName = doctor.LastName, +// SiteId = workloadTp.SiteId, +// SiteName = site.SiteName, +// Status = workloadTp.Status, +// StudyCode = study.StudyCode, +// StudyId = workloadTp.StudyId, +// TimepointCode = workloadTp.TimepointCode, +// SubjectCode = subject.Code, +// SubjectVisitId = workloadTp.SubjectVisitId, +// SubjectId = workloadTp.SubjectId, +// VisitNum = subjectVisit.VisitNum, +// VisitName = subjectVisit.VisitName, +// UpdateTime = workloadTp.UpdateTime +// }; +// } +// else +// { +// query = from workloadTp in _workloadTpRepository.Where(workloadTPLambda) +// join subject in _subjectRepository.Where(subjectLambda) on workloadTp.SubjectId equals subject.Id +// join subjectVisit in _subjectVisitRepository.AsQueryable() on workloadTp.SubjectVisitId equals subjectVisit.Id +// join study in _studyRepository.AsQueryable() on workloadTp.StudyId equals study.Id +// join site in _siteRepository.AsQueryable() on workloadTp.SiteId equals site.Id +// join doctor in _doctorRepository.Where(doctorLambda) on workloadTp.ReviewerId equals doctor.Id into cc +// from doctor in cc.DefaultIfEmpty() +// select new WorkloadTPDTO() +// { +// Id = workloadTp.Id, +// ReviewerCode = doctor.ReviewerCode, +// ReviewerChineseName = doctor.ChineseName, +// ReviewerFirstName = doctor.FirstName, +// ReviewerId = doctor.Id, +// ReviewerLastName = doctor.LastName, +// SiteId = workloadTp.SiteId, +// SiteName = site.SiteName, +// Status = workloadTp.Status, +// StudyCode = study.StudyCode, +// StudyId = workloadTp.StudyId, +// TimepointCode = workloadTp.TimepointCode, +// SubjectCode = subject.Code, +// SubjectVisitId = workloadTp.SubjectVisitId, +// SubjectId = workloadTp.SubjectId, +// VisitNum = subjectVisit.VisitNum, +// VisitName = subjectVisit.VisitName, +// UpdateTime = workloadTp.UpdateTime +// }; + +// } +// var count = query.Count(); + +// var propName = param.SortField == string.Empty ? "TimepointCode" : param.SortField; +// query = param.Asc +// ? query.OrderBy(propName).ThenBy(t => t.SiteName).ThenBy(t => t.SubjectCode) +// : query.OrderByDescending(propName).ThenBy(t => t.SiteName).ThenBy(t => t.SubjectCode); +// query = query.Skip((param.PageIndex - 1) * param.PageSize).Take(param.PageSize); +// var list = query.ToList(); + +// return new PageOutput(param.PageIndex, +// param.PageSize, count, list); +// } + +// //修改单个TP +// [HttpPost("{tpId:guid}/{reviewerId:guid}")] +// public IResponseOutput UpdateDistributeAD(Guid tpId, Guid reviewerId) +// { +// _workloadDetailRepository.Add(new WorkloadDetail +// { +// WorkloadId = tpId, +// OptUserName = _userInfo.RealName, +// OptTime = DateTime.Now, +// Status = (int)WorkloadStatus.Distributed, +// ReviewerId = reviewerId +// }); +// _workloadDetailRepository.SaveChanges(); +// var success= _workloadAdRepository.Update(t => t.Id == tpId, u => new WorkloadAD() +// { +// ReviewerId = reviewerId, +// Status = (int)WorkloadStatus.Distributed +// }); + +// return ResponseOutput.Result(success); +// } + +// //修改单个Global +// [HttpPost("{tpId:guid}/{reviewerId:guid}/{subjectId:guid}/{visitNum}")] +// public IResponseOutput UpdateDistributeGlobal(Guid tpId, Guid reviewerId, Guid subjectId, decimal visitNum) +// { +// var temp = _workloadGlobalRepository.Where(u => u.SubjectId == subjectId && +// u.VisitNum == visitNum && +// u.ReviewerId == reviewerId && u.Id != tpId); +// if (temp.Any()) +// { +// return ResponseOutput.NotOk("The Global of the other arm of this subject has already been assigned to this reviewer, and the assignment couldn't be performed."); +// } + +// _workloadDetailRepository.Add(new WorkloadDetail +// { +// WorkloadId = tpId, +// OptUserName = _userInfo.RealName, +// OptTime = DateTime.Now, +// Status = (int)WorkloadStatus.Distributed, +// ReviewerId = reviewerId +// }); +// _workloadDetailRepository.SaveChanges(); +// return ResponseOutput.Result(_workloadGlobalRepository.Update(t => t.Id == tpId, u => new WorkloadGlobal() +// { +// ReviewerId = reviewerId, +// Status = (int)WorkloadStatus.Distributed +// })); +// } + +// //修改单个TP +// [HttpPost("{tpId:guid}/{reviewerId:guid}/{studyId:guid}")] +// public IResponseOutput UpdateDistributeTP(Guid tpId, Guid reviewerId, Guid studyId) +// { +// var temp = _workloadTpRepository.Where(u => u.StudyId == studyId && u.ReviewerId == reviewerId && u.Id != tpId); +// if (temp.Any()) +// { +// return ResponseOutput.NotOk("The TP of the other arm of this subject has already been assigned to this reviewer, and the assignment couldn't be performed."); +// } + +// _workloadDetailRepository.Add(new WorkloadDetail +// { +// WorkloadId = tpId, +// OptUserName = _userInfo.RealName, +// OptTime = DateTime.Now, +// Status = (int)WorkloadStatus.Distributed, +// ReviewerId = reviewerId +// }); +// _workloadDetailRepository.SaveChanges(); +// return ResponseOutput.Result(_workloadTpRepository.Update(t => t.Id == tpId, u => new WorkloadTP() +// { +// ReviewerId = reviewerId, +// Status = (int)WorkloadStatus.Distributed +// })); +// } + +// [HttpGet("{workloadId:guid}")] +// public IResponseOutput> GetWorkloadDetail(Guid workloadId) +// { +// IQueryable query = null; +// query = from detail in _workloadDetailRepository.Where(u => u.WorkloadId == workloadId) +// join doctor in _doctorRepository.AsQueryable() on detail.ReviewerId equals doctor.Id into cc +// from doctor in cc.DefaultIfEmpty() +// select new WorkloadDetailDTO() +// { +// OptTime = detail.OptTime, +// OptUserName = detail.OptUserName, +// ReviewerChineseName = doctor.ChineseName, +// ReviewerFirstName = doctor.FirstName, +// ReviewerLastName = doctor.LastName, +// Status = detail.Status +// }; +// var list = query.OrderByDescending(u => u.OptTime).ToList(); +// return ResponseOutput.Ok(list); +// } + +// [HttpPost("UpdateGlobalStatus/{globalId:guid}")] +// public IResponseOutput UpdateGlobalStatus(Guid globalId) +// { +// return ResponseOutput.Result(_workloadGlobalRepository.Update(u => u.Id == globalId, t => new WorkloadGlobal() +// { +// Status = 0 +// })); +// } + +// } +//} diff --git a/IRaCIS.Core.Application/Service/WorkLoad/_MapConfig.cs b/IRaCIS.Core.Application/Service/WorkLoad/_MapConfig.cs new file mode 100644 index 0000000..3e9e551 --- /dev/null +++ b/IRaCIS.Core.Application/Service/WorkLoad/_MapConfig.cs @@ -0,0 +1,21 @@ +using AutoMapper; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Service.WorkLoad.DTO; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Application.Service +{ + public class WorkLoadConfig : Profile + { + public WorkLoadConfig() + { + CreateMap(); + + CreateMap(); + + CreateMap(); + + } + } + +} diff --git a/IRaCIS.Core.Application/TestService.cs b/IRaCIS.Core.Application/TestService.cs new file mode 100644 index 0000000..2409d61 --- /dev/null +++ b/IRaCIS.Core.Application/TestService.cs @@ -0,0 +1,320 @@ +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Options; +using System.Linq.Expressions; +using System.Text.RegularExpressions; + + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Institution")] + public class TestService : BaseService + { + private readonly IRepository _dicRepository; + private readonly IRepository _trialRepository; + + //private readonly IDistributedCache _cache; + + private readonly IOptionsMonitor _systemEmailConfig; + + private readonly IOptionsMonitor _basicConfig; + private readonly IRepository _visitTaskRepositoryy; + + + public TestService(IRepository dicRepository, IRepository trialRepository/*, IDistributedCache cache*/ + + , IOptionsMonitor systemEmailConfig, IOptionsMonitor basicConfig, IRepository visitTaskRepository) + { + _visitTaskRepositoryy = visitTaskRepository; + + _systemEmailConfig = systemEmailConfig; + _basicConfig = basicConfig; + + _dicRepository = dicRepository; + _trialRepository = trialRepository; + //_cache = cache; + } + [AllowAnonymous] + public async Task TestTimeOut() + { + await Task.Delay(15000); + return ResponseOutput.Ok(); + } + + + [UnitOfWork] + public async Task Get() + { + + Expression> visitTaskLambda = x => x.TrialId == Guid.Empty && x.SubjectId == Guid.Empty && x.TrialReadingCriterionId == Guid.Empty; + + var visitTaskIdQueryable = _visitTaskRepositoryy.Where(visitTaskLambda).Where(t => t.Subject.SubjectVisitTaskList.AsQueryable().Where(visitTaskLambda).Any(c => c.IsNeedClinicalDataSign == true && c.IsClinicalDataSign == false && c.VisitTaskNum < t.VisitTaskNum)).Select(t => t.Id); + + await _visitTaskRepositoryy.BatchUpdateNoTrackingAsync(t => visitTaskIdQueryable.Contains(t.Id), u => new VisitTask() + { + IsFrontTaskNeedSignButNotSign = true + }); + + + //var a = ((Decimal)1.00).ToString().TrimEnd(new char[] { '.', '0' }); + //var b = ((Decimal)1.01).ToString().TrimEnd(new char[] { '.', '0' }); + //var c = ((Decimal)100).ToString().TrimEnd(new char[] { '.', '0' }); + //var subject1 = Guid.Parse("431D0C58-ABC5-4166-B9BC-08DA0E391693"); + //var subject2 = Guid.Parse("431D0C58-ABC5-4166-B9BC-08DA0E391694"); + + // var subjectList = new List() { Guid.Parse("431D0C58-ABC5-4166-B9BC-08DA0E391693") , + // Guid.Parse("431D0C58-ABC5-4166-B9BC-08DA0E391694") , + // Guid.Parse("431D0C58-ABC5-4166-B9BC-08DA0E391695") + // }; + + //string[] citys = new string[] { "广州", "深圳", "上海", "北京" }; + //foreach (var item in subjectList) + //{ + // Console.WriteLine(await BNRFactory.Default.Create($"[CN:{item}][N:[CN:{item}]/0000000]")); + //} + //foreach (var item in subjectList) + //{ + // Console.WriteLine(await BNRFactory.Default.Create($"[N:[CN:{item}]/0000000]")); + //} + + //foreach (var item in subjectList) + //{ + // Console.WriteLine(await BNRFactory.Default.Create($"[CN:{item}][redis:city/0000000]")); + //} + + //var needAddVisitList = await _repository.Where(t => t.TrialId == Guid.Empty).DistinctBy(t => t.VisitTaskNum).ToListAsync(); + + + //await _repository.BatchUpdateAsync(t => t.Id == Guid.Empty, u => new VisitTask() + //{ + // SuggesteFinishedTime = u.IsUrgent ? DateTime.Now.AddDays(2) : DateTime.Now.AddDays(7), + + // Code = u.Code + 1 + //}); + + //var query = from item1 in _repository.Where() + // join item2 in _repository.Where() on item1.ValueType equals item2.ValueType + // select new + // { + // item1.ValueType, + // dd = item2.ValueType + // }; + + //var list2 = query.ToList(); + + //await Task.CompletedTask; + + //var list = await _repository.Where(t => t.TrialId == Guid.Parse("40400000-3e2c-0016-239b-08da581f0e74")).ToListAsync(); + + ////await _repository.BatchDeleteAsync(t => t.TrialId == Guid.Parse("40400000-3e2c-0016-239b-08da581f0e74")); + + //await _repository.AddRangeAsync(list, true); + + //await _repository.SaveChangesAsync(); + + //await _repository.BatchUpdateAsync(t => t.TrialId == Guid.Parse("40400000-3e2c-0016-239b-08da581f0e74") && t.EntityName== "ClinicalDataTrialSet", t => new DataInspection() { CreateTime= DateTime.Now.AddMonths(-2) } ); + + //await _visitTaskRepositoryy.UpdatePartialFromQueryAsync( Guid.Parse("78360000-3E2C-0016-9B53-08DA6A002040"), c => new VisitTask() { UpdateTime = DateTime.Now }); + + //await _visitTaskRepositoryy.UpdatePartialFromQueryAsync( Guid.Parse("78360000-3E2C-0016-9B53-08DA6A002040"), c => new VisitTask() { UpdateTime = DateTime.Now.AddMinutes(1) }); + + var a = _userInfo.IsTestUser; + + var list1 = await _repository.Where().Select(t => t.TranslateValue(t.Value, t.ValueCN, true)).ToListAsync(); + var list2 = await _repository.Where().Select(t => t.TranslateValue(t.Value, t.ValueCN, false)).ToListAsync(); + + await _repository.SaveChangesAsync(); + return _userInfo.LocalIp; + } + + + + private static Dictionary _replacePatterns = new Dictionary() + { + { "test", "Atlanta Knight" }, + { "GAME_TIME", "7:30pm" }, + { "GAME_NUMBER", "161" }, + { "DATE", "October 18 2018" }, + }; + + private static string ReplaceFunc(string findStr) + { + if (_replacePatterns.ContainsKey(findStr)) + { + return _replacePatterns[findStr]; + } + return findStr; + } + + + [AllowAnonymous] + public async Task testwwwww([FromServices] IWebHostEnvironment env) + { + await Task.CompletedTask; + } + + + [AllowAnonymous] + public async Task GetEnvironmentName([FromServices] IWebHostEnvironment env) + { + + + //update DicomInstance set Path = '/IRaCISData/TrialData/' + cast(DicomInstance.TrialId as varchar) + '/' + DicomInstance.SiteId + '/' + DicomInstance.SubjectId + '/' + DicomInstance.SubjectVisitId + '/Dicom/' + DicomInstance.StudyId + '/' + DicomInstance.Id + '.dcm' + + + //await _repository.BatchUpdateAsync(u => u.Path.Contains(".dcm"), t => new DicomInstance() { Path = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{t.TrialId}/{t.SiteId}/{t.SubjectId}/{t.SubjectVisitId}/{StaticData.Folder.DicomFolder}/{t.StudyId}/{t.Id}.dcm" }); + + //await _repository.BatchUpdateAsync(u => !u.Path.Contains(".dcm"), t => new DicomInstance() { Path = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{t.TrialId}/{t.SiteId}/{t.SubjectId}/{t.SubjectVisitId}/{StaticData.Folder.DicomFolder}/{t.StudyId}/{t.Id}" }); + + + return new { env.EnvironmentName, EMailConfig = _systemEmailConfig.CurrentValue, BasicConfig = _basicConfig.CurrentValue }; + + + // Load a document. + //using (var document = DocX.Load(Path.Combine(_hostEnvironment.ContentRootPath, "ReplaceText.docx"))) + //{ + // // Check if all the replace patterns are used in the loaded document. + // if (document.FindUniqueByPattern(@"<[\w \=]{4,}>", RegexOptions.IgnoreCase).Count > 0) + // { + // // Do the replacement of all the found tags and with green bold strings. + // //for (int i = 0; i < _replacePatterns.Count; ++i) + // //{ + // // document.ReplaceText("<(.*?)>", TestService.ReplaceFunc, false, RegexOptions.IgnoreCase, new Formatting() { Bold = true, FontColor = System.Drawing.Color.Green }); + // //} + // // Save this document to disk. + + // document.ReplaceText("test", "jfdksajfkljflsdjf", false, RegexOptions.IgnoreCase, new Formatting() { Bold = true, FontColor = System.Drawing.Color.Green }); + // document.SaveAs("ReplacedText.docx"); + // } + + //} + //return await NpoiWordHelper.TemplateExportWordAsync(Path.Combine(_hostEnvironment.ContentRootPath, "ReplaceText.docx"), new { test = "xiugai", ZZZZ = "ModiffyZZZZ" }, null, _hostEnvironment); + + //_cache.SetString("test" , "cacheStr"); + + //var aa= _trialRepository.Where(t => t.Id == Guid.Empty).First(); + + //var aaabb = _trialRepository.BatchDeleteNoTrackingAsync(t => t.Id == Guid.Empty).Result; + + //var aaaa = _dicRepository.BatchDeleteNoTrackingAsync(t => t.Id == Guid.Empty).Result; + + + //var waitModifyEntity = _dicRepository.FirstOrDefaultAsync(t => t.Id == Guid.Parse("e2b97a6c-35a6-4aa3-7f27-08da13ab33ff")).GetAwaiter().GetResult(); + + //var tt = _dicRepository.UpdateAsync(waitModifyEntity, t => new Dictionary() { Description = "xxxxx" }, true).Result; + + + + //var tt2 = _trialRepository.UpdatePartialFromQueryAsync(Guid.Parse("543d0000-3e10-0016-77e9-08da2827228a"), t => new Trial() { Indication = "WCH测试稽查002" }, true).Result; + + + + + //var d = _repository.Where(t => t.FullName.Contains("cc")).Select(t => t.FullName).FirstOrDefault(); + //var c = _dicRepository.Where(t => t.ParentId != null).Select(t => t.MappedValue).First(); + //CultureInfo culture = CultureInfo.CurrentUICulture; + + //var dd = _dicRepository.UpdatePartialFields(Guid.Parse("8a90c96e-0776-4f7b-82a6-18933d339584"), + // u => new Dictionary() { ParentId = null, Code = "test" }, true); + + + //var ggggg = _dicRepository.UpdateFromQueryAsync(t => t.ParentId == Guid.Parse("8a90c96e-0776-4f7b-82a6-18933d339584"), u=>new Dictionary(){Code = "test"}).Result; + + //var ddd = _dicRepository.BatchUpdateAsync(t => t.Id == Guid.Parse("8a90c96e-0776-4f7b-82a6-18933d339584"), + // u => new Dictionary() { /*ParentId = null,*/ Code = "test" }).Result; + + + //var aaaaa= _trialRepository.BatchUpdateAsync(t => t.Id == Guid.Empty, + // u => new SubjectVisit() { CurrentActionUserId = null }).Result; + + + //var d = _dicRepository.UpdateFromDTOAsync(new AddOrEditBasicDic() { Id = Guid.Parse("60d86683-c33b-4349-b672-08da1e91b622"), ParentId = null, ChildGroup = null, Code = null }, true, true).Result; + //var a = 123; + + throw new BusinessValidationFailedException(_localizer["TrialIsDoubleCheck{0}", "测试"]); + + var b = _localizer["test{0}", "测试"]; + return _localizer["test{0}", "测试"]; + + + //var list = _repository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.VisitTaskList).ToList(); + + //var list2 = _repository.Where(t => t.Id == Guid.NewGuid()).Select(t => t.SourceSubjectVisit).ToList(); + + //var list3 = _repository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.SourceSubjectVisit.VisitTaskList).ToList(); + //var list2 = _repository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.SubjectVisitTaskList).ToList(); + //return _userInfo.LocalIp; + } + + + + public string PostData(TestModel testModelList) + { + return String.Empty; + } + + /// + /// 维护临床数据 --一定要在同步表前同步数据才行 + /// + /// + [AllowAnonymous] + public async Task ModifyClinicalDataTable() + { + var needAddList = _repository.Where(t => t.CriterionEnumListStr != String.Empty && t.TrialClinicalDataSetCriteriaList.Count() == 0).ToList(); + + + //遍历项目 + foreach (var needAddTrialGroup in needAddList.GroupBy(t => t.TrialId)) + { + //找到项目存在的项目标准 + + var trialCritrialList = _repository.Where(t => t.TrialId == needAddTrialGroup.Key).ToList(); + + //遍历项目下的临床数据 + foreach (var needAddTrial in needAddTrialGroup) + { + + //遍历每条临床数据配置的标准 + + foreach (var item in needAddTrial.CriterionEnumList) + { + var find = trialCritrialList.Where(t => (int)t.CriterionType == item).FirstOrDefault(); + + if (find != null) + { + await _repository.AddAsync(new TrialClinicalDataSetCriterion() { TrialClinicalDataSetId = needAddTrial.Id, TrialReadingCriterionId = find.Id }); + } + } + } + } + await _repository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } + + + } + + public class TestModel + { + + //[Required] + public string Id { get; set; } + + public string Name { get; set; } + + public DateTime? Time { get; set; } + } +} + + +namespace Localization +{ + public class SharedResource + { + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Triggers/AddSubjectTrigger.cs b/IRaCIS.Core.Application/Triggers/AddSubjectTrigger.cs new file mode 100644 index 0000000..468eddf --- /dev/null +++ b/IRaCIS.Core.Application/Triggers/AddSubjectTrigger.cs @@ -0,0 +1,103 @@ +using AutoMapper; +using EntityFrameworkCore.Triggered; +using IRaCIS.Core.Domain.Share; +using MassTransit; + +namespace IRaCIS.Core.Application.Triggers +{ + /// + /// 添加检查批次计划 要给改项目下的所有Subject 添加该检查批次 + /// + public class AddSubjectTrigger : IBeforeSaveTrigger + { + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _clinicalDataTrialSetRepository; + private readonly IRepository _readingClinicalDataRepository; + private readonly IRepository _visitStageRepository; + private readonly IRepository _trialRepository; + private readonly IMapper _mapper; + + public AddSubjectTrigger(IRepository subjectVisitRepository, + IRepository clinicalDataTrialSetRepository, + IRepository readingClinicalDataRepository, + IMapper mapper, IRepository visitStageRepository, IRepository trialRepository) + { + _subjectVisitRepository = subjectVisitRepository; + this._clinicalDataTrialSetRepository = clinicalDataTrialSetRepository; + this._readingClinicalDataRepository = readingClinicalDataRepository; + _trialRepository = trialRepository; + + _visitStageRepository = visitStageRepository; + _mapper = mapper; + } + + public async Task BeforeSave(ITriggerContext context, CancellationToken cancellationToken) + { + var subject = context.Entity; + + if (context.ChangeType == ChangeType.Added) + { + + + + //添加患者的时候,获取检查批次计划列表,添加到患者检查批次表。 + var visitPlanList = await _visitStageRepository.Where(t => t.TrialId == subject.TrialId && t.IsConfirmed).ToListAsync(); + + var svList = _mapper.Map>(visitPlanList); + + var IsEnrollementQualificationConfirm = await _trialRepository.Where(t => t.Id == subject.TrialId).Select(u => u.IsEnrollementQualificationConfirm).FirstOrDefaultAsync(); + + + + + + svList.ForEach(t => + { + t.Subject = subject; + t.SubjectId = subject.Id; + t.TrialId = subject.TrialId; + t.SiteId = subject.SiteId; + t.IsEnrollmentConfirm = t.IsBaseLine ? IsEnrollementQualificationConfirm : false; + t.Id = NewId.NextGuid(); + + }); + + + await _subjectVisitRepository.AddRangeAsync(svList); + + await _subjectVisitRepository.SaveChangesAsync(); + + #region 在f检查批次计划确认的时候处理 给subject添加检查批次计划 + ////已添加患者 都不存在该新增的计划名称 那么该项目所有患者都增加一个检查批次记录 + //if (!await _subjectVisitRepository.AnyAsync(t => t.VisitName == visitPlan.VisitName && t.TrialId == visitPlan.TrialId)) + //{ + // var subjectSVS = await _subjectVisitRepository.Where(t => t.TrialId == visitPlan.TrialId).Select(t => new { t.SubjectId, t.SiteId, t.IsFinalVisit }).Distinct().ToListAsync(); + + // var subjects = subjectSVS.Select(t => new { t.SubjectId, t.SiteId }).Distinct().ToList(); + + // foreach (var subject in subjects) + // { + + // var svItem = _mapper.Map(visitPlan); + // svItem.SiteId = subject.SiteId; + // svItem.SubjectId = subject.SubjectId; + // svItem.Id = NewId.NextGuid(); + + // //设置了末次检查批次,那么加检查批次计划的时候,设置为不可用 + // if (subjectSVS.Any(t => t.SubjectId == svItem.SubjectId && t.IsFinalVisit)) + // { + // svItem.VisitExecuted = VisitExecutedEnum.Unavailable; + // } + + // await _subjectVisitRepository.AddAsync(svItem); + // } + + // await _subjectVisitRepository.SaveChangesAsync(); + //} + #endregion + + + } + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Triggers/AddlTrialUserTrigger.cs b/IRaCIS.Core.Application/Triggers/AddlTrialUserTrigger.cs new file mode 100644 index 0000000..7952239 --- /dev/null +++ b/IRaCIS.Core.Application/Triggers/AddlTrialUserTrigger.cs @@ -0,0 +1,63 @@ +using EntityFrameworkCore.Triggered; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Triggers +{ + + // 统一处理 外部用户、中心调研(先添加 再发送邮件)、参与医生加入到项目 ----废弃 + public class AddlTrialUserTrigger : IBeforeSaveTrigger + + { + private readonly IRepository _trialRepository; + private readonly IRepository _userRepository; + + public AddlTrialUserTrigger(IRepository trialRepository, IRepository userRepository) + { + _trialRepository = trialRepository; + + _userRepository = userRepository; + + } + public async Task BeforeSave(ITriggerContext context, CancellationToken cancellationToken) + { + var trialUser = context.Entity; + + if (context.ChangeType == ChangeType.Added) + { + //批量添加的时候,使用Find 不会多次查询,优先从跟踪的内存中查找 + var trialInfo = await _trialRepository.FindAsync(trialUser.TrialId); + + // 必须包在同一个事务,有的时候是数据库还没用户,不能直接查询数据库 + var user = await _userRepository.FindAsync(trialUser.UserId); + + if (trialInfo.TrialType == Domain.Share.TrialType.OfficialTrial || trialInfo.TrialType == Domain.Share.TrialType.Training) + { + + if (user.IsTestUser) + { + throw new BusinessValidationFailedException("正式类型 、培训类型的项目 不允许加入测试用户 "); + + } + } + + if (trialInfo.TrialType == TrialType.NoneOfficial) + { + + if (user.IsTestUser == false ) + { + throw new BusinessValidationFailedException("测试项目 不允许加入正式用户 "); + + } + } + } + } + } + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Triggers/ChallengeStateTrigger.cs b/IRaCIS.Core.Application/Triggers/ChallengeStateTrigger.cs new file mode 100644 index 0000000..1ec912f --- /dev/null +++ b/IRaCIS.Core.Application/Triggers/ChallengeStateTrigger.cs @@ -0,0 +1,48 @@ +using EntityFrameworkCore.Triggered; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Triggers +{ + + //检查批次 质疑状态 触发修改 + public class ChallengeStateTrigger : IAfterSaveTrigger + { + private readonly IRepository _repository; + + public ChallengeStateTrigger(IRepository repository) + { + _repository = repository; + } + + public async Task AfterSave(ITriggerContext context, CancellationToken cancellationToken) + { + var subjectVisitId = context.Entity.SubjectVisitId; + + ChallengeStateEnum subjectVisitChallengeState = default; + + + var closedStateList = await _repository.Where(t => t.SubjectVisitId == subjectVisitId).Select(t => t.IsClosed).ToListAsync(); + + if (closedStateList.Count == 0) + { + subjectVisitChallengeState = ChallengeStateEnum.No; + } + else if (closedStateList.All(t => t is true)) + { + subjectVisitChallengeState = ChallengeStateEnum.HaveAndAllClosed; + } + else + { + subjectVisitChallengeState = ChallengeStateEnum.HaveAndHaveNotClosed; + } + + + await _repository.BatchUpdateAsync(t => t.Id == subjectVisitId, + u => new SubjectVisit() { ChallengeState = subjectVisitChallengeState }); + } + } + + + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Triggers/SubjectStateTrigger.cs b/IRaCIS.Core.Application/Triggers/SubjectStateTrigger.cs new file mode 100644 index 0000000..e00a5cd --- /dev/null +++ b/IRaCIS.Core.Application/Triggers/SubjectStateTrigger.cs @@ -0,0 +1,98 @@ +using AutoMapper; +using EntityFrameworkCore.Triggered; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using MassTransit; + +namespace IRaCIS.Core.Application.Triggers +{ + /// + /// + /// + public class SubjectStateTrigger : IAfterSaveTrigger + { + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _repository; + private readonly IMapper _mapper; + + public SubjectStateTrigger(IRepository subjectVisitRepository, IRepository repository, IMapper mapper) + { + _subjectVisitRepository = subjectVisitRepository; + _repository = repository; + _mapper = mapper; + } + + public async Task AfterSave(ITriggerContext context, CancellationToken cancellationToken) + { + + var dbSubject = context.Entity; + + if (context.ChangeType == ChangeType.Modified ) + { + //Site变更 + + if ( context.Entity.SiteId != context.UnmodifiedEntity?.SiteId) + { + var subjectId = context.Entity.Id; + var siteId = context.Entity.SiteId; + + + await _repository.BatchUpdateAsync(t => t.SubjectId == subjectId, u => new SubjectVisit() { SiteId = siteId }); + + await _repository.BatchUpdateAsync(t => t.SubjectId == subjectId, u => new DicomStudy() { SiteId = siteId }); + + + #region 废弃 + ////如果检查批次结束了 需要删除计划外未执行的检查批次 + //if (mapedSubject.Status == SubjectStatus.EndOfVisit) + //{ + // await _repository.DeleteFromQueryAsync(t => t.VisitExecuted == VisitExecutedEnum.UnExecuted && t.SubjectId == mapedSubject.Id && t.InPlan == false); + //} + + ////如果是出组了 将患者未执行的 设置为不可用 + //if (mapedSubject.Status == SubjectStatus.OutOfEnrollment) + //{ + // await _repository.UpdateFromQueryAsync(t => t.SubjectId == mapedSubject.Id && t.VisitExecuted == VisitExecutedEnum.UnExecuted, u => new SubjectVisit() { VisitExecuted = VisitExecutedEnum.Unavailable }); + //} + #endregion + } + + + // 出组 状态发生了变更 + if (context.Entity.Status == SubjectStatus.OutOfVisit && context.Entity.Status != context.UnmodifiedEntity?.Status) + { + if (context.Entity.FinalSubjectVisitId != null) + { + if (await _subjectVisitRepository.AnyAsync(t => t.SubjectId == dbSubject.Id && t.IsFinalVisit && t.Id != dbSubject.FinalSubjectVisitId)) + { + + throw new BusinessValidationFailedException( + "该患者已经有检查批次被设置为末次检查批次,不允许将当前检查批次设置为末次检查批次。"); + } + + var sv = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == dbSubject.FinalSubjectVisitId).IfNullThrowException(); + + if (await _subjectVisitRepository.AnyAsync(t => t.SubjectId == dbSubject.Id && t.VisitNum > sv.VisitNum && t.SubmitState == SubmitStateEnum.ToSubmit)) + { + + throw new BusinessValidationFailedException( + "该患者当前检查批次后有检查批次的影像已上传,当前检查批次不允许设置为末次检查批次。"); + + } + + sv.IsFinalVisit = true; + + await _subjectVisitRepository.SaveChangesAsync(); + //末次检查批次后的 检查批次设置为不可用 + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == dbSubject.Id && t.VisitNum > sv.VisitNum, u => new SubjectVisit() { VisitExecuted = VisitExecutedEnum.Unavailable }); + } + } + + + + } + + + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Triggers/SubjectVisitCheckPassedTrigger.cs b/IRaCIS.Core.Application/Triggers/SubjectVisitCheckPassedTrigger.cs new file mode 100644 index 0000000..1cea485 --- /dev/null +++ b/IRaCIS.Core.Application/Triggers/SubjectVisitCheckPassedTrigger.cs @@ -0,0 +1,96 @@ +using AutoMapper; +using EasyCaching.Core; +using EntityFrameworkCore.Triggered; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; + +namespace IRaCIS.Core.Application.Triggers +{ + /// + /// 处理 检查批次 末次评估 会影响Subject 状态 + /// + public class SubjectVisitCheckPassedTrigger : IBeforeSaveTrigger + { + + private readonly IVisitTaskHelpeService _visitTaskHelpeService; + + private readonly IRepository _subjectRepository; + private readonly IRepository _clinicalDataTrialSetRepository; + private readonly IRepository _readingClinicalDataRepository; + + public SubjectVisitCheckPassedTrigger(IRepository subjectRepository, + IRepository clinicalDataTrialSetRepository, + IRepository readingClinicalDataRepository, + IVisitTaskHelpeService visitTaskHelpeService) + { + + _subjectRepository = subjectRepository; + _clinicalDataTrialSetRepository = clinicalDataTrialSetRepository; + _readingClinicalDataRepository = readingClinicalDataRepository; + this._clinicalDataTrialSetRepository = clinicalDataTrialSetRepository; + _visitTaskHelpeService = visitTaskHelpeService; + } + + + + public async Task BeforeSave(ITriggerContext context, CancellationToken cancellationToken) + { + var subjectVisit = context.Entity; + + + if (context.ChangeType == ChangeType.Modified) + { + + // 一致性核查通过 生成读片任务 + if (context.UnmodifiedEntity?.CheckState != subjectVisit.CheckState && subjectVisit.CheckState == CheckStateEnum.CVPassed) + { + //退回或者重阅的任务一致性核查通过了 此时设置Subject 重阅影响状态 + if (context.Entity.IsPMBackOrReReading == true) + { + await _subjectRepository.UpdatePartialFromQueryAsync(context.Entity.SubjectId, u => new Subject() { IsReReadingOrBackInfluenceAnalysis = false }); + } + + context.Entity.IsPMBackOrReReading = false; + + await _visitTaskHelpeService.GenerateVisitTaskAsync(subjectVisit.TrialId, new List() { subjectVisit.Id }, true); + } + + } + // 触发临床数据 + if (context.ChangeType == ChangeType.Added) + { + var cRCClinicalDatas = await _clinicalDataTrialSetRepository.Where(x => x.TrialId == context.Entity.TrialId && x.UploadRole == UploadRole.CRC && x.IsConfirm) + + .Select(x => new + { + x.Id, + x.ClinicalDataLevel, + }).ToListAsync(); + + List readingClinicals = new List(); + readingClinicals.AddRange( + + cRCClinicalDatas.WhereIf(!context.Entity.IsBaseLine, x => x.ClinicalDataLevel == ClinicalLevel.SubjectVisit).Select(x => new ReadingClinicalData() + { + ClinicalDataTrialSetId = x.Id, + IsVisit = true, + SubjectId = context.Entity.SubjectId, + ReadingId = context.Entity.Id, + TrialId = context.Entity.TrialId + + }).ToList() + + ); + + + await _readingClinicalDataRepository.AddRangeAsync(readingClinicals); + await _readingClinicalDataRepository.SaveChangesAsync(); + + } + + + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Triggers/SubjectVisitFinalVisitTrigger.cs b/IRaCIS.Core.Application/Triggers/SubjectVisitFinalVisitTrigger.cs new file mode 100644 index 0000000..f79b2a6 --- /dev/null +++ b/IRaCIS.Core.Application/Triggers/SubjectVisitFinalVisitTrigger.cs @@ -0,0 +1,190 @@ +using EntityFrameworkCore.Triggered; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using MassTransit; + +namespace IRaCIS.Core.Application.Triggers +{ + /// + /// 处理 检查批次 末次评估 会影响Subject 状态 + /// + public class SubjectVisitFinalVisitTrigger : IAfterSaveTrigger + { + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _readingPeriodSetRepository; + private readonly IRepository _readingPeriodPlanRepository; + private readonly IRepository _readModuleRepository; + private readonly IRepository _subjectRepository; + private readonly IRepository _visitTaskRepository; + private readonly IVisitTaskHelpeService _ivisitTaskHelpeService; + private readonly IRepository _repository; + + public SubjectVisitFinalVisitTrigger(IRepository subjectVisitRepository, + + IRepository readingPeriodSetRepository, + IRepository readingPeriodPlanRepository, + IRepository visitTaskRepository, + IVisitTaskHelpeService visitTaskHelpeService, + IRepository readModuleRepository, + IRepository subjectRepository, IRepository repository) + { + _subjectVisitRepository = subjectVisitRepository; + this._readingPeriodSetRepository = readingPeriodSetRepository; + this._visitTaskRepository = visitTaskRepository; + this._ivisitTaskHelpeService = visitTaskHelpeService; + this._readingPeriodPlanRepository = readingPeriodPlanRepository; + this._readModuleRepository = readModuleRepository; + _subjectRepository = subjectRepository; + _repository = repository; + } + + public async Task AfterSave(ITriggerContext context, CancellationToken cancellationToken) + { + + var subjectVisit = context.Entity; + + + if (context.ChangeType == ChangeType.Modified) + { + + // 修改了IsFinalVisit + if (context.UnmodifiedEntity?.IsFinalVisit != subjectVisit.IsFinalVisit) + { + if (subjectVisit.IsFinalVisit) + { + await VerifyDealFinalVisitAsync(subjectVisit); + + await DealGenerateReadModuleAndSubjectVisit(subjectVisit); + + + } + else + { + //回退 + + await _subjectRepository.BatchUpdateNoTrackingAsync(t => t.Id == subjectVisit.SubjectId, + u => new Subject() { Status = SubjectStatus.OnVisit, FinalSubjectVisitId = null }); + + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == subjectVisit.SubjectId && t.VisitExecuted == VisitExecutedEnum.Unavailable, u => new SubjectVisit() { VisitExecuted = VisitExecutedEnum.UnExecuted }); + + + } + } + + } + else if (context.ChangeType == ChangeType.Added && subjectVisit.IsFinalVisit) + { + await VerifyDealFinalVisitAsync(subjectVisit); + + await DealGenerateReadModuleAndSubjectVisit(subjectVisit); + } + } + + /// + /// 处理生成阅片期 以及后续检查批次状态 + /// + /// + /// + private async Task DealGenerateReadModuleAndSubjectVisit(SubjectVisit subjectVisit) + { + + await _subjectRepository.BatchUpdateNoTrackingAsync(t => t.Id == subjectVisit.SubjectId, + u => new Subject() { Status = SubjectStatus.OutOfVisit, FinalSubjectVisitId = subjectVisit.Id }); + + + //末次检查批次后的 检查批次设置为不可用 + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == subjectVisit.SubjectId && t.VisitNum > subjectVisit.VisitNum && + t.VisitExecuted == VisitExecutedEnum.UnExecuted, u => new SubjectVisit() { VisitExecuted = VisitExecutedEnum.Unavailable }); + + + #region 末次检查批次生成阅片器和任务 + + + var trialId = subjectVisit.TrialId; + var subjectVisitId = subjectVisit.Id; + // 是否全局阅片 + + + var trial = await _repository.Where(x => x.Id == trialId).FirstNotNullAsync(); + + + if (!subjectVisit.IsBaseLine && subjectVisit.SubmitState == SubmitStateEnum.Submitted) + { + var criterionList = await _repository.Where(x => x.TrialId == trialId && x.ReadingInfoSignTime != null && x.IsConfirm && x.IsReadingPeriod && x.IsReadingTaskViewInOrder).ToListAsync(); + foreach (var item in criterionList) + { + + var readModule = await _readModuleRepository.Where(x => x.ReadingSetType == ReadingSetType.ImageReading && x.TrialReadingCriterionId == item.Id && x.SubjectVisitId == subjectVisitId).FirstOrDefaultAsync(); + + if (readModule == null) + + { + + ReadModule newReadModule = new ReadModule() + { + //ReadingPeriodSetId = readingPeriodSet.Id, + Id = NewId.NextGuid(), + IsUrgent = subjectVisit.IsUrgent, + SubjectVisitId = subjectVisitId, + ReadingStatus = ReadingStatusEnum.TaskAllocate, + SubjectId = subjectVisit.SubjectId, + ModuleName = $"G-{subjectVisit.VisitName}",// 全局阅片 + ReadingSetType = ReadingSetType.ImageReading, + ModuleType = ModuleTypeEnum.Global, + TrialId = subjectVisit.TrialId, + //VisitNum = subjectVisit.VisitNum, + TrialReadingCriterionId = item.Id, + }; + + readModule = await _readModuleRepository.AddAsync(newReadModule); + } + + var visitTaskList = await _visitTaskRepository.Where(x => x.TrialReadingCriterionId == item.Id && x.SourceSubjectVisitId == subjectVisitId && x.TaskState == TaskState.Effect && x.ReadingTaskState == ReadingTaskState.HaveSigned).ToListAsync(); + + foreach (var visitTask in visitTaskList) + { + await _ivisitTaskHelpeService.AddTaskAsync(new GenerateTaskCommand() + { + OriginalVisitId = visitTask.Id, + ReadingCategory = GenerateTaskCategory.Global, + TrialId = subjectVisit.TrialId, + + ReadingGenerataTaskList = new List() { + + new ReadingGenerataTaskDTO (){ + IsUrgent=subjectVisit.IsUrgent, + ReadingCategory=ReadingCategory.Global, + ReadingName=readModule.ModuleName, + ReadModuleId=readModule.Id, + SubjectId=subjectVisit.SubjectId, + VisitNum=visitTask.VisitTaskNum, + }, + + }, + }); + + } + + } + await _readModuleRepository.SaveChangesAsync(); + } + + + #endregion + } + + private async Task VerifyDealFinalVisitAsync(SubjectVisit subjectVisit) + { + if (await _subjectVisitRepository.AnyAsync(t => t.SubjectId == subjectVisit.SubjectId && t.VisitNum > subjectVisit.VisitNum && + (t.SubmitState == SubmitStateEnum.ToSubmit || t.SubmitState == SubmitStateEnum.Submitted))) + { + throw new BusinessValidationFailedException("该患者已有后续检查批次已上传影像或已提交,当前检查批次不允许设置为末次检查批次。"); + } + + } + } + + +} diff --git a/IRaCIS.Core.Application/Triggers/SubjectVisitTrigger.cs b/IRaCIS.Core.Application/Triggers/SubjectVisitTrigger.cs new file mode 100644 index 0000000..51d1bed --- /dev/null +++ b/IRaCIS.Core.Application/Triggers/SubjectVisitTrigger.cs @@ -0,0 +1,134 @@ +using EntityFrameworkCore.Triggered; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Triggers +{ + /// + /// 处理 检查批次 1、提交状态 2、执行状态 3、最早最晚 拍片日期 + /// + public class SubjectVisitTrigger : IAfterSaveTrigger, IAfterSaveTrigger, IAfterSaveTrigger + { + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _repository; + + public SubjectVisitTrigger(IRepository subjectVisitRepository,IRepository repository) + { + _subjectVisitRepository = subjectVisitRepository; + + _repository = repository; + } + + //注意删除不能用扩展方法,必须用EF跟踪的实体 否则不能取到 SubjectVisitId + public async Task AfterSave(ITriggerContext context, CancellationToken cancellationToken) + { + var subjectVisitId = context.Entity.SubjectVisitId; + + if (context.ChangeType == ChangeType.Added || context.ChangeType == ChangeType.Modified) + { + await UpdateSubjectVisitImageDateAsync(context.Entity.SubjectVisitId); + + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == subjectVisitId, u => new SubjectVisit() + { + VisitExecuted = VisitExecutedEnum.Executed + }); + await UpdateSubjectVisitSubmitStateAsync(subjectVisitId); + } + + + if (context.ChangeType == ChangeType.Deleted) + { + await UpdateSubjectVisitImageDateAsync(context.Entity.SubjectVisitId); + + await UpdateSubjectVisitSubmitStateAsync(subjectVisitId); + } + } + + public async Task AfterSave(ITriggerContext context, CancellationToken cancellationToken) + { + var subjectVisitId = context.Entity.SubjectVisitId; + if (context.ChangeType == ChangeType.Added ) + { + await UpdateSubjectVisitImageDateAsync(subjectVisitId); + + var studyId = context.Entity.Id; + //处理Modality + var seriesModalityList = _repository.Where(t => t.StudyId == studyId).Select(u => u.Modality).Distinct(); + string ModaliyStr = string.Join('、', seriesModalityList.ToList()); + + + context.Entity.Modalities = ModaliyStr; + + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == subjectVisitId, u => new SubjectVisit() + { + VisitExecuted = VisitExecutedEnum.Executed + }); + + await UpdateSubjectVisitSubmitStateAsync(subjectVisitId); + + } + + if (context.ChangeType == ChangeType.Deleted) + { + await UpdateSubjectVisitImageDateAsync(subjectVisitId); + await UpdateSubjectVisitSubmitStateAsync(subjectVisitId); + } + + } + + public async Task AfterSave(ITriggerContext context, CancellationToken cancellationToken) + { + var subjectVisitId =await _repository.Where(x=>x.Id== context.Entity.NoneDicomStudyId).Select(x=>x.SubjectVisitId).FirstOrDefaultAsync(); + + if (context.ChangeType == ChangeType.Deleted || context.ChangeType == ChangeType.Added) + { + await UpdateSubjectVisitSubmitStateAsync(subjectVisitId); + } + + } + + /// 处理提交状态 + + public async Task UpdateSubjectVisitSubmitStateAsync(Guid subjectVisitId) + { + + //一个检查批次下面有多个检查,所以需要检测 没有的时候才清空 非dicom 是检查文件 不是表记录 + if (await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).SelectMany(t => t.StudyList).CountAsync() == 0 && + await _subjectVisitRepository.Where(t => t.Id == subjectVisitId) + .SelectMany(t => t.NoneDicomStudyList).SelectMany(u => u.NoneDicomFileList).CountAsync() == 0) + { + + await _subjectVisitRepository.UpdatePartialFromQueryAsync(t => t.Id == subjectVisitId && t.SubmitState == SubmitStateEnum.ToSubmit, + u => new SubjectVisit() { VisitExecuted = 0, SVENDTC = null, SVSTDTC = null, SubmitState = SubmitStateEnum.None },true); + } + else + { + // 上传非Dicom 后 将状态改为待提交 分为普通上传 和QC后重传 普通上传时才改为待提交 + await _subjectVisitRepository.UpdatePartialFromQueryAsync(t => t.Id == subjectVisitId && t.SubmitState == SubmitStateEnum.None, u => new SubjectVisit() { SubmitState = SubmitStateEnum.ToSubmit },true); + } + } + + /// 处理拍片日期 + private async Task UpdateSubjectVisitImageDateAsync(Guid subjectVisitId) + { + var svTime = _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new + { + DicomStudyMinStudyTime = t.StudyList.Min(t => (DateTime?)t.StudyTime), + DicomStudyMaxStudyTime = t.StudyList.Max(t => (DateTime?)t.StudyTime), + NoneDicomStudyMinStudyTime = t.NoneDicomStudyList.Min(t => (DateTime?)t.ImageDate), + NoneDicomStudyMaxStudyTime = t.NoneDicomStudyList.Max(t => (DateTime?)t.ImageDate) + }).FirstOrDefault().IfNullThrowException(); + + + var minArray = new DateTime?[] { svTime.DicomStudyMinStudyTime, svTime.NoneDicomStudyMinStudyTime }; + var maxArray = new DateTime?[] { svTime.DicomStudyMaxStudyTime, svTime.NoneDicomStudyMaxStudyTime }; + + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t=>t.Id ==subjectVisitId, u => new SubjectVisit() + { + + EarliestScanDate = minArray.Min(), + + LatestScanDate = maxArray.Max() + }); + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Triggers/TableQuestionRowTrigger.cs b/IRaCIS.Core.Application/Triggers/TableQuestionRowTrigger.cs new file mode 100644 index 0000000..0fa72d5 --- /dev/null +++ b/IRaCIS.Core.Application/Triggers/TableQuestionRowTrigger.cs @@ -0,0 +1,55 @@ +using EntityFrameworkCore.Triggered; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Application.Triggers +{ + //病灶编号维护 + public class TableQuestionRowTrigger : IBeforeSaveTrigger + { + + + public Task BeforeSave(ITriggerContext context, CancellationToken cancellationToken) + { + Dictionary splitLesionDic = new Dictionary() + { + {1, "a" }, + {2, "b" }, + {3, "c" }, + {4, "d" }, + {5, "e" }, + {6, "f" }, + {7, "g" }, + {8, "h" }, + {9, "i" }, + {10, "j" }, + {11, "k" }, + {12, "l" }, + {13, "m" }, + {14, "n" }, + {15, "o" }, + {16, "p" }, + {17, "q" }, + {18, "r" }, + {19, "s" }, + {20, "t" }, + {21, "u" }, + {22, "v" }, + {23, "w" }, + {24, "x" }, + {25, "y" }, + {26, "z" }, + + }; + if (context.Entity.RowIndex % 1 == 0) + { + context.Entity.RowMark = context.Entity.OrderMark + decimal.ToInt32(context.Entity.RowIndex).ToString().PadLeft(2, '0'); + } + else + { + context.Entity.RowMark = context.Entity.OrderMark + Math.Floor(context.Entity.RowIndex).ToString().PadLeft(2, '0') + splitLesionDic[decimal.ToInt32((context.Entity.RowIndex % 1) * 100)]; + } + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Triggers/TrialCriterionSignTrigger.cs b/IRaCIS.Core.Application/Triggers/TrialCriterionSignTrigger.cs new file mode 100644 index 0000000..d0d21d6 --- /dev/null +++ b/IRaCIS.Core.Application/Triggers/TrialCriterionSignTrigger.cs @@ -0,0 +1,48 @@ +using AutoMapper; +using EasyCaching.Core; +using EntityFrameworkCore.Triggered; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; + +namespace IRaCIS.Core.Application.Triggers +{ + /// + /// 因为可能先一致性核查通过,生成其他标准的任务了,新签名的标准也需要产生任务 + /// + public class TrialCriterionSignTrigger : IBeforeSaveTrigger + { + + private readonly IVisitTaskHelpeService _visitTaskHelpeService; + + + public TrialCriterionSignTrigger( + + IVisitTaskHelpeService visitTaskHelpeService) + { + + + _visitTaskHelpeService = visitTaskHelpeService; + } + + public async Task BeforeSave(ITriggerContext context, CancellationToken cancellationToken) + { + var trialCriterion = context.Entity; + + + if (context.ChangeType == ChangeType.Modified) + { + + // 一致性核查通过 生成读片任务 + if (context.UnmodifiedEntity?.ReadingInfoSignTime != trialCriterion.ReadingInfoSignTime && trialCriterion.ReadingInfoSignTime != null) + { + + + await _visitTaskHelpeService.BaseCritrionGenerateVisitTask(trialCriterion.TrialId, trialCriterion.Id); + } + + } + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Triggers/VisitTaskIAfterSignTrigger.cs b/IRaCIS.Core.Application/Triggers/VisitTaskIAfterSignTrigger.cs new file mode 100644 index 0000000..ebff97d --- /dev/null +++ b/IRaCIS.Core.Application/Triggers/VisitTaskIAfterSignTrigger.cs @@ -0,0 +1,61 @@ +//using AutoMapper; +//using EasyCaching.Core; +//using EntityFrameworkCore.Triggered; +//using IRaCIS.Core.Application.Service; +//using IRaCIS.Core.Application.ViewModel; +//using IRaCIS.Core.Domain.Share; +//using IRaCIS.Core.Infrastructure; +//using System.Linq.Expressions; + +//namespace IRaCIS.Core.Application.Triggers +//{ + +// public class VisitTaskIAfterSignTrigger : IAfterSaveTrigger +// { + +// private readonly IRepository _visitTaskRepository; + + +// public VisitTaskIAfterSignTrigger( + +// IRepository visitTaskRepository) +// { + +// _visitTaskRepository = visitTaskRepository; +// } + + +// //添加任务的时候 如果需要签名 并且已经签名了 那么要维护该标准 该Subject IsFrontTaskNeedSignButNotSign字段 +// public async Task AfterSave(ITriggerContext context, CancellationToken cancellationToken) +// { +// var visitTask = context.Entity; + + +// if(visitTask.SignTime!=null && visitTask.ReadingTaskState == ReadingTaskState.HaveSigned) +// { +// switch (visitTask.ReadingCategory) +// { +// case ReadingCategory.Visit: + +// break; +// case ReadingCategory.Global: + +// break; +// case ReadingCategory.Judge: + +// break; +// case ReadingCategory.Oncology: + +// break; + +// default: +// break; +// } +// } + + +// } + + +// } +//} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Triggers/VisitTaskIsFrontTaskNeedSignButNotSignTrigger.cs b/IRaCIS.Core.Application/Triggers/VisitTaskIsFrontTaskNeedSignButNotSignTrigger.cs new file mode 100644 index 0000000..5a35237 --- /dev/null +++ b/IRaCIS.Core.Application/Triggers/VisitTaskIsFrontTaskNeedSignButNotSignTrigger.cs @@ -0,0 +1,74 @@ +using AutoMapper; +using EasyCaching.Core; +using EntityFrameworkCore.Triggered; +using IRaCIS.Core.Application.Service; +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using System.Linq.Expressions; + +namespace IRaCIS.Core.Application.Triggers +{ + /// + /// 因为可能先一致性核查通过,生成其他标准的任务了,新签名的标准也需要产生任务 + /// + public class VisitTaskIsFrontTaskNeedSignButNotSignTrigger : IAfterSaveTrigger + { + + private readonly IRepository _visitTaskRepository; + + + public VisitTaskIsFrontTaskNeedSignButNotSignTrigger( + + IRepository visitTaskRepository) + { + + + _visitTaskRepository = visitTaskRepository; + } + + + //添加任务的时候 如果需要签名 并且已经签名了 那么要维护该标准 该Subject IsFrontTaskNeedSignButNotSign字段 + public async Task AfterSave(ITriggerContext context, CancellationToken cancellationToken) + { + var visitTask = context.Entity; + + + Expression> visitTaskLambda = x => x.TrialId == visitTask.TrialId && x.SubjectId == visitTask.SubjectId && x.TrialReadingCriterionId == visitTask.TrialReadingCriterionId; + if (context.ChangeType == ChangeType.Added) + { + + if (visitTask.IsNeedClinicalDataSign ) + { + + var visitTaskIdQueryable = _visitTaskRepository.Where(visitTaskLambda) //该Subject 该标准的任务 + //小于自己任务号的任务 存在需要签名 但是没签名 + .Where(t => t.Subject.SubjectVisitTaskList.AsQueryable().Where(visitTaskLambda).Any(c => c.IsNeedClinicalDataSign == true && c.IsClinicalDataSign == false && c.VisitTaskNum < t.VisitTaskNum) + && t.IsFrontTaskNeedSignButNotSign == false) + .Select(t => t.Id); + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => visitTaskIdQueryable.Contains(t.Id), u => new VisitTask() + { + IsFrontTaskNeedSignButNotSign = true + }); + + + + var visitTaskIdQueryable2 = _visitTaskRepository.Where(visitTaskLambda) //该Subject 该标准的任务 + //小于自己任务号的任务 存在需要签名 但是没签名 + .Where(t => !t.Subject.SubjectVisitTaskList.AsQueryable().Where(visitTaskLambda).Any(c => c.IsNeedClinicalDataSign == true && c.IsClinicalDataSign == false && c.VisitTaskNum < t.VisitTaskNum) + && t.IsFrontTaskNeedSignButNotSign == true) + .Select(t => t.Id); + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => visitTaskIdQueryable2.Contains(t.Id), u => new VisitTask() + { + IsFrontTaskNeedSignButNotSign = false + }); + } + + } + } + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/WebAppConfig.cs b/IRaCIS.Core.Application/WebAppConfig.cs new file mode 100644 index 0000000..639e67c --- /dev/null +++ b/IRaCIS.Core.Application/WebAppConfig.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Configuration; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Json; + +namespace IRaCIS.Application +{ + //public class WebAppConfig + //{ + // public static string RootUrl = ConfigurationManager.AppSettings["rootUrl"].ToString(); + // public static string CodePrefix = ConfigurationManager.AppSettings["CodePrefix"].ToString(); + // public static string UserCodePrefix = ConfigurationManager.AppSettings["UserCodePrefix"].ToString(); + // public static string VisitPlanNumPrefix = ConfigurationManager.AppSettings["VisitPlanNumPrefix"].ToString(); + + // public static int CodeLength { get; private set; } + // public static List ExcludeCodeList { get; private set; } + + //} + + + // .net core 迁移替换 + public class WebAppConfig + { + public static string RootUrl { get; set; } + public static string CodePrefix { get; set; } + public static string UserCodePrefix { get; set; } + public static string VisitPlanNumPrefix { get; set; } + + public static int CodeLength { get; private set; } + public static List ExcludeCodeList { get; private set; } + static WebAppConfig() + { + var configuration = new ConfigurationBuilder() + .Add(new JsonConfigurationSource { Path = "appsettings.json", ReloadOnChange = true }) + .Build(); + + RootUrl = configuration.GetSection("RootUrl").Value; + + CodePrefix = configuration.GetSection("CodePrefix").Value; + + UserCodePrefix = configuration.GetSection("UserCodePrefix").Value; + + VisitPlanNumPrefix = configuration.GetSection("VisitPlanNumPrefix").Value; + } + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/_MediatR/CommandAndQueries/ConsistencyVerificationRequest.cs b/IRaCIS.Core.Application/_MediatR/CommandAndQueries/ConsistencyVerificationRequest.cs new file mode 100644 index 0000000..95b00b1 --- /dev/null +++ b/IRaCIS.Core.Application/_MediatR/CommandAndQueries/ConsistencyVerificationRequest.cs @@ -0,0 +1,177 @@ +using Magicodes.ExporterAndImporter.Core; +using Magicodes.ExporterAndImporter.Core.Filters; +using Magicodes.ExporterAndImporter.Core.Models; +using Magicodes.ExporterAndImporter.Excel; +using MediatR; +using MiniExcelLibs.Attributes; +using System.ComponentModel.DataAnnotations; +using System.Linq; + +namespace IRaCIS.Core.Application.MediatR.CommandAndQueries +{ + public class ConsistencyVerificationRequest : IRequest + { + public List ETCList { get; set; } = new List(); + + public Guid TrialId { get; set; } + } + + public class CheckDBModel : CheckViewModel + { + + public Guid SubjectVisitId { get; set; } + + public Guid StudyId { get; set; } + } + + + + public class ImportResultFilteTest : IImportResultFilter + { + + + public ImportResult Filter(ImportResult importResult) where T : class, new() + { + if (typeof(T).IsAssignableFrom(typeof(CheckViewModel))) + { + var data = (List)importResult.Data; + + var dt = DateTime.Now ; + + foreach (var item in data) + { + + var index= data.IndexOf(item); + if ( DateTime.TryParse(item.StudyDate, out dt) == false) + { + importResult.RowErrors.Add(new DataRowErrorInfo() { RowIndex = index, FieldErrors = new Dictionary { { "检查技术", "时间格式不对" } } }); + } + } + } + + return importResult; + } + } + + + public class ParamInfoDto + { + public string Modality { get; set; } + + public string StudyDate { get; set; } + + //public int ErrorType { get; set; } + + + + } + + + [ExcelImporter(/*ImportResultFilter = typeof(ImportResultFilteTest),*/ IsLabelingError = true)] + + public class CheckViewModel + { + //[Required(ErrorMessage = "中心编号不能为空")] + [ImporterHeader(Name = "中心编号", AutoTrim = true)] + [ExcelColumnName("中心编号")] + public string SiteCode { get; set; } = string.Empty; + + + //[Required(ErrorMessage = "患者筛选号不能为空")] + [ImporterHeader(Name = "患者筛选号", AutoTrim = true)] + [ExcelColumnName("患者筛选号")] + public string SubjectCode { get; set; } = string.Empty; + + //[Required(ErrorMessage = "检查批次名称不能为空")] + [ImporterHeader(Name = "检查批次名称", AutoTrim = true)] + [ExcelColumnName("检查批次名称")] + public string VisitName { get; set; } = string.Empty; + + + + //[Required(ErrorMessage = "检查日期不能为空")] + [CanConvertToTime(ErrorMessage = "检查日期格式有问题")] + + [ImporterHeader(Name = "检查日期", AutoTrim = true)] + [ExcelColumnName("检查日期")] + public string StudyDate { get; set; } = string.Empty; + + + + //[Required(ErrorMessage = "Modality不能为空")] + [ImporterHeader(Name = "检查技术", AutoTrim = true)] + [ExcelColumnName("检查技术")] + public string Modality { get; set; } = string.Empty; + + + + + public override bool Equals(object? obj) + { + if (obj == null) return false; + + var checkModel = obj as CheckViewModel; + + if (checkModel is not null) + { + return SiteCode == checkModel.SiteCode && SubjectCode == checkModel.SubjectCode && VisitName == checkModel.VisitName && StudyDate == checkModel.StudyDate && Modality == checkModel.Modality; + } + else + { + return false; + } + } + public override int GetHashCode() + { + return (SiteCode + SubjectCode + VisitName + StudyDate + Modality).GetHashCode(); + } + } + + public class VisitPlanInfluenceSubjectVisitDTO + { + [ExporterHeader(IsIgnore = true)] + public Guid StudyId { get; set; } + + [ExporterHeader(IsIgnore = true)] + public Guid TrialId { get; set; } + + [ExporterHeader(IsIgnore = true)] + public Guid SubjectVisitId { get; set; } + + [ExporterHeader(DisplayName = "中心编号")] + public string TrialSiteCode { get; set; } = string.Empty; + + [ExporterHeader(DisplayName = "患者")] + public string SubjectCode { get; set; } = string.Empty; + + [ExporterHeader(DisplayName = "检查批次名称")] + public string VisitName { get; set; } = string.Empty; + + [ExporterHeader(DisplayName = "检查时间", Format = "yyyy-mm-dd hh:mm:ss")] + public DateTime StudyTime { get; set; } + + [ExporterHeader(DisplayName = "检查技术")] + public string Modality { get; set; } = string.Empty; + + [ExporterHeader(IsIgnore = true)] + public bool IsDicomStudy { get; set; } + + [ExporterHeader(DisplayName = "影像类型")] + public string ImageType => IsDicomStudy ? "Dicom" : "非Dicom"; + + [ExporterHeader(DisplayName = "历史窗口")] + public string HistoryWindow { get; set; } = string.Empty; + + [ExporterHeader(DisplayName = "之前超窗调整后没超窗")] + [ValueMapping(text: "yes", true)] + [ValueMapping(text: "no", false)] + public bool IsOverWindowNowNotOverWindow { get; set; } + + [ExporterHeader(DisplayName = "目前窗口")] + public string NowWindow { get; set; } = string.Empty; + + [ExporterHeader(IsIgnore = true)] + public DateTime CreateTime { get; set; } + } + +} diff --git a/IRaCIS.Core.Application/_MediatR/Handlers/AnonymizeCacheHandler.cs b/IRaCIS.Core.Application/_MediatR/Handlers/AnonymizeCacheHandler.cs new file mode 100644 index 0000000..6fb08a7 --- /dev/null +++ b/IRaCIS.Core.Application/_MediatR/Handlers/AnonymizeCacheHandler.cs @@ -0,0 +1,44 @@ +using EasyCaching.Core; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore; +using MediatR; + +namespace IRaCIS.Core.Application.MediatR.Handlers +{ + + public class AnonymizeCacheRequest : IRequest + { + + } + public class AnonymizeCacheHandler : IRequestHandler + { + private readonly IRepository _repository; + + private readonly IEasyCachingProvider _provider; + + /// + /// 构造函数注入 + /// + public AnonymizeCacheHandler(IRepository repository, IEasyCachingProvider provider) + { + _repository = repository; + + _provider = provider; + } + + + public Task Handle(AnonymizeCacheRequest request, CancellationToken cancellationToken) + { + var systemAnonymizationList = _repository.Where(t => t.IsEnable).ToList(); + + _provider.Set(StaticData.Anonymize.Anonymize_AddFixedFiled, systemAnonymizationList.Where(t => t.IsAdd && t.IsFixed).ToList(), TimeSpan.FromDays(7)); + _provider.Set(StaticData.Anonymize.Anonymize_AddIRCInfoFiled, systemAnonymizationList.Where(t => t.IsAdd && t.IsFixed == false).ToList(), TimeSpan.FromDays(7)); + _provider.Set(StaticData.Anonymize.Anonymize_FixedField, systemAnonymizationList.Where(t => t.IsAdd == false && t.IsFixed).ToList(), TimeSpan.FromDays(7)); + _provider.Set(StaticData.Anonymize.Anonymize_IRCInfoField, systemAnonymizationList.Where(t => t.IsAdd == false && t.IsFixed == false).ToList(), TimeSpan.FromDays(7)); + + return Task.FromResult(true); + } + } + + +} diff --git a/IRaCIS.Core.Application/_MediatR/Handlers/ConsistencyVerificationHandler.cs b/IRaCIS.Core.Application/_MediatR/Handlers/ConsistencyVerificationHandler.cs new file mode 100644 index 0000000..a65dc03 --- /dev/null +++ b/IRaCIS.Core.Application/_MediatR/Handlers/ConsistencyVerificationHandler.cs @@ -0,0 +1,269 @@ +using AutoMapper; +using IRaCIS.Core.Application.MediatR.CommandAndQueries; +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; +using MediatR; +using System.Linq.Expressions; +using System.Text; + +namespace IRaCIS.Core.Application.MediatR.Handlers +{ + public class ConsistencyVerificationHandler : IRequestHandler + { + private readonly IRepository _studyRepository; + private readonly IUserInfo _userInfo; + private readonly IRepository _subjectRepository; + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _trialSiteRepository; + private readonly IMapper _mapper; + private readonly IRepository _noneDicomStudyRepository; + + /// + /// 构造函数注入 + /// + + public ConsistencyVerificationHandler(IRepository studyRepository, IUserInfo userInfo, + IRepository subjectRepository, IRepository subjectVisitRepository, + IRepository trialSiteRepository, IRepository noneDicomStudyRepository, + IMapper mapper) + { + _noneDicomStudyRepository = noneDicomStudyRepository; + _studyRepository = studyRepository; + _userInfo = userInfo; + _subjectRepository = subjectRepository; + _subjectVisitRepository = subjectVisitRepository; + _trialSiteRepository = trialSiteRepository; + _mapper = mapper; + } + + async Task IRequestHandler.Handle(ConsistencyVerificationRequest request, CancellationToken cancellationToken) + { + var trialId = request.TrialId; + + //处理Excel大小写 + request.ETCList.ForEach(t => { t.Modality = t.Modality.ToUpper().Trim(); t.StudyDate = Convert.ToDateTime(t.StudyDate).ToString("yyyy-MM-dd"); t.SiteCode = t.SiteCode.ToUpper().Trim(); t.VisitName = t.VisitName.ToUpper().Trim(); t.SubjectCode = t.SubjectCode.ToUpper().Trim(); }); + var etcList = request.ETCList; + + //Expression> subjectVisitLambda2 = x => x.TrialId == request.TrialId; + + //subjectVisitLambda2= subjectVisitLambda2.And(x => x.CheckState == CheckStateEnum.ToCheck && x.AuditState == AuditStateEnum.QCPassed || (x.CheckState == CheckStateEnum.CVIng && x.AuditState == AuditStateEnum.QCPassed)); + + Expression> subjectVisitLambda = x => x.TrialId == request.TrialId && + (x.CheckState == CheckStateEnum.ToCheck && x.AuditState == AuditStateEnum.QCPassed || (x.CheckState == CheckStateEnum.CVIng && x.AuditState == AuditStateEnum.QCPassed)); + + var dicomQuery = from sv in _subjectVisitRepository.Where(subjectVisitLambda) + join trialSite in _trialSiteRepository.Where(t => t.TrialId == trialId) on sv.SiteId equals trialSite.SiteId + join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id + join study in _studyRepository.AsQueryable() on sv.Id equals study.SubjectVisitId + select new CheckDBModel() + { + SubjectVisitId = sv.Id, + SiteCode = trialSite.TrialSiteCode, + StudyDate = study.StudyTime == null ? string.Empty : ((DateTime)study.StudyTime).ToString("yyyy-MM-dd"), + StudyId = study.Id, + Modality = study.ModalityForEdit, + SubjectCode = subject.Code, + VisitName = sv.VisitName, + }; + + var noneDicomQuey = from sv in _subjectVisitRepository.Where(subjectVisitLambda) + join trialSite in _trialSiteRepository.Where(t => t.TrialId == trialId) on sv.SiteId equals trialSite.SiteId + join subject in _subjectRepository.AsQueryable() on sv.SubjectId equals subject.Id + join noneDicomStudy in _noneDicomStudyRepository.AsQueryable() on sv.Id equals noneDicomStudy.SubjectVisitId + select new CheckDBModel() + { + SubjectVisitId = sv.Id, + SiteCode = trialSite.TrialSiteCode, + StudyDate = noneDicomStudy.ImageDate.ToString("yyyy-MM-dd"), + StudyId = noneDicomStudy.Id, + Modality = noneDicomStudy.Modality, + SubjectCode = subject.Code, + VisitName = sv.VisitName, + }; + + var dbList = (await dicomQuery.ToListAsync()).Union(await noneDicomQuey.ToListAsync()).ToList(); + + //处理数据库 大小写 + dbList.ForEach(t => { t.Modality = t.Modality.ToUpper().Trim(); t.SiteCode = t.SiteCode.ToUpper().Trim(); t.VisitName = t.VisitName.ToUpper().Trim(); t.SubjectCode = t.SubjectCode.ToUpper().Trim(); }); + + var dbCheckList = _mapper.Map>(dbList); + + ////按照数据库数据检查批次分组 按照数据库的数据 一个个的检查批次对比 + //var svGroup = dbList.GroupBy(t => new { t.SubjectVisitId, t.SiteCode, t.SubjectCode, t.VisitName }) + // .Select(g => new { g.Key.SubjectCode, g.Key.VisitName, g.Key.SiteCode, g.Key.SubjectVisitId, StudyList = g.ToList() }).ToList(); + + //按照Excel数据检查批次分组 按照数据库的数据 一个个的检查批次对比 + var svExcelGroup = etcList.GroupBy(t => new { t.SiteCode, t.SubjectCode, t.VisitName }) + .Select(g => new { g.Key.SubjectCode, g.Key.VisitName, g.Key.SiteCode, ExcelStudyList = g.ToList() }).ToList(); + + + foreach (var sv in svExcelGroup) + { + + //Excel 的数据 在IRC 中可以找到该检查批次 + if (dbCheckList.Any(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName)) + { + + var dbVisitStudyList = dbList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList(); + + //找到etc 当前visit site 和subject 一致的检查列表 + var etcVisitStudyList = etcList.Where(t => t.SubjectCode == sv.SubjectCode && t.SiteCode == sv.SiteCode && t.VisitName == sv.VisitName).ToList(); + + + + StringBuilder dialogMsg = new StringBuilder(); + dialogMsg.Append("您好,根据本系统自动识别,该患者当前检查批次在IRC系统中已提交的影像检查情况如下:"); + var num = 0; + List paramInfoList = new List(); + foreach (var item in dbVisitStudyList) + { + num++; + dialogMsg.AppendLine($"
{num}.{item.StudyDate}的{item.Modality}影像检查"); + paramInfoList.Add(new ParamInfoDto() + { + Modality = item.Modality, + StudyDate = item.StudyDate, + }); + } + + var subjectVisitId = dbVisitStudyList.FirstOrDefault().SubjectVisitId; + var dbSV = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); + + #region 更换核对的顺序 以Excel 数据为准 注释 + //// 该检查批次 在EDC Excel中没有任何数据 + //if (etcVisitStudyList.Count == 0) + //{ + // dialogMsg.AppendLine($"
"); + // dialogMsg.AppendLine($"
存在问题如下:"); + // num = 0; + // foreach (var item in sv.StudyList) + // { + // num++; + // dialogMsg.AppendLine($"
{num}.{item.StudyDate}的{item.Modality}影像检查(EDC 缺少) "); + // } + + // dialogMsg.AppendLine($"
"); + // dialogMsg.AppendLine(@$"
说明:为高效解决/处理以上全部质疑问题,麻烦您准确核实实际影像检查情况。请注意影像日期与实际检查的日期可能会不一致,部分检查(如PET -CT)可能同时存在多种模态影像。准确核实后,请回复该检查批次正确的影像检查情况。"); + + // dbSV.CheckResult = "当前检查批次在EDC表中未找到数据,请核对 SubjectCode、 SiteCode 、VisitName 是否和EDC系统保持一致"; + // dbSV.CheckState = CheckStateEnum.CVIng; + // dbSV.ForwardState = ForwardStateEnum.ToForward; + // dbSV.CheckChallengeState = CheckChanllengeTypeEnum.PMWaitCRCReply; + // dbSV.CheckChallengeDialogList.Add(new CheckChallengeDialog() + // { + // SubjectVisitId = sv.SubjectVisitId, + // IsCRCNeedReply = true, + // TalkContent = dialogMsg.ToString(), + // ParamInfo = JsonConvert.SerializeObject(paramInfoList), + // UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, + // CreateTime = DateTime.Now + // }); + //} + //else + #endregion + + { + //etc 和数据库 并集 + var unionList = dbVisitStudyList.Union(etcVisitStudyList); + // 数据库存在 + var dbExceptExcel = dbVisitStudyList.Except(etcVisitStudyList); + + // excel 存在 + var excelExceptDB = etcVisitStudyList.Except(dbCheckList); + + //ETC 和系统的完全一致 两者没有差别 + if (dbExceptExcel.Count() == 0 && excelExceptDB.Count() == 0) + { + dialogMsg.AppendLine($"
"); + dialogMsg.AppendLine($"核对EDC数据,完全一致, 审核通过"); + + + // dialogMsg.AppendLine(@$"
说明:为高效解决/处理以上全部质疑问题,麻烦您准确核实实际影像检查情况。请注意影像日期与实际检查的日期可能会不一致,部分检查(如PET -CT)可能同时存在多种模态影像。准确核实后,请回复该检查批次正确的影像检查情况。"); + dbSV.CheckState = CheckStateEnum.CVPassed; + dbSV.CheckUserId = _userInfo.Id; + dbSV.CheckPassedTime = DateTime.Now; + dbSV.CheckResult = "核对EDC数据,完全一致"; + dbSV.ManualPassReason = "自动核查通过"; + + //维护状态 + dbSV.ReadingStatus = ReadingStatusEnum.TaskAllocate; + + dbSV.RequestBackState = dbSV.RequestBackState == RequestBackStateEnum.CRC_RequestBack ? RequestBackStateEnum.PM_NotAgree : RequestBackStateEnum.NotRequest; + dbSV.CheckChallengeDialogList.Add(new CheckChallengeDialog() + { + SubjectVisitId = subjectVisitId, + ParamInfo = JsonConvert.SerializeObject(paramInfoList), + TalkContent = dialogMsg.ToString(), + UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, + CreateTime = DateTime.Now + }); + } + // ETC 和系统的有区别 + else + { + dialogMsg.AppendLine($"
"); + dialogMsg.AppendLine($"
存在问题如下:"); + + num = 0; + foreach (var item in dbExceptExcel) + { + num++; + dialogMsg.AppendLine($"
{num}.{item.StudyDate}的{item.Modality}影像检查(EDC 缺少) "); + } + + foreach (var item in excelExceptDB) + { + num++; + dialogMsg.AppendLine($"
{num}.{item.StudyDate}的{item.Modality}影像检查(IRC 缺少) "); + } + + dialogMsg.AppendLine($"
"); + dialogMsg.AppendLine(@$"
说明:为高效解决/处理以上全部质疑问题,麻烦您准确核实实际影像检查情况。请注意影像日期与实际检查的日期可能会不一致,部分检查(如PET -CT)可能同时存在多种模态影像。准确核实后,请回复该检查批次正确的影像检查情况。"); + + dbSV.CheckResult = "根据导入的一致性核查数据,请确认本检查批次以下不一致检查项信息:" + String.Join(" | ", dbExceptExcel.Select(t => $"EDC 缺少:{t.StudyDate} {t.Modality} ")) + " | " + + String.Join(" | ", excelExceptDB.Select(t => $"IRC 缺少:{t.StudyDate} {t.Modality}")); + //新增一致性核查质疑记录 + + + dbSV.CheckState = CheckStateEnum.CVIng; + dbSV.CheckChallengeState = CheckChanllengeTypeEnum.PMWaitCRCReply; + + //讲核查结果发送消息给IC + dbSV.CheckChallengeDialogList.Add(new CheckChallengeDialog() + { + SubjectVisitId = subjectVisitId, + IsCRCNeedReply = true, + TalkContent = dialogMsg.ToString(), + ParamInfo = JsonConvert.SerializeObject(paramInfoList), + UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, + CreateTime = DateTime.Now + }); + } + + } + dbSV.CheckTime = DateTime.Now; + } + + + + + + + + + + + + + + + } + await _subjectVisitRepository.SaveChangesAsync(); + return "OK"; + + } + + } + + +} diff --git a/IRaCIS.Core.Application/_MediatR/Handlers/TrialStateCacheHandler.cs b/IRaCIS.Core.Application/_MediatR/Handlers/TrialStateCacheHandler.cs new file mode 100644 index 0000000..0f01115 --- /dev/null +++ b/IRaCIS.Core.Application/_MediatR/Handlers/TrialStateCacheHandler.cs @@ -0,0 +1,45 @@ +using EasyCaching.Core; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore; +using MediatR; + +namespace IRaCIS.Core.Application.MediatR.Handlers +{ + + public class TrialStateCacheRequest : IRequest + { + + } + public class TrialStateCacheHandler : IRequestHandler + { + private readonly IRepository _trialRepository; + + private readonly IEasyCachingProvider _provider; + + /// + /// 构造函数注入 + /// + public TrialStateCacheHandler(IRepository trialRepository, IEasyCachingProvider provider) + { + _trialRepository = trialRepository; + + _provider = provider; + + } + + + public async Task Handle(TrialStateCacheRequest request, CancellationToken cancellationToken) + { + //项目启动,将项目状态缓存,因为hangfire 加入后台任务,还是向队列添加任务,执行都有延迟,效果不好 + var list = await _trialRepository.Select(t => new { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr }).ToListAsync(); + + // 每天都会有任务刷新状态,项目编辑 添加时 都会处理到缓存中 + list.ForEach(t => _provider.Set(t.TrialId.ToString(), t.TrialStatusStr, TimeSpan.FromDays(7))); + + + return true; + } + } + + +} diff --git a/IRaCIS.Core.Domain.Share/Allocation/AllocationRelation.cs b/IRaCIS.Core.Domain.Share/Allocation/AllocationRelation.cs new file mode 100644 index 0000000..52885dd --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Allocation/AllocationRelation.cs @@ -0,0 +1,392 @@ + +using System.ComponentModel.DataAnnotations; +namespace IRaCIS.Core.Domain.Share +{ + public enum ReadingCategory + { + //检查批次 + Visit = 1, + + ////阅片周期 + //ReadingPeriod = 3, + + //全局 + Global = 2, + + //裁判 + Judge = 4, + + /// + /// 肿瘤学 + /// + Oncology = 5, + + + + + } + + public enum GenerateTaskCategory + { + //检查批次 + Visit = 1, + + ////阅片周期 + //ReadingPeriod = 3, + + //全局 + Global = 2, + + //裁判 + Judge = 4, + + /// + /// 肿瘤学 + /// + Oncology = 5, + + + + //生成任务 额外增加的 前端那边不加 + + ReReading = 6, + + SelfConsistent = 7, + + GroupConsistent = 8, + } + + public enum TaskAllocationState + { + //未分配 + NotAllocate = 0, + + //预分配 + InitAllocated = 1, + + //已分配 + Allocated = 2, + + + } + + + public enum MedicalReviewAuditState + { + //待审核 + WaitAudit=0, + + //审核中 + Auditing=1, + + HaveSigned=2 + } + + public enum MedicalDialogClose + { + /// + /// 无 + /// + [Display(Name = "")] + None = 0, + + /// + /// 问题已解决 + /// + [Display(Name = "问题已解决")] + ProblemSolved = 1, + + /// + /// 问题无法解决 + /// + [Display(Name = "问题无法解决")] + Unresolvable = 2, + + /// + /// IR申请重阅 + /// + [Display(Name = "IR申请重阅")] + IRApplyReReading = 3, + + /// + /// 其他原因 + /// + [Display(Name = "IR申请重阅")] + OtherReason = 4 + } + + /// + /// + /// + public enum AuditAdvice + { + /// + /// 无 + /// + None=0, + + /// + /// 退回重阅 + /// + HeavyReading = 1, + + /// + /// 提醒注意 + /// + Remind = 2, + } + + + public enum MedicalReviewDoctorUserIdea + { + defalut=0, + + Agree=1, + + NotAgree=2 + } + + public enum Arm + { + SingleReadingArm = 0, + + DoubleReadingArm1 = 1, + + DoubleReadingArm2 = 2, + + JudgeArm = 3, + + TumorArm = 4, + + //单独的 + GroupConsistentArm=8 + } + + /// + /// 分配对象 + /// + public enum TaskAllocateObj + { + Subject = 0, + + SubjectVisit = 1 + } + + public enum TableQuestionType + { + /// + /// 自定义 + /// + Customize=0, + + /// + /// 关联数据 + /// + LinkedData=1, + + /// + /// 关联Question + /// + LinkedQuestion = 2, + + /// + /// 字典 + /// + Dictionary = 3, + + } + + public enum FormType + { + None=0, + + /// + /// 单页 + /// + SinglePage=1, + + /// + /// 多页 + /// + MultiplePage = 2, + + } + + //分配默认状态 + public enum TaskAllocateDefaultState + { + //默认值 看是否需要项目初始化时就给默认值 1 或者2 + None = 0, + + //预分配 + InitAllocated = 1, + + //已分配 + Allocated = 2, + } + + public enum ReadingTool + { + Dicom=0, + + NoDicom=1, + } + + public enum ArbitrationRule + { + //默认值 看是否需要项目初始化时就给默认值 1 或者2 + None = 0, + + /// + /// 检查批次 + /// + Visit=1, + + /// + /// 阅片 + /// + Reading = 2, + + /// + /// 无 + /// + NA=3, + } + + + public enum ReadingMethod + { + Single = 1, + + Double = 2, + + Special = 3 + } + + public enum ReadingTaskViewMethod + { + //患者 + Subject = 0, + + //检查批次/阅片期 + ReadingPeriodOrVisit = 2, + + } + + public enum TaskState + { + //有效 + Effect = 0, + + //未生效 + NotEffect = 1, + + // 失效 + Adbandon = 3, + + //重置 (裁判任务做了,签名了 算工作量) + HaveReturned = 4 + } + + //阅片状态 + public enum ReadingTaskState + { + WaitReading = 0, + + Reading = 1, + + HaveSigned = 2, + + + } + + /// + /// 任务加急状态 + /// + public enum TaskUrgentType + { + /// + /// 入组确认 + /// + EnrollmentConfirm=1, + + /// + /// PD进展 + /// + PDProgress=2, + + /// + /// 检查批次加急 + /// + VisitUrgent=3, + + /// + /// 其他 + /// + Other=4, + } + + + //public enum AnalysisType + //{ + // Default=0, + + // Self=1, + + // InnerGroup=2 + //} + + // + public enum RequestReReadingType + { + Default = 0, + + //IR 申请 + DocotorApply = 1, + + //PM 申请 + TrialGroupApply = 2 + } + + //重阅申请结果 + + public enum RequestReReadingResult + { + Default = 0, + + Agree = 1, + + Reject = 2 + } + + public enum ReReadingApplyState + { + Default = 0, + + + Agree = 2, + + Reject = 3, + + + //IR 申请 + DocotorHaveApplyed = 4, + + //PM 申请 + TrialGroupHaveApplyed = 5 + + + + + } + + public enum CompleteClinicalDataEnum + { + NA=-1, + + NotComplete=0, + Complete=1, + } + + +} diff --git a/IRaCIS.Core.Domain.Share/AuthUser/IUserInfo.cs b/IRaCIS.Core.Domain.Share/AuthUser/IUserInfo.cs new file mode 100644 index 0000000..eb4448c --- /dev/null +++ b/IRaCIS.Core.Domain.Share/AuthUser/IUserInfo.cs @@ -0,0 +1,60 @@ +using System; + +namespace IRaCIS.Core.Domain.Share +{ + /// + /// 用户信息接口 + /// + public interface IUserInfo + { + /// + /// 主键 + /// + Guid Id { get; } + + /// + /// 用户名 + /// + string UserName { get; } + + /// + /// 昵称 + /// + string RealName { get; } + string ReviewerCode { get; } + + bool IsAdmin { get; } + + bool IsTestUser { get; } + + string UserTypeShortName { get; } + + string UserTypeEnumStr { get; } + + int UserTypeEnumInt { get; } + + Guid UserTypeId { get; } + + string UserToken { get; } + + string PermissionStr { get; } + + string IP { get; } + + string LocalIp { get; } + + bool IsEn_Us { get; } + + string RequestUrl { get; } + + Guid? SignId { get; set; } + + Guid? BatchId { get; set; } + + + /// + /// 字符串形式 标识时区 + /// + string TimeZoneId { get; } + } +} diff --git a/IRaCIS.Core.Domain.Share/AuthUser/UserInfo.cs b/IRaCIS.Core.Domain.Share/AuthUser/UserInfo.cs new file mode 100644 index 0000000..890033c --- /dev/null +++ b/IRaCIS.Core.Domain.Share/AuthUser/UserInfo.cs @@ -0,0 +1,344 @@ +using System; +using System.Linq; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Http; + +namespace IRaCIS.Core.Domain.Share +{ + /// + /// 用户信息 + /// + public class UserInfo : IUserInfo + { + private readonly IHttpContextAccessor _accessor; + + public UserInfo(IHttpContextAccessor accessor) + { + _accessor = accessor; + } + + /// + /// 用户Id + /// + public Guid Id + { + get + { + var id = _accessor?.HttpContext?.User?.FindFirst(JwtIRaCISClaimType.Id); + if (id != null && !string.IsNullOrEmpty(id.Value)) + { + return Guid.Parse(id.Value); + } + return Guid.Empty; + } + } + + public Guid UserTypeId + { + get + { + var userTypeId = _accessor?.HttpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeId); + if (userTypeId != null && !string.IsNullOrEmpty(userTypeId.Value)) + { + return Guid.Parse(userTypeId.Value); + } + return Guid.Empty; + } + } + + + /// + /// 用户名 + /// + public string UserName + { + get + { + var name = _accessor?.HttpContext?.User?.FindFirst(JwtIRaCISClaimType.Name); + + if (name != null && !string.IsNullOrEmpty(name.Value)) + { + return name.Value; + } + + return ""; + } + } + + + public string RealName + { + get + { + var name = _accessor?.HttpContext?.User?.FindFirst(JwtIRaCISClaimType.RealName); + + if (name != null && !string.IsNullOrEmpty(name.Value)) + { + return name.Value; + } + + return ""; + } + } + + public string ReviewerCode + { + get + { + var reviewerCode = _accessor?.HttpContext?.User?.FindFirst(JwtIRaCISClaimType.Code); + + if (reviewerCode != null && !string.IsNullOrEmpty(reviewerCode.Value)) + { + return reviewerCode.Value; + } + return string.Empty; + } + } + + + public string UserTypeShortName + { + get + { + var userType = _accessor?.HttpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName); + + if (userType != null && !string.IsNullOrEmpty(userType.Value)) + { + return userType.Value; + } + return string.Empty; + } + } + + + + public string UserTypeEnumStr + { + get + { + var userType = _accessor?.HttpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeEnum); + + if (userType != null && !string.IsNullOrEmpty(userType.Value)) + { + return userType.Value; + } + return UserTypeEnum.ShareImage.ToString(); + } + } + + public int UserTypeEnumInt + { + get + { + var userType = _accessor?.HttpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeEnumInt); + + if (userType != null && !string.IsNullOrEmpty(userType.Value)) + { + return int.Parse(userType.Value); + } + return 0; + } + } + + public bool IsAdmin + { + get + { + var userType = _accessor?.HttpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeEnumInt); + + if (userType != null && !string.IsNullOrEmpty(userType.Value)) + { + return int.Parse(userType.Value) == (int)UserTypeEnum.SuperAdmin; + } + return false; + } + } + + public bool IsTestUser + { + get + { + var isTestUserClaime = _accessor?.HttpContext?.User?.FindFirst(JwtIRaCISClaimType.IsTestUser); + + if (isTestUserClaime != null && !string.IsNullOrEmpty(isTestUserClaime.Value)) + { + return bool.Parse(isTestUserClaime.Value); + } + return false; + } + } + + public string UserToken + { + get + { + + var authorizationHeader = _accessor?.HttpContext?.Request.Headers["Authorization"].ToString(); + if (!string.IsNullOrWhiteSpace(authorizationHeader)) + { + return authorizationHeader.Substring(7); + + } + + var token = _accessor?.HttpContext?.Request.Query["access_token"].ToString(); + + if (!string.IsNullOrWhiteSpace(token)) + { + return token; + + } + return string.Empty; + + + } + } + + public string PermissionStr + { + get + { + var permissionStr = _accessor?.HttpContext?.User?.FindFirst(JwtIRaCISClaimType.PermissionStr); + + if (permissionStr != null && !string.IsNullOrEmpty(permissionStr.Value)) + { + return permissionStr.Value; + } + return string.Empty; + } + } + + public string IP + { + get + { + + return _accessor?.HttpContext?.Connection.RemoteIpAddress.ToString(); + } + } + + + public string LocalIp + { + get + { + + return _accessor?.HttpContext?.Connection.LocalIpAddress.MapToIPv4().ToString(); + } + } + + + public bool IsEn_Us + { + get + { + var lan = _accessor?.HttpContext?.Request?.Headers["Accept-Language"]; + + if (lan is not null && !string.IsNullOrEmpty(lan.Value)) + { + return lan.Value == "en-US,en;q=0.5".ToString(); + } + return true; + } + } + + + public string RequestUrl + { + + get + { + var url = _accessor?.HttpContext?.Request?.Path.ToString(); + + var list = url.Split('/').Where(t => !string.IsNullOrWhiteSpace(t)).ToList(); + + if (url.Contains("Inspection", StringComparison.OrdinalIgnoreCase)) + { + list.RemoveAt(0); + + return string.Join('/', list.Take(2)); + } + else + { + return string.Join('/', list.Take(2)); + } + + + } + } + + public string TimeZoneId + { + + get + { + var timeZoneId = _accessor?.HttpContext?.Request?.Headers["TimeZoneId"]; + + if (timeZoneId is not null && !string.IsNullOrEmpty(timeZoneId.Value)) + { + return timeZoneId.Value; + } + return "Asia/Shanghai"; + + + } + } + + + public Guid? SignId + { + + get; set; + } + + + public Guid? BatchId + { + + get; set; + } + } + + public static class ClaimAttributes + { + /// + /// 用户Id + /// + public const string UserId = "id"; + + public const string UserTypeId = "userTypeId"; + + /// + /// 用户名 + /// + public const string UserName = "name"; + + /// + /// 姓名 + /// + public const string RealName = "realName"; + + public const string ReviewerCode = "reviewerCode"; + + public const string UserType = "userTypeShortName"; + } + + public struct JwtIRaCISClaimType + { + public const string Id = "id"; + public const string Code = "code"; + public const string Name = "name"; + public const string RealName = "realName"; + public const string UserTypeId = "userTypeId"; + public const string UserTypeEnum = "userTypeEnum"; + public const string UserTypeEnumName = "userTypeEnumName"; + public const string UserTypeEnumInt = "userTypeEnumInt"; + public const string UserTypeShortName = "userTypeShortName"; + public const string IsAdmin = "isAdmin"; + + public const string IsTestUser = "isTestUser"; + + public const string PermissionStr = "permissionStr"; + + + } +} diff --git a/IRaCIS.Core.Domain.Share/Common/DataTypeEnum.cs b/IRaCIS.Core.Domain.Share/Common/DataTypeEnum.cs new file mode 100644 index 0000000..18d1263 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Common/DataTypeEnum.cs @@ -0,0 +1,22 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + public enum DataTypeEnum + { + //医生相关 + Reviewer = 0, + + //项目基本信息相关 + TrialBasicDate= 1, + + + //项目Site调研相关 + //TrialSiteSurvey=2, + + + ////项目QC相关 + //TrialQCChanlengeType = 2, + + } +} diff --git a/IRaCIS.Core.Domain.Share/Common/EmailScenarioEnum.cs b/IRaCIS.Core.Domain.Share/Common/EmailScenarioEnum.cs new file mode 100644 index 0000000..d14ccc5 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Common/EmailScenarioEnum.cs @@ -0,0 +1,71 @@ +using System.ComponentModel; + +namespace IRaCIS.Core.Domain.Share +{ + public enum EmailScenarioEnum + { + + None = 0, + + [Description("用户体系")] + User = 1, + + [Description("中心调研邀请")] + SiteSurveyInvite = 2, + + [Description("中心调研审批通知")] + SiteSurveyApproval = 3, + + } + + + + + public enum CommonDocumentFileType + { + UploadDataTemplate = 1, + + ExportDataTempalate = 2, + + EmailTemplate = 3, + + NormalDataTemplate = 4, + + } + + + public enum CommonDocumentBusinessScenario + { + + EnrollConfirmed = 1, + + PDConfirmed = 2, + + Trial=3, + + Reviewer=4, + } + + + public enum EmailUserType + { + + //变为手动输入了,这里不需要了 + From =1, + + To=2, + + Copy=3 + } + + + + public enum BasicDataTypeEnum + { + None = 0, + + Email = 1, + + Sign = 2 + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain.Share/Common/EmailStoreMode.cs b/IRaCIS.Core.Domain.Share/Common/EmailStoreMode.cs new file mode 100644 index 0000000..7ea2861 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Common/EmailStoreMode.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Domain.Share.Common +{ + + + public enum EmailStoreSendMode + { + //不存储本地 发送 + NotStoreLocalSend = 0, + + //存储本地发送 + StoreLocalSend = 1, + + //存储本地不发送 + OnlyStoreLocalNotSentEmail = 2, + + //不存储本地 发送 + NotStoreLocalOnlySentEmail = 3, + + } + +} diff --git a/IRaCIS.Core.Domain.Share/Common/SignEnum.cs b/IRaCIS.Core.Domain.Share/Common/SignEnum.cs new file mode 100644 index 0000000..43cbcf9 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Common/SignEnum.cs @@ -0,0 +1,34 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + public enum SignEnum + { + //Trial 逻辑配置确认 + + TrialLogicConfim = 101, + + + + TrialProcessConfim = 102, + + + + TrialUrgentConfim = 103, + + + + TrialLogicConfimUpdate = 104, + + + TrialProcessUpdate = 105, + + + TrialUrgentConfimUpdate = 106, + + + TrialQCQuestionConfirm=107, + + } + +} diff --git a/IRaCIS.Core.Domain.Share/Common/VerifyType.cs b/IRaCIS.Core.Domain.Share/Common/VerifyType.cs new file mode 100644 index 0000000..9018cf6 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Common/VerifyType.cs @@ -0,0 +1,30 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + public enum VerifyType + { + Email = 0, + + Phone = 1 + + } + + + public enum DicDataTypeEnum + { + //字典下拉配置 + Config = 0, + + //下拉框 + Select = 1, + + //枚举 + Enum=2, + + //Bool + + Bool=3 + + } +} diff --git a/IRaCIS.Core.Domain.Share/Doctor/DoctorStatus.cs b/IRaCIS.Core.Domain.Share/Doctor/DoctorStatus.cs new file mode 100644 index 0000000..f938f37 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Doctor/DoctorStatus.cs @@ -0,0 +1,37 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum ContractorStatusEnum + { + None = 0,//搜索时不条件查询--未添加信息时数据库的默认值 + Cooperation = 1, + Noncooperation = 2 + } + + public enum ReviewerInformationConfirmStatus + { + None = 0, + ConfirmPass = 1, + ConfirmRefuse = 2 + } + + public enum ResumeStatusEnum + { + None = 0, + + Pass = 1, + + Failed = 2 + } + + + + public enum ReviewerEnrollStatus + { + Yes = 1, + No = 0 + } + + + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain.Share/Doctor/WorkLoadStatus.cs b/IRaCIS.Core.Domain.Share/Doctor/WorkLoadStatus.cs new file mode 100644 index 0000000..0edddb8 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Doctor/WorkLoadStatus.cs @@ -0,0 +1,15 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum WorkloadStatus + { + ToBeAllocated=0,//待分配 + + Distributed = 30,//已分配 + + Reading = 40,//都片中,未提交读片报告之前 + + ReviewFinish = 80,//读片完成 + + //Archived=100,//流程结束,归档锁库 + } +} diff --git a/IRaCIS.Core.Domain.Share/IRaCIS.Core.Domain.Share.csproj b/IRaCIS.Core.Domain.Share/IRaCIS.Core.Domain.Share.csproj new file mode 100644 index 0000000..e29721e --- /dev/null +++ b/IRaCIS.Core.Domain.Share/IRaCIS.Core.Domain.Share.csproj @@ -0,0 +1,27 @@ + + + + net6.0 + AnyCPU;x64 + + + + ..\bin + + + + ..\bin + + + + ..\bin + + + + + + + + + + diff --git a/IRaCIS.Core.Domain.Share/Management/SystemNotice.cs b/IRaCIS.Core.Domain.Share/Management/SystemNotice.cs new file mode 100644 index 0000000..1e86720 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Management/SystemNotice.cs @@ -0,0 +1,100 @@ +using System.ComponentModel.DataAnnotations; + +namespace IRaCIS.Core.Domain.Share.Management +{ + public enum SystemNotice_NoticeModeEnum + { + //跑马灯 + RunningLight = 0, + + //页面 + Pages = 1 + } + + public enum SystemNotice_NoticeStateEnum + { + NotPublish = 0, + + HavePublished = 1, + + HaveExpired = 2 + + } + + + public enum SystemNotice_NoticeLevelEnum + { + SystemLevel = 0, + + TrialLevel = 1 + } + + public enum SystemNotice_NoticeTypeEnum + { + SystemUpgrade = 0, + + TrialChanges = 1 + } + + + public enum SystemNotice_ApplicableProjectEnum + { + //非正式项目 + OfficialTrial = 0, + + NoneOfficial = 1, + + Training = 3, + + All = 2, + + + } + + public enum FrontAudit + { + /// + ///id + /// + [DisplayAttribute(Name = "id")] + Id = 1, + + /// + ///ChildGroup + /// + [DisplayAttribute(Name = "ChildGroup")] + ChildGroup = 2, + + /// + ///Code + /// + [DisplayAttribute(Name = "Code")] + Code =3, + + + /// + ///DictionaryCode + /// + [DisplayAttribute(Name = "DictionaryType")] + DictionaryType = 4, + } + + + + public enum FrontAuditDateType + { + /// + ///Date + /// + [DisplayAttribute(Name = "Date")] + Date = 0, + + /// + ///DateTime + /// + [DisplayAttribute(Name = "DateTime")] + DateTime = 1, + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain.Share/NetResource.cs b/IRaCIS.Core.Domain.Share/NetResource.cs new file mode 100644 index 0000000..85834fc --- /dev/null +++ b/IRaCIS.Core.Domain.Share/NetResource.cs @@ -0,0 +1,50 @@ +using System.Runtime.InteropServices; + +namespace IRaCIS.Core.Domain.Share +{ + [StructLayout(LayoutKind.Sequential)] + public class NetResource + { + public int dwScope; + public int dwType; + public int dwDisplayType; + public int dwUsage; + public string lpLocalName; + public string lpRemoteName; + public string lpComment; + public string lpProvider; + } + + + public class WNetAddConnectionHelper + { + [DllImport("mpr.dll", EntryPoint = "WNetAddConnection2")] + private static extern uint WNetAddConnection2(NetResource lpNetResource, string lpPassword, string lpUsername, uint dwFlags); + + [DllImport("mpr.dll")] + public static extern int WNetCancelConnection2A(string sharename, int dwFlags, int fForce); + + public static void Connect() + { + + var remoteName = @"\\192.168.1.119\Potomac_01"; + NetResource netResource = new NetResource(); + netResource.dwScope = 1; + netResource.dwType = 1; + netResource.dwDisplayType = 3; + netResource.dwUsage = 1; + netResource.lpLocalName = "W:"; + netResource.lpRemoteName = remoteName.TrimEnd('\\'); + + WNetAddConnection2(netResource, "Everest@suzhou406", "share", 1); + + } + + public static int Disconnect() + { + return WNetCancelConnection2A("W:", 1, 1); + } + + } +} + diff --git a/IRaCIS.Core.Domain.Share/QC/AuditStateEnum.cs b/IRaCIS.Core.Domain.Share/QC/AuditStateEnum.cs new file mode 100644 index 0000000..3e32a27 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/QC/AuditStateEnum.cs @@ -0,0 +1,30 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + + public enum AuditStateEnum + { + //不可用 + None = 0, + + // 待审核\ 待初审 + ToAudit = 3, + + //1st QC进行了操作 + InPrimaryQC = 4, + + PrimaryQCPassed = 5, + + //2nd QC进行操作 + InSecondaryQC = 6, + + + //任何QC设置为QC Failed + QCFailed = 7, + + //2nd QC设置为QC Passed + QCPassed = 8, + } + +} diff --git a/IRaCIS.Core.Domain.Share/QC/ChallengeStateEnum.cs b/IRaCIS.Core.Domain.Share/QC/ChallengeStateEnum.cs new file mode 100644 index 0000000..c7bbda8 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/QC/ChallengeStateEnum.cs @@ -0,0 +1,15 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + + public enum ChallengeStateEnum + { + No = 0, + + HaveAndAllClosed = 1, + + HaveAndHaveNotClosed = 2 + } + +} diff --git a/IRaCIS.Core.Domain.Share/QC/CheckChanllengeTypeEnum.cs b/IRaCIS.Core.Domain.Share/QC/CheckChanllengeTypeEnum.cs new file mode 100644 index 0000000..2e4725e --- /dev/null +++ b/IRaCIS.Core.Domain.Share/QC/CheckChanllengeTypeEnum.cs @@ -0,0 +1,22 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + + + public enum CheckChanllengeTypeEnum + { + //不可用 + None = 0, + + //IC 已回复 PM 待回复 + CRCWaitPMReply = 1, + + //PM 已回复 IC 待回复 + PMWaitCRCReply = 2, + + Closed = 3 + + } + +} diff --git a/IRaCIS.Core.Domain.Share/QC/CheckStateEnum.cs b/IRaCIS.Core.Domain.Share/QC/CheckStateEnum.cs new file mode 100644 index 0000000..390c52d --- /dev/null +++ b/IRaCIS.Core.Domain.Share/QC/CheckStateEnum.cs @@ -0,0 +1,20 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + + public enum CheckStateEnum + { + //不可用 + None = 0, + + ToCheck = 9, + + + //核查中 + CVIng = 10, + + // 通过CV,等待转发 + CVPassed = 11, + } +} diff --git a/IRaCIS.Core.Domain.Share/QC/ForwardStateEnum.cs b/IRaCIS.Core.Domain.Share/QC/ForwardStateEnum.cs new file mode 100644 index 0000000..b5754e8 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/QC/ForwardStateEnum.cs @@ -0,0 +1,19 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + + + + public enum ForwardStateEnum + { + //不可用 + None = 0, + + ToForward = 14, + + Forwarded = 15, + + ForwardFailed = 16 + } +} diff --git a/IRaCIS.Core.Domain.Share/QC/PDStateEnum.cs b/IRaCIS.Core.Domain.Share/QC/PDStateEnum.cs new file mode 100644 index 0000000..ae1aba9 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/QC/PDStateEnum.cs @@ -0,0 +1,12 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum PDStateEnum + { + //不可用 + None = 0, + + PDProgress=1 + } + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain.Share/QC/QCChallengeCloseEnum.cs b/IRaCIS.Core.Domain.Share/QC/QCChallengeCloseEnum.cs new file mode 100644 index 0000000..69c84f9 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/QC/QCChallengeCloseEnum.cs @@ -0,0 +1,20 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + + public enum QCChallengeCloseEnum + { + //不可用 + None = 0, + + // 问题已解决 + ProblemSolved = 1, + + //问题无法解决 + Unresolvable = 2, + + OtherReason=3 + + } +} diff --git a/IRaCIS.Core.Domain.Share/QC/RequestBackStateEnum.cs b/IRaCIS.Core.Domain.Share/QC/RequestBackStateEnum.cs new file mode 100644 index 0000000..17fab1b --- /dev/null +++ b/IRaCIS.Core.Domain.Share/QC/RequestBackStateEnum.cs @@ -0,0 +1,22 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + + + + public enum RequestBackStateEnum + { + + NotRequest = 0, + + //IC 申请,PM待同意 + CRC_RequestBack = 1, + + //PM 已同意 IC + PM_AgressBack = 2, + + PM_NotAgree=3, + } + +} diff --git a/IRaCIS.Core.Domain.Share/QC/SubmitStateEnum.cs b/IRaCIS.Core.Domain.Share/QC/SubmitStateEnum.cs new file mode 100644 index 0000000..9569eaf --- /dev/null +++ b/IRaCIS.Core.Domain.Share/QC/SubmitStateEnum.cs @@ -0,0 +1,18 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + + public enum SubmitStateEnum + { + //不可用 + None = 0, + + //上传了影像或PDF,但没有点击Request QC + ToSubmit = 1, + + //点击了Request QC,没有人处理 + Submitted = 2, + + } +} diff --git a/IRaCIS.Core.Domain.Share/QC/TrialQCProcess.cs b/IRaCIS.Core.Domain.Share/QC/TrialQCProcess.cs new file mode 100644 index 0000000..bffe753 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/QC/TrialQCProcess.cs @@ -0,0 +1,40 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum TrialQCProcess + { + NotAudit=0, + + SingleAudit=1, + + DoubleAudit=2 + } + + public enum CurrentQC + { + + First = 1, + + Second = 2 + } + + + public enum QCChanllengeReuploadEnum + { + None=0, + + CRCRequestReupload=1, + + QCAgreeUpload=2, + + + CRCReuploaded=3 + } + + public enum CRCReuploadType + { + AdditionalUpload = 0, + + ReUpload = 1, + } + +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain.Share/QC/VisitExecutedEnum.cs b/IRaCIS.Core.Domain.Share/QC/VisitExecutedEnum.cs new file mode 100644 index 0000000..ce94aca --- /dev/null +++ b/IRaCIS.Core.Domain.Share/QC/VisitExecutedEnum.cs @@ -0,0 +1,16 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + public enum VisitExecutedEnum + { + UnExecuted = 0, + + Executed = 1, + + //不可用 + Unavailable = 2 + + } + +} diff --git a/IRaCIS.Core.Domain.Share/Reading/ReadEnum.cs b/IRaCIS.Core.Domain.Share/Reading/ReadEnum.cs new file mode 100644 index 0000000..98453d7 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Reading/ReadEnum.cs @@ -0,0 +1,1381 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Domain.Share +{ + + public static class ReadingQestionType + { + + public static readonly string Table = "table"; + + public static readonly string Group = "group"; + } + + + public enum RelevanceType + { + /// + /// 关联的检查批次任务ID (当前任务是检查批次任务的话会有自己)集合 + /// + Related = 0, + + /// + /// 既往任务Id 不包括自己 + /// + PastResult = 1, + } + + /// + /// 数据来源 + /// + public enum DataSources + { + + /// + /// 手动录入 + /// + ManualEntry = 0, + + /// + /// 自动计算 + /// + Automatic = 1 + } + + + /// + /// 自定义计算标记 + /// + public enum CustomCalculateMark + { + + /// + /// + + /// + Add = 1, + + /// + /// - + /// + Subtract = 2, + + /// + /// × + /// + Multiply = 3, + + /// + /// ÷ + /// + Divide = 4, + + /// + /// ∏ 求积 + /// + Quadrature = 5, + + /// + /// ∑ + /// + Sum = 6, + + /// + /// Avg + /// + Avg = 7, + + /// + /// Max + /// + Max = 8, + + /// + /// Min + /// + Min = 9 + } + + /// + /// 数值单位 + /// + public enum ValueUnit + { + None = 0, + + /// + /// MM + /// + MM = 1, + + /// + /// CM + /// + CM = 2, + + /// + /// 个 + /// + Individual = 3, + + /// + /// 自定义 + /// + Custom = 4, + } + + + /// + /// 数值类型 + /// + public enum ValueOfType + { + + /// + /// 整形 + /// + Plastic = 0, + + /// + /// 小数 + /// + Decimals = 1, + + /// + /// 百分数 + /// + Percentage = 2, + } + + /// + /// 报告层级 + /// + public enum ReportLayType + { + /// + /// 分组 + /// + Group=0, + + /// + /// 问题 + /// + Question = 1, + + /// + /// 病灶 + /// + Lesions = 2, + + /// + /// 表格问题 + /// + TableQuestion = 3, + } + + public enum ImagePlatform + { + None = 0, + + + MINT = 1, + + Ambra = 2, + + /// + /// 展影 + /// + PACS = 3, + } + + + /// + /// 标准文件类型 + /// + public enum CriterionFileType + { + + /// + /// 声明 + /// + Statement = 0, + + /// + /// 回执 + /// + Acknowledgement = 1 + } + + + /// + /// 标准类型 + /// + public enum CriterionType + { + NoCriterion = -1, + + //自定义 + SelfDefine = 0, + + /// + /// RECIST 1.1 + /// + RECIST1Pointt1 = 1, + + /// + /// PCWG3 + /// + PCWG3 = 10, + + /// + /// mRECIST Mesothelioma + /// + mRECISTMesothelioma = 11, + + /// + /// RECIL + /// + RECIL = 12, + + /// + /// RECIST 1.0 + /// + RECIST1Point0 = 13, + + /// + /// WHO + /// + WHO = 14, + + /// + /// PERCIST + /// + PERCIST = 15, + + /// + /// Forrest + /// + Forrest = 16, + + /// + /// Lugano 2014 + /// + Lugano2014 = 2, + + /// + /// iRECIST + /// + iRECIST = 3, + + /// + /// RANO-BM + /// + RANO_BM = 4, + + /// + /// RANO + /// + RANO = 5, + + /// + /// IWCLL 2018 + /// + IWCLL2018 = 6, + + /// + /// mRECIST HCC + /// + mRECISTHCC = 7, + + /// + /// Cheson 2007 + /// + Cheson2007 = 8, + + /// + /// IMWG 2016 + /// + IMWG2016 = 9 + } + + + /// + /// 是否存在疾病 + /// + public enum ExistDisease + { + + /// + /// 无 + /// + None = 0, + + /// + /// 是 + /// + Yes = 1, + + /// + /// 否 + /// + No = 2 + } + + + /// + /// 病灶分裂或合并 + /// + public enum SplitOrMergeType + { + + /// + /// 分裂 + /// + Split = 0, + + /// + /// 合并 + /// + Merge = 1 + } + + /// + /// 是否存在或者NA + /// + public enum ExistOrNA + { + /// + /// NA + /// + NA = -1, + + /// + /// 不存在 + /// + NotExist = 0, + + /// + /// 存在 + /// + Exist = 1, + + + } + + /// + /// 是否存在(是否存在和NA) + /// + public enum YesOrNoOrNa + { + + /// + /// NA + /// + NA = -1, + + /// + /// 否 + /// + No = 0, + + /// + /// 是 + /// + Yes = 1, + + + } + + /// + /// 是否淋巴结 + /// + public enum IsLymph + { + + /// + /// 否 + /// + No = 0, + + /// + /// 是 + /// + Yes = 1 + } + + /// + /// 整体肿瘤评估 + /// + public enum OverallAssessment + { + /// + /// NA + /// + NA = -1, + + /// + /// CR + /// + CR = 0, + + /// + /// ND + /// + ND = 1, + + /// + /// NE + /// + NE = 2, + + /// + /// NN + /// + NN = 3, + + /// + /// PD + /// + PD = 4, + + /// + /// PR + /// + PR = 5, + + /// + /// SD + /// + SD = 6, + + + } + + + /// + /// 病灶评估状态 + /// + public enum EvaluationOfState + { + + /// + /// 消失 + /// + Loss = 1, + + /// + /// 存在 + /// + Exists = 2, + + /// + /// 良性 + /// + Benign = 3, + + /// + /// 不可评估 + /// + UnableEvaluate = 4, + } + + + /// + /// 新病灶评估 + /// + public enum NewLesionAssessment + { + /// + /// NA + /// + NA = -1, + + /// + /// 是 + /// + Yes = 0, + + /// + /// 疑似 + /// + Suspected = 1, + + /// + /// NE + /// + NE = 2, + + /// + /// 否 + /// + No = 3, + + + } + + /// + /// 非靶病灶评估 + /// + public enum NoTargetAssessment + { + /// + /// NA + /// + NA = -1, + + /// + /// PD + /// + PD = 0, + + /// + /// CR + /// + CR = 1, + + /// + /// NE + /// + NE = 2, + + /// + /// ND + /// + ND = 3, + + /// + /// NN + /// + NN = 4, + + + } + + /// + /// 靶病灶评估 + /// + public enum TargetAssessment + { + /// + /// NA + /// + NA = -1, + + /// + /// CR + /// + CR = 0, + + /// + /// PR + /// + PR = 1, + + /// + /// SD + /// + SD = 2, + + /// + /// PD + /// + PD = 3, + + /// + /// NE + /// + NE = 4, + + /// + /// ND + /// + ND = 5, + + + } + + + /// + /// 非靶病灶状态 + /// + public enum NoTargetState + { + /// + /// 存在 + /// + Exist = 0, + + /// + /// 显著增大 + /// + Increase = 1, + + /// + /// 无法评估 + /// + UnableEvaluate = 2, + + /// + /// 消失 + /// + Loss = 3 + } + + + /// + /// 新病灶状态 + /// + public enum NewLesionState + { + + /// + /// 存在 + /// + Exist = 0, + + /// + /// 疑似 + /// + Suspected = 1, + + /// + /// 无法评估 + /// + UnableEvaluate = 2, + + /// + /// 消失 + /// + Loss = 3 + } + + /// + /// 靶病灶状态 + /// + public enum TargetState + { + + /// + /// 存在 + /// + Exist = 0, + + /// + /// 太小 + /// + TooSmall = 1, + + /// + /// 无法评估 + /// + UnableEvaluate = 2, + + /// + /// 消失 + /// + Loss = 3 + } + + /// + /// 模块枚举 + /// + public enum ModuleTypeEnum + { + /// + /// 计划内检查批次 + /// + InPlanSubjectVisit = 0, + + /// + /// 计划外检查批次 + /// + OutPlanSubjectVisit = 1, + + ///// + ///// 阅片期 + ///// + //Read = 2, + + /// + /// 全局阅片 + /// + Global = 3, + + /// + /// 裁判 + /// + Referee = 4, + + /// + /// 肿瘤学 + /// + Oncology = 5, + + + } + + public enum ReadingSetType + { + /// + /// 影像阅片 + /// + ImageReading = 0, + + /// + /// 肿瘤阅片 + /// + TumorReading = 1, + } + + /// + /// 阅片期范围 + /// + public enum ReadingScopeEnum + { + /// + /// 全部 + /// + All = 0, + + /// + /// 根据Site + /// + Site = 1, + } + + /// + /// 全局阅片展示类型 + /// + public enum GlobalReadingShowType + { + + /// + /// 全都显示 + /// + AllShow = 0, + + /// + /// 基线显示 + /// + BaseLineShow = 1, + + /// + /// 随访显示 + /// + FollowVisitShow = 2, + + /// + /// 不显示 + /// + NotShow =3 + } + + + + /// + /// 状态 + /// + public enum ReadingPeriodStatus + { + /// + /// 未生效 + /// + NotTakeEffect = 0, + + /// + /// 已生效 + /// + TakeEffect = 1, + + /// + /// 已撤销 + /// + Revocation = 2, + } + + /// + /// 上传角色 + /// + public enum UploadRole + { + /// + /// IC + /// + CRC = 0, + + /// + /// PM + /// + PM = 1, + } + + /// + /// 上传方式 + /// + public enum ClinicalUploadType + { + /// + /// 表格 + /// + [Display(Name = "表格")] + Table = 0, + + /// + /// PDF + /// + [Display(Name = "PDF")] + PDF = 1, + + } + + /// + /// 数据类型 + /// + public enum ClinicalDataType + { + /// + /// 既往局部治疗史 + /// + MedicalHistory = 0, + + /// + /// 影像模态(PET临床信息) + /// + PET = 1, + + /// + /// 胸水 + /// + Hydrothorax = 2, + } + + public enum LesionType + { + /// + /// 靶病灶 + /// + TargetLesion = 0, + + /// + /// 非靶病灶 + /// + NonTargetLesions = 1, + + /// + /// 新病灶 + /// + NewLesions = 2, + + /// + /// 既往新病灶 + /// + AlwaysNewLesions = 3, + + /// + /// 基线病灶 + /// + BaselineLesions=4, + + } + + + public enum OrganType + { + /// + /// 非靶和新病灶 + /// + OffTargetAndNewLesions = 0, + + + /// + /// 淋巴结病灶 + /// + NodularTargetLesions = 1, + + + /// + /// 非淋巴结靶病灶 + /// + NonNodularTargetLesions = 2, + + /// + /// 通用 + /// + General = 3, + } + + /// + /// 临床分组级别 + /// + public enum ClinicalLevel + { + /// + /// 患者 + /// + [DisplayAttribute(Name = "患者")] + Subject = 0, + + /// + /// 检查批次 + /// + [DisplayAttribute(Name = "检查批次")] + SubjectVisit = 1, + + /// + /// 影像学阅片 + /// + [DisplayAttribute(Name = "影像学阅片")] + ImageRead = 2, + + /// + /// 肿瘤学阅片 + /// + [DisplayAttribute(Name = "肿瘤学阅片")] + OncologyRead = 3, + } + + /// + /// 全局答案类型 + /// + public enum GlobalAnswerType + { + + /// + /// 问题答案 + /// + Question = 0, + + /// + /// 原因 + /// + Reason = 1, + + /// + /// 是否同意 + /// + AgreeOrNot = 2, + + /// + /// 评估更新类型 + /// + UpdateType = 3 + } + + public enum QuestionMark + { + /// + /// 长径 + /// + MajorAxis = 0, + + /// + /// 短径 + /// + ShortAxis = 1, + + /// + /// 是否淋巴结 + /// + IsLymph = 2, + + /// + /// 自增Id + /// + AutoId = 3, + + /// + /// 病灶名称 + /// + Lesion = 4, + + /// + /// 所在器官 + /// + Organ = 5, + + /// + /// 所在位置 + /// + Location = 6, + + /// + /// 状态 + /// + State = 7, + + /// + /// 所在部位 + /// + Part = 8, + + /// + /// 病灶数量 + /// + LesionNumber=11, + } + + /// + /// 问题类型 这里序号关系着计算顺序 请勿修改 + /// + public enum QuestionType + { + /// + /// 靶病灶径线之和(SOD) + /// + SOD = 0, + + /// + /// 非淋巴结靶病灶长径之和 + /// + SumOfDiameter = 1, + + /// + /// 与基线SOD相比变化量(mm) + /// + SODChange = 2, + + /// + /// 与基线检查批次相比SOD变化百分比 + /// + SODPercent = 3, + + /// + /// 与整个检查批次期间SOD最低点相比增加的值(mm) 其他任务需要改 + /// + LowestIncrease = 4, + + /// + /// 与整个检查批次期间SOD最低点相比增加的百分比 其他任务需要改 + /// + LowPercent = 5, + + /// + /// 整个检查批次期间SOD最低点检查批次名称 其他任务需要改 + /// + LowVisit = 6, + + /// + /// 是否存在非淋巴结靶病灶 + /// + IsLymphTarget = 7, + + /// + /// 是否存在淋巴结靶病灶且该病灶比上一检查批次短径增加5MM以上 + /// + IsAddFive = 8, + + /// + /// 被评估为NE的单个靶病灶 + /// + NETarget = 9, + + /// + /// 靶病灶评估 TargetAssessment + /// + TargetLesion = 10, + + /// + /// 非靶病灶评估 NoTargetAssessment + /// + NoTargetLesion = 11, + + /// + /// 是否存在新病灶 NewLesionAssessment + /// + NewLesions = 12, + + /// + /// 整体肿瘤评估 + /// + Tumor = 13, + + /// + /// 检查批次点备注 --注册证环境 评估总结 + /// + /// + AdjustReason = 14, + + /// + /// 是否存在疾病 + /// + ExistDisease = 15, + + /// + /// 基线病灶计数 + /// + BaseLineLesionsCount = 16, + + /// + /// 新病灶计数 + /// + NewLesionsCount = 17, + + /// + /// 既往新病灶 + /// + AlwaysNewLesionsCount = 18, + + /// + /// 自治疗后第二个检查批次点以来持续的新骨病变数量 + /// + NewBoneLesionsCount = 19, + + /// + /// 疗效评估分组 + /// + TherapeuticEffectEvaluationGroup=20, + + /// + /// 检查批次点肿瘤评估 + /// + SiteVisitForTumorEvaluation = 21, + + /// + /// 间隔天数 + /// + DaysBetween = 22, + + + } + + /// + /// 检查批次点肿瘤评估 + /// + public enum VisitTumorEvaluation + { + + /// + /// PD + /// + PD = 1, + + /// + /// ND + /// + ND = 2, + + /// + /// NE + /// + NE = 3, + + /// + /// 非PD + /// + NoPD = 4, + + /// + /// NA + /// + NA = 5 + } + + /// + /// 裁判阅片问题类型 + /// + public enum JudgeReadingQuestionType + { + /// + /// 问题 + /// + Question = 1, + + /// + /// 全局修改 + /// + GlobalChange = 2, + + /// + /// 检查批次点注释 + /// + VisitRemark = 3, + } + + public enum JudgeTypeEnum + { + None = 0, + + /// + /// 答案不相同 + /// + AnswerDisaffinity = 1, + + /// + /// 答案分组 + /// + AnswerGroup = 2, + + /// + /// 答案组合 + /// + AnswerCombination = 3, + + /// + /// 不计算 + /// + NotCalculate=4, + + } + + /// + /// 限制编辑 + /// + public enum LimitEdit + { + + /// + /// 否 + /// + None = 0, + + /// + /// 仅基线 + /// + OnlyBaseLine = 1, + + /// + /// 仅随访 + /// + OnlyVisit = 2 + } + + + /// + /// 是否必填 + /// + public enum IsRequired + { + /// + /// 必填 + /// + Required = 0, + + /// + /// 依赖父问题 + /// + Rely = 1, + + /// + /// 不必填 + /// + NotRequired = 2, + + + } + + + /// + /// 是否关联 + /// + public enum IsDepend + { + /// + /// 是 + /// + Yes = 0, + + /// + /// 依赖父问题 + /// + Rely = 1, + + /// + /// 否 + /// + No = 2, + + } + + /// + /// 是否显示问题 + /// + public enum ShowQuestion + { + /// + /// 显示 + /// + Show = 0, + + /// + /// 依赖父问题 + /// + Rely = 1, + + /// + /// 不显示 + /// + Hide = 2, + + } + + /// + /// 阅片状态 + /// + public enum ReadingStatusEnum + { + /// + /// 影像上传(未提交) + /// + ImageNotSubmit = 0, + + /// + /// 影像质控(未质控通过 --已提交) + /// + ImageQuality = 1, + + /// + /// 一致性核查(未一致性核查通过 ---已质控通过) + /// + ConsistencyCheck = 2, + + /// + /// 任务分配 (一致性核查通过 --未分配) + /// + TaskAllocate = 3, + + /// + /// 影像阅片中(任务已分配--未阅片完成) + /// + ImageReading = 4, + + /// + /// 阅片完成 + /// + ReadCompleted = 5, + + + } + + + public enum ReadingClinicalDataStatus + { + WaitUpload = 0, + + HaveUploaded = 1, + + HaveChecked = 2, + + HaveSigned = 3 + + } + + public enum ClinicalFileType + { + //既往手术 + PreviousSurgery = 1, + + //既往放疗历史 + PreviousHistory = 2, + + //其他 + PreviousOther = 3 + + } + + + +} diff --git a/IRaCIS.Core.Domain.Share/Reading/UrgentType.cs b/IRaCIS.Core.Domain.Share/Reading/UrgentType.cs new file mode 100644 index 0000000..270e3b8 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Reading/UrgentType.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Domain.Share.Reading +{ + + + public enum UrgentType + { + NotUrget = 0, + + EnrollConfirm = 1, + + EnrollConfirmOtherNotUrgent=5, + + PDVProgressVisitOrGlobal = 2, + + PDVProgressJudge = 3, + + NormalUrgent = 4, + + + + } +} diff --git a/IRaCIS.Core.Domain.Share/Trial/EnrollStatus.cs b/IRaCIS.Core.Domain.Share/Trial/EnrollStatus.cs new file mode 100644 index 0000000..a34feaa --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Trial/EnrollStatus.cs @@ -0,0 +1,36 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum EnrollStatus + { + None = 0, + + HasApplyDownloadResume = 1,//下载简历 申请 + + AuditRefuseDownload = 2,//下载简历审核 不同意 + + HasAuditPassDownload = 3,//下载简历审核 同意 + + HasDownloadResume = 4, // 下载简历 完成 + + HasCommittedToCRO = 5, //已提交CRO + + RefuseCommitCRO = 6, //已提交CRO + + RefuseInviteIntoGroup = 7,// 不邀请入组 + + InviteIntoGroup = 8, //名单被确认 邀请入组 + + RefuseIntoGroup = 9, // 拒绝入组 + + ConfirmIntoGroup = 10, // 确认入组 + + ConfirmIntoGroupFailed = 11, + + DoctorTrained = 12, // 参加培训 + + DoctorReading = 13, //正在读 + + Finished = 14 // 结束,锁库 + + } +} diff --git a/IRaCIS.Core.Domain.Share/Trial/ProjectStatusEnum.cs b/IRaCIS.Core.Domain.Share/Trial/ProjectStatusEnum.cs new file mode 100644 index 0000000..5d7a3e3 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Trial/ProjectStatusEnum.cs @@ -0,0 +1,16 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum ProjectStatusEnum + { + Created = 0, //创建项目 + ChooseDoctor = 1, //晒选医生 + ApplyDownLoadResume = 2, //申请下载简历 + DownloadApproved = 3,//下载审核已通过 + Downloaded = 4,//已下载 + ConfirmDoctorNames = 5,//确认医生名单 + DoctorConfirm = 6,//医生确认完毕 + EndInGroup = 7, //结束入组 + Going = 8,//进行中 + End = 9 // 结束,锁库 + } +} diff --git a/IRaCIS.Core.Domain.Share/Trial/StudyStatus.cs b/IRaCIS.Core.Domain.Share/Trial/StudyStatus.cs new file mode 100644 index 0000000..d42a91f --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Trial/StudyStatus.cs @@ -0,0 +1,75 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum StudyStatus + { + + //NeedReupload = 15,//需要重传15 + + //QAReuploaded = 20,//QA没通过后重新上传完成 + + Pending=0, //配合前端 查询待处理的消息 + + Uploading = 1,// study记录插入 默认状态 正在上传 + + Uploaded = 5,//已上传 + + QARequested=7, + + QAing = 10,//QA中 (各种对话回复、重传啥的中间过程不管) + + + //Abandon = 16,//废弃16 重传的和之前的不是同一份影像,前一份设置为废弃 + + + QAFinish = 25,//QA完成25,合格 + + QAFInishNotPass = 26, //QA结束,不合格 + + QAOver=27,// 为了前端 同时查询 25 26两个状态的Study记录 + + + Anonymizing=28,//匿名化中 + + Anonymized = 30,//匿名化完成 + + AnonymizeFailed = 32,//匿名化失败 + + Forwarding = 34,//转发中 + + Forwarded = 36,//已经转发 + + ForwardFailed = 38,//转发失败 + + + + + + Distributed = 42,//已分配读片任务 + + Reading = 48,//都片中,未提交读片报告之前 + + NeedAd = 52, + + AdDistributed = 58, + + AdReading = 64, + + Review = 80,//待审核 + + Archived = 100,//流程结束,归档锁库 + + + + + //Anonymize = 28,//匿名化完成 30 + //Forwarded = 29,//已经转发 36 + //Distributed = 30,//已分配读片任务 42 + ////Distributed2 = 32, + //Reading = 40,//都片中,未提交读片报告之前 + //NeedAd = 45, + //AdDistributed = 50, + //AdReading = 60, + //Review = 80,//待审核 + //Archived = 100,//流程结束,归档锁库 + } +} diff --git a/IRaCIS.Core.Domain.Share/Trial/SubjectStatus.cs b/IRaCIS.Core.Domain.Share/Trial/SubjectStatus.cs new file mode 100644 index 0000000..e0b5532 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Trial/SubjectStatus.cs @@ -0,0 +1,11 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum SubjectStatus + { + OnVisit = 1, + + OutOfVisit = 2, + + EndOfVisit = 3, + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain.Share/Trial/TrialEnrollStatus.cs b/IRaCIS.Core.Domain.Share/Trial/TrialEnrollStatus.cs new file mode 100644 index 0000000..6d85724 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Trial/TrialEnrollStatus.cs @@ -0,0 +1,25 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum TrialEnrollStatus + { + //Created = 0, //创建项目 + + ChooseDoctor = 1, //筛选医生 + + HasApplyDownLoadResume = 2, //已申请下载简历 + + HasApprovedDownload = 3, //已审核通过 + + //HasDownloaded = 4, //已下载 --进入医生挑选阶段 + + HasCommitCRO = 5, //已提交CRO + + HasConfirmedDoctorNames = 6, //已确认医生名单 + + EndInGroup = 9, //结束入组--参与项目的医生都确认了 + + ProjectGoing = 10, //进行中 + + Finished = 11 // 结束,锁库 + } +} diff --git a/IRaCIS.Core.Domain.Share/Trial/TrialExpedited.cs b/IRaCIS.Core.Domain.Share/Trial/TrialExpedited.cs new file mode 100644 index 0000000..beb621e --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Trial/TrialExpedited.cs @@ -0,0 +1,38 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum TrialExpedited + { + + All=-1, + + None=0, + + ExpeditedIn24H = 1, + + ExpeditedIn48H = 2 + } + + public enum TrialType + { + + //正式项目 + OfficialTrial = 1, + + NoneOfficial = 0, + + Training = 2 + + + } + + public enum AttendedReviewerType + { + //0全部中国医生 1美国医生 2既有中国医生,也有美国医生 + + CN=0, + + US=1, + + USAndCN=2, + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain.Share/Trial/TrialExpeditedStatus.cs b/IRaCIS.Core.Domain.Share/Trial/TrialExpeditedStatus.cs new file mode 100644 index 0000000..9e4b939 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Trial/TrialExpeditedStatus.cs @@ -0,0 +1,9 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum TrialExpeditedStatus + { + Normal = 0, //不加急 + In24HourExpedited = 1,//24小时 加急 + In48HourExpedited = 2,//48小时 加急 + } +} diff --git a/IRaCIS.Core.Domain.Share/Trial/TrialSiteSurveyEnum.cs b/IRaCIS.Core.Domain.Share/Trial/TrialSiteSurveyEnum.cs new file mode 100644 index 0000000..5a426e6 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Trial/TrialSiteSurveyEnum.cs @@ -0,0 +1,23 @@ +namespace IRaCIS.Core.Domain.Share +{ + + + public enum TrialSiteSurveyEnum + { + + ToSubmit = 0, + + + CRCSubmitted = 1, + + + SPMApproved = 2, + + //账号生成成功的 没成功的状态不变 + PMCreatedAndLock = 3,// Created + + + } + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain.Share/Trial/WorkLoadFromStatus.cs b/IRaCIS.Core.Domain.Share/Trial/WorkLoadFromStatus.cs new file mode 100644 index 0000000..e8e8ed7 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/Trial/WorkLoadFromStatus.cs @@ -0,0 +1,10 @@ +namespace IRaCIS.Core.Domain.Share +{ + public enum WorkLoadFromStatus + { + Doctor = 0, + CRO = 1, + FinalConfirm = 2, + All = 3 + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain.Share/User/SystemUserType.cs b/IRaCIS.Core.Domain.Share/User/SystemUserType.cs new file mode 100644 index 0000000..f022ed0 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/User/SystemUserType.cs @@ -0,0 +1,12 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + + public enum SystemUserType + { + AdminUser = 0, + + DoctorUser = 1 + } +} diff --git a/IRaCIS.Core.Domain.Share/User/TrialExternalEnum.cs b/IRaCIS.Core.Domain.Share/User/TrialExternalEnum.cs new file mode 100644 index 0000000..9950196 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/User/TrialExternalEnum.cs @@ -0,0 +1,49 @@ + + +using System.ComponentModel; + +namespace IRaCIS.Core.Domain.Share +{ + public enum TrialExternalUserStateEnum + { + //待发送 + WaitSent = 0, + + //已发送 + HasSend = 1, + + //用户已确认 + UserConfirmed = 2, + + UserReject = 3, + + OverTime = 4 + } + + + public enum TrialSiteUserStateEnum + { + [Description("待邀请")] + //待发送 + WaitSent = 0, + + [Description("已邀请")] + //已发送 + HasSend = 1, + + [Description("已确认")] + //用户已确认 + UserConfirmed = 2, + + [Description("已拒绝")] + //用户已确认 + UserReject = 3, + + [Description("已超时")] + OverTime = 4 + + + } + + +} diff --git a/IRaCIS.Core.Domain.Share/User/UserStateEnum.cs b/IRaCIS.Core.Domain.Share/User/UserStateEnum.cs new file mode 100644 index 0000000..cd0d993 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/User/UserStateEnum.cs @@ -0,0 +1,12 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + + public enum UserStateEnum + { + Disable = 0, + + Enable = 1 + } +} diff --git a/IRaCIS.Core.Domain.Share/User/UserType.cs b/IRaCIS.Core.Domain.Share/User/UserType.cs new file mode 100644 index 0000000..a32ac6f --- /dev/null +++ b/IRaCIS.Core.Domain.Share/User/UserType.cs @@ -0,0 +1,91 @@ +namespace IRaCIS.Core.Domain.Share +{ + + + public enum UserTypeEnum + { + + //PM + ProjectManager=1, + + //IC + ClinicalResearchCoordinator=2, + + //IQC + IQC = 3, + + + ////简历管理人员 + //ResumeManager=4, + ////简历运维人员 + //ReviewerCoordinator = 5, + + ReviewerCoordinator = 4, + + // 大屏展示 + Dashboard = 6, + + // 超级管理员用户类型,用于取代 SuperAdmin字段 数据库不内置这个用户类型和角色的配置,因为只允许有一个 + SuperAdmin=8, + + + + + CRA=9, + + SPM=10, + + APM=11, + + CPM=12, + + IndependentReviewer=13, + // 医学影像经理 + MIM = 14, + + + QA=15, + + EA=16, + + MW=17, + + + SMM=18, + + CMM=19, + + + AIR=21, + + //医生用户类型暂不处理 + + ShareImage = 125, + + + + + Undefined=0 + + + } + + + public enum UserTypeSelectEnum + { + + None=0, + + ExternalUser=1, + + InnerUser=2, + + SiteSurvey=3, + + EnrollOrPD_EmailReceive=4, + EnrollOrPD_EMailCopy=5, + } + + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain.Share/User/UserTypeGroup.cs b/IRaCIS.Core.Domain.Share/User/UserTypeGroup.cs new file mode 100644 index 0000000..916bf18 --- /dev/null +++ b/IRaCIS.Core.Domain.Share/User/UserTypeGroup.cs @@ -0,0 +1,14 @@ + + +namespace IRaCIS.Core.Domain.Share +{ + + public enum UserTypeGroup + { + TrialUser = 1, + + ResumeUser = 2, + + OtherUser = 3, + } +} diff --git a/IRaCIS.Core.Domain/Abandon/ImageLabel.cs b/IRaCIS.Core.Domain/Abandon/ImageLabel.cs new file mode 100644 index 0000000..2b98540 --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/ImageLabel.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("ImageLabel")] + public class ImageLabel : Entity + { + public string TpCode { get; set; } = string.Empty; + public Guid StudyId { get; set; } = Guid.Empty; + public Guid SeriesId { get; set; } = Guid.Empty; + public Guid InstanceId { get; set; } = Guid.Empty; + public string LabelValue { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Abandon/KeyInstance.cs b/IRaCIS.Core.Domain/Abandon/KeyInstance.cs new file mode 100644 index 0000000..bea65ae --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/KeyInstance.cs @@ -0,0 +1,18 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("KeyInstance")] + public class KeyInstance : Entity, IAuditAdd, IAuditUpdate + { + + public string TpCode { get; set; } + public Guid SeriesId { get; set; } + public Guid InstanceId { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + } +} diff --git a/IRaCIS.Core.Domain/Abandon/Message.cs b/IRaCIS.Core.Domain/Abandon/Message.cs new file mode 100644 index 0000000..51689a9 --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/Message.cs @@ -0,0 +1,18 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Message")] + public class Message : Entity + { + public Guid ToDoctorId { get; set; } + public Guid FromUserId { get; set; } + public string Title { get; set; } = string.Empty; + public string Content { get; set; } = string.Empty; + public DateTime MessageTime { get; set; } + public bool HasRead { get; set; } + public string Memo { get; set; } = string.Empty; + + } +} diff --git a/IRaCIS.Core.Domain/Abandon/Report/GlobalRS.cs b/IRaCIS.Core.Domain/Abandon/Report/GlobalRS.cs new file mode 100644 index 0000000..a5eb5ca --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/Report/GlobalRS.cs @@ -0,0 +1,15 @@ +using System; + +namespace IRaCIS.Core.Domain.Models +{ + public class GlobalRS : Entity + { + public Guid GlobalId { get; set; } + public string TpCode { get; set; } + public decimal VisitNum { get; set; } + public bool Agree { get; set; } + public string NewRS { get; set; } + public string Note { get; set; } + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/Abandon/Report/GlobalResult.cs b/IRaCIS.Core.Domain/Abandon/Report/GlobalResult.cs new file mode 100644 index 0000000..1e6a2d7 --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/Report/GlobalResult.cs @@ -0,0 +1,16 @@ +using System; + +namespace IRaCIS.Core.Domain.Models +{ + public class GlobalResult:Entity + { + public Guid GlobalId { get; set; } + public Guid SubjectId { get; set; } + public string SubjectCode { get; set; } + + public decimal VisitNum { get; set; } + public string SubjectNote { get; set; }=String.Empty; + public string Result { get; set; } = String.Empty; + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/Abandon/Report/Report.cs b/IRaCIS.Core.Domain/Abandon/Report/Report.cs new file mode 100644 index 0000000..8e59d14 --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/Report/Report.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Report")] + public class Report : Entity + { + public bool Qualified { get; set; } = true; + public string DiseaseProgression { get; set; } = string.Empty; + public string NotEvaluable { get; set; } = string.Empty; + public string Timepoint { get; set; } = string.Empty; + public string TrialCode { get; set; } = string.Empty; + public string SubjectCode { get; set; } = string.Empty; + public decimal? VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + public int? DiseaseSituation { get; set; } + public string Comment { get; set; } + public Guid? TPId { get; set; } + public Guid? DicomStudyId { get; set; } + public string TpCode { get; set; } = string.Empty; + public bool AffectRead { get; set; } = false;//是否影响读片 + public string AffectReadNote { get; set; } = string.Empty;//备注说明 + } +} diff --git a/IRaCIS.Core.Domain/Abandon/Report/TU_TR_RS.cs b/IRaCIS.Core.Domain/Abandon/Report/TU_TR_RS.cs new file mode 100644 index 0000000..f074612 --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/Report/TU_TR_RS.cs @@ -0,0 +1,129 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("TU")] + public class TU : Entity + { + //病灶类型 1-非淋巴结靶病灶,2-淋巴结靶病灶,3-非靶病灶,,4-疑似新病灶,5-明确新病灶 + public int LesionType { get; set; } + public string STUDYID { get; set; } + public string DOMAIN { get; set; } = "TU"; + public string USUBJID { get; set; } + public int TUSEQ { get; set; } + public string TUGRPID { get; set; } = string.Empty; + public string TUREFID { get; set; } = string.Empty;//内部或外部的肿瘤/病灶标识。 例如:医学影像 ID + public string TUSPID { get; set; } = string.Empty; + public string TULNKID { get; set; } = string.Empty; + public string TUTESTCD { get; set; } = string.Empty; + public string TUTEST { get; set; } = string.Empty; + + //肿瘤标识的结果。 肿瘤标识的结果是对标识肿瘤的分类。 + //例如,当 TUTESTCD=TUMIDENT (肿瘤/病灶标识)时, + //TUORRES的值可能是 TARGET, NON-TARGET, NEW或者 BENIGN ABNORMALITY + public string TUORRES { get; set; } = string.Empty; + public string TUSTRESC { get; set; } = string.Empty; + public string TUNAM { get; set; } = string.Empty; + public string TULOC { get; set; } = string.Empty; + public string TULAT { get; set; } = string.Empty; + public string TUDIR { get; set; } = string.Empty; + public string TUPORTOT { get; set; } = string.Empty; + public string TUMETHOD { get; set; } = string.Empty; + public string TUEVAL { get; set; } = string.Empty; + public string TUEVALID { get; set; } = string.Empty; + public string TUACPTFL { get; set; } = string.Empty; + public decimal VISITNUM { get; set; } = 0; + public string VISIT { get; set; } = string.Empty; + public int VISITDY { get; set; } + public string EPOCH { get; set; } = string.Empty; + public string TUDTC { get; set; } = string.Empty; + public int TUDY { get; set; } = 0; + public string LocDescription { get; set; } = string.Empty; + + public string TpCode { get; set; } = string.Empty; + } + + [Table("TR")] + public class TR : Entity + { + public string STUDYID { get; set; } = string.Empty; + public string DOMAIN { get; set; } = "TR"; + public string USUBJID { get; set; } = string.Empty; + public int TRSEQ { get; set; } + public string TRGRPID { get; set; } = string.Empty; + public string TRREFID { get; set; } = string.Empty; + public string TRSPID { get; set; } = string.Empty; + public string TRLNKID { get; set; } = string.Empty; + public string TRLNKGRP { get; set; } = string.Empty; + public string TRTESTCD { get; set; } = string.Empty; + public string TRTEST { get; set; } = string.Empty; + public string TRORRES { get; set; } = string.Empty; + public double TRORRES_Double + { + get { + double temp = 0; + double.TryParse(TRORRES, out temp); + return temp; + } + } + public string TRORRESU { get; set; } = string.Empty; + public string TRSTRESC { get; set; } = string.Empty; + public double TRSTRESN { get; set; } = 0; + public string TRSTRESU { get; set; } = string.Empty; + public string TRSTAT { get; set; } = string.Empty; + public string TRREASND { get; set; } = string.Empty; + public string TRNAM { get; set; } = string.Empty; + public string TRMETHOD { get; set; } = string.Empty; + public string TREVAL { get; set; } = string.Empty; + public string TREVALID { get; set; } = string.Empty; + public string TRACPTFL { get; set; } = string.Empty; + public decimal VISITNUM { get; set; } = 0; + public string VISIT { get; set; } = string.Empty; + public int VISITDY { get; set; } = 0; + public string EPOCH { get; set; } = string.Empty; + public string TRDTC { get; set; } = string.Empty; + public int TRDY { get; set; } = 0; + public string Note { get; set; } // 备注 + public bool CoveredLesion { get; set; } = true;//本次扫描是否覆盖该病灶 + public string TpCode { get; set; } = string.Empty; + } + + [Table("RS")] + public class RS : Entity + { + public string STUDYID { get; set; } = string.Empty; + public string DOMAIN { get; set; } = "RS"; + public string USUBJID { get; set; } = string.Empty; + public int RSSEQ { get; set; } + public string RSGRPID { get; set; } = string.Empty; + public string RSREFID { get; set; } = string.Empty; + public string RSSPID { get; set; } = string.Empty; + public string RSLNKID { get; set; } = string.Empty; + public string RSLNKGRP { get; set; } = string.Empty; + public string RSTESTCD { get; set; } = string.Empty; + public string RSTEST { get; set; } = string.Empty; + public string RSCAT { get; set; } = string.Empty; + public string RSORRES { get; set; } = string.Empty; + public string RSSTRESC { get; set; } = string.Empty; + public string RSSTAT { get; set; } = string.Empty; + public string RSREASND { get; set; } = string.Empty; + public string RSNAM { get; set; } = string.Empty; + + public string RSEVAL { get; set; } = string.Empty; + public string RSEVALID { get; set; } = string.Empty; + public string RSACPTFL { get; set; } = string.Empty; + public decimal VISITNUM { get; set; } = 0; + public string VISIT { get; set; } = string.Empty; + public int VISITDY { get; set; } = 0; + public string EPOCH { get; set; } = string.Empty; + public string RSDTC { get; set; } = string.Empty; + public int RSDY { get; set; } = 0; + public string TpCode { get; set; } = string.Empty; + public Guid StudyGuid { get; set; } = Guid.Empty; + public Guid TrialGuid { get; set; } = Guid.Empty; + public Guid SubjectGuid { get; set; } = Guid.Empty; + public string Note { get; set; } = string.Empty; + + } +} diff --git a/IRaCIS.Core.Domain/Abandon/StudyDTF.cs b/IRaCIS.Core.Domain/Abandon/StudyDTF.cs new file mode 100644 index 0000000..0523fa8 --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/StudyDTF.cs @@ -0,0 +1,24 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + public class StudyDTF : Entity, IAuditAdd + { + [ForeignKey("StudyId")] + public DicomStudy DicomStudy { get; set; } + public Guid StudyId { get; set; } = Guid.Empty; + + public string FileName { get; set; } = string.Empty; + + public string Path { get; set; } = string.Empty; + + + //public byte[] RowVersion { get; set; } = default; + + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/Abandon/StudyReviewer.cs b/IRaCIS.Core.Domain/Abandon/StudyReviewer.cs new file mode 100644 index 0000000..8dc5192 --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/StudyReviewer.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("StudyReviewer")] + public class StudyReviewer : Entity, IAuditAdd + { + public Guid StudyId { get; set; } + public Guid ReviewerId { get; set; } + + public Guid TrialId { get; set; } + + // 2是 ad 1是tp + public int WorkloadType { get; set; } = 0; + public int Status { get; set; } = 30; + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + } +} diff --git a/IRaCIS.Core.Domain/Abandon/StudyStatusDetail.cs b/IRaCIS.Core.Domain/Abandon/StudyStatusDetail.cs new file mode 100644 index 0000000..825d380 --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/StudyStatusDetail.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("StudyStatusDetail")] + public class StudyStatusDetail : Entity + { + public Guid StudyId { get; set; } + public int Status { get; set; } = 0; + public string OptUserName { get; set; } = string.Empty; + public DateTime OptTime { get; set; } = DateTime.Now; + public string Note { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Abandon/SystemLog.cs b/IRaCIS.Core.Domain/Abandon/SystemLog.cs new file mode 100644 index 0000000..37e6e24 --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/SystemLog.cs @@ -0,0 +1,21 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("SystemLog")] + public partial class SystemLog : Entity + { + public string ApiPath { get; set; } = string.Empty; + public string Params { get; set; } = string.Empty; + public string Result { get; set; } = string.Empty; + public DateTime RequestTime { get; set; } = DateTime.Now; + public long ElapsedMilliseconds { get; set; } = 0; + public Guid OptUserId { get; set; } = Guid.Empty; + public string OptUserName { get; set; } = string.Empty; + public string ClientIP { get; set; } = string.Empty; + public bool Status { get; set; } = true; + public string Message { get; set; } = string.Empty; + public string LogCategory { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Abandon/TrialAttachment.cs b/IRaCIS.Core.Domain/Abandon/TrialAttachment.cs new file mode 100644 index 0000000..9cbae13 --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/TrialAttachment.cs @@ -0,0 +1,21 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("TrialAttachment")] + public class TrialAttachment : Entity, IAuditUpdate, IAuditAdd + { + public Guid TrialId { get; set; } + public string Type { get; set; } = String.Empty; + public string DocumentName { get; set; } = String.Empty; + public string DocumentPath { get; set; } = String.Empty; + + public string UserTypes { get; set; } = String.Empty; + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Abandon/WorkloadDistribution.cs b/IRaCIS.Core.Domain/Abandon/WorkloadDistribution.cs new file mode 100644 index 0000000..4fed63a --- /dev/null +++ b/IRaCIS.Core.Domain/Abandon/WorkloadDistribution.cs @@ -0,0 +1,67 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("WorkloadTP")] + public class WorkloadTP : Entity + { + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + public Guid StudyId { get; set; } + public string TimepointCode { get; set; } + public Guid ReviewerId { get; set; } + public int Status { get; set; } + public DateTime UpdateTime { get; set; } + } + + [Table("WorkloadGlobal")] + public class WorkloadGlobal : Entity + { + public Guid SiteId { get; set; } + public Guid VisitId { get; set; } + public string VisitName { get; set; } + + + // 项目Id,患者Id,num 共同决定 Global 关联的所有study, + // 暂定设计成这样,后期如有需要,爱用中间表 关联。 + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + public decimal VisitNum { get; set; } + + public string GlobalCode { get; set; } + public Guid ReviewerId { get; set; } + public int Status { get; set; } + public DateTime UpdateTime { get; set; } + } + + [Table("WorkloadAD")] + public class WorkloadAD : Entity + { + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public string ADCode { get; set; } + public Guid ReviewerId { get; set; } + public int Status { get; set; } + public DateTime UpdateTime { get; set; } + public Guid Global1Id { get; set; } + public Guid Global2Id { get; set; } + + public Guid? SelectGlobalId { get; set; } + + public string AdNote { get; set; } + } + + [Table("WorkloadDetail")] + public class WorkloadDetail : Entity + { + public Guid WorkloadId { get; set; } + public string OptUserName { get; set; } + public DateTime OptTime { get; set; } = DateTime.Now; + public int Status { get; set; } + public Guid ReviewerId { get; set; } = Guid.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Allocation/SubjectTaskCategory.cs b/IRaCIS.Core.Domain/Allocation/SubjectTaskCategory.cs new file mode 100644 index 0000000..9f3aa4e --- /dev/null +++ b/IRaCIS.Core.Domain/Allocation/SubjectTaskCategory.cs @@ -0,0 +1,52 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 14:09:29 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///是否需要拆表 + /// + [Table("SubjectTaskCategory")] + public class SubjectTaskCategory : Entity, IAuditAdd + { + public Guid TrialId { get; set; } + + public string TaskName { get; set; } = string.Empty; + public string TaskBlindName { get; set; } = string.Empty; + + + //任务来源检查批次Id 方便回更检查批次读片状态 + public Guid? SourceSubjectVisitId { get; set; } + + [JsonIgnore] + [ForeignKey("SourceSubjectVisitId")] + public SubjectVisit SubjectVisit { get; set; } + + + public Guid? SouceReadModuleId { get; set; } + [JsonIgnore] + [ForeignKey("SouceReadModuleId")] + public ReadModule ReadModule { get; set; } + + public ReadingCategory ReadingCategory { get; set; } + + public Guid SubjectId { get; set; } + + public Subject Subject { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + + + + } + +} diff --git a/IRaCIS.Core.Domain/Allocation/SubjectUser.cs b/IRaCIS.Core.Domain/Allocation/SubjectUser.cs new file mode 100644 index 0000000..3ce7742 --- /dev/null +++ b/IRaCIS.Core.Domain/Allocation/SubjectUser.cs @@ -0,0 +1,90 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-10 11:59:27 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SubjectUser + /// + [Table("SubjectUser")] + public class SubjectUser : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + public Trial Trial { get; set; } + public Guid TrialId { get; set; } + + + public Guid TrialReadingCriterionId { get; set; } + + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + + public DateTime? AssignTime { get; set; } + + /// + /// SubjectId + /// + [Required] + public Guid SubjectId { get; set; } + + public Subject Subject { get; set; } + + + public Guid DoctorUserId { get; set; } + + [JsonIgnore] + public User DoctorUser { get; set; } + + public Arm ArmEnum { get; set; } + + public bool IsConfirmed { get; set; } = true; + + //该属性有值 说明该医生被替换了 分配的时候 要过滤掉 + public Guid? OrignalSubjectUserId { get; set; } + + [JsonIgnore] + //Parent + [ForeignKey("OrignalSubjectUserId")] + public SubjectUser OrignalSubjectUser { get; set; } + + //ChildList + [JsonIgnore] + public List EarlierSubjectUserList { get; set; } + + //public List SubjectArmVisitTaskList { get; set; } + + + } + +} diff --git a/IRaCIS.Core.Domain/Allocation/TaskAllocationRule.cs b/IRaCIS.Core.Domain/Allocation/TaskAllocationRule.cs new file mode 100644 index 0000000..a277c1e --- /dev/null +++ b/IRaCIS.Core.Domain/Allocation/TaskAllocationRule.cs @@ -0,0 +1,62 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 13:13:13 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TaskAllocationRule + /// + [Table("TaskAllocationRule")] + public class TaskAllocationRule : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + public Trial Trial { get; set; } + public Guid TrialId { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + public int PlanSubjectCount { get; set; } + + + public bool IsEnable { get; set; } + + public string Note { get; set; } = string.Empty; + + + + public Guid DoctorUserId { get; set; } + + [ForeignKey("DoctorUserId")] + [JsonIgnore] + public User DoctorUser { get; set; } + + + + + + + public Guid EnrollId { get; set; } + [JsonIgnore] + public Enroll Enroll { get; set; } + + //是否是裁判医生 裁判医生单独加入 + public bool IsJudgeDoctor { get; set; } + + public int PlanReadingRatio { get; set; } + + + + + + } + +} diff --git a/IRaCIS.Core.Domain/Allocation/TaskConsistentRule.cs b/IRaCIS.Core.Domain/Allocation/TaskConsistentRule.cs new file mode 100644 index 0000000..1ce5902 --- /dev/null +++ b/IRaCIS.Core.Domain/Allocation/TaskConsistentRule.cs @@ -0,0 +1,76 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-07-01 15:32:56 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using EntityFrameworkCore.Projectables; +using System.Linq; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TaskConsistentRule + /// + [Table("TaskConsistentRule")] + public class TaskConsistentRule : Entity, IAuditUpdate, IAuditAdd + { + + + public Trial Trial { get; set; } + + public Guid CreateUserId { get; set; } + + public DateTime CreateTime { get; set; } + + public DateTime UpdateTime { get; set; } + + public Guid UpdateUserId { get; set; } + + public Guid TrialId { get; set; } + + + public int PlanSubjectCount { get; set; } + + public int PlanVisitCount { get; set; } + + public int IntervalWeeks { get; set; } + + + public bool IsHaveReadingPeriod { get; set; } + + + public bool IsGenerateGlobalTask { get; set; } + + + public int BlindSubjectNumberOfPlaces { get; set; } + + public string BlindTrialSiteCode { get; set; } = string.Empty; + + + public bool IsSelfAnalysis { get; set; } + + //public Guid? CompareDoctorUserId { get; set; } + //public User AnalysisDoctorUser { get; set; } + //public Guid AnalysisDoctorUserId { get; set; } + //public List DoctorVisitTaskList { get; set; } + + public bool IsEnable { get; set; } + + public string Note { get; set; } + + + public Guid TrialReadingCriterionId { get; set; } + + [JsonIgnore] + [ForeignKey("TrialReadingCriterionId")] + public ReadingQuestionCriterionTrial TrialReadingCriterion { get; set; } + + + //[Projectable] + //public List DoctorConsistentTaskList => DoctorVisitTaskList.Where(t => t.IsAnalysisCreate && t.TaskConsistentRuleId == Id).ToList(); + } + +} diff --git a/IRaCIS.Core.Domain/Allocation/TaskInfluence.cs b/IRaCIS.Core.Domain/Allocation/TaskInfluence.cs new file mode 100644 index 0000000..2b2e393 --- /dev/null +++ b/IRaCIS.Core.Domain/Allocation/TaskInfluence.cs @@ -0,0 +1,58 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-07-21 13:44:02 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TaskInfluence + /// + [Table("TaskInfluence")] + public class TaskInfluence : Entity, IAuditAdd + { + + + + public Guid CreateUserId { get; set; } + + + public DateTime CreateTime { get; set; } + + + public Guid OriginalTaskId { get; set; } + + public VisitTask OriginalTask { get; set; } + + public VisitTask InfluenceTask { get; set; } + + + public Guid InfluenceTaskId { get; set; } + + + + //对影响任务进行的操作 + public ReReadingOrBackOptType OptType { get; set; } + + } + + + public enum ReReadingOrBackOptType + { + //取消分配 + CancelAssign = 0, + + //失效 + Abandon = 1, + + //重阅重置 + + Return = 2, + + } + +} diff --git a/IRaCIS.Core.Domain/Allocation/TaskMedicalReview.cs b/IRaCIS.Core.Domain/Allocation/TaskMedicalReview.cs new file mode 100644 index 0000000..4ee122c --- /dev/null +++ b/IRaCIS.Core.Domain/Allocation/TaskMedicalReview.cs @@ -0,0 +1,220 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-29 10:56:50 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TaskMedicalReview + /// + [Table("TaskMedicalReview")] + public class TaskMedicalReview : Entity, IAuditUpdate, IAuditAdd + { + + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + /// + /// 分配时间 + /// + public DateTime? AllocateTime { get; set; } + + /// + /// 审核状态 + /// + [Required] + public MedicalReviewAuditState AuditState { get; set; } + + + + /// + /// 审核通过时间 + /// + public DateTime? AuditSignTime { get; set; } + + /// + /// 阅片人是否认同 + /// + public MedicalReviewDoctorUserIdea DoctorUserIdeaEnum { get; set; } + + /// + /// MedicalManagerUserId + /// + public Guid? MedicalManagerUserId { get; set; } + + public User MedicalManagerUser { get; set; } + + public Guid VisitTaskId { get; set; } + + [ForeignKey("VisitTaskId")] + public VisitTask VisitTask { get; set; } + + public Guid TrialId { get; set; } + + /// + /// 冗余 废弃 + /// + public Guid DoctorUserId { get; set; } + + [JsonIgnore] + public TaskMedicalReviewRule TaskMedicalReviewRule { get; set; } + + + /// + /// 是否有问题 + /// + public bool IsHaveQuestion { get; set; } = false; + + /// + /// 质询问题 + /// + public string Questioning { get; set; } = string.Empty; + + + /// + /// 图片路径 + /// + public string ImagePath { get; set; } = string.Empty; + + /// + /// 审核建议 + /// + public AuditAdvice AuditAdviceEnum { get; set; } + + /// + /// 是否关闭对话 + /// + public bool IsClosedDialog { get; set; } + + /// + /// 保存问题的时间 + /// + public DateTime? SaveQuestionTime { get; set; } + + + /// + /// 不同意重阅原因 + /// + public string DisagreeReason { get; set; } = string.Empty; + + + /// + /// 是否申请重阅 + /// + public bool IsApplyHeavyReading { get; set; } = false; + + + /// + /// 保存结论时间 + /// + public DateTime? SaveConclusionTime { get; set; } + + + /// + /// 文件名称 + /// + public string FileName { get; set; } = string.Empty; + + [JsonIgnore] + public List ReadingMedicalReviewDialogList { get; set; } + + + /// + /// 是否发送消息 + /// + public bool IsSendMessage { get; set; } = false; + + + /// + /// 医学审核对话关闭原因 + /// + public MedicalDialogClose MedicalDialogCloseEnum { get; set; } + + /// + /// 对话关闭原因 + /// + public string DialogCloseReason { get; set; } = string.Empty; + + + /// + /// 无效的 为True无效 + /// + public bool IsInvalid { get; set; } + + /// + /// 文件 + /// + [NotMapped] + public List FileList + { + get + { + + + try + { + var result= JsonConvert.DeserializeObject>(this.ImagePath); + return result==null?new List() : result; + } + catch (Exception) + { + + return new List(); + } + + } + } + + + public bool IsAutoGenerate { get; set; } + + // | 分割 + public string PDRelationTaskIdListStr { get; set; }=string.Empty; + + [NotMapped] + public List PDRelationTaskIdList=> PDRelationTaskIdListStr.Split('|',StringSplitOptions.RemoveEmptyEntries).Select(t=> Guid.Parse(t) ).ToList(); + } + + #region 文件对象 + + public class ImageInfo + { + public string FileName { get; set; } + + public string ImagePath { get; set; } + } + + #endregion + +} diff --git a/IRaCIS.Core.Domain/Allocation/TaskMedicalReviewRule.cs b/IRaCIS.Core.Domain/Allocation/TaskMedicalReviewRule.cs new file mode 100644 index 0000000..ddefaf3 --- /dev/null +++ b/IRaCIS.Core.Domain/Allocation/TaskMedicalReviewRule.cs @@ -0,0 +1,53 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-29 13:32:34 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TaskTaskMedicalReviewRule + /// + [Table("TaskMedicalReviewRule")] + public class TaskMedicalReviewRule : Entity, IAuditUpdate, IAuditAdd + { + + public Guid CreateUserId { get; set; } + + public DateTime CreateTime { get; set; } + + public DateTime UpdateTime { get; set; } + + public Guid UpdateUserId { get; set; } + + public Guid DoctorUserId { get; set; } + + public bool IsEnable { get; set; } + + public string Note { get; set; } + + public Guid TrialId { get; set; } + + public int PlanVisitCount { get; set; } + + public int PlanJudgeCount { get; set; } + + public int PlanGlobalCount { get; set; } + + public int PlanTumorCount { get; set; } + [JsonIgnore] + public User DoctorUser { get; set; } + [JsonIgnore] + public List DoctorTrialVisitTaskList { get; set; } + [JsonIgnore] + public List TaskMedicalReviewList { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Allocation/VisitTask.cs b/IRaCIS.Core.Domain/Allocation/VisitTask.cs new file mode 100644 index 0000000..e9e705b --- /dev/null +++ b/IRaCIS.Core.Domain/Allocation/VisitTask.cs @@ -0,0 +1,374 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 14:09:29 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///VisitTask + /// + [Table("VisitTask")] + public class VisitTask : Entity, IAuditUpdate, IAuditAdd + { + + public string TaskName { get; set; } = string.Empty; + public string TaskBlindName { get; set; } = string.Empty; + + + //任务来源检查批次Id 方便回更检查批次读片状态 + public Guid? SourceSubjectVisitId { get; set; } + public Guid? SouceReadModuleId { get; set; } + + + /// + /// 任务类型 + /// + public ReadingCategory ReadingCategory { get; set; } + + + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + public Guid TrialId { get; set; } + + + /// + /// 分配时间 + /// + public DateTime? AllocateTime { get; set; } + + public Guid SubjectId { get; set; } + + + public int Code { get; set; } + + public string TaskCode { get; set; } = string.Empty; + + public bool IsUrgent { get; set; } + + /// + /// 加急类型 + /// + public TaskUrgentType? TaskUrgentType { get; set; } + + /// + /// 任务加急类型 + /// + public string TaskUrgentRemake { get; set; } = string.Empty; + + /// + /// 是否和编辑加急状态 + /// + public bool IsCanEditUrgentState { get; set; } = true; + + /// + /// 0 代表 单重阅片 产生的任务 否则就是双重 任务 1 任务 2 + /// + [Required] + public Arm ArmEnum { get; set; } + + /// + /// 分配状态 + /// + public TaskAllocationState TaskAllocationState { get; set; } + + + public TaskState TaskState { get; set; } + + + /// + /// 重阅状态 + /// + public ReReadingApplyState ReReadingApplyState { get; set; } + + public Guid? DoctorUserId { get; set; } + + + public Guid TrialReadingCriterionId { get; set; } + + [JsonIgnore] + [ForeignKey("TrialReadingCriterionId")] + public ReadingQuestionCriterionTrial TrialReadingCriterion { get; set; } + + + + [ForeignKey("DoctorUserId")] + public User DoctorUser { get; set; } + + [ForeignKey("SouceReadModuleId")] + public ReadModule ReadModule { get; set; } + + [ForeignKey("SourceSubjectVisitId")] + public SubjectVisit SourceSubjectVisit { get; set; } + [JsonIgnore] + [ForeignKey("JudgeVisitTaskId")] + public VisitTask JudgeVisitTask { get; set; } + + //public SubjectUser SujectArm { get; set; } + + [JsonIgnore] + public Subject Subject { get; set; } + + + + [JsonIgnore] + public List TaskMedicalReviewList { get; set; } + + + + //排除一致性分析 因为souceVisitId 没值 + //public List SameVisitTaskList { get; set; } + + //public List SameSubjectVisiTaskList { get; set; } + + + //public TaskAllocationRule DoctorTaskAllocationRule { get; set; } + + [JsonIgnore] + public TaskMedicalReviewRule DoctorTaskMedicalReviewRule { get; set; } + + + + //裁判任务的Id + public Guid? JudgeVisitTaskId { get; set; } + + //任务阅片状态 + public ReadingTaskState ReadingTaskState { get; set; } + + //签名时间 + public DateTime? SignTime { get; set; } + + public DateTime? SuggesteFinishedTime { get; set; } + + + /// + /// 是否是重阅产生的,方便过滤数据 + /// + public bool IsReReadingCreate { get; set; } + + /// + /// PM 对该任务进行了回退 影响的任务不设置 + /// + public bool IsPMSetBack { get; set; } + + + /// 裁判结果的任务ID + public Guid? JudgeResultTaskId { get; set; } + + [ForeignKey("JudgeResultTaskId")] + public VisitTask JudgeResultTask { get; set; } + + + + //随访任务号 取检查批次的号 计划外是 检查批次+0.1 裁判任务在检查批次任务上+0.002 全局任务在截止检查批次号上+0.03 肿瘤待定 + public decimal VisitTaskNum { get; set; } + + + + /// + /// 首次阅片时间 + /// + public DateTime? FirstReadingTime { get; set; } + + /// + /// 全局是否有更新 + /// + public bool? IsGlobalHaveUpdate { get; set; } + + /// + /// IR是否阅读临床数据 + /// + public bool IsReadClinicalData { get; set; } = false; + + /// + /// 关联的检查批次任务ID (当前任务是检查批次任务的话会有自己) + /// + public string RelatedVisitTaskIds { get; set; } = "[]"; + + /// + /// 关联的检查批次任务ID (当前任务是检查批次任务的话会有自己)集合 + /// + [NotMapped] + public List RelatedVisitTaskIdList + { + get + { + + try + { + + return JsonConvert.DeserializeObject>(this.RelatedVisitTaskIds); + } + catch (Exception) + { + + return new List(); + } + + } + } + + /// + /// 既往任务Id 不包括自己 + /// + + public string PastResultTaskIds { get; set; } = "[]"; + + + + /// + /// 既往任务Id 不包括自己集合 + /// + [NotMapped] + public List PastResultTaskIdList + { + get + { + + try + { + return JsonConvert.DeserializeObject>(this.PastResultTaskIds); + } + catch (Exception) + { + + return new List(); + } + + } + } + + #region 裁判任务特有 + + [JsonIgnore] + //对于裁判任务而言,触发裁判的列表 + public List JudgeVisitList { get; set; } + + /// + /// 裁判结果的备注 + /// + public string JudgeResultRemark { get; set; } = string.Empty; + /// + /// 裁判结果的图片路径 + /// + public string JudgeResultImagePath { get; set; } = string.Empty; + + #endregion + + + #region 一致性分析的任务特有数据 + + /// + /// 阅片结果是否和原数据有差异 + /// + public bool? IsAnalysisDiffToOriginalData { get; set; } + + /// + /// 组件一致性和原Arm1是否有差异 + /// + public bool? IsGroupDiffArm1 { get; set; } + + /// + /// 组件一致性和原Arm2是否有差异 + /// + public bool? IsGroupDiffArm2 { get; set; } + + /// + /// 是否是一致性分析产生 + /// + public bool IsAnalysisCreate { get; set; } + + public bool? IsSelfAnalysis { get; set; } + + public string BlindSubjectCode { get; set; } = string.Empty; + public string BlindTrialSiteCode { get; set; } = string.Empty; + + //一致性分析规则Id 用于最后统计 + //public Guid? TaskConsistentRuleId { get; set; } + + /// + /// 针对产生的一致性任务而言,这个字段存储的是原始任务 + /// + public Guid? ConsistentAnalysisOriginalTaskId { get; set; } + + #endregion + + + //临床数据 + + public bool IsNeedClinicalDataSign { get; set; } + + public bool IsClinicalDataSign { get; set; } + + //前序任务需要签名 但是未签名 + public bool IsFrontTaskNeedSignButNotSign { get; set; } + + + + + + [JsonIgnore] + + public Trial Trial { get; set; } + + + + //影像质量 等等第一层级问题答案 + [JsonIgnore] + public List ReadingTaskQuestionAnswerList { get; set; } = new List(); + + + //病灶表 + [JsonIgnore] + public List LesionList { get; set; } = new List(); + + //病灶答案表 + [JsonIgnore] + public List LesionAnswerList { get; set; } = new List(); + + + + //重阅或者退回影像的记录中间表 + [JsonIgnore] + public List TaskInfluenceList { get; set; } = new List(); + + + [JsonIgnore] + //对于全局任务而言 才可以用的 关联的检查批次阅片结果 + public List GlobalVisitResultList { get; set; } = new List(); + + + /// + /// 裁判结果图片地址 + /// + [NotMapped] + public List JudgeResultImagePathList + { + get + { + + try + { + return this.JudgeResultImagePath.Trim().Split(',').ToList(); + } + catch (Exception) + { + + return new List(); + } + + } + } + + } +} diff --git a/IRaCIS.Core.Domain/Allocation/VisitTaskReReading.cs b/IRaCIS.Core.Domain/Allocation/VisitTaskReReading.cs new file mode 100644 index 0000000..f471319 --- /dev/null +++ b/IRaCIS.Core.Domain/Allocation/VisitTaskReReading.cs @@ -0,0 +1,82 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-07 14:09:29 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///重阅申请流程记录表 + /// + [Table("VisitTaskReReading")] + public class VisitTaskReReading : Entity, IAuditUpdate, IAuditAdd + { + + + //重阅原始任务Id 重阅会产生新的任务 + public Guid OriginalReReadingTaskId { get; set; } + + + //产生重阅的根任务Id + public Guid RootReReadingTaskId { get; set; } + + //重阅申请 产生的新任务Id + public Guid? NewReReadingTaskId { get; set; } + + public Guid RequestReReadingUserId { get; set; } + [JsonIgnore] + public User RequestReReadingUser { get; set; } + + public DateTime RequestReReadingTime { get; set; } + [JsonIgnore] + public VisitTask NewReReadingTask { get; set; } + [JsonIgnore] + public VisitTask RootReReadingTask { get; set; } + [JsonIgnore] + public VisitTask OriginalReReadingTask { get; set; } + + //申请回退类型 + public RequestReReadingType RequestReReadingType { get; set; } + + + public string RequestReReadingReason { get; set; } = string.Empty; + + public string RequestReReadingRejectReason { get; set; } = string.Empty; + + public RequestReReadingResult RequestReReadingResultEnum { get; set; } + + + + public Guid? RequestReReadingConfirmUserId { get; set; } + + public User RequestReReadingConfirmUser { get; set; } + + + + public bool IsCopyOrigenalForms { get; set; } + + //仅仅包括全局和检查批次 + public bool IsCopyFollowForms { get; set; } + + + public Guid CreateUserId { get; set; } + + + public DateTime CreateTime { get; set; } + + + public DateTime UpdateTime { get; set; } + + public Guid UpdateUserId { get; set; } + + [JsonIgnore] + public User CreateUser { get; set; } + } + +} diff --git a/IRaCIS.Core.Domain/BaseModel/Entity.cs b/IRaCIS.Core.Domain/BaseModel/Entity.cs new file mode 100644 index 0000000..939bf9f --- /dev/null +++ b/IRaCIS.Core.Domain/BaseModel/Entity.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + public abstract class Entity : IEntity + { + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + } + + public interface IEntity + { + abstract TKey Id { get; set; } + } + +} diff --git a/IRaCIS.Core.Domain/BaseModel/IAuditAdd.cs b/IRaCIS.Core.Domain/BaseModel/IAuditAdd.cs new file mode 100644 index 0000000..70cd25e --- /dev/null +++ b/IRaCIS.Core.Domain/BaseModel/IAuditAdd.cs @@ -0,0 +1,25 @@ +using System; + +namespace IRaCIS.Core.Domain.Models +{ + public interface IAuditAdd where TKey : struct + { + public TKey CreateUserId { get; set; } + + public DateTime CreateTime { get; set; } + + } + + + + public interface IAuditAdd : IAuditAdd + { + + } + + public interface IAuditAddWithUserName : IAuditAdd + { + string CreateUser { get; set; } + } + +} diff --git a/IRaCIS.Core.Domain/BaseModel/IAuditUpdate.cs b/IRaCIS.Core.Domain/BaseModel/IAuditUpdate.cs new file mode 100644 index 0000000..e154cce --- /dev/null +++ b/IRaCIS.Core.Domain/BaseModel/IAuditUpdate.cs @@ -0,0 +1,16 @@ +using System; + +namespace IRaCIS.Core.Domain.Models +{ + public interface IAuditUpdate where TKey : struct + { + public TKey UpdateUserId { get; set; } + //string UpdateUserName { get; set; } + public DateTime UpdateTime { get; set; } + } + + public interface IAuditUpdate : IAuditUpdate + { + + } +} diff --git a/IRaCIS.Core.Domain/BaseModel/ISoftDelete.cs b/IRaCIS.Core.Domain/BaseModel/ISoftDelete.cs new file mode 100644 index 0000000..8cfa535 --- /dev/null +++ b/IRaCIS.Core.Domain/BaseModel/ISoftDelete.cs @@ -0,0 +1,17 @@ +using System; + +namespace IRaCIS.Core.Domain.Models +{ + + + public interface ISoftDelete + { + bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + } + + +} diff --git a/IRaCIS.Core.Domain/Common/CommonDocument.cs b/IRaCIS.Core.Domain/Common/CommonDocument.cs new file mode 100644 index 0000000..be9a95f --- /dev/null +++ b/IRaCIS.Core.Domain/Common/CommonDocument.cs @@ -0,0 +1,88 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-31 13:18:42 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///CommonDocument + /// + [Table("CommonDocument")] + public class CommonDocument : Entity, IAuditUpdate, IAuditAdd,ISoftDelete + { + + + [Required] + public string Code { get; set; } = String.Empty; + + [Required] + public string Name { get; set; } = String.Empty; + + /// + /// Path + /// + [Required] + public string Path { get; set; } = String.Empty; + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + /// + /// Description + /// + [Required] + public string Description { get; set; } = String.Empty; + + /// + /// IsDeleted + /// + [Required] + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + + public CriterionType? CriterionTypeEnum { get; set; } + public CommonDocumentFileType FileTypeEnum { get; set; } + public CommonDocumentBusinessScenario BusinessScenarioEnum { get; set; } + + + //[Required] + //public Guid FileTypeId { get; set; } + //public Guid ModuleTypeId { get; set; } + + + //public Dictionary FileType { get; set; } + //public Dictionary ModuleType { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Common/Dictionary.cs b/IRaCIS.Core.Domain/Common/Dictionary.cs new file mode 100644 index 0000000..6b51857 --- /dev/null +++ b/IRaCIS.Core.Domain/Common/Dictionary.cs @@ -0,0 +1,71 @@ +using EntityFrameworkCore.Projectables; +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Dictionary")] + public partial class Dictionary : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + public List DoctorDicRelationList { get; set; } = new List(); + + public string ChildGroup { get; set; } + + public int ChildCodeEnum { get; set; } + + + public DicDataTypeEnum DataTypeEnum { get; set; } + + + [Column("Value")] + public string Value { get; set; } = string.Empty; + + [Column("ValueCN")] + public string ValueCN { get; set; } = string.Empty; + + + [StringLength(512)] + public string Description { get; set; } = string.Empty; + + public int ShowOrder { get; set; } + + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + + + public string Code { get; set; } + + public Guid? ParentId { get; set; } + + public bool IsEnable { get; set; } + + public Guid? ConfigTypeId { get; set; } + + [JsonIgnore] + [ForeignKey("ConfigTypeId")] + public Dictionary ConfigDictionary { get; set; } + + [JsonIgnore] + [ForeignKey("ParentId")] + public Dictionary Parent { get; set; } + [JsonIgnore] + public List ChildList { get; set; } = new List(); + + + [NotMapped] + public string MappedValue { get; set; } + + + [Projectable] + public string TranslateValue( string value, string valueCN,bool isCN) => isCN?valueCN:value; + + + } +} diff --git a/IRaCIS.Core.Domain/Common/EmailNoticeConfig.cs b/IRaCIS.Core.Domain/Common/EmailNoticeConfig.cs new file mode 100644 index 0000000..dd1a99f --- /dev/null +++ b/IRaCIS.Core.Domain/Common/EmailNoticeConfig.cs @@ -0,0 +1,74 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-02-15 11:55:43 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///EmailNoticeConfig + /// + [Table("EmailNoticeConfig")] + public class EmailNoticeConfig : Entity, IAuditUpdate, IAuditAdd,ISoftDelete + { + public string Code { get; set; } = String.Empty; + + public CommonDocumentBusinessScenario BusinessScenarioEnum { get; set; } + + /// + /// 是否区分标准 + /// + public bool IsDistinguishCriteria { get; set; } + + + /// + /// 是否需要回执 + /// + [Required] + public bool IsReturnRequired { get; set; } + + + [Required] + public bool IsUrgent { get; set; } + + public bool IsAutoSend { get; set; } + + public bool IsEnable { get; set; } + + [Required] + public DateTime CreateTime { get; set; } + + + [Required] + public Guid CreateUserId { get; set; } + + + [Required] + public Guid UpdateUserId { get; set; } + [Required] + public DateTime UpdateTime { get; set; } + + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + + + + + + + + + } + +} diff --git a/IRaCIS.Core.Domain/Common/FrontAuditConfig.cs b/IRaCIS.Core.Domain/Common/FrontAuditConfig.cs new file mode 100644 index 0000000..535bee9 --- /dev/null +++ b/IRaCIS.Core.Domain/Common/FrontAuditConfig.cs @@ -0,0 +1,204 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-28 16:43:12 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///FrontAuditConfig + /// + [Table("FrontAuditConfig")] + public class FrontAuditConfig : Entity, IAuditUpdate, IAuditAdd + { + + public string Value { get; set; } = String.Empty; + + public string ValueCN { get; set; } = String.Empty; + + public string Description { get; set; } = String.Empty; + + public DateTime CreateTime { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public Guid UpdateUserId { get; set; } + + /// + /// 是否有签名 + /// + public bool IsHaveSign { get; set; } + + /// + /// 是否有原因 + /// + public bool IsHaveReason { get; set; } + + /// + /// 是否完成 + /// + public bool IsFinish { get; set; } + + /// + /// 是否加入计划 + /// + public bool IsJoinPlan { get; set; } + + /// + /// 标识 + /// + public string Identification { get; set; } + + public Guid? ParentId { get; set; } + + + public bool IsEnable { get; set; } + + public int Sort { get; set; } + + + public Guid? ModuleTypeId { get; set; } + + public Guid? ObjectTypeId { get; set; } + public Guid? OptTypeId { get; set; } + + public Guid? ChildrenTypeId { get; set; } + + public int IsShowParent { get; set; } + + public string InterfaceName { get; set; } = String.Empty; + + //前端使用 C M + public string ConfigType { get; set; } = String.Empty; + + + //翻译的字段名 这里有可能是一个数组名 那么具体的翻译字段名就不是这个了 + public string Code { get; set; } = String.Empty; + + + //前端渲染数组 数组名 和数组值 + public string ChildDataLabel { get; set; } + public string ChildDataValue { get; set; } + + + + /// + /// 翻译的字典名(单个字段翻译的时候) + /// + + public string DictionaryCode { get; set; } = String.Empty; + + + /// + /// 前端展示类型 Router, Array,Table + /// + + public string DataType { get; set; } + + // 后端翻译的类型 对应前端界面 "",Dictionary,Date + public string EnumType { get; set; } + + + + /// + /// 翻译的类型 FrontAudit 的描述 可能是Id Code + /// + public string DictionaryType { get; set; } = String.Empty; + + + /// + /// 后端翻译的 日期类型 + /// + + public string DateType { get; set; } = String.Empty; + + + + + /// 字典表 + public string ForeignKeyTableName { get; set; } = String.Empty; + + /// 字典Value + public string ForeignKeyValue { get; set; } = String.Empty; + + /// 字典 + public string ForeignKeyText { get; set; } = String.Empty; + + + + + public string TableConfigJsonStr { get; set; } = String.Empty; + + public string UrlConfigJsonStr { get; set; } = String.Empty; + + + + + #region 废弃 + //未知是否有用 + public bool IsConfig { get; set; } + /// + /// 是否为特殊类型 + /// + public bool IsSpecialType { get; set; } + public string DictionaryKey { get; set; } + + + public bool IsShowByTrialConfig { get; set; } + public string TrialConfigRelyFieldName { get; set; } + + #endregion + + } + + + + + public class UrlConfig + { + public bool IsRoute { get; set; } + + public string RoutePath { get; set; } + + public bool IsHaveParameters { get; set; } + + public List ParameterList { get; set; } = new List(); + + public class ParameterConfig + { + public string UrlParameterName { get; set; } = String.Empty; + public string UrlParameterValueName { get; set; } = String.Empty; + } + } + + public class TableConfig + { + public bool IsList { get; set; } + public string ListName { get; set; } = String.Empty; + public bool IsFixedColumn { get; set; } + public string FixedColumnName { get; set; } = String.Empty; + public string ColumnName { get; set; } = String.Empty; + public string ColumnValue { get; set; } = String.Empty; + public bool IsMerge { get; set; } + public string MergeColumnName { get; set; } = String.Empty; + + + public bool IsPicture { get; set; } + + + public bool IsNeedTransalate { get; set; } + public string TranslateDictionaryName { get; set; } = String.Empty; + + + public bool IsDynamicTranslate { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Common/NotDefault.cs b/IRaCIS.Core.Domain/Common/NotDefault.cs new file mode 100644 index 0000000..b387381 --- /dev/null +++ b/IRaCIS.Core.Domain/Common/NotDefault.cs @@ -0,0 +1,78 @@ +namespace System.ComponentModel.DataAnnotations +{ + [AttributeUsage( + AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, + AllowMultiple = false)] + public class GuidNotEmptyAttribute : ValidationAttribute + { + public const string DefaultErrorMessage = "The {0} field must not be empty"; + public GuidNotEmptyAttribute() : base(DefaultErrorMessage) { } + + public override bool IsValid(object value) + { + //NotEmpty doesn't necessarily mean required + if (value is null) + { + return true; + } + + switch (value) + { + case Guid guid: + return guid != Guid.Empty; + default: + return true; + } + } + } + + public class NotDefaultAttribute : ValidationAttribute + { + public const string DefaultErrorMessage = "The {0} field is is not passed or not set a valid value"; + public NotDefaultAttribute() : base(DefaultErrorMessage) { } + + public override bool IsValid(object value) + { + //NotDefault doesn't necessarily mean required + if (value is null) + { + return true; + } + + var type = value.GetType(); + if (type.IsValueType) + { + var defaultValue = Activator.CreateInstance(type); + return !value.Equals(defaultValue); + } + + // non-null ref type + return true; + } + } + + + public class CanConvertToTimeAttribute : ValidationAttribute + { + public const string DefaultErrorMessage = "The {0} field is is not a valid DateTime value"; + public CanConvertToTimeAttribute() : base(DefaultErrorMessage) { } + + public override bool IsValid(object value) + { + if (value is null) + { + return false; + } + + if (DateTime.TryParse(value.ToString(), out _) == true) + { + return true; + } + else + { + return false; + } + } + } + +} diff --git a/IRaCIS.Core.Domain/Common/SystemBasicData.cs b/IRaCIS.Core.Domain/Common/SystemBasicData.cs new file mode 100644 index 0000000..6694f3b --- /dev/null +++ b/IRaCIS.Core.Domain/Common/SystemBasicData.cs @@ -0,0 +1,75 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-02-15 15:45:52 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SystemBasicData + /// + [Table("SystemBasicData")] + public class SystemBasicData : Entity, IAuditUpdate, IAuditAdd + { + + [Required] + public string Name { get; set; }=string.Empty; + + + [Required] + public string Value { get; set; } = string.Empty; + + + [Required] + public string Description { get; set; } = string.Empty; + + + [Required] + public int ShowOrder { get; set; } + + [Required] + public DateTime CreateTime { get; set; } + + + [Required] + public Guid CreateUserId { get; set; } + + + [Required] + public DateTime UpdateTime { get; set; } + + [Required] + public Guid UpdateUserId { get; set; } + + + [Required] + public string Code { get; set; } + + + public Guid? ParentId { get; set; } + + [JsonIgnore] + [ForeignKey("ParentId")] + public SystemBasicData Parent { get; set; } + + + public string ValueCN { get; set; } = string.Empty; + + public bool IsEnable { get; set; } + + + public BasicDataTypeEnum BasicDataTypeEnum { get; set; } + + + + + + + } + +} diff --git a/IRaCIS.Core.Domain/Common/VerificationCode.cs b/IRaCIS.Core.Domain/Common/VerificationCode.cs new file mode 100644 index 0000000..ad1daf8 --- /dev/null +++ b/IRaCIS.Core.Domain/Common/VerificationCode.cs @@ -0,0 +1,34 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("VerificationCode")] + public class VerificationCode:Entity, IAuditAdd + { + //public Guid Id { get; set; } + + public Guid UserId { get; set; } = Guid.Empty; + + //验证码 + public string Code { get; set; } + + //什么类型的验证码 邮箱|手机 + + public VerifyType CodeType { get; set; } + + public bool HasSend { get; set; } + + + //发送的邮箱或者手机 + public string EmailOrPhone { get; set; } + + + //过期时间 + public DateTime ExpirationTime { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/Dcotor/Attachment.cs b/IRaCIS.Core.Domain/Dcotor/Attachment.cs new file mode 100644 index 0000000..bf61dd5 --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/Attachment.cs @@ -0,0 +1,34 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + //public enum AttachmentType + //{ + // Avatar=1,//ͷ + // Resume=2,// + // GCP=3,//GCP֤ + // MedicalLicence=4,//ҽʦʸ֤ + // PracticeCertificate=5,//ִҵʸ֤ + // LargeEquipmentWorkingCertificate=6,//еϸ֤ + // HighestDegreeCertificate=7//ѧ֤ + //} + [Table("Attachment")] + public partial class Attachment : Entity, IAuditAdd + { + public Guid? DoctorId { get; set; } + public string Type { get; set; } + public bool IsOfficial { get; set; } = false; + public string Path { get; set; } = string.Empty; + public string Code { get; set; } = string.Empty; + public DateTime? ExpiryDate { get; set; } + public string FileName { get; set; } = string.Empty; + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } = Guid.Empty; + //language=1 ģ 2ΪӢ + public int Language { get; set; } = 0; + + //public Guid CreateUserId { get; set; } = Guid.Empty; + //public DateTime? CreateTime { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Dcotor/Doctor.cs b/IRaCIS.Core.Domain/Dcotor/Doctor.cs new file mode 100644 index 0000000..ab0837d --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/Doctor.cs @@ -0,0 +1,207 @@ +using EntityFrameworkCore.Projectables; +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Doctor")] + public partial class Doctor : Entity, IAuditUpdate, IAuditAdd + { + + // + [JsonIgnore] + public List DoctorDicRelationList { get; set; }=new List(); + [JsonIgnore] + public List TrialExperienceCriteriaList { get; set; } + + + // UserDoctor һҽ ɱû + [JsonIgnore] + public List UserList { get; set; } + + [JsonIgnore] + public List EnrollList { get; set; } + + + + public string ReviewerCode { get; set; } + + public int Code { get; set; } + + + [StringLength(100)] + public string Phone { get; set; } = string.Empty; + + + [StringLength(100)] + public string Password { get; set; } + + + [StringLength(50)] + public string ChineseName { get; set; } = string.Empty; + + + [StringLength(100)] + public string FirstName { get; set; } = string.Empty; + + + [StringLength(100)] + public string LastName { get; set; } = string.Empty; + + [Projectable] + public string FullName => LastName + " / " + FirstName; + + public int Sex { get; set; } + + + [StringLength(100)] + public string EMail { get; set; } = string.Empty; + + + [StringLength(100)] + public string WeChat { get; set; } = string.Empty; + + + [StringLength(1000)] + public string Introduction { get; set; } = string.Empty; + + public Guid? DepartmentId { get; set; } = Guid.Empty; + + + [StringLength(100)] + public string DepartmentOther { get; set; } = string.Empty; + + public Guid? PhysicianId { get; set; } + public string Physician { get; set; } = string.Empty; + public string PhysicianCN { get; set; } = string.Empty; + + public Guid? RankId { get; set; } = Guid.Empty; + + + [StringLength(100)] + public string RankOther { get; set; } = string.Empty; + + + + public Guid? PositionId { get; set; } = Guid.Empty; + + + [StringLength(100)] + public string PositionOther { get; set; } = string.Empty; + + // Ƿɿ ζ һֱ + public Guid? HospitalId { get; set; } = Guid.Empty; + + + [StringLength(200)] + public string HospitalOther { get; set; } = string.Empty; + + + + + + [StringLength(100)] + public string ReadingTypeOther { get; set; } = string.Empty; + + + + + [StringLength(100)] + public string SubspecialityOther { get; set; } = string.Empty; + + public int GCP { get; set; } + + public Guid? GCPId { get; set; } = Guid.Empty; + + public Guid OrganizationId { get; set; } = Guid.Empty; + + public string OtherClinicalExperience { get; set; } = string.Empty; + public string OtherClinicalExperienceCN { get; set; } = string.Empty; + + public ContractorStatusEnum CooperateStatus { get; set; } = ContractorStatusEnum.Noncooperation; + + public ResumeStatusEnum ResumeStatus { get; set; } = ResumeStatusEnum.Failed; + + + + [StringLength(512)] + public string PhotoPath { get; set; } = string.Empty; + + [StringLength(512)] + public string ResumePath { get; set; } = string.Empty; + public Guid? SpecialityId { get; set; } = Guid.Empty; + + public string SpecialityOther { get; set; } = string.Empty; + + public string AdminComment { get; set; } = string.Empty; + + public ReviewerInformationConfirmStatus ReviewStatus { get; set; } = ReviewerInformationConfirmStatus.ConfirmRefuse; + + public bool AcceptingNewTrial { get; set; } = false; + public bool ActivelyReading { get; set; } = false; + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public DateTime? LastLoginTime { get; set; } + + public Guid AuditUserId { get; set; } = Guid.Empty; + + public DateTime? AuditTime { get; set; } + + public int Nation { get; set; } = 0; // ֧ͣ0-йҽCN1-ҽUS + + + public string ReadingTypeOtherCN { get; set; } = string.Empty; + public string HospitalOtherCN { get; set; } = string.Empty; + public string PositionOtherCN { get; set; } = string.Empty; + public string RankOtherCN { get; set; } = string.Empty; + public string DepartmentOtherCN { get; set; } = string.Empty; + public string SubspecialityOtherCN { get; set; } = string.Empty; + public string SpecialityOtherCN { get; set; } = string.Empty; + + [JsonIgnore] + [ForeignKey("HospitalId")] + public Hospital Hospital { get; set; } + [JsonIgnore] + [ForeignKey("SpecialityId")] + public virtual Dictionary Speciality { get; set; } + [JsonIgnore] + [ForeignKey("DepartmentId")] + public virtual Dictionary Department { get; set; } + + [JsonIgnore] + [ForeignKey("RankId")] + public virtual Dictionary Rank { get; set; } + + [JsonIgnore] + [ForeignKey("PositionId")] + public virtual Dictionary Position { get; set; } + [JsonIgnore] + public List AttachmentList { get; set; } + [JsonIgnore] + public List CriterionFileList { get; set; } + + [JsonIgnore] + public User User { get; set; } + + + //[JsonIgnore] + //public List VisitTaskList { get; set; } + + public bool IsVirtual { get; set; } + + public string BlindName { get; set; } = string.Empty; + + public string BlindNameCN { get; set; } = string.Empty; + + + public string BlindPublications { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Dcotor/DoctorCriterionFile.cs b/IRaCIS.Core.Domain/Dcotor/DoctorCriterionFile.cs new file mode 100644 index 0000000..84baafc --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/DoctorCriterionFile.cs @@ -0,0 +1,76 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2023-01-09 14:34:17 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///DoctorCriterionFile + /// + [Table("DoctorCriterionFile")] + public class DoctorCriterionFile : Entity, IAuditAdd + { + + /// + /// 文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件路径 + /// + public string FilePath { get; set; } + + /// + /// 标准类型 + /// + public CriterionType CriterionType { get; set; } + + /// + /// 医生Id + /// + public Guid DoctorId { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 文件类型 + /// + public CriterionFileType FileType { get; set; } + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } = true; + + [JsonIgnore] + [ForeignKey("DoctorId")] + public Doctor Doctor { get; set; } + + public string CriterionName { get; set; } + public Guid? TrialReadingCriterionId { get; set; } + public Guid? TrialId { get; set; } + + + } + + +} diff --git a/IRaCIS.Core.Domain/Dcotor/DoctorDictionary.cs b/IRaCIS.Core.Domain/Dcotor/DoctorDictionary.cs new file mode 100644 index 0000000..24394b9 --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/DoctorDictionary.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("DoctorDictionary")] + public partial class DoctorDictionary : Entity + { + [JsonIgnore] + [ForeignKey("DoctorId")] + public Doctor Doctor { get; set; } + + [JsonIgnore] + [ForeignKey("DictionaryId")] + public Dictionary Dictionary { get; set; } + + [StringLength(50)] + public string KeyName { get; set; } = string.Empty; + + public Guid DoctorId { get; set; } + + public Guid DictionaryId { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Dcotor/DoctorWorkload.cs b/IRaCIS.Core.Domain/Dcotor/DoctorWorkload.cs new file mode 100644 index 0000000..46186c5 --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/DoctorWorkload.cs @@ -0,0 +1,54 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("DoctorWorkload")] + public partial class Workload : Entity, IAuditUpdate, IAuditAdd + { + [Required] + public Guid TrialId { get; set; } + + public Guid DoctorId { get; set; } + + public int DataFrom { get; set; } + + public DateTime WorkTime { get; set; } + + public int Training { get; set; } + + public int Downtime { get; set; } + + public int Timepoint { get; set; } + + public int TimepointIn24H { get; set; } + + public int TimepointIn48H { get; set; } + + public int Adjudication { get; set; } + + public int AdjudicationIn24H { get; set; } + + public int AdjudicationIn48H { get; set; } + + public int Global { get; set; } + public int RefresherTraining { get; set; } + + public int CreateUserType { get; set; } + + [Required] + public string YearMonth { get; set; } + + public bool IsLock { get; set; } = false; + + + public Guid CreateUserId { get; set; } + + public DateTime CreateTime { get; set; } + + public Guid UpdateUserId { get; set; } + + public DateTime UpdateTime { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Dcotor/Education.cs b/IRaCIS.Core.Domain/Dcotor/Education.cs new file mode 100644 index 0000000..498478f --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/Education.cs @@ -0,0 +1,69 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Education")] + public partial class Education : Entity, IAuditUpdate, IAuditAdd + { + public Guid DoctorId { get; set; } + + [Column(TypeName = "date")] + public DateTime? BeginDate { get; set; } + [Column(TypeName = "date")] + public DateTime? EndDate { get; set; } + + [StringLength(50)] + public string Degree { get; set; } = string.Empty; + [StringLength(100)] + public string Major { get; set; } = string.Empty; + + [StringLength(100)] + public string Organization { get; set; } = string.Empty; + + + [StringLength(50)] + public string Country { get; set; } = string.Empty; + + + [StringLength(50)] + public string Province { get; set; } = string.Empty; + + + [StringLength(50)] + public string City { get; set; } = string.Empty; + + + [StringLength(50)] + public string DegreeCN { get; set; } = string.Empty; + [StringLength(100)] + public string MajorCN { get; set; } = string.Empty; + + [StringLength(100)] + public string OrganizationCN { get; set; } = string.Empty; + + + [StringLength(50)] + public string CountryCN { get; set; } = string.Empty; + + + [StringLength(50)] + public string ProvinceCN { get; set; } = string.Empty; + + + [StringLength(50)] + public string CityCN { get; set; } = string.Empty; + + + + public int ShowOrder { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Dcotor/Postgraduate.cs b/IRaCIS.Core.Domain/Dcotor/Postgraduate.cs new file mode 100644 index 0000000..7486316 --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/Postgraduate.cs @@ -0,0 +1,66 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Postgraduate")] + public partial class Postgraduate : Entity, IAuditUpdate, IAuditAdd + { + // public Guid Id { get; set; } + + public Guid DoctorId { get; set; } + + [Column(TypeName = "date")] + public DateTime? BeginDate { get; set; } + + [Column(TypeName = "date")] + public DateTime? EndDate { get; set; } + + + [StringLength(50)] + public string Training { get; set; } = string.Empty; + + [StringLength(100)] + public string Major { get; set; } = string.Empty; + + [StringLength(100)] + public string Hospital { get; set; } = string.Empty; + + [StringLength(100)] + public string School { get; set; } = string.Empty; + [StringLength(100)] + public string Country { get; set; } = string.Empty; + + [StringLength(100)] + public string Province { get; set; } = string.Empty; + + [StringLength(100)] + public string City { get; set; } = string.Empty; + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + [StringLength(50)] + public string TrainingCN { get; set; } = string.Empty; + + [StringLength(100)] + public string MajorCN { get; set; } = string.Empty; + + [StringLength(100)] + public string HospitalCN { get; set; } = string.Empty; + + [StringLength(100)] + public string SchoolCN { get; set; } = string.Empty; + [StringLength(100)] + public string CountryCN { get; set; } = string.Empty; + + [StringLength(100)] + public string ProvinceCN { get; set; } = string.Empty; + + [StringLength(100)] + public string CityCN { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Dcotor/ResearchPublication.cs b/IRaCIS.Core.Domain/Dcotor/ResearchPublication.cs new file mode 100644 index 0000000..f86f505 --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/ResearchPublication.cs @@ -0,0 +1,24 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("ResearchPublication")] + public partial class ResearchPublication : Entity, IAuditUpdate, IAuditAdd + { + public Guid DoctorId { get; set; } + public string Research { get; set; } = string.Empty; + public string Grants { get; set; } = string.Empty; + public string Publications { get; set; } = string.Empty; + public string AwardsHonors { get; set; } = string.Empty; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; }=DateTime.Now; + + public string ResearchCN { get; set; } = string.Empty; + public string GrantsCN { get; set; } = string.Empty; + public string PublicationsCN { get; set; } = string.Empty; + public string AwardsHonorsCN { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Dcotor/TrialExperience.cs b/IRaCIS.Core.Domain/Dcotor/TrialExperience.cs new file mode 100644 index 0000000..c3ecb39 --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/TrialExperience.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("TrialExperience")] + public partial class TrialExperience : Entity, IAuditUpdate, IAuditAdd + { + + public List ExperienceCriteriaList { get; set; } + + + public Guid DoctorId { get; set; } + + //[StringLength(100)] + //public string Term { get; set; } + //[StringLength(100)] + //public string EvaluationCriteria { get; set; } + + public Guid? PhaseId { get; set; } + + public Dictionary Phase { get; set; } + + [StringLength(512)] + public string EvaluationContent { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public int VisitReadingCount { get; set; } + + public DateTime? StartTime { get; set; } + + public DateTime? EndTime { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Dcotor/TrialExperienceCriteria.cs b/IRaCIS.Core.Domain/Dcotor/TrialExperienceCriteria.cs new file mode 100644 index 0000000..7328aa3 --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/TrialExperienceCriteria.cs @@ -0,0 +1,18 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + public class TrialExperienceCriteria:Entity + { + public Guid DoctorId { get; set; } + public Guid TrialExperienceId { get; set; } + public Guid EvaluationCriteriaId { get; set; } + + [ForeignKey("EvaluationCriteriaId")] + public Dictionary EvaluationCriteria { get; set; } + + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/Dcotor/UserDoctor.cs b/IRaCIS.Core.Domain/Dcotor/UserDoctor.cs new file mode 100644 index 0000000..0f93551 --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/UserDoctor.cs @@ -0,0 +1,22 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// ûҽϵ - ʵ + /// + [Table("UserDoctor")] + public partial class UserDoctor : Entity + { + [ForeignKey("User")] + public Guid UserId { get; set; } + + [ForeignKey("Doctor")] + public Guid DoctorId { get; set; } + + public Doctor Doctor { get; set; } + + public User User { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Dcotor/Vacation.cs b/IRaCIS.Core.Domain/Dcotor/Vacation.cs new file mode 100644 index 0000000..ae5670c --- /dev/null +++ b/IRaCIS.Core.Domain/Dcotor/Vacation.cs @@ -0,0 +1,18 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Vacation")] + public class Vacation : Entity, IAuditUpdate, IAuditAdd + { + public Guid DoctorId { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public int Status { get; set; } = 1; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Document/SystemDocConfirmedUser.cs b/IRaCIS.Core.Domain/Document/SystemDocConfirmedUser.cs new file mode 100644 index 0000000..ec048ce --- /dev/null +++ b/IRaCIS.Core.Domain/Document/SystemDocConfirmedUser.cs @@ -0,0 +1,65 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-14 15:04:22 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SystemDocConfirmedUser + /// + [Table("SystemDocConfirmedUser")] + public class SystemDocConfirmedUser : Entity, IAuditAdd,ISoftDelete + + { + [JsonIgnore] + public SystemDocument SystemDocument { get; set; } + + [JsonIgnore] + [ForeignKey("ConfirmUserId")] + public User User { get; set; } + + /// + /// TrialDocumentId + /// + [Required] + public Guid SystemDocumentId { get; set; } + + /// + /// ConfirmTime + /// + public DateTime? ConfirmTime { get; set; } + + /// + /// ConfirmUserId + /// + [Required] + public Guid ConfirmUserId { get; set; } + + /// + /// SignFirstViewTime + /// + public DateTime? SignFirstViewTime { get; set; } + + public string SignText { get; set; } = string.Empty; + + public Guid CreateUserId { get; set; } + + public DateTime CreateTime { get; set; } + + /// + /// 是否废除 + /// + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + + } + +} diff --git a/IRaCIS.Core.Domain/Document/SystemDocNeedConfirmedUserType.cs b/IRaCIS.Core.Domain/Document/SystemDocNeedConfirmedUserType.cs new file mode 100644 index 0000000..cc7186d --- /dev/null +++ b/IRaCIS.Core.Domain/Document/SystemDocNeedConfirmedUserType.cs @@ -0,0 +1,37 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-14 15:04:23 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SystemDocNeedConfirmedUserType + /// + [Table("SystemDocNeedConfirmedUserType")] + public class SystemDocNeedConfirmedUserType : Entity + { + [JsonIgnore] + [ForeignKey("NeedConfirmUserTypeId")] + public UserType UserTypeRole { get; set; } + + [JsonIgnore] + public SystemDocument SystemDocument { get; set; } + /// + /// SystemDocumentId + /// + [Required] + public Guid SystemDocumentId { get; set; } + + /// + /// NeedConfirmUserTypeId + /// + [Required] + public Guid NeedConfirmUserTypeId { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Document/SystemDocument.cs b/IRaCIS.Core.Domain/Document/SystemDocument.cs new file mode 100644 index 0000000..696d118 --- /dev/null +++ b/IRaCIS.Core.Domain/Document/SystemDocument.cs @@ -0,0 +1,83 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-05 09:11:49 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SystemDocument + /// + [Table("SystemDocument")] + public class SystemDocument : Entity, IAuditUpdate, IAuditAdd,ISoftDelete + { + [JsonIgnore] + public List SystemDocConfirmedUserList { get; set; } + [JsonIgnore] + public List NeedConfirmedUserTypeList { get; set; } + + + + [JsonIgnore] + [ForeignKey("FileTypeId")] + public Dictionary FileType { get; set; } + + + + public Guid FileTypeId { get; set; } + + /// + /// Name + /// + [Required] + public string Name { get; set; } = string.Empty; + + + + + public int SignViewMinimumMinutes { get; set; } + + /// + /// Path + /// + [Required] + public string Path { get; set; } = string.Empty; + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + + public DateTime? DeletedTime { get; set; } + + public bool IsDeleted { get; set; } + + public Guid? DeleteUserId { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Document/TrialDocConfirmedUser.cs b/IRaCIS.Core.Domain/Document/TrialDocConfirmedUser.cs new file mode 100644 index 0000000..adfc388 --- /dev/null +++ b/IRaCIS.Core.Domain/Document/TrialDocConfirmedUser.cs @@ -0,0 +1,67 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-05 18:02:45 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialDocUserTypeConfirmUser + /// + [Table("TrialDocConfirmedUser")] + public class TrialDocConfirmedUser : Entity, IAuditAdd,ISoftDelete + { + //public Guid TrialId { get; set; } + + //public TrialUser TrialUser { get; set; } + [JsonIgnore] + public TrialDocument TrialDocument { get; set; } + + + /// + /// TrialDocumentId + /// + [Required] + public Guid TrialDocumentId { get; set; } + + /// + /// ConfirmTime + /// + public DateTime? ConfirmTime { get; set; } + + /// + /// ConfirmUserId + /// + [Required] + public Guid ConfirmUserId { get; set; } + + [JsonIgnore] + [ForeignKey("ConfirmUserId")] + public User User { get; set; } + + + public DateTime? SignFirstViewTime { get; set; } + + + public string SignText { get; set; } = string.Empty; + + public Guid CreateUserId { get; set; } + + public DateTime CreateTime { get; set; } + + /// + /// 是否废除 + /// + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Document/TrialDocNeedConfirmedUserType.cs b/IRaCIS.Core.Domain/Document/TrialDocNeedConfirmedUserType.cs new file mode 100644 index 0000000..fbc61e7 --- /dev/null +++ b/IRaCIS.Core.Domain/Document/TrialDocNeedConfirmedUserType.cs @@ -0,0 +1,43 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-05 09:11:50 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialDocumentUserConfirm + /// + [Table("TrialDocNeedConfirmedUserType")] + public class TrialDocNeedConfirmedUserType : Entity + { + [JsonIgnore] + [ForeignKey("NeedConfirmUserTypeId")] + public UserType UserTypeRole { get; set; } + + [JsonIgnore] + public TrialDocument TrialDocument { get; set; } + + + /// + /// TrialDocumentId + /// + [Required] + public Guid TrialDocumentId { get; set; } + + + [Required] + public Guid NeedConfirmUserTypeId { get; set; } + + + //[ForeignKey("NeedConfirmUserTypeId")] + + //public UserTypeRole UserTypeRole { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Document/TrialDocument.cs b/IRaCIS.Core.Domain/Document/TrialDocument.cs new file mode 100644 index 0000000..48b3cca --- /dev/null +++ b/IRaCIS.Core.Domain/Document/TrialDocument.cs @@ -0,0 +1,97 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-05 09:11:50 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialDocument + /// + [Table("TrialDocument")] + public class TrialDocument : Entity, IAuditUpdate, IAuditAdd + { + + //需要确认的项目用户 通过TrialId 关联 用中间表过滤 + + [JsonIgnore] + public List TrialDocConfirmedUserList { get; set; } + [JsonIgnore] + public List NeedConfirmedUserTypeList { get; set; } + [JsonIgnore] + public Trial Trial { get; set; } + + [JsonIgnore] + [ForeignKey("FileTypeId")] + public Dictionary FileType { get; set; } + + + + public Guid FileTypeId { get; set; } + + + + /// + /// Name + /// + [Required] + public string Name { get; set; } = string.Empty; + + /// + /// Path + /// + [Required] + public string Path { get; set; } = string.Empty; + + /// + /// TrialId + /// + [Required] + public Guid TrialId { get; set; } + + /// + /// Description + /// + [Required] + public string Description { get; set; } = string.Empty; + + + + public int SignViewMinimumMinutes { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + public DateTime? DeletedTime { get; set; } + + + public bool IsDeleted { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Document/TrialEmailNoticeConfig.cs b/IRaCIS.Core.Domain/Document/TrialEmailNoticeConfig.cs new file mode 100644 index 0000000..5b3bf06 --- /dev/null +++ b/IRaCIS.Core.Domain/Document/TrialEmailNoticeConfig.cs @@ -0,0 +1,104 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-10-20 11:51:58 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialEmailNoticeConfig + /// + [Table("TrialEmailNoticeConfig")] + public class TrialEmailNoticeConfig : Entity, IAuditUpdate, IAuditAdd + { + + [Required] + public Guid TrialId { get; set; } + + + public string SMTPServerAddress { get; set; } = string.Empty; + + public int SMTPServerPort { get; set; } + + [Required] + public string AuthorizationCode { get; set; } = string.Empty; + + + public string FromName { get; set; } = string.Empty; + public List TrialEmailNoticeUserList { get; set; } = new List(); + + + //[Required] + + //public string ReceiveEmailsStr { get; set; } = string.Empty; + //public string CopyEmailsStr { get; set; } = string.Empty; + + //[NotMapped] + //public List ReceiveEmailList => ReceiveEmailsStr.Split('|', StringSplitOptions.RemoveEmptyEntries).Where(t => !string.IsNullOrEmpty(t)).Select(t=>t.Trim()).ToList(); + + //[NotMapped] + //public List CopyEmailList => CopyEmailsStr.Split('|', StringSplitOptions.RemoveEmptyEntries).Where(t=> !string.IsNullOrEmpty(t)).Select(t => t.Trim()).ToList(); + + + + public string FromEmail { get; set; } = string.Empty; + + [Required] + public bool IsUrgent { get; set; } + + + [Required] + public string Code { get; set; } = string.Empty; + + + + [Required] + public bool IsReturnRequired { get; set; } + + + + [Required] + public bool IsAutoSend { get; set; } + + + + public CommonDocumentBusinessScenario BusinessScenarioEnum { get; set; } + + public CriterionType? CriterionTypeEnum { get; set; } + + + public Guid? TrialReadingCriterionId { get; set; } + + [ForeignKey("TrialReadingCriterionId")] + [JsonIgnore] + public ReadingQuestionCriterionTrial TrialReadingCriterion { get; set; } + + [Required] + public string FilePath { get; set; } = string.Empty; + + [Required] + public string FileName { get; set; } = string.Empty; + + [Required] + public Guid CreateUserId { get; set; } + + [Required] + public DateTime CreateTime { get; set; } + + [Required] + public Guid UpdateUserId { get; set; } + + [Required] + public DateTime UpdateTime { get; set; } + + + } + +} diff --git a/IRaCIS.Core.Domain/Document/TrialEmailNoticeUser.cs b/IRaCIS.Core.Domain/Document/TrialEmailNoticeUser.cs new file mode 100644 index 0000000..cf8844a --- /dev/null +++ b/IRaCIS.Core.Domain/Document/TrialEmailNoticeUser.cs @@ -0,0 +1,33 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-10-21 16:06:20 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialEmailNoticeUser + /// + [Table("TrialEmailNoticeUser")] + public class TrialEmailNoticeUser : Entity + { + + + [JsonIgnore] + public TrialEmailNoticeConfig TrialEmailNoticeConfig { get; set; } + + + public UserTypeEnum UserType { get; set; } + + + public Guid TrialEmailNoticeConfigId { get; set; } + + + public EmailUserType EmailUserType { get; set; } + } + +} diff --git a/IRaCIS.Core.Domain/Financial/CalculateTask.cs b/IRaCIS.Core.Domain/Financial/CalculateTask.cs new file mode 100644 index 0000000..d46027c --- /dev/null +++ b/IRaCIS.Core.Domain/Financial/CalculateTask.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("CalculateTask")] + public class CalculateTask : Entity + { + public Guid ReviewerId { get; set; } + + [Required] + public string YearMonth { get; set; } + public bool IsLock { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Financial/ExchangeRate.cs b/IRaCIS.Core.Domain/Financial/ExchangeRate.cs new file mode 100644 index 0000000..c77e3ce --- /dev/null +++ b/IRaCIS.Core.Domain/Financial/ExchangeRate.cs @@ -0,0 +1,18 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("ExchangeRate")] + public class ExchangeRate : Entity, IAuditUpdate, IAuditAdd + { + public string YearMonth { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Rate { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Financial/Payment.cs b/IRaCIS.Core.Domain/Financial/Payment.cs new file mode 100644 index 0000000..800ce71 --- /dev/null +++ b/IRaCIS.Core.Domain/Financial/Payment.cs @@ -0,0 +1,49 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + + [Table("Payment")] + public partial class Payment : Entity, IAuditUpdate, IAuditAdd + { + public Guid DoctorId { get; set; } + public string YearMonth { get; set; } = string.Empty; + public bool IsLock { get; set; } + public DateTime YearMonthDate { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal PaymentUSD { get; set; } + + [Column(TypeName = "decimal(18,4)")] + public decimal PaymentCNY { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal ExchangeRate { get; set; } + + + [Column(TypeName = "decimal(18,4)")] + public decimal AdjustmentCNY { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal AdjustmentUSD { get; set; } + public DateTime CalculateTime { get; set; } = DateTime.Now; + + [StringLength(100)] + public string CalculateUser { get; set; } = string.Empty; + + [StringLength(500)] + public string Note { get; set; } = string.Empty; + + //public double TaxCNY { get; set; } + //public double ActuallyPaidCNY { get; set; } + //public double BankTransferCNY { get; set; } + + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Financial/PaymentAdjustment.cs b/IRaCIS.Core.Domain/Financial/PaymentAdjustment.cs new file mode 100644 index 0000000..44d4755 --- /dev/null +++ b/IRaCIS.Core.Domain/Financial/PaymentAdjustment.cs @@ -0,0 +1,33 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("PaymentAdjustment")] + public partial class PaymentAdjustment : Entity, IAuditUpdate, IAuditAdd + { + public Guid ReviewerId { get; set; } + + public DateTime YearMonthDate { get; set; } + public string YearMonth { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal AdjustmentUSD { get; set; } + + [Column(TypeName = "decimal(18,4)")] + public decimal AdjustmentCNY { get; set; } + + + public Guid TrialId { get; set; } = Guid.Empty; + + [Column(TypeName = "decimal(18,2)")] + public decimal ExchangeRate { get; set; } + public bool IsLock { get; set; } = false; + public string Note { get; set; } = string.Empty; + + public Guid CreateUserId { get; set; } = Guid.Empty; + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } = Guid.Empty; + public DateTime UpdateTime { get; set; } = DateTime.Now; + } +} diff --git a/IRaCIS.Core.Domain/Financial/PaymentDetail.cs b/IRaCIS.Core.Domain/Financial/PaymentDetail.cs new file mode 100644 index 0000000..feb8e50 --- /dev/null +++ b/IRaCIS.Core.Domain/Financial/PaymentDetail.cs @@ -0,0 +1,48 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("PaymentDetail")] + public partial class PaymentDetail : Entity, IAuditUpdate, IAuditAdd + { + public Guid PaymentId { get; set; } + public Guid DoctorId { get; set; } + public string YearMonth { get; set; } + public Guid TrialId { get; set; } + + [StringLength(50)] + public string TrialCode { get; set; } + + [StringLength(50)] + public string PaymentType { get; set; } + public int Count { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal BasePrice { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal PersonalAdditional { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal TrialAdditional { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal ExchangeRate { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal PaymentUSD { get; set; } + + [Column(TypeName = "decimal(18,4)")] + public decimal PaymentCNY { get; set; } + + public int ShowTypeOrder { get; set; } + public int ShowCodeOrder { get; set; } + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Financial/RankPrice.cs b/IRaCIS.Core.Domain/Financial/RankPrice.cs new file mode 100644 index 0000000..01daf2b --- /dev/null +++ b/IRaCIS.Core.Domain/Financial/RankPrice.cs @@ -0,0 +1,49 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("RankPrice")] + public partial class RankPrice : Entity, IAuditUpdate, IAuditAdd + { + [StringLength(200)] + public string RankName { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Timepoint { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal TimepointIn24H { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal TimepointIn48H { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Adjudication { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal AdjudicationIn24H { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal AdjudicationIn48H { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Global { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Training { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Downtime { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal RefresherTraining { get; set; } + public int ShowOrder { get; set; } + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } = Guid.Empty; + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } = Guid.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Financial/ReviewerPayInformation.cs b/IRaCIS.Core.Domain/Financial/ReviewerPayInformation.cs new file mode 100644 index 0000000..ebf486a --- /dev/null +++ b/IRaCIS.Core.Domain/Financial/ReviewerPayInformation.cs @@ -0,0 +1,32 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("DoctorPayInformation")] + public partial class ReviewerPayInformation : Entity, IAuditAdd, IAuditUpdate + { + public Guid DoctorId { get; set; } + [StringLength(200)] + public string DoctorNameInBank { get; set; } + + [StringLength(100)] + public string IDCard { get; set; } + + [StringLength(100)] + public string BankCardNumber { get; set; } + + [StringLength(200)] + public string BankName { get; set; } + public Guid RankId { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Additional { get; set; } + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } = Guid.Empty; + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } = Guid.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Financial/TrialPaymentPrice.cs b/IRaCIS.Core.Domain/Financial/TrialPaymentPrice.cs new file mode 100644 index 0000000..f883a57 --- /dev/null +++ b/IRaCIS.Core.Domain/Financial/TrialPaymentPrice.cs @@ -0,0 +1,34 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("TrialPaymentPrice")] + public partial class TrialPaymentPrice : Entity, IAuditAdd, IAuditUpdate + { + public Guid TrialId { get; set; } + + public Trial Trial { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal TrialAdditional { get; set; } = 0; + + public string SowName { get; set; } = string.Empty; + public string SowPath { get; set; } = string.Empty; + + [Column(TypeName = "decimal(18,2)")] + public decimal AdjustmentMultiple { get; set; } = 1; + + + + /// + /// Ƿ ΪĿ + /// + public bool? IsNewTrial { get; set; } = false; + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Financial/TrialRevenuesPrice.cs b/IRaCIS.Core.Domain/Financial/TrialRevenuesPrice.cs new file mode 100644 index 0000000..1055722 --- /dev/null +++ b/IRaCIS.Core.Domain/Financial/TrialRevenuesPrice.cs @@ -0,0 +1,46 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("TrialRevenuesPrice")] + public class TrialRevenuesPrice : Entity, IAuditUpdate, IAuditAdd + { + public Guid TrialId { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Timepoint { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal TimepointIn24H { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal TimepointIn48H { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Adjudication { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal AdjudicationIn24H { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal AdjudicationIn48H { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Global { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Training { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Downtime { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal RefresherTraining { get; set; } + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Financial/TrialRevenuesPriceVerification.cs b/IRaCIS.Core.Domain/Financial/TrialRevenuesPriceVerification.cs new file mode 100644 index 0000000..822c312 --- /dev/null +++ b/IRaCIS.Core.Domain/Financial/TrialRevenuesPriceVerification.cs @@ -0,0 +1,33 @@ +using System; + +namespace IRaCIS.Core.Domain.Models +{ + public class TrialRevenuesPriceVerification : Entity + { + public Guid TrialId { get; set; } + + public Guid ReviewerId { get; set; } + + public string YearMonth { get; set; } + + public bool Training { get; set; } = false; + + public bool Downtime { get; set; } = false; + + public bool Global { get; set; } = false; + + public bool Timepoint { get; set; } = false; + + public bool TimepointIn24H { get; set; } = false; + + public bool TimepointIn48H { get; set; } = false; + + public bool Adjudication { get; set; } = false; + + public bool AdjudicationIn24H { get; set; } = false; + + public bool AdjudicationIn48H { get; set; } = false; + public bool RefresherTraining { get; set; } = false; + public DateTime WorkLoadDate { get; set; } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/Financial/VolumeReward.cs b/IRaCIS.Core.Domain/Financial/VolumeReward.cs new file mode 100644 index 0000000..937d56e --- /dev/null +++ b/IRaCIS.Core.Domain/Financial/VolumeReward.cs @@ -0,0 +1,18 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("VolumeReward")] + public partial class VolumeReward : Entity, IAuditUpdate, IAuditAdd + { + [Column(TypeName = "decimal(18,2)")] + public decimal Price { get; set; } + public int Min { get; set; } + public int Max { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/IRaCIS.Core.Domain.csproj b/IRaCIS.Core.Domain/IRaCIS.Core.Domain.csproj new file mode 100644 index 0000000..5158a9e --- /dev/null +++ b/IRaCIS.Core.Domain/IRaCIS.Core.Domain.csproj @@ -0,0 +1,39 @@ + + + + net6.0 + AnyCPU;x64 + + + + ..\bin + + + + ..\bin + + + + ..\bin + + + + + + + + + + + + + + + + + + + + + + diff --git a/IRaCIS.Core.Domain/Image/DicomInstance.cs b/IRaCIS.Core.Domain/Image/DicomInstance.cs new file mode 100644 index 0000000..20c45db --- /dev/null +++ b/IRaCIS.Core.Domain/Image/DicomInstance.cs @@ -0,0 +1,54 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("DicomInstance")] + public class DicomInstance : Entity, IAuditAdd, IAuditUpdate + { + [JsonIgnore] + [ForeignKey("SeriesId")] + public DicomSeries DicomSerie { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + + public Guid SeqId { get; set; } + public Guid StudyId { get; set; } + public Guid SeriesId { get; set; } + public string StudyInstanceUid { get; set; } + public string SeriesInstanceUid { get; set; } + public string SopInstanceUid { get; set; } + public int InstanceNumber { get; set; } + public DateTime? InstanceTime { get; set; } + public bool CPIStatus { get; set; } + public int ImageRows { get; set; } + public int ImageColumns { get; set; } + public int SliceLocation { get; set; } + + + public string SliceThickness { get; set; } + public int NumberOfFrames { get; set; } + public string PixelSpacing { get; set; } + + public string ImagerPixelSpacing { get; set; } + public string FrameOfReferenceUID { get; set; } + public string WindowCenter { get; set; } + public string WindowWidth { get; set; } + + + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + public bool Anonymize { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + + + public string Path { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Image/DicomSeries.cs b/IRaCIS.Core.Domain/Image/DicomSeries.cs new file mode 100644 index 0000000..094c4f6 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/DicomSeries.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("DicomSeries")] + public class DicomSeries : Entity, IAuditAdd, IAuditUpdate, ISoftDelete + { + [JsonIgnore] + [ForeignKey("StudyId")] + public DicomStudy DicomStudy { get; set; } + + [JsonIgnore] + public List DicomInstanceList { get; set; } + + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid SeqId { get; set; } + public Guid StudyId { get; set; } + public string StudyInstanceUid { get; set; } + public string SeriesInstanceUid { get; set; } + public int SeriesNumber { get; set; } + public DateTime? SeriesTime { get; set; } + public string Modality { get; set; } + public string Description { get; set; } + public int InstanceCount { get; set; } + public string SliceThickness { get; set; } + + + + + public string ImagePositionPatient { get; set; } + public string ImageOrientationPatient { get; set; } + public string BodyPartExamined { get; set; } + public string SequenceName { get; set; } + public string ProtocolName { get; set; } + public string ImagerPixelSpacing { get; set; } + + + public string AcquisitionTime { get; set; } = string.Empty; + public string AcquisitionNumber { get; set; } = string.Empty; + public string TriggerTime { get; set; } = string.Empty; + + + + + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public Guid SubjectVisitId { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + public bool IsDeleted {get;set;} + public bool IsReading { get; set; } = true; + + public string BodyPartForEdit { get; set; } = string.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Image/DicomStudy.cs b/IRaCIS.Core.Domain/Image/DicomStudy.cs new file mode 100644 index 0000000..eb5b0ec --- /dev/null +++ b/IRaCIS.Core.Domain/Image/DicomStudy.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("DicomStudy")] + public class DicomStudy : Entity, IAuditUpdate, IAuditAdd, ISoftDelete + { + //一个检查 由多个人管理 + //public List TrialSiteUserList { get; set; } = new List(); + [JsonIgnore] + public List DicomStudyMonitorList { get; set; } = new List(); + [JsonIgnore] + public List StudyDTFList { get; set;} = new List(); + [JsonIgnore] + public TrialSite TrialSite { get; set; } + [JsonIgnore] + public Site Site { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid SeqId { get; set; } + + public Guid TrialId { get; set; } + + public Guid SiteId { get; set; } + + public Guid SubjectId { get; set; } + + public Guid SubjectVisitId { get; set; } + + public int Code { get; set; } = 0; + + public string StudyCode { get; set; } = string.Empty; + + public int Status { get; set; } = 1; + + public string StudyInstanceUid { get; set; } = string.Empty; + public DateTime? StudyTime { get; set; } + public string Modalities { get; set; } = string.Empty; + + public string Description { get; set; } = string.Empty; + public int SeriesCount { get; set; } = 0; + public int InstanceCount { get; set; } = 0; + + //public bool SoftDelete { get; set; } = false; + + public string InstitutionName { get; set; } = string.Empty; + public string PatientId { get; set; } = string.Empty; + public string PatientName { get; set; } = string.Empty; + public string PatientAge { get; set; } = string.Empty; + public string PatientSex { get; set; } = string.Empty; + + public string StudyId { get; set; } = string.Empty; + public string AccessionNumber { get; set; } = string.Empty; + public string PatientBirthDate { get; set; } = string.Empty; + public string AcquisitionTime { get; set; } = string.Empty; + public string AcquisitionNumber { get; set; } = string.Empty; + public string TriggerTime { get; set; } = string.Empty; + + + //0 未知 1 单重 2 双重 + public bool IsDoubleReview { get; set; } + + public string Comment { get; set; } = string.Empty;//上传的时候的 + + public string BodyPartExamined { get; set; } = string.Empty; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + + [JsonIgnore] + [ForeignKey("CreateUserId")] + public User Uploader { get; set; } + + //public DateTime? UploadedTime { get; set; } + + //public string Uploader { get; set; } = string.Empty; + + + + + public DateTime? DeadlineTime { get; set; } + + public string QAComment { get; set; } = string.Empty; + + + public string BodyPartForEdit { get; set; } = string.Empty; + + public string ModalityForEdit { get; set; } = string.Empty; + + + public bool CheckPassed { get; set; } + + public string CheckResult { get; set; }=string.Empty; + + [JsonIgnore] + [ForeignKey("SubjectId")] + public Subject Subject { get; set; } + + [JsonIgnore] + [ForeignKey("SubjectVisitId")] + public SubjectVisit SubjectVisit { get; set; } + + //软删除 + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + + } +} diff --git a/IRaCIS.Core.Domain/Image/DicomStudyMonitor.cs b/IRaCIS.Core.Domain/Image/DicomStudyMonitor.cs new file mode 100644 index 0000000..39d9407 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/DicomStudyMonitor.cs @@ -0,0 +1,92 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-01-25 13:26:03 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///DicomStudyMonitor + /// + [Table("StudyMonitor")] + public class StudyMonitor : Entity, IAuditAdd + { + [JsonIgnore] + [ForeignKey("StudyId")] + public DicomStudy DicomStudy { get; set; } + [JsonIgnore] + [ForeignKey("StudyId")] + public NoneDicomStudy NoneDicomStudy { get; set; } + + public DateTime CreateTime { get; set; } + + public Guid CreateUserId { get; set; } + + //可能是Dicom 也可能是非Dicom 该字段废弃 + public Guid StudyId { get; set; } + + + public DateTime UploadStartTime { get; set; } + + + public DateTime? UploadFinishedTime { get; set; } + + + public DateTime? ArchiveFinishedTime { get; set; } + + public int FailedFileCount { get; set; } + + + public decimal FileSize { get; set; } + + public string IP { get; set; } + + + public bool IsDicomReUpload { get; set; } + + public bool IsDicom { get; set; } + + public int FileCount { get; set; } + + public string StudyCode { get; set; } = string.Empty; + + + public Guid TrialId { get; set; } + + public Guid SiteId { get; set; } + + public Guid SubjectId { get; set; } + + public Guid SubjectVisitId { get; set; } + + [JsonIgnore] + [ForeignKey("SubjectId")] + public Subject Subject { get; set; } + + [JsonIgnore] + [ForeignKey("SubjectVisitId")] + public SubjectVisit SubjectVisit { get; set; } + + [JsonIgnore] + public TrialSite TrialSite { get; set; } + [JsonIgnore] + [ForeignKey("SiteId")] + public Site Site { get; set; } + [JsonIgnore] + [ForeignKey("TrialId")] + public Trial Trial { get; set; } + [JsonIgnore] + [ForeignKey("CreateUserId")] + public User Uploader { get; set; } + + + public bool IsSuccess { get; set; } + + public string Note = string.Empty; + + } + +} diff --git a/IRaCIS.Core.Domain/Image/ImageShare.cs b/IRaCIS.Core.Domain/Image/ImageShare.cs new file mode 100644 index 0000000..b688db3 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/ImageShare.cs @@ -0,0 +1,17 @@ +using System; + +namespace IRaCIS.Core.Domain.Models +{ + public class ImageShare: Entity + { + public Guid TrialId { get; set; } + public Guid SiteId { get; set; } + public Guid SubjectId { get; set; } + public Guid StudyId { get; set; } + + public DateTime ExpireTime { get; set; } + + public string Password { get; set; } + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/Image/SystemAnonymization.cs b/IRaCIS.Core.Domain/Image/SystemAnonymization.cs new file mode 100644 index 0000000..c57917a --- /dev/null +++ b/IRaCIS.Core.Domain/Image/SystemAnonymization.cs @@ -0,0 +1,91 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-03 15:26:35 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SystemAnonymization + /// + [Table("SystemAnonymization")] + public class SystemAnonymization : Entity, IAuditUpdate, IAuditAdd + { + + + + /// + /// Group + /// + [Required] + public string Group { get; set; } = String.Empty; + + /// + /// Element + /// + [Required] + public string Element { get; set; } = String.Empty; + + /// + /// TagDescription + /// + [Required] + public string TagDescription { get; set; } = String.Empty; + + /// + /// TagDescriptionCN + /// + [Required] + public string TagDescriptionCN { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + /// + /// ReplaceValue + /// + [Required] + public string ReplaceValue { get; set; } = String.Empty; + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// ValueRepresentation + /// + [Required] + public string ValueRepresentation { get; set; } = String.Empty; + + + public bool IsAdd { get; set; } + + public bool IsEnable { get; set; } + + + public bool IsFixed { get; set; } + + + } + +} diff --git a/IRaCIS.Core.Domain/Institution/CRO.cs b/IRaCIS.Core.Domain/Institution/CRO.cs new file mode 100644 index 0000000..6293649 --- /dev/null +++ b/IRaCIS.Core.Domain/Institution/CRO.cs @@ -0,0 +1,18 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("CROCompany")] + public partial class CRO : Entity, IAuditUpdate, IAuditAdd + { + public string CROName { get; set; } = string.Empty; + public string CRONameCN { get; set; } = string.Empty; + public string CROCode { get; set; } + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Institution/Hospital.cs b/IRaCIS.Core.Domain/Institution/Hospital.cs new file mode 100644 index 0000000..683dc8d --- /dev/null +++ b/IRaCIS.Core.Domain/Institution/Hospital.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Hospital")] + public class Hospital : Entity, IAuditUpdate, IAuditAdd + { + public string HospitalName { get; set; } = string.Empty; + public string UniversityAffiliated { get; set; } = string.Empty; + public string Country { get; set; } = string.Empty; + public string Province { get; set; } = string.Empty; + public string City { get; set; } = string.Empty; + + public string HospitalNameCN { get; set; } = string.Empty; + public string UniversityAffiliatedCN { get; set; } = string.Empty; + public string CountryCN { get; set; } = string.Empty; + public string ProvinceCN { get; set; } = string.Empty; + public string CityCN { get; set; } = string.Empty; + + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } = Guid.Empty; + + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } = Guid.Empty; + + /// + /// 中心Id + /// + public Guid? SiteId { get; set; } = Guid.Empty; + + + [JsonIgnore] + [ForeignKey("SiteId")] + public Site Site { get; set; } + + [JsonIgnore] + public List DoctorList { get; set; } + + } +} diff --git a/IRaCIS.Core.Domain/Institution/Site.cs b/IRaCIS.Core.Domain/Institution/Site.cs new file mode 100644 index 0000000..7465935 --- /dev/null +++ b/IRaCIS.Core.Domain/Institution/Site.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Site")] + public partial class Site : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + public Hospital Hospital { get; set; } + public string SiteName { get; set; } + + public string AliasName { get; set; } = string.Empty; + public string SiteCode { get; set; } + + public int Code { get; set; } + + public string City { get; set; } + public string Country { get; set; } + public Guid? HospitalId { get; set; } + public int State { get; set; } + + public string Province { get; set; } = string.Empty; + + public string UniqueCode { get; set; } = string.Empty; + + public string Address { get; set; } + + public string DirectorName { get; set; } = string.Empty; + public string DirectorPhone { get; set; } = string.Empty; + public string ContactName { get; set; } = string.Empty; + public string ContactPhone { get; set; } = string.Empty; + + public Guid CreateUserId { get; set; } = Guid.Empty; + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } = Guid.Empty; + public DateTime UpdateTime { get; set; } = DateTime.Now; + [JsonIgnore] + public List SubjectList { get; set; } + + // + [JsonIgnore] + public List TrialSiteList { get; set; } + [JsonIgnore] + public List< TrialSiteUser> TrialSiteUserList { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Institution/Sponsor.cs b/IRaCIS.Core.Domain/Institution/Sponsor.cs new file mode 100644 index 0000000..9d6eb90 --- /dev/null +++ b/IRaCIS.Core.Domain/Institution/Sponsor.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Sponsor")] + public partial class Sponsor : Entity, IAuditUpdate, IAuditAdd + { + public string SponsorName { get; set; } = String.Empty; + public string SponsorNameCN { get; set; } = String.Empty; + + public string SponsorCode { get; set; } = String.Empty; + + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } = Guid.Empty; + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } = Guid.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Management/Menu.cs b/IRaCIS.Core.Domain/Management/Menu.cs new file mode 100644 index 0000000..2703fe1 --- /dev/null +++ b/IRaCIS.Core.Domain/Management/Menu.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Menu")] + public class Menu : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + public List UserTypeMenuList { get; set; } + + + //上级菜单 + public Guid? ParentId { get; set; } = Guid.Empty; + + // 类型(M目录 C菜单 F按钮 L链接) + public string MenuType { get; set; } = string.Empty; + + public string MenuIcon { get; set; } + + public string MenuName { get; set; } = string.Empty; + + //路由地址 + public string Path { get; set; } = string.Empty; + + //组件路径 + public string Component { get; set; } = string.Empty; + + public int ShowOrder { get; set; } + + //启用 禁用 + public bool IsEnable { get; set; } = true; + + public bool IsCache { get; set; } = false; + + public bool IsDisplay { get; set; } + + public bool IsInTabDisplay { get; set; } + + public bool IsExternalLink { get; set; } + + //权限点 + public string PermissionStr { get; set; } + + //Api 接口地址 + public string ApiPath { get; set; } + + public string Note { get; set; } = string.Empty; + + public string Meta { get; set; } = string.Empty; + + public string Redirect { get; set; } = string.Empty; + + + public string LanguageMark { get; set; } + + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } = Guid.Empty; + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } = Guid.Empty; + + } +} diff --git a/IRaCIS.Core.Domain/Management/Notice/SystemNotice.cs b/IRaCIS.Core.Domain/Management/Notice/SystemNotice.cs new file mode 100644 index 0000000..ec6117c --- /dev/null +++ b/IRaCIS.Core.Domain/Management/Notice/SystemNotice.cs @@ -0,0 +1,91 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-04-25 09:46:09 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.Collections.Generic; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using IRaCIS.Core.Domain.Share.Management; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SystemNotice + /// + [Table("SystemNotice")] + public class SystemNotice : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + public List NoticeUserTypeList { get; set; }=new List(); + [JsonIgnore] + public List NoticeUserReadList { get; set; }=new List(); + + [JsonIgnore] + public User CreateUser { get; set; } + + + /// + /// NoticeContent + /// + [Required] + public string NoticeContent { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + + + public SystemNotice_NoticeTypeEnum NoticeTypeEnum { get; set; } + + public SystemNotice_NoticeLevelEnum NoticeLevelEnum { get; set; } + + public SystemNotice_ApplicableProjectEnum ApplicableProjectEnum { get; set; } + + public SystemNotice_NoticeModeEnum NoticeModeEnum { get; set; } + + public SystemNotice_NoticeStateEnum NoticeStateEnum { get; set; } + + + public DateTime? StartDate { get; set; } + + public DateTime? EndDate { get; set; } + + [Required] + public string FileName { get; set; } + + [Required] + public string Path { get; set; } + + public Guid? PublishedUserId { get; set; } + + public User PublishedUser { get; set; } + + public DateTime? PublishedTime { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Management/Notice/SystemNoticeUserRead.cs b/IRaCIS.Core.Domain/Management/Notice/SystemNoticeUserRead.cs new file mode 100644 index 0000000..59d3d5a --- /dev/null +++ b/IRaCIS.Core.Domain/Management/Notice/SystemNoticeUserRead.cs @@ -0,0 +1,41 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-04-25 09:46:09 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SystemNoticeUserRead + /// + [Table("SystemNoticeUserRead")] + public class SystemNoticeUserRead : Entity, IAuditAdd + { + + + + /// + /// SystemNoticeId + /// + [Required] + public Guid SystemNoticeId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Management/Notice/SystemNoticeUserType.cs b/IRaCIS.Core.Domain/Management/Notice/SystemNoticeUserType.cs new file mode 100644 index 0000000..6c5ff0f --- /dev/null +++ b/IRaCIS.Core.Domain/Management/Notice/SystemNoticeUserType.cs @@ -0,0 +1,52 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-04-25 09:46:09 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SystemNoticeUserType + /// + [Table("SystemNoticeUserType")] + public class SystemNoticeUserType : Entity, IAuditAdd + { + + + + /// + /// SystemNoticeId + /// + [Required] + public Guid SystemNoticeId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// UserTypeId + /// + [Required] + public Guid UserTypeId { get; set; } + + [JsonIgnore] + + [ForeignKey("UserTypeId")] + public UserType NoticeUserType { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Management/Role.cs b/IRaCIS.Core.Domain/Management/Role.cs new file mode 100644 index 0000000..e110af3 --- /dev/null +++ b/IRaCIS.Core.Domain/Management/Role.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Role")] + public partial class Role : Entity, IAuditUpdate, IAuditAdd + { + + public string RoleName { get; set; } = string.Empty; + + public string RoleDescription { get; set; } = string.Empty; + + public int Status { get; set; } + public int PrivilegeLevel { get; set; } //Ȩ޼ + + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } = Guid.Empty; + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } = Guid.Empty; + } +} diff --git a/IRaCIS.Core.Domain/Management/User.cs b/IRaCIS.Core.Domain/Management/User.cs new file mode 100644 index 0000000..c91ed7e --- /dev/null +++ b/IRaCIS.Core.Domain/Management/User.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using EntityFrameworkCore.Projectables; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("User")] + public partial class User : Entity, IAuditAdd, IAuditUpdate + { + [ForeignKey("UserTypeId")] + public UserType UserTypeRole { get; set; } + + [JsonIgnore] + public List SystemDocConfirmedList { get; set; } + [JsonIgnore] + public List UserDoctors { get; set; } = new List(); + [JsonIgnore] + public List UserTrials { get; set; } = new List(); + + [JsonIgnore] + public List VisitTaskList { get; set; } + + + [StringLength(255)] + public string UserName { get; set; } = String.Empty; + + [StringLength(255)] + public string Password { get; set; } = String.Empty; + [StringLength(255)] + + public string LastName { get; set; } = String.Empty; + public string FirstName { get; set; } = String.Empty; + + public string Phone { get; set; } = string.Empty; + public string EMail { get; set; } = string.Empty; + public int Sex { get; set; } + public UserStateEnum Status { get; set; } = UserStateEnum.Enable; + + public DateTime? LastLoginTime { get; set; } + + public Guid UserTypeId { get; set; } = Guid.Empty; + + + // ڲû ⲿû + public bool? IsZhiZhun { get; set; } + + public UserTypeEnum UserTypeEnum { get; set; } + + public string OrganizationName { get; set; } = String.Empty; + + public bool PasswordChanged { get; set; } + + //public Guid OrganizationId { get; set; } = Guid.Empty; + //public Guid OrganizationTypeId { get; set; } = Guid.Empty; + //public string OrganizationType { get; set; } = String.Empty; + //public string UserType { get; set; } = string.Empty; + //public bool SuperAdmin { get; set; } = false; + //public string RealName { get; set; } + + public string UserCode { get; set; } = string.Empty; + + public int Code { get; set; } + + + public string DepartmentName { get; set; } = String.Empty; + + public string PositionName { get; set; } = String.Empty; + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public bool IsFirstAdd { get; set; } = true; + + public string EmailToken { get; set; } = string.Empty; + + //ҽ˺ź󣬻ֵ + public Guid? DoctorId { get; set; } + + [JsonIgnore] + [ForeignKey("DoctorId")] + public Doctor Doctor { get; set; } + + + public bool IsTestUser { get; set; } + + [Projectable] public string FullName => LastName + " / " + FirstName; + + //[Projectable] public string FullName => $"{LastName} / {FirstName}"; + } +} diff --git a/IRaCIS.Core.Domain/Management/UserType.cs b/IRaCIS.Core.Domain/Management/UserType.cs new file mode 100644 index 0000000..87fd86c --- /dev/null +++ b/IRaCIS.Core.Domain/Management/UserType.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Domain.Models +{ + public class UserType:Entity + { + //public Guid RoleId { get; set; } + //public List TrialUserList { get; set; } + + + + public UserTypeEnum UserTypeEnum { get; set; } + + public string UserTypeName { get; set; } + + public string Description { get; set; } + + public int Order { get; set; } + + public string UserTypeShortName { get; set; } = string.Empty; + + public bool IsEnable { get; set; } = true; + + + public string PermissionStr { get; set; } + + + + [JsonIgnore] + public List UserTypeMenuList { get; set; } + [JsonIgnore] + public List UserTypeGroupList { get; set; } + + [JsonIgnore] + public List SystemDocNeedConfirmedUserTypeList { get; set; } + + [JsonIgnore] + public List UserList { get; set; } + + + + + //public bool IsInternal { get; set; } + //public UserTypeGroup Type { get; set; } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/Management/UserTypeGroup.cs b/IRaCIS.Core.Domain/Management/UserTypeGroup.cs new file mode 100644 index 0000000..c9d960b --- /dev/null +++ b/IRaCIS.Core.Domain/Management/UserTypeGroup.cs @@ -0,0 +1,41 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-16 09:50:51 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///UserTypeGroup + /// + [Table("UserTypeGroup")] + public class UserTypeGroup : Entity + { + + /// + /// UserTypeId + /// + [Required] + public Guid UserTypeId { get; set; } + + /// + /// DictionaryId + /// + [Required] + public Guid DictionaryId { get; set; } + + [JsonIgnore] + [ForeignKey("DictionaryId")] + public Dictionary Group { get; set; } + + [JsonIgnore] + [ForeignKey("UserTypeId")] + public UserType UserType { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Management/UserTypeMenu.cs b/IRaCIS.Core.Domain/Management/UserTypeMenu.cs new file mode 100644 index 0000000..017cf37 --- /dev/null +++ b/IRaCIS.Core.Domain/Management/UserTypeMenu.cs @@ -0,0 +1,21 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("UserTypeMenu")] + public partial class UserTypeMenu : Entity + { + + public Menu Menu { get; set; } + public Guid UserTypeId { get; set; } + public Guid MenuId { get; set; } + + [JsonIgnore] + [ForeignKey("UserTypeId")] + public UserType UserType { get; set; } + + + } +} diff --git a/IRaCIS.Core.Domain/QC/CheckChallengeDialog.cs b/IRaCIS.Core.Domain/QC/CheckChallengeDialog.cs new file mode 100644 index 0000000..865b43c --- /dev/null +++ b/IRaCIS.Core.Domain/QC/CheckChallengeDialog.cs @@ -0,0 +1,29 @@ +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.Text; + +namespace IRaCIS.Core.Domain.Models +{ + + public class CheckChallengeDialog : Entity, IAuditAdd + { + public string TalkContent { get; set; } = string.Empty; + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } + public Guid SubjectVisitId { get; set; } + + public User CreateUser { get; set; } + + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } = Guid.Empty; + + public UserTypeEnum UserTypeEnum { get; set; } + + // + public bool? IsCRCNeedReply { get; set; } + + public string ParamInfo { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/QC/ClinicalData/PreviousHistory.cs b/IRaCIS.Core.Domain/QC/ClinicalData/PreviousHistory.cs new file mode 100644 index 0000000..15a2568 --- /dev/null +++ b/IRaCIS.Core.Domain/QC/ClinicalData/PreviousHistory.cs @@ -0,0 +1,95 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-22 11:15:18 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///PreviousHistory + /// + [Table("PreviousHistory")] + public class PreviousHistory : Entity, IAuditAddWithUserName + { + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUser + /// + [Required] + public string CreateUser { get; set; } + + /// + /// StartTime + /// + public DateTime? StartTime { get; set; } + + /// + /// EndTime + /// + public DateTime? EndTime { get; set; } + + /// + /// IsPD + /// + public int? IsPD { get; set; } + + /// + /// SubjectVisitId + /// + [Required] + public Guid SubjectVisitId { get; set; } + + /// + /// IsSubjectLevel + /// + [Required] + public bool IsSubjectLevel { get; set; } + + /// + /// Path + /// + [Required] + public string Path { get; set; } = String.Empty; + + /// + /// FileName + /// + [Required] + public string FileName { get; set; } = String.Empty; + + /// + /// Position + /// + [Required] + public string Position { get; set; } = String.Empty; + + + /// + /// 临床数据类型Id + /// + [Required] + public Guid ClinicalDataTrialSetId { get; set; } + + + //[Required] + //public Guid SubjectId { get; set; } + } + + //移动到DBContext文件中 +} diff --git a/IRaCIS.Core.Domain/QC/ClinicalData/PreviousOther.cs b/IRaCIS.Core.Domain/QC/ClinicalData/PreviousOther.cs new file mode 100644 index 0000000..0fb5453 --- /dev/null +++ b/IRaCIS.Core.Domain/QC/ClinicalData/PreviousOther.cs @@ -0,0 +1,96 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-22 11:15:18 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///PreviousOther + /// + [Table("PreviousOther")] + public class PreviousOther : Entity, IAuditAddWithUserName + { + + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUser + /// + [Required] + public string CreateUser { get; set; } + + /// + /// StartTime + /// + public DateTime? StartTime { get; set; } + + /// + /// EndTime + /// + public DateTime? EndTime { get; set; } + + /// + /// IsPD + /// + [Required] + public bool IsPD { get; set; } + + /// + /// SubjectVisitId + /// + [Required] + public Guid SubjectVisitId { get; set; } + + /// + /// IsSubjectLevel + /// + [Required] + public bool IsSubjectLevel { get; set; } + + /// + /// Path + /// + [Required] + public string Path { get; set; } = String.Empty; + + /// + /// FileName + /// + [Required] + public string FileName { get; set; } = String.Empty; + + /// + /// TreatmentType + /// + [Required] + public string TreatmentType { get; set; } = String.Empty; + + /// + /// 临床数据类型Id + /// + [Required] + public Guid ClinicalDataTrialSetId { get; set; } + + + //public Guid SubjectId { get; set; } + + } + + //移动到DBContext文件中 +} diff --git a/IRaCIS.Core.Domain/QC/ClinicalData/PreviousPDF.cs b/IRaCIS.Core.Domain/QC/ClinicalData/PreviousPDF.cs new file mode 100644 index 0000000..f695ad7 --- /dev/null +++ b/IRaCIS.Core.Domain/QC/ClinicalData/PreviousPDF.cs @@ -0,0 +1,87 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-09 11:35:31 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using IRaCIS.Core.Domain.Share; +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///PreviousPDF + /// + [Table("PreviousPDF")] + public class PreviousPDF : Entity, IAuditAdd + { + + public SubjectVisit SubjectVisit { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// SubjectVisitId + /// + [Required] + public Guid SubjectVisitId { get; set; } + + /// + /// Path + /// + [Required] + public string Path { get; set; } + + /// + /// FileName + /// + [Required] + public string FileName { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + + /// + /// 是否是检查批次 + /// + public bool? IsVisist { get; set; } + + /// + /// 临床级别 + /// + public ClinicalLevel? ClinicalLevel { get; set; } + + /// + /// 数据类型 + /// + public ClinicalDataType? DataType { get; set; } + + /// + /// 上传方式 + /// + public ClinicalUploadType? UploadType { get; set; } + + /// + /// 项目Id + /// + public Guid? TrialId { get; set; } + + /// + /// 患者ID + /// + public Guid? SubjectId { get; set; } + + } + + + + +} diff --git a/IRaCIS.Core.Domain/QC/ClinicalData/PreviousSurgery.cs b/IRaCIS.Core.Domain/QC/ClinicalData/PreviousSurgery.cs new file mode 100644 index 0000000..a8c3098 --- /dev/null +++ b/IRaCIS.Core.Domain/QC/ClinicalData/PreviousSurgery.cs @@ -0,0 +1,87 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-22 11:15:18 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///PreviousSurgery + /// + [Table("PreviousSurgery")] + public class PreviousSurgery : Entity, IAuditAddWithUserName + { + + + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUser + /// + [Required] + public string CreateUser { get; set; } + + /// + /// OperationTime + /// + public DateTime? OperationTime { get; set; } + + /// + /// SubjectVisitId + /// + [Required] + public Guid SubjectVisitId { get; set; } + + /// + /// IsSubjectLevel + /// + [Required] + public bool IsSubjectLevel { get; set; } + + /// + /// Path + /// + [Required] + public string Path { get; set; } = String.Empty; + + /// + /// FileName + /// + [Required] + public string FileName { get; set; } = String.Empty; + + /// + /// OperationName + /// + [Required] + public string OperationName { get; set; } = String.Empty; + + + /// + /// 临床数据类型Id + /// + [Required] + public Guid ClinicalDataTrialSetId { get; set; } + + + //[Required] + //public Guid SubjectId { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/QC/ConsistencyCheckFile.cs b/IRaCIS.Core.Domain/QC/ConsistencyCheckFile.cs new file mode 100644 index 0000000..ac90904 --- /dev/null +++ b/IRaCIS.Core.Domain/QC/ConsistencyCheckFile.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 一致性核查文件 + /// + [Table("ConsistencyCheckFile")] + public class ConsistencyCheckFile : Entity, IAuditAdd + { + /// + /// 文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件路径 + /// + public string FilePath { get; set; } + + /// + /// 相对路径 + /// + public string RelativePath { get; set; } + + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + [JsonIgnore] + /// + /// 创建人 + /// + [ForeignKey("CreateUserId")] + public User User { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/QC/NoneDicom/NoneDicomStudy.cs b/IRaCIS.Core.Domain/QC/NoneDicom/NoneDicomStudy.cs new file mode 100644 index 0000000..ab967b5 --- /dev/null +++ b/IRaCIS.Core.Domain/QC/NoneDicom/NoneDicomStudy.cs @@ -0,0 +1,119 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-06 10:49:39 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///NoneDicomStudy + /// + [Table("NoneDicomStudy")] + public class NoneDicomStudy : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + public List NoneDicomFileList { get; set; } + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } + [JsonIgnore] + public TrialSite TrialSite { get; set; } + [JsonIgnore] + public Subject Subject { get; set; } + + public string StudyCode { get; set; } = string.Empty; + + public int FileCount { get; set; } + + public int Code { get; set; } + + /// + /// TrialId + /// + [Required] + public Guid TrialId { get; set; } + + /// + /// SiteId + /// + [Required] + public Guid SiteId { get; set; } + + /// + /// SubjectId + /// + [Required] + public Guid SubjectId { get; set; } + + /// + /// SubjectVisitId + /// + [Required] + public Guid SubjectVisitId { get; set; } + + /// + /// BodyPart + /// + [Required] + public string BodyPart { get; set; } + + /// + /// Modality + /// + [Required] + public string Modality { get; set; } + + /// + /// ImageDate + /// + [Required] + public DateTime ImageDate { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + [ForeignKey("CreateUserId")] + public User CreateUser { get; set; } + + + /// + /// Description + /// + public string Description { get; set; } = string.Empty; + + + public string VideoName { get; set; } = string.Empty; + + public string VideoObjectName { get; set; } = string.Empty; + + public DateTime? UploadVideoTime { get; set; } + + public string VideoUrl { get; set; } = string.Empty; + + } + +} diff --git a/IRaCIS.Core.Domain/QC/NoneDicom/NoneDicomStudyFile.cs b/IRaCIS.Core.Domain/QC/NoneDicom/NoneDicomStudyFile.cs new file mode 100644 index 0000000..5b5a318 --- /dev/null +++ b/IRaCIS.Core.Domain/QC/NoneDicom/NoneDicomStudyFile.cs @@ -0,0 +1,43 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-06 10:49:39 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///NoneDicomStudyFile + /// + [Table("NoneDicomStudyFile")] + public class NoneDicomStudyFile : Entity, IAuditAdd + { + [ForeignKey("NoneDicomStudyId")] + [JsonIgnore] + public NoneDicomStudy NoneDicomStudy { get; set; } + + [Required] + public Guid NoneDicomStudyId { get; set; } + + [Required] + public string Path { get; set; } + + + [Required] + public string FileName { get; set; } + + + [Required] + public DateTime CreateTime { get; set; } + + + [Required] + public Guid CreateUserId { get; set; } + + + + } + +} diff --git a/IRaCIS.Core.Domain/QC/QCChallenge.cs b/IRaCIS.Core.Domain/QC/QCChallenge.cs new file mode 100644 index 0000000..225efe9 --- /dev/null +++ b/IRaCIS.Core.Domain/QC/QCChallenge.cs @@ -0,0 +1,103 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + public class QCChallenge : Entity, IAuditAdd + { + [JsonIgnore] + [ForeignKey("CreateUserId")] + + public User CreateUser { get; set; } + + + + public Guid TrialId { get; set; } + public Guid SubjectVisitId { get; set; } + + public DateTime? DeadlineTime { get; set; } + + public string Note { get; set; } = string.Empty; + + public QCChanllengeReuploadEnum ReuploadEnum { get; set; } + + + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } = Guid.Empty; + + public DateTime? ReUploadedTime { get; set; } + + public string ReUploader { get; set; } = string.Empty; + + public Guid? ReUploadUserId { get; set; } + + public TrialQCProcess QCProcessEnum { get; set; } + + public CurrentQC CurrentQCEnum { get; set; } + + public DateTime? LatestMsgTime { get; set; } + + //public int ChallengeState { get; set; } + + public Guid? LatestReplyUserId { get; set; } + + [JsonIgnore] + [ForeignKey("LatestReplyUserId")] + public User LatestReplyUser { get; set; } + public string ChallengeCode { get; set; } + + public int Code { get; set; } + + + + public bool IsClosed { get; set; } + + public DateTime? ClosedTime { get; set; } + + public string ClosedUser { get; set; } = string.Empty; + + + + public QCChallengeCloseEnum CloseResonEnum { get; set;} + + public string Content { get; set; } = string.Empty; + + public string ActionContent { get; set; } = string.Empty; + + public UserTypeEnum UserTypeEnum { get; set; } + + + public string ChallengeType { get; set; } = string.Empty; + + + [JsonIgnore] + //导航属性 + [ForeignKey("SubjectVisitId")] + public SubjectVisit SubjectVisit { get; set; } + + [JsonIgnore] + public List DialogList { get; set; } = new List(); + + + + //public Guid QATrialTemplateId { get; set; } + //public QATrialTemplate TrialTemplate { get; set; } + + //public virtual ICollection QaTrialTemplateItemList { get; set; } + + //public virtual ICollection QARecordTemplateItemDetailList { get; set; } + + //public QAQuestion() + //{ + // //存放医生关联 Title、等各种多选项 + // QaTrialTemplateItemList = new HashSet(); + + // QARecordTemplateItemDetailList= new HashSet(); + //} + + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/QC/QCChallengeDialog.cs b/IRaCIS.Core.Domain/QC/QCChallengeDialog.cs new file mode 100644 index 0000000..f0402c3 --- /dev/null +++ b/IRaCIS.Core.Domain/QC/QCChallengeDialog.cs @@ -0,0 +1,26 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + public class QCChallengeDialog : Entity, IAuditAdd + { + [JsonIgnore] + public QCChallenge QCChallenge { get; set; } + public string TalkContent { get; set; } = string.Empty; + + public Guid QCChallengeId { get; set; } + + public Guid SubjectVisitId { get; set; } + + [JsonIgnore] + [ForeignKey("CreateUserId")] + public User CreateUser { get; set; } + + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } = Guid.Empty; + + public UserTypeEnum UserTypeEnum { get; set; } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/QC/QCQuestion.cs b/IRaCIS.Core.Domain/QC/QCQuestion.cs new file mode 100644 index 0000000..cd59767 --- /dev/null +++ b/IRaCIS.Core.Domain/QC/QCQuestion.cs @@ -0,0 +1,86 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-11 11:19:10 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///QCQuestionConfigure + /// + [Table("QCQuestion")] + public class QCQuestion : Entity, IAuditUpdate, IAuditAdd + { + /// + /// QuestionName + /// + [Required] + public string QuestionName { get; set; } = string.Empty; + + /// + /// IsRequired + /// + [Required] + public bool IsRequired { get; set; } + + /// + /// IsEnable + /// + [Required] + public bool IsEnable { get; set; } + + /// + /// 下拉框、文本、单选、多选 + /// + [Required] + public string Type { get; set; } = string.Empty; + + /// + /// TypeValue + /// + [Required] + public string TypeValue { get; set; } = string.Empty; + + [JsonIgnore] + [ForeignKey("ParentId")] + public QCQuestion ParentQuestion { get; set; } + + public string ParentTriggerValue { get; set; } + public Guid? ParentId { get; set; } + + /// + /// ShowOrder + /// + [Required] + public int ShowOrder { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/QC/TrialQCQuestion.cs b/IRaCIS.Core.Domain/QC/TrialQCQuestion.cs new file mode 100644 index 0000000..9cb7357 --- /dev/null +++ b/IRaCIS.Core.Domain/QC/TrialQCQuestion.cs @@ -0,0 +1,112 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-11 11:19:10 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialQCQuestionConfigure + /// + [Table("TrialQCQuestion")] + public class TrialQCQuestion : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + public Trial Trial { get; set; } + + /// + /// TrialId + /// + [Required] + public Guid TrialId { get; set; } + + /// + /// QuestionName + /// + [Required] + public string QuestionName { get; set; } = string.Empty; + + /// + /// IsRequired + /// + [Required] + public bool IsRequired { get; set; } + + /// + /// IsEnable + /// + [Required] + public bool IsEnable { get; set; } + + /// + /// 下拉框、文本、单选、多选 + /// + [Required] + public string Type { get; set; } = string.Empty; + + + public Guid? ParentId { get; set; } + [JsonIgnore] + [ForeignKey("ParentId")] + public TrialQCQuestion ParentQCQuestion { get; set; } + + /// + /// TypeValue + /// + [Required] + public string TypeValue { get; set; } + + /// + /// ChildInvalidValue + /// + [Required] + public string ParentTriggerValue { get; set; } + + /// + /// ShowOrder + /// + [Required] + public int ShowOrder { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + /// + /// 是否确认 + /// + public bool? IsConfirm { get; set; } + + + + [JsonIgnore] + public List TrialQCQuestionAnswerList { get; set; } + + + } + + +} diff --git a/IRaCIS.Core.Domain/QC/TrialQCQuestionAnswer.cs b/IRaCIS.Core.Domain/QC/TrialQCQuestionAnswer.cs new file mode 100644 index 0000000..6dc4f6e --- /dev/null +++ b/IRaCIS.Core.Domain/QC/TrialQCQuestionAnswer.cs @@ -0,0 +1,81 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-11 17:01:49 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using IRaCIS.Core.Domain.Share; +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialQCQuestionRecord + /// + [Table("TrialQCQuestionAnswer")] + public class TrialQCQuestionAnswer : Entity, IAuditUpdate, IAuditAdd + { + + + /// + /// TrialId + /// + [Required] + public Guid TrialId { get; set; } + + /// + /// Answer + /// + [Required] + public string Answer { get; set; } + + /// + /// ChildAnswer + /// + //[Required] + //public string ChildAnswer { get; set; } + + + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + public TrialQCProcess QCProcessEnum { get; set; } + + // 1代表第一个人QC数据 2 代表第二个人QC数据 + public CurrentQC CurrentQCEnum { get; set; } + + public Guid SubjectVisitId { get; set; } + + //public string Note { get; set; } + + public Guid TrialQCQuestionConfigureId { get; set; } + + [JsonIgnore] + public TrialQCQuestion TrialQCQuestionConfigure { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ClinicalData/ClinicalDataSystemSet.cs b/IRaCIS.Core.Domain/Reading/ClinicalData/ClinicalDataSystemSet.cs new file mode 100644 index 0000000..ab84708 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ClinicalData/ClinicalDataSystemSet.cs @@ -0,0 +1,87 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 临床资料系统配置 + /// + [Table("ClinicalDataSystemSet")] + public class ClinicalDataSystemSet : Entity, IAuditAdd + { + + /// + /// 枚举(字典里面取的) + /// + public int ClinicalDataSetEnum { get; set; } + + /// + /// 名称 + /// + public string ClinicalDataSetName { get; set; } + + /// + /// 临床级别 + /// + public ClinicalLevel ClinicalDataLevel { get; set; } + + + /// + /// 上传方式 + /// + public ClinicalUploadType ClinicalUploadType { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 上传角色 + /// + public UploadRole UploadRole { get; set; } + + /// + /// 模板文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件路径 + /// + public string Path { get; set; } + + public string CriterionEnumListStr { get; set; }=String.Empty; + + + + + [NotMapped] + public List CriterionEnumList => CriterionEnumListStr.Split('|', StringSplitOptions.RemoveEmptyEntries).Where(t => !string.IsNullOrEmpty(t) && int.TryParse(t.Trim(),out var s)).Select(t => int.Parse(t.Trim()) ).ToList(); + + //public List SystemClinicalDataCriterionList { get; set; } = new List(); + + } + + + + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ClinicalData/ClinicalDataTrialSet.cs b/IRaCIS.Core.Domain/Reading/ClinicalData/ClinicalDataTrialSet.cs new file mode 100644 index 0000000..d1e8209 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ClinicalData/ClinicalDataTrialSet.cs @@ -0,0 +1,104 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 临床资料项目配置 + /// + [Table("ClinicalDataTrialSet")] + public class ClinicalDataTrialSet : Entity, IAuditAdd + { + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 名称 + /// + public string ClinicalDataSetName { get; set; } + + /// + /// 临床级别 + /// + public ClinicalLevel ClinicalDataLevel { get; set; } + + + /// + /// 上传方式 + /// + public ClinicalUploadType ClinicalUploadType { get; set; } + + + /// + /// 系统的ClinicalDataSetId + /// + public Guid? SystemClinicalDataSetId { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 是否确认 + /// + public bool IsConfirm { get; set; } + + /// + /// 上传角色 + /// + public UploadRole UploadRole { get; set; } + + /// + /// 模板文件名称 + /// + public string FileName { get; set; } + + /// + /// 文件路径 + /// + public string Path { get; set; } + + [JsonIgnore] + /// + /// + /// + public List ReadingClinicalDataList { get; set; } + + [JsonIgnore] + + [ForeignKey("TrialId")] + public Trial Trial { get; set; } + + + + [JsonIgnore] + public List TrialClinicalDataSetCriteriaList { get; set; } + + + public string CriterionEnumListStr { get; set; } = String.Empty; + + public List CriterionEnumList => CriterionEnumListStr.Split('|', StringSplitOptions.RemoveEmptyEntries).Where(t => !string.IsNullOrEmpty(t) && int.TryParse(t.Trim(), out var s)).Select(t => int.Parse(t.Trim())).ToList(); + + } + + + + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingClinicalData.cs b/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingClinicalData.cs new file mode 100644 index 0000000..47f9737 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingClinicalData.cs @@ -0,0 +1,102 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 项目的临床数据 + /// + [Table("ReadingClinicalData")] + public class ReadingClinicalData : Entity, IAuditAdd + { + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 检查批次Id 或者模块Id + /// + public Guid ReadingId { get; set; } + + /// + /// 患者ID + /// + public Guid SubjectId { get; set; } + + /// + /// 临床数据类型Id + /// + public Guid ClinicalDataTrialSetId { get; set; } + + /// + /// 是否为检查批次 + /// xiu + public bool IsVisit { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 是否签名 + /// + public bool IsSign { get; set; } + + /// + /// 是否盲化 + /// + public bool? IsBlind { get; set; } + + /// + /// 是否完整 + /// + public bool? IsComplete { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + public int FileCount { get; set; } + + + //临床数据状态 + public ReadingClinicalDataStatus ReadingClinicalDataState { get; set; } + + [JsonIgnore] + [ForeignKey("ClinicalDataTrialSetId")] + + public ClinicalDataTrialSet ClinicalDataTrialSet { get; set; } + + [JsonIgnore] + [ForeignKey("ReadingId")] + + public SubjectVisit SubjectVisit { get; set; } + + [JsonIgnore] + [ForeignKey("ReadingId")] + + public ReadModule ReadModule { get; set; } + + /// + /// PDF文件 + /// + [JsonIgnore] + public List ReadingClinicalDataPDFList { get; set; } + + + } + + + + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingClinicalDataPDF.cs b/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingClinicalDataPDF.cs new file mode 100644 index 0000000..a42e9c8 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingClinicalDataPDF.cs @@ -0,0 +1,55 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 项目的临床数据 + /// + [Table("ReadingClinicalDataPDF")] + public class ReadingClinicalDataPDF : Entity, IAuditAdd + { + [JsonIgnore] + [ForeignKey("ReadingClinicalDataId")] + public ReadingClinicalData ReadingClinicalData { get; set; } + /// + /// 阅片临床数据ID + /// + public Guid ReadingClinicalDataId { get; set; } + + /// + /// Path + /// + [Required] + public string Path { get; set; } + + /// + /// FileName + /// + [Required] + public string FileName { get; set; } + + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + + } + + + + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ClinicalData/TrialClinicalDataSetCriterion.cs b/IRaCIS.Core.Domain/Reading/ClinicalData/TrialClinicalDataSetCriterion.cs new file mode 100644 index 0000000..0ee1571 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ClinicalData/TrialClinicalDataSetCriterion.cs @@ -0,0 +1,64 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2023-01-29 13:40:06 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialClinicalDataSetCriterion + /// + [Table("TrialClinicalDataSetCriterion")] + public class TrialClinicalDataSetCriterion : Entity, IAuditUpdate, IAuditAdd + { + + /// + /// TrialClinicalDataSetId + /// + [Required] + public Guid TrialClinicalDataSetId { get; set; } + + /// + /// TrialReadingCriterionId + /// + [Required] + public Guid TrialReadingCriterionId { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + + [ForeignKey("TrialClinicalDataSetId")] + public ClinicalDataTrialSet TrialClinicalDataSet { get; set; } + + [ForeignKey("TrialReadingCriterionId")] + public ReadingQuestionCriterionTrial TrialReadingCriterion { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicalReviewDialog.cs b/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicalReviewDialog.cs new file mode 100644 index 0000000..c3f4625 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicalReviewDialog.cs @@ -0,0 +1,140 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-07-01 10:28:40 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///阅片医学审核对话 + /// + [Table("ReadingMedicalReviewDialog")] + public class ReadingMedicalReviewDialog : Entity, IAuditAdd + { + /// + /// 医学审核Id + /// + public Guid TaskMedicalReviewId { get; set; } + + /// + /// 任务Id + /// + public Guid VisitTaskId { get; set; } + + /// + /// 对话内容 + /// + public string Content { get; set; } = string.Empty; + + /// + /// 用户角色 + /// + public string UserTypeShortName { get; set; } + + /// + /// 医学审核对话关闭原因 + /// + public MedicalDialogClose? MedicalDialogCloseEnum { get; set; } + + /// + /// 用户角色枚举 + /// + public int UserTypeEnumInt { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 阅片人是否认同 + /// + public MedicalReviewDoctorUserIdea DoctorUserIdeaEnum { get; set; } = MedicalReviewDoctorUserIdea.defalut; + + /// + /// 是否有问题 + /// + public bool IsHaveQuestion { get; set; } = false; + + /// + /// 质询问题 + /// + public string Questioning { get; set; } = string.Empty; + + /// + /// 审核建议 + /// + public AuditAdvice AuditAdviceEnum { get; set; } = AuditAdvice.None; + + /// + /// 不同意重阅原因 + /// + public string DisagreeReason { get; set; } = string.Empty; + + + /// + /// 是否申请重阅 + /// + public bool? IsApplyHeavyReading { get; set; } + + [JsonIgnore] + [ForeignKey("CreateUserId")] + public User CreateUser { get; set; } + + + /// + /// 图片路径 + /// + public string ImagePath { get; set; } = string.Empty; + + /// + /// 文件名称 + /// + public string FileName { get; set; } = string.Empty; + + [JsonIgnore] + [ForeignKey("TaskMedicalReviewId")] + public TaskMedicalReview TaskMedicalReview { get; set; } + + + /// + /// 文件 + /// + [NotMapped] + public List FileList + { + get + { + + + try + { + var result = JsonConvert.DeserializeObject>(this.ImagePath); + return result == null ? new List() : result; + } + catch (Exception) + { + + return new List(); + } + + } + } + + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicineQuestionAnswer.cs b/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicineQuestionAnswer.cs new file mode 100644 index 0000000..4214d5a --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicineQuestionAnswer.cs @@ -0,0 +1,70 @@ + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 阅片医学问题答案 + /// + [Table("ReadingMedicineQuestionAnswer")] + public class ReadingMedicineQuestionAnswer : Entity, IAuditUpdate, IAuditAdd + { + + + /// + /// 医学审核问题Id + /// + public Guid ReadingMedicineQuestionId { get; set; } + + /// + /// 医学审核Id + /// + + public Guid TaskMedicalReviewId { get; set; } + + /// + /// 任务Id + /// + + public Guid VisitTaskId { get; set; } + + /// + /// 答案 + /// + + public string Answer { get; set; } + + /// + /// 创建人 + /// + + public Guid CreateUserId { get; set; } + + /// + /// 创建时间 + /// + + public DateTime CreateTime { get; set; } + + /// + /// 修改时间 + /// + + public DateTime UpdateTime { get; set; } + + /// + /// 修改人 + /// + + public Guid UpdateUserId { get; set; } + + [JsonIgnore] + [ForeignKey("TaskMedicalReviewId")] + public TaskMedicalReview TaskMedicalReview { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicineSystemQuestion.cs b/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicineSystemQuestion.cs new file mode 100644 index 0000000..1cefa76 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicineSystemQuestion.cs @@ -0,0 +1,106 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-29 13:54:08 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 阅片医学审核系统问题 + /// + [Table("ReadingMedicineSystemQuestion")] + public class ReadingMedicineSystemQuestion : Entity, IAuditUpdate, IAuditAdd + { + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发 + /// + public string ParentTriggerValue { get; set; } = string.Empty; + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 是否是必须 + /// + public bool IsRequired { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 父问题ID + /// + public Guid? ParentId { get; set; } + + /// + /// 更新时间 + /// + public DateTime UpdateTime { get; set; } + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + /// + /// 修改人 + /// + public Guid UpdateUserId { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 任务类型 + /// + public ReadingCategory ReadingCategory { get; set; } + + [JsonIgnore] + + [ForeignKey("ParentId")] + public ReadingMedicineSystemQuestion ParentQuestion { get; set; } + + + + public CriterionType? CriterionTypeEnum { get; set; } + + public bool IsGeneral { get; set; } + + + //// |1|2| 这种保存 + // public string CriterionEnumStr { get; set; } = string.Empty; + + // [NotMapped] + // public List CriterionEnumList => CriterionEnumStr.Split('|', StringSplitOptions.RemoveEmptyEntries).Where(t => !string.IsNullOrEmpty(t)).Select(t=> Convert.ToInt32(t.Trim()) ).ToList(); + + } +} diff --git a/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicineTrialQuestion.cs b/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicineTrialQuestion.cs new file mode 100644 index 0000000..276808a --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/MedicalAudit/ReadingMedicineTrialQuestion.cs @@ -0,0 +1,106 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-06-29 14:04:36 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///阅片医学审核项目问题 + /// + [Table("ReadingMedicineTrialQuestion")] + public class ReadingMedicineTrialQuestion : Entity, IAuditUpdate, IAuditAdd + { + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发值 + /// + public string ParentTriggerValue { get; set; } = string.Empty; + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 修改人 + /// + public Guid UpdateUserId { get; set; } + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 修改时间 + /// + public DateTime UpdateTime { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 是否必须 + /// + public bool IsRequired { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 父问题 + /// + public Guid? ParentId { get; set; } + + /// + /// 是否确认 + /// + public bool IsConfirm { get; set; } + + /// + /// 任务类型 + /// + public ReadingCategory ReadingCategory { get; set; } + + /// + /// 项目标准 + /// + public Guid TrialReadingCriterionId { get; set; } + + [JsonIgnore] + [ForeignKey("ParentId")] + public ReadingMedicineTrialQuestion ParentQuestion { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterion/CriterionNidus.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterion/CriterionNidus.cs new file mode 100644 index 0000000..dfe935b --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterion/CriterionNidus.cs @@ -0,0 +1,60 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-16 15:47:47 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 标准病灶中间表 + /// + [Table("CriterionNidus")] + public class CriterionNidus : Entity, IAuditAdd + { + + /// + /// 标准ID + /// + public Guid CriterionId { get; set; } + + + /// + /// 器官类型 + /// + public OrganType OrganType { get; set; } + + + /// + /// 病灶类型 + /// + public LesionType LesionType { get; set; } + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + /// + /// 是否是系统标准 + /// + public bool IsSystemCriterion { get; set; } + + + [ForeignKey("CriterionId")] + [JsonIgnore] + public ReadingQuestionCriterionTrial TrialReadingCriterion { get; set; } + + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterion/OrganInfo.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterion/OrganInfo.cs new file mode 100644 index 0000000..5072b01 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterion/OrganInfo.cs @@ -0,0 +1,106 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-12 13:58:25 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///OrganInfo + /// + [Table("OrganInfo")] + public class OrganInfo : Entity, IAuditAdd + { + + + /// + /// 分类 + /// + public string Classification { get; set; } = string.Empty; + + /// + /// 分类 英文 + /// + public string ClassificationEN { get; set; } = string.Empty; + + /// + /// 部位 + /// + public string Part { get; set; } + + /// + /// 部位 英文 + /// + public string PartEN { get; set; } = string.Empty; + + /// + /// TULOC 器官 + /// + public string TULOC { get; set; } + + /// + /// TULOC 器官 英文 + /// + public string TULOCEN { get; set; } = string.Empty; + + /// + /// 位置 + /// + public string TULAT { get; set; } + + + /// + /// 位置 英文 + /// + public string TULATEN { get; set; } = string.Empty; + + /// + /// 备注 + /// + public string Remark { get; set; } + + + /// + /// 是否是淋巴结 + /// + public IsLymph IsLymphNodes { get; set; } + + /// + /// 器官类型 + /// + public OrganType OrganType { get; set; } + + /// + /// 标准Id + /// + public Guid SystemCriterionId { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 是否可编辑位置 + /// + public bool IsCanEditPosition { get; set; } + + /// + /// 序号 + /// + public int ShowOrder { get; set; } = 0; + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterion/OrganTrialInfo.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterion/OrganTrialInfo.cs new file mode 100644 index 0000000..dfed36c --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterion/OrganTrialInfo.cs @@ -0,0 +1,59 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-12 14:29:17 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///OrganTrialInfo + /// + [Table("OrganTrialInfo")] + public class OrganTrialInfo : Entity, IAuditAdd + { + + /// + /// 器官Id + /// + public Guid OrganInfoId { get; set; } + + /// + /// 项目Id + /// + public Guid TrialId { get; set; } + + public bool IsEnable { get; set; } + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + + ///// + ///// 病灶类型 项目自定义标准 可能会更改 + ///// + //public OrganType OrganType { get; set; } + + /// + /// 标准Id + /// + public Guid TrialCriterionId { get; set; } + + [JsonIgnore] + [ForeignKey("OrganInfoId")] + public OrganInfo OrganInfo { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingCriterionPage.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingCriterionPage.cs new file mode 100644 index 0000000..5e95903 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingCriterionPage.cs @@ -0,0 +1,64 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-07-13 09:48:30 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///阅片标准分页 + /// + [Table("ReadingCriterionPage")] + public class ReadingCriterionPage : Entity, IAuditAdd + { + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 分页名称 + /// + public string PageName { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建用户ID + /// + public Guid CreateUserId { get; set; } + + + /// + /// 是否公共分页 + /// + public bool IsPublicPage { get; set; } = false; + + /// + /// 排序 + /// + public int ShowOrder { get; set; } = 0; + + + public Guid ReadingQuestionCriterionTrialId { get; set; } + + [JsonIgnore] + public List ReadingQuestionList { get; set; } = new List(); + + + } +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingQuestionCriterionSystem.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingQuestionCriterionSystem.cs new file mode 100644 index 0000000..c7873d4 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingQuestionCriterionSystem.cs @@ -0,0 +1,94 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 系统阅片标准 + /// + [Table("ReadingQuestionCriterionSystem")] + public class ReadingQuestionCriterionSystem : Entity, IAuditAdd + { + /// + /// 标准 + /// + public string CriterionName { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 是否完成配置 + /// + public bool IsCompleteConfig { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 确认时间 + /// + public DateTime ConfirmTime { get; set; } + + /// + /// 描述 + /// + public string Description { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 标准类型 + /// + public CriterionType CriterionType { get; set; } + + + /// + /// 肿瘤学阅片 + /// + public bool IsOncologyReading { get; set; } = false; + + /// + /// eCRF报告是否显示在图像页面 + /// + public bool IseCRFShowInDicomReading { get; set; } = false; + + /// + /// 是否必须全局阅片 + /// + public bool IsMustGlobalReading { get; set; } = false; + + [JsonIgnore] + public List ReadingQuestionSystemList { get; set; } = new List(); + + //[JsonIgnore] + //[ForeignKey("CriterionId")] + //public Dictionary Dictionary { get; set; } + + + + } + + + + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingQuestionCriterionTrial.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingQuestionCriterionTrial.cs new file mode 100644 index 0000000..afce4ce --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingQuestionCriterionTrial.cs @@ -0,0 +1,255 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 项目阅片标准 + /// + [Table("ReadingQuestionCriterionTrial")] + public class ReadingQuestionCriterionTrial : Entity, IAuditAdd + { + /// + /// 系统标准ID + /// + public Guid? ReadingQuestionCriterionSystemId { get; set; } + + /// + /// 项目Id + /// + public Guid TrialId { get; set; } + + /// + /// 标准 + /// + public string CriterionName { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 是否完成配置 + /// + public bool IsCompleteConfig { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + + /// + /// 描述 + /// + public string Description { get; set; } + + /// + /// 是否确认 + /// + public bool IsConfirm { get; set; } + + /// + /// 表单类型 + /// + + public FormType FormType { get; set; } = FormType.SinglePage; + + + /// + /// 修约小数点 + /// + public int? DigitPlaces { get; set; } = 1; + + /// + /// 评估结果 + /// + public string EvaluationResult { get; set; } = string.Empty; + + /// + /// 全局阅片评估更新类型 + /// + public string GlobalUpdateType { get; set; } = string.Empty; + + + /// + /// 评估原因 + /// + public string EvaluationReason { get; set; } = "肿瘤学阅片评估原因请依据临床数据填写,在与影像学结果不一致时必填。"; + + + /// + /// 是否显示详情 + /// + public bool IsShowDetail { get; set; } + + /// + /// 同步时间 + /// + public DateTime SynchronizeTime { get; set; } + + + /// + /// 同步器官时间 + /// + public DateTime? SynchronizeOriginalTime { get; set; } + + /// + /// 标准类型 + /// + public CriterionType CriterionType { get; set; } + + /// + /// eCRF报告是否显示在图像页面 + /// + public bool IseCRFShowInDicomReading { get; set; } = false; + + #region 阅片单元配置 新加 + + /// + /// 阅片平台 + /// + public ImagePlatform ImagePlatform { get; set; } = ImagePlatform.PACS; + + /// + /// 阅片工具 + /// + public ReadingTool? ReadingTool { get; set; } + + /// + /// 任务组织级别 + /// + public ReadingTaskViewMethod ReadingTaskViewEnum { get; set; } + + + /// + /// 阅片是否显示患者信息 + /// + public bool IsReadingShowSubjectInfo { get; set; } = true; + + /// + /// IR阅片页面是否可以查看既往任务结果 + /// + public bool IsReadingShowPreviousResults { get; set; } = true; + + + /// + /// 是确认医学审核问题 + /// + + public bool IsConfirmMedicineQuestion { get; set; } = false; + + + /// + /// 仲裁对象 + /// + public ArbitrationRule ArbitrationRule { get; set; } = ArbitrationRule.Reading; + + + /// + /// 阅片模式 + /// + public ReadingMethod ReadingType { get; set; } = ReadingMethod.Double; + + + + /// + /// 是否有阅片期 + /// + public bool IsReadingPeriod { get; set; } = true; + + /// + /// 是否生成全局阅片任务 + /// + public bool IsGlobalReading { get; set; } = true; + + /// + /// 是否签名 + /// + public bool IsSigned { get; set; } = false; + + + /// + /// 仲裁阅片 + /// + public bool IsArbitrationReading { get; set; } = true; + + + /// + /// 肿瘤学阅片 原字段 IsClinicalReading + /// + public bool IsOncologyReading { get; set; } + + /// + /// 任务展示检查批次 读片任务显示是否顺序 + /// + public bool IsReadingTaskViewInOrder { get; set; } = true; + + + //任务分配对象 + public TaskAllocateObj TaskAllocateObjEnum { get; set; } + + + //后续检查批次任务自动分配 + public bool IsFollowVisitAutoAssign { get; set; } = true; + + //后续全局自动分配 + public bool IsFollowGlobalVisitAutoAssign { get; set; } = true; + + public bool IsFollowJudgeTaskAutoAssign { get; set; } = true; + + public TaskAllocateDefaultState FollowJudgeTaskAutoAssignDefaultState { get; set; } = TaskAllocateDefaultState.Allocated; + + //后续检查批次自动分配默认状态 + public TaskAllocateDefaultState FollowVisitAutoAssignDefaultState { get; set; } = TaskAllocateDefaultState.Allocated; + + //后续全局自动分配默认状态 + public TaskAllocateDefaultState FollowGlobalVisitAutoAssignDefaultState { get; set; } = TaskAllocateDefaultState.Allocated; + + + /// + /// 阅片信息签名时间 + /// + public DateTime? ReadingInfoSignTime { get; set; } + + /// + /// 是否必须全局阅片 + /// + public bool IsMustGlobalReading { get; set; } = false; + + #endregion + + /// + /// 项目 + /// + [ForeignKey("TrialId")] + [JsonIgnore] + public Trial Trial { get; set; } + + [JsonIgnore] + + public List ReadingQuestionTrialList = new List(); + } + + + + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterion/SystemCriterionDictionaryCode.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterion/SystemCriterionDictionaryCode.cs new file mode 100644 index 0000000..89f0f97 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterion/SystemCriterionDictionaryCode.cs @@ -0,0 +1,45 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2023-02-03 11:20:54 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SystemCriterionDictionaryCode + /// + [Table("SystemCriterionDictionaryCode")] + public class SystemCriterionDictionaryCode : Entity, IAuditAdd + { + /// + /// SystemCriterionId + /// + [Required] + public Guid SystemCriterionId { get; set; } + + /// + /// Code + /// + [Required] + public string Code { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterion/TrialCriterionDictionaryCode.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterion/TrialCriterionDictionaryCode.cs new file mode 100644 index 0000000..cd9a2cd --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterion/TrialCriterionDictionaryCode.cs @@ -0,0 +1,49 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2023-02-03 11:21:56 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialCriterionDictionaryCode + /// + [Table("TrialCriterionDictionaryCode")] + public class TrialCriterionDictionaryCode : Entity, IAuditAdd + { + [ForeignKey("TrialCriterionId")] + [JsonIgnore] + public ReadingQuestionCriterionTrial TrialReadingCriterion { get; set; } + + /// + /// TrialCriterionId + /// + [Required] + public Guid TrialCriterionId { get; set; } + + /// + /// Code + /// + [Required] + public string Code { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterion/TumorAssessment.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterion/TumorAssessment.cs new file mode 100644 index 0000000..21189df --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterion/TumorAssessment.cs @@ -0,0 +1,46 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-22 10:06:13 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///肿瘤评估(系统标准) + /// + [Table("TumorAssessment")] + public class TumorAssessment : Entity + { + + /// + /// 靶病灶 + /// + public TargetAssessment TargetLesion { get; set; } + + /// + /// 非靶病灶 + /// + public NoTargetAssessment NonTargetLesions { get; set; } + + /// + /// 新病灶 + /// + public NewLesionAssessment NewLesion { get; set; } + + /// + /// 整体疗效 + /// + public OverallAssessment OverallEfficacy { get; set; } + + /// + /// 标准ID(系统标准Id) + /// + public Guid CriterionId { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingCriterionDictionary.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingCriterionDictionary.cs new file mode 100644 index 0000000..f899b45 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingCriterionDictionary.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///ReadingCriterionDictionary + /// + [Table("ReadingCriterionDictionary")] + public class ReadingCriterionDictionary : Entity, IAuditAdd + { + /// + /// CriterionId + /// + public Guid CriterionId { get; set; } + + /// + /// DictionaryId + /// + public Guid DictionaryId { get; set; } + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + /// + /// IsSystemCriterion + /// + public bool IsSystemCriterion { get; set; } = false; + + /// + /// ParentCode + /// + public string ParentCode { get; set; } = string.Empty; + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + + /// + /// IsBaseLineUse + /// + public bool IsBaseLineUse { get; set; } = false; + + /// + /// IsBaseUse + /// + public bool IsFollowVisitUse { get; set; } = false; + + [JsonIgnore] + [ForeignKey("DictionaryId")] + public Dictionary Dictionary { get; set; } + + [ForeignKey("CriterionId")] + [JsonIgnore] + public ReadingQuestionCriterionTrial TrialReadingCriterion { get; set; } + + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionSystem.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionSystem.cs new file mode 100644 index 0000000..805bd9a --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionSystem.cs @@ -0,0 +1,228 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 系统阅片问题 + /// + [Table("ReadingQuestionSystem")] + public class ReadingQuestionSystem : Entity, IAuditAdd + { + /// + /// 系统标准Id + /// + public Guid ReadingQuestionCriterionSystemId { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发 + /// + public string ParentTriggerValue { get; set; } + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 是否是必须 + /// + public IsRequired IsRequired { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 父问题ID + /// + public Guid? ParentId { get; set; } + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 是否是裁判问题 + /// + public bool IsJudgeQuestion { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 关联ID + /// + public Guid? RelevanceId { get; set; } + + /// + /// 关联Value + /// + public string RelevanceValue { get; set; } = string.Empty; + + /// + /// 分组 + /// + public string GroupName { get; set; } + + /// + /// 图片数量 + /// + public int ImageCount { get; set; } = 1; + + /// + /// 是否显示 + /// + public ShowQuestion ShowQuestion { get; set; } = ShowQuestion.Show; + + /// + /// 最大问题数 + /// + public int? MaxQuestionCount { get; set; } + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + /// + /// 问题类型 + /// + public QuestionType? QuestionType { get; set; } + + /// + /// 是否显示在Dicom阅片中 + /// + public bool IsShowInDicom { get; set; } = false; + + /// + /// 序号标记 + /// + public string OrderMark { get; set; } = string.Empty; + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// 全局阅片显示类型 + /// + public GlobalReadingShowType GlobalReadingShowType { get; set; } = GlobalReadingShowType.NotShow; + + /// + /// 默认值 + /// + public string DefaultValue { get; set; } = string.Empty; + + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + + /// + /// 问题英文名称 + /// + public string QuestionEnName { get; set; } = string.Empty; + + /// + /// 问题英文分组 + /// + public string GroupEnName { get; set; } = string.Empty; + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + + /// + /// 数据来源 + /// + public DataSources DataSource { get; set; } = DataSources.ManualEntry; + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 分组ID + /// + public Guid? GroupId { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + + + [JsonIgnore] + [ForeignKey("GroupId")] + public ReadingQuestionSystem GroupInfo { get; set; } + + + [JsonIgnore] + [ForeignKey("ReadingQuestionCriterionSystemId")] + public ReadingQuestionCriterionSystem ReadingQuestionCriterionSystem { get; set; } + + [JsonIgnore] + [ForeignKey("ParentId")] + public ReadingQuestionSystem ParentReadingQuestionSystem { get; set; } + + + [JsonIgnore] + [ForeignKey("RelevanceId")] + public ReadingQuestionSystem RelevanceReadingQuestionSystem { get; set; } + + + + } + + + + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionTrial.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionTrial.cs new file mode 100644 index 0000000..69db85b --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionTrial.cs @@ -0,0 +1,350 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 项目阅片问题 + /// + [Table("ReadingQuestionTrial")] + public class ReadingQuestionTrial : Entity, IAuditAdd + { + /// + /// 项目标准Id + /// + public Guid ReadingQuestionCriterionTrialId { get; set; } + + /// + /// 项目Id + /// + public Guid TrialId { get; set; } + + /// + /// 类型 + /// + public string Type { get; set; } + + /// + /// 父问题触发 + /// + public string ParentTriggerValue { get; set; } + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// 是否是必须 + /// + public IsRequired IsRequired { get; set; } + + /// + /// 排序 + /// + public int ShowOrder { get; set; } + + /// + /// 父问题ID + /// + public Guid? ParentId { get; set; } + + + /// + /// 类型值 + /// + public string TypeValue { get; set; } + + public bool IsEnable { get; set; } + + /// + /// 是否是裁判问题 + /// + public bool IsJudgeQuestion { get; set; } + + /// + /// 病灶类型 + /// + public LesionType? LesionType { get; set; } + + /// + /// 问题类型 + /// + public QuestionType? QuestionType { get; set; } + + /// + /// 全局阅片显示类型 + /// + public GlobalReadingShowType GlobalReadingShowType { get; set; } = GlobalReadingShowType.NotShow; + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 分组 + /// + public string GroupName { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 系统问题ID + /// + public Guid? ReadingQuestionSystemId { get; set; } + + /// + /// 系统标准的ParentId + /// + public Guid? SystemParentId { get; set; } + + /// + /// 答案分组 + /// + public string AnswerGroup { get; set; } = string.Empty; + + /// + /// 答案组合 + /// + public string AnswerCombination { get; set; } = string.Empty; + + /// + /// 裁判类型 + /// + public JudgeTypeEnum JudgeType { get; set; } = JudgeTypeEnum.AnswerDisaffinity; + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 标准分页Id + /// + public Guid? ReadingCriterionPageId { get; set; } + + /// + /// 关联ID + /// + public Guid? RelevanceId { get; set; } + + /// + /// 关联Value + /// + public string RelevanceValue { get; set; }=string.Empty; + + /// + /// 图片数量 + /// + public int ImageCount { get; set; } = 1; + + /// + /// 是否显示 + /// + public ShowQuestion ShowQuestion { get; set; } = ShowQuestion.Show; + + /// + /// 默认值 + /// + public string DefaultValue { get; set; } =string.Empty; + + /// + /// 最大问题数 + /// + public int? MaxQuestionCount { get; set; } + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + + /// + /// 是否显示在Dicom阅片中 + /// + public bool IsShowInDicom { get; set; } = false; + + + /// + /// 序号标记 + /// + public string OrderMark { get; set; } = string.Empty; + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + /// + /// 问题类型 + /// + public TableQuestionType? QuestionGenre { get; set; } + + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + /// + /// 自定义单位 + /// + public string CustomUnit { get; set; } = string.Empty; + + + /// + /// 自定义计算标记 + /// + public CustomCalculateMark? CustomCalculateMark { get; set; } + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + /// + /// 自定义计算标记 + /// + public string CalculateQuestions { get; set; } = "[]"; + + /// + /// 数据来源 + /// + public DataSources DataSource { get; set; } = DataSources.ManualEntry; + + /// + /// 问题英文名称 + /// + public string QuestionEnName { get; set; } = string.Empty; + + /// + /// 问题英文分组 + /// + public string GroupEnName { get; set; } = string.Empty; + + /// + /// 是否复制病灶 + /// + public bool IsCopyLesions { get; set; } = false; + + /// + /// 分组ID + /// + public Guid? GroupId { get; set; } + + [JsonIgnore] + [ForeignKey("GroupId")] + public ReadingQuestionTrial GroupInfo { get; set; } + + /// + /// 分页标准 + /// + [ForeignKey("ReadingCriterionPageId")] + [JsonIgnore] + public ReadingCriterionPage ReadingCriterionPage { get; set; } + [JsonIgnore] + [ForeignKey("ParentId")] + public ReadingQuestionTrial ParentReadingQuestionTrial { get; set; } + [JsonIgnore] + [ForeignKey("RelevanceId")] + public ReadingQuestionTrial RelevanceReadingQuestionTrial { get; set; } + [JsonIgnore] + [ForeignKey("ReadingQuestionCriterionTrialId")] + public ReadingQuestionCriterionTrial ReadingQuestionCriterionTrial { get; set; } + + [JsonIgnore] + public List ReadingTableQuestionTrialList { get; set; } + + + [NotMapped] + public List ParentTriggerValueList + { + get + { + try + { + return this.ParentTriggerValue.Split(',').ToList(); + } + catch (Exception) + { + + return new List(); + } + } + + } + + + [NotMapped] + public List RelevanceValueList + { + get + { + try + { + return this.RelevanceValue.Split(',').ToList(); + } + catch (Exception) + { + + return new List(); + } + } + + } + + + [NotMapped] + public List CalculateQuestionList + { + get + { + + try + { + var result = JsonConvert.DeserializeObject>(this.CalculateQuestions); + return result == null ? new List() : result; + } + catch (Exception) + { + + return new List(); + } + + } + } + + } + + + public class CalculateInfo + { + public bool IsTable { get; set; } + + public Guid? QuestionId { get; set; } + + public Guid? TableQuestionId { get; set; } + + } + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingTableQuestionSystem.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingTableQuestionSystem.cs new file mode 100644 index 0000000..ba44b85 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingTableQuestionSystem.cs @@ -0,0 +1,182 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-11 14:15:27 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///系统表格问题 + /// + [Table("ReadingTableQuestionSystem")] + public class ReadingTableQuestionSystem : Entity, IAuditAdd + { + + /// + /// 系统表的问题Id ReadingQuestionSystem的Id + /// + public Guid ReadingQuestionId { get; set; } + + /// + /// Type + /// + public string Type { get; set; } + + /// + /// ParentId + /// + public Guid? ParentId { get; set; } + + /// + ///父问题触发值 + /// + public string ParentTriggerValue { get; set; } + + /// + /// 问题名称 + /// + public string QuestionName { get; set; } + + /// + /// IsRequired + /// + public IsRequired IsRequired { get; set; } + + /// + /// 排序号 + /// + public int ShowOrder { get; set; } + + /// + /// 值 + /// + public string TypeValue { get; set; } + + /// + /// 是否启用 + /// + public bool IsEnable { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建用户 + /// + public Guid CreateUserId { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 显示父问题 + /// + public Guid? RelevanceId { get; set; } + + /// + /// 显示父问题的值 + /// + public string RelevanceValue { get; set; } + + /// + /// 是否显示 + /// + public int ShowQuestion { get; set; } + + /// + /// 最大问题数 + /// + public int? MaxRowCount { get; set; } + + /// + /// 数据表名称 + /// + public string DataTableName { get; set; } + + /// + /// 数据列 + /// + public string DataTableColumn { get; set; } + + + /// + /// 关联父问题 + /// + public Guid? DependParentId { get; set; } + + /// + /// 是否关联 + /// + public IsDepend IsDepend { get; set; } + + /// + /// 表格问题类型 + /// + public TableQuestionType? TableQuestionType { get; set; } + + /// + /// 系统标准Id + /// + public Guid SystemCriterionId { get; set; } + + /// + /// 问题标识 + /// + public QuestionMark? QuestionMark { get; set; } + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } + + + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + /// + /// 问题英文名称 + /// + public string QuestionEnName { get; set; } = string.Empty; + + /// + /// 数据来源 + /// + public DataSources DataSource { get; set; } = DataSources.ManualEntry; + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + + [ForeignKey("DependParentId")] + [JsonIgnore] + public ReadingTableQuestionSystem DependParentQuestion { get; set; } + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingTableQuestionTrial.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingTableQuestionTrial.cs new file mode 100644 index 0000000..df02d64 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingTableQuestionTrial.cs @@ -0,0 +1,277 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-17 14:36:04 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 项目阅片问题 + /// + [Table("ReadingTableQuestionTrial")] + public class ReadingTableQuestionTrial : Entity, IAuditAdd + { + /// + /// TrialId + /// + public Guid TrialId { get; set; } + + /// + /// 项目问题的Id ReadingQuestionTrial的id + /// + public Guid ReadingQuestionId { get; set; } + + /// + /// Type + /// + public string Type { get; set; } = string.Empty; + + /// + /// ParentId + /// + public Guid? ParentId { get; set; } + + /// + /// ParentTriggerValue + /// + public string ParentTriggerValue { get; set; } = string.Empty; + + /// + /// QuestionName + /// + public string QuestionName { get; set; } = string.Empty; + + /// + /// IsRequired + /// + public IsRequired IsRequired { get; set; } + + /// + /// ShowOrder + /// + public int ShowOrder { get; set; } + + /// + /// TypeValue + /// + public string TypeValue { get; set; } = string.Empty; + + /// + /// IsEnable + /// + public bool IsEnable { get; set; } + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + /// + /// Remark + /// + public string Remark { get; set; } = string.Empty; + + /// + /// RelevanceId + /// + public Guid? RelevanceId { get; set; } + + /// + /// RelevanceValue + /// + public string RelevanceValue { get; set; } = string.Empty; + + /// + /// ShowQuestion + /// + public int ShowQuestion { get; set; } + + /// + /// MaxRowCount + /// + public int? MaxRowCount { get; set; } + + /// + /// DataTableName + /// + public string DataTableName { get; set; } = string.Empty; + + /// + /// DataTableColumn + /// + public string DataTableColumn { get; set; } + + /// + /// TableQuestionType + /// + public TableQuestionType? TableQuestionType { get; set; } + + /// + /// DependParentId + /// + public Guid? DependParentId { get; set; } + + /// + /// IsDepend + /// + public IsDepend IsDepend { get; set; } + + + /// + /// 项目标准Id + /// + public Guid TrialCriterionId { get; set; } + + /// + /// 问题标识 + /// + public QuestionMark? QuestionMark { get; set; } + + + /// + /// 字典code + /// + public string DictionaryCode { get; set; } = string.Empty; + + /// + /// 数值类型 + /// + public ValueOfType? ValueType { get; set; } + + /// + /// 单位 + /// + public ValueUnit? Unit { get; set; } + + /// + /// 自定义单位 + /// + public string CustomUnit { get; set; } = string.Empty; + + /// + /// 自定义计算标记 + /// + public CustomCalculateMark? CustomCalculateMark { get; set; } + + /// + /// 限制编辑 + /// + public LimitEdit LimitEdit { get; set; } = LimitEdit.None; + + /// + /// 自定义计算标记 + /// + public string CalculateQuestions { get; set; } = "[]"; + + /// + /// 问题英文名称 + /// + public string QuestionEnName { get; set; } = string.Empty; + + /// + /// 数据来源 + /// + public DataSources DataSource { get; set; } = DataSources.ManualEntry; + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } + + /// + /// 文件类型 + /// + public string? FileType { get; set; } + + [JsonIgnore] + [ForeignKey("DependParentId")] + public ReadingTableQuestionTrial DependParentQuestion { get; set; } + + [JsonIgnore] + [ForeignKey("ReadingQuestionId")] + public ReadingQuestionTrial ReadingQuestionTrial { get; set; } + + /// + /// 复制病灶的时候 是否复制这个问题 + /// + public bool IsCopy { get; set; } = false; + + /// + /// 系统表格问题Id + /// + public Guid? SystemTableQuestionId { get; set; } + + + [NotMapped] + public List ParentTriggerValueList + { + get + { + try + { + return this.ParentTriggerValue.Split(',').ToList(); + } + catch (Exception) + { + + return new List(); + } + } + + } + + + [NotMapped] + public List RelevanceValueList + { + get + { + try + { + return this.RelevanceValue.Split(',').ToList(); + } + catch (Exception) + { + + return new List(); + } + } + + } + + + [NotMapped] + public List CalculateQuestionList + { + get + { + try + { + var result = JsonConvert.DeserializeObject>(this.CalculateQuestions); + return result == null ? new List() : result; + } + catch (Exception) + { + + return new List(); + } + + } + } + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingGlobalTaskInfo.cs b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingGlobalTaskInfo.cs new file mode 100644 index 0000000..b6b89f0 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingGlobalTaskInfo.cs @@ -0,0 +1,80 @@ + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 阅片全局任务信息 + /// + [Table("ReadingGlobalTaskInfo")] + public class ReadingGlobalTaskInfo : Entity, IAuditAdd + { + + /// + /// 全局任务Id + /// + public Guid GlobalTaskId { get; set; } + + /// + /// 原任务ID + /// + + public Guid TaskId { get; set; } + + [JsonIgnore] + [ForeignKey("TaskId")] + public VisitTask VisitTask { get; set; } + + [JsonIgnore] + [ForeignKey("GlobalTaskId")] + public VisitTask GlobalVisitTask { get; set; } + + + /// + /// 问题ID + /// + public Guid? QuestionId { get; set; } + + [JsonIgnore] + [ForeignKey("QuestionId")] + public ReadingQuestionTrial TrialReadingQuestion { get; set; } + + + /// + /// 问题答案 + /// + + public string Answer { get; set; } + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + /// + /// TrialId + /// + + public Guid TrialId { get; set; } + + /// + /// 全局答案类型 + /// + public GlobalAnswerType GlobalAnswerType { get; set; } + + /// + /// SubjectId + /// + public Guid SubjectId { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingJudgeInfo.cs b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingJudgeInfo.cs new file mode 100644 index 0000000..5cd5577 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingJudgeInfo.cs @@ -0,0 +1,60 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///阅片裁判信息 + /// + [Table("ReadingJudgeInfo")] + public class ReadingJudgeInfo : Entity, IAuditAdd + { + /// + /// 第一个任务ID + /// + [Required] + public Guid TaskIdOne { get; set; } + + /// + /// 第二个任务ID + /// + [Required] + public Guid TaskIdTwo { get; set; } + + /// + /// 裁判任务ID + /// + [Required] + public Guid JudgeTaskId { get; set; } + + /// + /// 创建时间 + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// 项目ID + /// + [Required] + public Guid TrialId { get; set; } + + /// + /// 患者ID + /// + [Required] + public Guid SubjectId { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingOncologyTaskInfo.cs b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingOncologyTaskInfo.cs new file mode 100644 index 0000000..7085220 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingOncologyTaskInfo.cs @@ -0,0 +1,70 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-01 14:50:23 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 阅片肿瘤学 针对检查批次任务 添加了一个结果 + /// + [Table("ReadingOncologyTaskInfo")] + public class ReadingOncologyTaskInfo : Entity, IAuditAdd + { + + /// + /// 肿瘤学 阅片任务ID + /// + public Guid OncologyTaskId { get; set; } + + /// + /// 产生肿瘤学阅片任务的 检查批次类型的阅片任务Id + /// + public Guid VisitTaskId { get; set; } + + /// + /// 结果 + /// + public string EvaluationResult { get; set; } + + /// + /// 原因 + /// + public string EvaluationReason { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 项目Id + /// + public Guid TrialId { get; set; } + + /// + /// 患者Id + /// + public Guid SubjectId { get; set; } + + [JsonIgnore] + [ForeignKey("OncologyTaskId")] + public VisitTask OncologyVisitTask { get; set; } + + [JsonIgnore] + [ForeignKey("VisitTaskId")] + public VisitTask VisitTask { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTableAnswerRowInfo.cs b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTableAnswerRowInfo.cs new file mode 100644 index 0000000..2bd50cd --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTableAnswerRowInfo.cs @@ -0,0 +1,183 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-26 10:38:09 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 表格问题答案行数据 + /// + [Table("ReadingTableAnswerRowInfo")] + public class ReadingTableAnswerRowInfo : Entity, IAuditAdd, IAuditUpdate, ISoftDelete + { + [JsonIgnore] + [ForeignKey("InstanceId")] + public DicomInstance Instance { get; set; } + + + + /// + /// 第一层的Question + /// + public Guid QuestionId { get; set; } + + /// + /// VisitTaskId + /// + public Guid VisitTaskId { get; set; } + + /// + /// TrialId + /// + public Guid TrialId { get; set; } + + + /// + /// InstanceId + /// + public Guid? InstanceId { get; set; } + + /// + /// SeriesId + /// + public Guid? SeriesId { get; set; } + + + /// + /// StudyId + /// + public Guid? StudyId { get; set; } + + public bool IsCanEditPosition { get; set; } = false; + + + /// + /// 是Dicom阅片 + /// + public bool IsDicomReading { get; set; } = true; + + /// + /// RowIndex + /// + public decimal RowIndex { get; set; } + + /// + /// MeasureData + /// + public string MeasureData { get; set; } = string.Empty; + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + /// + /// 是否是当前任务添加 + /// + public bool IsCurrentTaskAdd { get; set; } = false; + + + /// + /// SplitRowId + /// + public Guid? SplitRowId { get; set; } + + + /// + /// MergeRowId + /// + public Guid? MergeRowId { get; set; } + + public string BlindName { get; set; } = string.Empty; + + public string OrderMark { get; set; } = string.Empty; + + /// + /// 截图地址 + /// + public string PicturePath { get; set; } = string.Empty; + + /// + /// 第一次添加的任务ID + /// + public decimal FristAddTaskNum { get; set; } = 0; + + /// + /// 首次添加任务ID + /// + public Guid FristAddTaskId { get; set; } + + + public SplitOrMergeType? SplitOrMergeType { get; set; } + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + + public int? NumberOfFrames { get; set; } + + public Guid UpdateUserId { get; set; } + //string UpdateUserName { get; set; } + public DateTime UpdateTime { get; set; } + + /// + /// 器官Id + /// + public Guid? OrganInfoId { get; set; } + + [ForeignKey("OrganInfoId")] + public OrganInfo OrganInfo { get; set; } + + /// + /// 窗宽WW + /// + public decimal? WW { get; set; } + + /// + /// 窗位WL + /// + public decimal? WL { get; set; } + + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + [JsonIgnore] + [ForeignKey("SplitRowId")] + public ReadingTableAnswerRowInfo SplitRow { get; set; } + [JsonIgnore] + [ForeignKey("MergeRowId")] + public ReadingTableAnswerRowInfo MergeRow { get; set; } + [JsonIgnore] + [ForeignKey("QuestionId")] + public ReadingQuestionTrial ReadingQuestionTrial { get; set; } + + + //病灶编号 + public string RowMark { get; set; } = string.Empty; + + + [JsonIgnore] + [ForeignKey("VisitTaskId")] + public VisitTask VisitTask { get; set; } + + //病灶答案表 + [JsonIgnore] + public List LesionAnswerList { get; set; } = new List(); + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTableQuestionAnswer.cs b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTableQuestionAnswer.cs new file mode 100644 index 0000000..243942f --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTableQuestionAnswer.cs @@ -0,0 +1,102 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-08-22 09:26:04 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///ReadingTableQuestionAnswer + /// + [Table("ReadingTableQuestionAnswer")] + public class ReadingTableQuestionAnswer : Entity, IAuditAdd, IAuditUpdate, ISoftDelete + { + /// + /// 问题Id + /// + + public Guid QuestionId { get; set; } + + /// + /// 表格问题Id + /// + public Guid TableQuestionId { get; set; } + + /// + /// 任务Id + /// + public Guid VisitTaskId { get; set; } + + /// + /// 项目Id + /// + public Guid TrialId { get; set; } + + /// + /// 行号 + /// + public decimal RowIndex { get; set; } + + /// + /// 答案 + /// + public string Answer { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + + /// + /// RowId + /// + public Guid RowId { get; set; } + + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + + [JsonIgnore] + [ForeignKey("RowId")] + + public ReadingTableAnswerRowInfo Lesion { get; set; } + + + [JsonIgnore] + [ForeignKey("QuestionId")] + + public ReadingQuestionTrial ReadingQuestionTrial { get; set; } + + + [JsonIgnore] + [ForeignKey("TableQuestionId")] + + public ReadingTableQuestionTrial ReadingTableQuestionTrial { get; set; } + + + + [JsonIgnore] + [ForeignKey("VisitTaskId")] + public VisitTask VisitTask { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTaskQuestionAnswer.cs b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTaskQuestionAnswer.cs new file mode 100644 index 0000000..3cba08f --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTaskQuestionAnswer.cs @@ -0,0 +1,84 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 阅片任务答案 + /// + [Table("ReadingTaskQuestionAnswer")] + public class ReadingTaskQuestionAnswer : Entity, IAuditAdd + { + /// + /// 项目问题Id + /// + public Guid ReadingQuestionTrialId { get; set; } + + /// + /// 项目问题标准Id + /// + public Guid ReadingQuestionCriterionTrialId { get; set; } + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 患者Id + /// + public Guid SubjectId { get; set; } + + /// + /// 任务Id + /// + public Guid VisitTaskId { get; set; } + + /// + /// 答案 + /// + public string Answer { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 全局阅片修改的答案 + /// + public string GlobalChangeAnswer { get; set; } + + + /// + /// 全局阅片是否修改 + /// + public bool IsGlobalChange { get; set; } = false; + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + [JsonIgnore] + [ForeignKey("VisitTaskId")] + public VisitTask VisitTask { get; set; } + + [JsonIgnore] + [ForeignKey("ReadingQuestionTrialId")] + public ReadingQuestionTrial ReadingQuestionTrial { get; set; } + + + } + + + + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTaskRelation.cs b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTaskRelation.cs new file mode 100644 index 0000000..3915730 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingTaskRelation.cs @@ -0,0 +1,52 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2023-02-03 09:43:09 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///任务关系表 + /// + [Table("ReadingTaskRelation")] + public class ReadingTaskRelation : Entity, IAuditAdd + { + + [ForeignKey("TaskId")] + [JsonIgnore] + public VisitTask VisitTask { get; set; } + /// + /// 任务ID + /// + public Guid TaskId { get; set; } + + /// + /// 关联的任务ID + /// + public Guid RelevanceTaskId { get; set; } + + /// + /// 类型具体解释 看枚举 + /// + public RelevanceType RelevanceType { get; set; } + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadModule.cs b/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadModule.cs new file mode 100644 index 0000000..4939466 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadModule.cs @@ -0,0 +1,134 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///读片模块 + /// + [Table("ReadModule")] + public class ReadModule : Entity, IAuditAdd, IAuditUpdate, ISoftDelete + { + + [JsonIgnore] + public ReadingQuestionCriterionTrial TrialReadingCriterion { get; set; } + + + [JsonIgnore] + public List ModuleTaskList { get; set; } + + /// + /// 患者ID + /// + public Guid SubjectId { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + + /// + /// 模块类型 + /// + public ModuleTypeEnum ModuleType { get; set; } + + /// + /// 模块名称 + /// + public string ModuleName { get; set; } + + /// + /// 是否加急 + /// + public bool? IsUrgent { get; set; } + + /// + /// 检查批次ID + /// + public Guid SubjectVisitId { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 阅片计划ID + /// + public Guid? ReadingPeriodSetId { get; set; } + + + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + public Guid UpdateUserId { get; set; } + + public DateTime UpdateTime { get; set; } + + + + + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + ///// + ///// + ///// + //public decimal VisitNum { get; set; } + + // [JsonIgnore] + ///// + ///// 对应 + ///// + //[ForeignKey("ReadModuleId")] + //public ReadModule ReadModuleModel { get; set; } + + [JsonIgnore] + /// + /// 患者 + /// + [ForeignKey("SubjectId")] + + public Subject Subject { get; set; } + + [JsonIgnore] + /// + /// 阅片期配置 + /// + [ForeignKey("ReadingPeriodSetId")] + public ReadingPeriodSet ReadingPeriodSet { get; set; } + + [JsonIgnore] + /// + /// 检查批次 + /// + [ForeignKey("SubjectVisitId")] + public SubjectVisit SubjectVisit { get; set; } + + + /// + /// 阅片配置的类型 + /// + public ReadingSetType ReadingSetType { get; set; } + + + + public ReadingStatusEnum ReadingStatus { get; set; } = ReadingStatusEnum.TaskAllocate; + } + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadingPeriodPlan.cs b/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadingPeriodPlan.cs new file mode 100644 index 0000000..4dbcd56 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadingPeriodPlan.cs @@ -0,0 +1,68 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 阅片计划 + /// + [Table("ReadingPeriodPlan")] + public class ReadingPeriodPlan : Entity, IAuditAdd, IAuditUpdate, ISoftDelete + { + + /// + /// 阅片期配置ID + /// + public Guid ReadingPeriodSetId { get; set; } + + /// + /// 检查批次 + /// + public Guid SubjectVisitId { get; set; } + + /// + /// 检查批次 + /// + [ForeignKey("SubjectVisitId")] + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } + + /// + /// 阅片期配置 + /// + [ForeignKey("ReadingPeriodSetId")] + [JsonIgnore] + public ReadingPeriodSet ReadingPeriodSet { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + public Guid UpdateUserId { get; set; } + + public DateTime UpdateTime { get; set; } + + + + + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + } + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadingPeriodSet.cs b/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadingPeriodSet.cs new file mode 100644 index 0000000..b8e08fa --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadingPeriodSet.cs @@ -0,0 +1,111 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///阅片期设置 只会设计到所有人 或者某个Site 针对全局 + /// + [Table("ReadingPeriodSet")] + public class ReadingPeriodSet : Entity, IAuditAdd, IAuditUpdate, ISoftDelete + { + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + + /// + /// 阅片期名称 + /// + public string ReadingPeriodName { get; set; } + + /// + /// 阅片范围 + /// + public ReadingScopeEnum ReadingScope { get; set; } + + /// + /// 截止日期 + /// + public DateTime? ExpirationDate { get; set; } + + /// + /// 截止检查批次 + /// + public decimal? ExpirationVisitNum { get; set; } + + /// + /// 检查批次计划ID + /// + public Guid? VisitStageId { get; set; } + + [JsonIgnore] + /// + /// 检查批次模板 + /// + [ForeignKey("VisitStageId")] + public VisitStage VisitStage { get; set; } + + /// + /// 是否生效 + /// + public ReadingPeriodStatus IsTakeEffect { get; set; } + + /// + /// 生效时间 + /// + public DateTime? EffectOfTime { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 是否为全局阅片 + /// + public bool IsGlobal { get; set; } + + /// + /// 阅片配置的类型 + /// + public ReadingSetType ReadingSetType { get; set; } + + public Guid UpdateUserId { get; set; } + + public DateTime UpdateTime { get; set; } + + + + + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + [JsonIgnore] + public List ReadingPeriodSites { get; set; } = new List(); + + + /// + /// 阅片期计划 + /// + [JsonIgnore] + public List ReadingPeriodPlanList { get; set; } = new List(); + } + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadingPeriodSite.cs b/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadingPeriodSite.cs new file mode 100644 index 0000000..120ac66 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ReadingPeriod/ReadingPeriodSite.cs @@ -0,0 +1,64 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 阅片期和中心关联 + /// + [Table("ReadingPeriodSite")] + public class ReadingPeriodSite : Entity, IAuditAdd + { + + /// + ///阅片期配置ID + /// + + public Guid ReadingPeriodSetId { get; set; } + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 中心ID + /// + public Guid SiteId { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + + /// + /// 中心 + /// + public Site Site { get; set; } + + + + public TrialSite TrialSite { get; set; } + + [JsonIgnore] + public ReadingPeriodSet ReadingPeriodSet { get; set; } + + + } + + + + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ShortcutKey/DefaultShortcutKey.cs b/IRaCIS.Core.Domain/Reading/ShortcutKey/DefaultShortcutKey.cs new file mode 100644 index 0000000..02e2709 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ShortcutKey/DefaultShortcutKey.cs @@ -0,0 +1,60 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2023-02-13 10:33:05 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///ShortcutKey + /// + [Table("ShortcutKey")] + public class ShortcutKey : Entity, IAuditAdd + { + /// + /// 对应的键盘按键 + /// + public string Keyboardkey { get; set; } = string.Empty; + + /// + /// 按键枚举 + /// + public int ShortcutKeyEnum { get; set; } + + /// + /// 影像工具类型 + /// + public int ImageToolType { get; set; } + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + public Guid UserId { get; set; } + + public bool AltKey { get; set; } = false; + + public bool CtrlKey { get; set; } = false; + + public bool ShiftKey { get; set; } = false; + + public bool MetaKey { get; set; } = false; + + public string Text { get; set; } + + public string Code { get; set; } = string.Empty; + + } + + +} diff --git a/IRaCIS.Core.Domain/Reading/View/ReadModuleView.cs b/IRaCIS.Core.Domain/Reading/View/ReadModuleView.cs new file mode 100644 index 0000000..86962eb --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/View/ReadModuleView.cs @@ -0,0 +1,161 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Domain.Models +{ + public class ReadModuleView: Entity + { + /// + /// Id(阅片期Id 或者 检查批次ID) + /// + public new Guid Id { get; set; } + + + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 名称 + /// + public string Name { get; set; } + + /// + /// 患者ID + /// + public Guid SubjectId { get; set; } + + /// + /// 模块类型 + /// + public ModuleTypeEnum ModuleType { get; set; } + + /// + /// 是否加急 + /// + public bool? IsUrgent { get; set; } + + /// + /// 检查批次ID + /// + public Guid SubjectVisitId { get; set; } + + + /// + /// 检查批次名称 + /// + public string SubjectVisitName { get; set; } + + + /// + /// 截止检查批次 + /// + public Guid? CutOffVisitId { get; set; } + + + /// + /// 截止检查批次名称 + /// + public string? CutOffVisitName { get; set; } + + /// + /// 对应阅片期 + /// + public Guid? ReadModuleId { get; set; } + + /// + /// 对应Name + /// + public string ReadModuleName { get; set; } + + + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 患者Code + /// + public string SubjectCode { get; set; } + + /// + /// 中心Id + /// + public string SiteCode { get; set; } + + /// + /// 项目中心Id + /// + public string TrialSiteCode { get; set; } + + /// + /// 是否为末次检查批次 + /// + public bool IsFinalVisit { get; set; } = false; + + /// + /// 上一检查批次 + /// + public Guid? OutPlanPreviousVisitId { get; set; } + + /// + /// 上一检查批次名称 + /// + public string OutPlanPreviousVisitName { get; set; } + + /// + /// 中心ID + /// + public Guid SiteId { get; set; } + + /// + /// 是否为检查批次 + /// + public bool IsVisit { get; set; } + + /// + /// 检查批次Num + /// + public decimal? VisitNum { get; set; } + + /// + /// 是否为PD进展 + /// + public PDStateEnum PDState { get; set; } = PDStateEnum.None; + + /// + /// 入组确认评估 + /// + public bool IsEnrollmentConfirm { get; set; } = false; + + + /// + /// 阅片状态 + /// + public ReadingStatusEnum ReadingStatus { get; set; } + + /// + /// 是否是基线 + /// + public bool IsBaseLine { get; set; } = false; + + + /// + /// 标准Id + /// + public Guid? TrialReadingCriterionId { get; set; } + + + /// + /// 标准名称 + /// + public string CriterionName { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/SQLFile/Portal.sql b/IRaCIS.Core.Domain/SQLFile/Portal.sql new file mode 100644 index 0000000..de4ed44 --- /dev/null +++ b/IRaCIS.Core.Domain/SQLFile/Portal.sql @@ -0,0 +1,8 @@ +-- 20221223同步表数据 +-- Dictionary FrontAuditConfig Menu UserType UserTypeMenu ReadingQuestionSystem ReadingTableQuestionSystem OrganInfo ReadingQuestionCriterionSystem TumorAssessment +-- 需要特别注意的表 CriterionNidus ReadingCriterionDictionary + +select * from CriterionNidus where IsSystemCriterion=1 +select * from ReadingCriterionDictionary where IsSystemCriterion=1 +delete CriterionNidus where IsSystemCriterion=1 +delete ReadingCriterionDictionary where IsSystemCriterion=1 diff --git a/IRaCIS.Core.Domain/SQLFile/Test.sql b/IRaCIS.Core.Domain/SQLFile/Test.sql new file mode 100644 index 0000000..3c75584 --- /dev/null +++ b/IRaCIS.Core.Domain/SQLFile/Test.sql @@ -0,0 +1,1004 @@ +-----2022-05-27------------------------ + +update DataInspection set BatchId=Id where BatchId is null + +update QCChallenge set Code=ChallengeCode + +update QCChallenge set ChallengeCode='Q'+ RIGHT('00000'+CAST( Code AS nvarchar(50)),5); + + + + +-----2022-08-11------------------------ + +update [dbo].[Site] set Code= convert(int,RIGHT(SiteCode,4)) +update Site set SiteName= substring(SiteName,1,charindex('医院',SiteName)+1),AliasName=substring(SiteName,charindex('医院',SiteName)+2,30) where SiteName like '%医院%医院' + + +update Doctor set Physician='Chief Physician',PhysicianCN='主任医师' where RankId='dcbf4d1a-8373-4539-a0ae-1ba76d57bb48' +update Doctor set Physician='Deputy Chief Physician',PhysicianCN='副主任医师' where RankId='3a6e3335-cea4-4803-a136-d6301883a937' +update Doctor set RankOther='Professor',RankOtherCN='教授' where RankId='dcbf4d1a-8373-4539-a0ae-1ba76d57bb48' +update Doctor set RankOther='Associate Professor',RankOtherCN='副教授' where RankId='3a6e3335-cea4-4803-a136-d6301883a937' +update Doctor set Physician='Staff',PhysicianCN='医师' where RankId='82411c2e-9832-4c6f-a859-e3b6e796760d' + + +update DataInspection set JsonDetail= replace(cast(JsonDetail as varchar(max)),'Insepection','CommonData') + +update ReadingClinicalData set FileCount=(select count(*) from ReadingClinicalDataPDF where ReadingClinicalDataId =ReadingClinicalData.Id) +update NoneDicomStudy set FileCount=(select count(*) from NoneDicomStudyFile where NoneDicomStudyId =NoneDicomStudy.Id) + + +update DataInspection set CreateUserName= (select UserName from [User] where [User].Id = DataInspection.CreateUserId) + +update DataInspection set RoleName=(select UserName from [User] INNER join UserType on [User].UserTypeId =UserType.Id where [User].Id=DataInspection.CreateUserId) + +update DataInspection set CreateUserRealName=(select LastName+' / '+FirstName from [User] where [User].Id=DataInspection.CreateUserId) + + +update TrialDocUserTypeConfirmedUser set CreateUserId=ConfirmUserId,CreateTime=SignFirstViewTime +update SystemDocConfirmedUser set CreateUserId=ConfirmUserId,CreateTime=SignFirstViewTime +update TrialDocUserTypeConfirmedUser set CreateUserId=ConfirmUserId,CreateTime=getdate() where SignFirstViewTime is null +update SystemDocConfirmedUser set CreateUserId=ConfirmUserId,CreateTime=getdate() where SignFirstViewTime is null +update TrialDocUserTypeConfirmedUser set IsDeleted=0 where IsDeleted is null +update SystemDocConfirmedUser set IsDeleted=0 where IsDeleted is null + + +update EnrollReadingCategory set TrialReadingCriterionId= ISNUll((select top 1 Id from ReadingQuestionCriterionTrial where IsConfirm=1 and TrialId=Enroll.TrialId ) ,'00000000-0000-0000-0000-000000000000') +FROM EnrollReadingCategory INNER JOIN Enroll ON (EnrollReadingCategory.EnrollId = Enroll.Id) + +update VisitTask set TrialReadingCriterionId=(select top 1 Id from ReadingQuestionCriterionTrial where IsConfirm=1 and TrialId=VisitTask.TrialId) +select * from EnrollReadingCategory where TrialReadingCriterionId is null +select * from VisitTask where TrialReadingCriterionId is null + + + update Site set Site.Code = (select b.code2 from (select Id,row_number() over(order by Site.SiteName) code2 from Site) b where b.Id =Site.Id) + update Site set SiteCode= 'S'+ RIGHT('0000'+CAST(Site.Code as varchar(10)),4) + + --不用同步 +update FrontAuditConfig set ValueCN='任务名称' where Value='TaskName' and ValueCN='检查批次/阅片期名称' and Code='TaskName' +update FrontAuditConfig set ValueCN='任务盲态标识' where Value='TaskBlindName' and ValueCN='检查批次盲态名称' and Code='TaskBlindName' + + -----------------------------------------之前的------------------------------------------------------------- + + + +--无序维护sql +update VisitTask set TaskBlindName='Follow-up' + from VisitTask +INNER join ReadingQuestionCriterionTrial on VisitTask.TrialReadingCriterionId=ReadingQuestionCriterionTrial.Id +INNER join SubjectVisit on VisitTask.SourceSubjectVisitId=SubjectVisit.Id + where SubjectVisit.IsBaseLine=0 and ReadingQuestionCriterionTrial.IsReadingTaskViewInOrder=0 + + +-- 有序维护sql +update VisitTask set TaskBlindName='Follow-up '+ cast(sv.rn as varchar) + from VisitTask +join ReadingQuestionCriterionTrial on VisitTask.TrialReadingCriterionId=ReadingQuestionCriterionTrial.Id +--join SubjectVisit on VisitTask.SourceSubjectVisitId=SubjectVisit.Id +join ( select Id SubjectVisitId, IsBaseLine, ROW_NUMBER() over( partition by SubjectId order by VisitNum asc )-1 rn,VisitNum from SubjectVisit where IsLostVisit=0) sv on VisitTask.SourceSubjectVisitId=sv.SubjectVisitId +where sv.IsBaseLine=0 and ReadingQuestionCriterionTrial.IsReadingTaskViewInOrder=1 + +update VisitTask set TaskBlindName='Baseline' from VisitTask +join SubjectVisit on VisitTask.SourceSubjectVisitId=SubjectVisit.Id where SubjectVisit.IsBaseLine=1 + + + + +----------------------------------------------修改 ---------------------------------------- +update Dictionary set code =0 where Id='15bb5529-a6fe-439f-5196-08da179a7080' +update Dictionary set code =1 where Id='0628d7be-afba-4471-5197-08da179a7080' +update Dictionary set code =2 where Id='41bfec4b-dbfb-401d-5198-08da179a7080' +update Dictionary set code =-1 where Id='1fac678d-69b6-41c3-5199-08da179a7080' +go + + +update ReadingQuestionCriterionTrial set DigitPlaces=DigitPlaces-1 where DigitPlaces is not null +update ReadingQuestionCriterionTrial set DigitPlaces=-1 where DigitPlaces=3 + + + + + + +--阅片标准维护 +update DataInspection set ObjectRelationParentId3 = (select top 1 TrialReadingCriterionId from SubjectUser where Id =DataInspection.GeneralId) where EntityName='SubjectUser' +update DataInspection set ObjectRelationParentId2 = (select top 1 TrialReadingCriterionId from ReadingPeriodSet where Id =DataInspection.GeneralId) where EntityName='ReadingPeriodSet' +update DataInspection set ObjectRelationParentId2 = (select top 1 TrialReadingCriterionId from ReadModule where Id =DataInspection.GeneralId) where EntityName='ReadModule' +update DataInspection set ObjectRelationParentId2 = (select top 1 TrialReadingCriterionId from TaskConsistentRule where Id =DataInspection.GeneralId) where EntityName='TaskConsistentRule' +update DataInspection set ObjectRelationParentId3 = (select top 1 TrialReadingCriterionId from VisitTask where Id =DataInspection.GeneralId) where EntityName='VisitTask' + +update ReadingTableQuestionTrial set DictionaryCode='' where DictionaryCode is null + + + + +--维护RowInfo的OrderMark + +select * from ReadingTableAnswerRowInfo +update ReadingTableAnswerRowInfo set ReadingTableAnswerRowInfo.OrderMark=ReadingQuestionTrial.OrderMark from ReadingQuestionTrial +inner join ReadingTableAnswerRowInfo on ReadingTableAnswerRowInfo.QuestionId=ReadingQuestionTrial.Id + + + +delete ReadingTableQuestionAnswer where TableQuestionId in (select id from ReadingTableQuestionTrial where QuestionMark=3) +go +delete ReadingTableQuestionTrial where QuestionMark=3 +go +delete ReadingTableQuestionSystem where QuestionMark=3 +go + + +------------------------------------2022-12-13---------------------------- +update ReadingQuestionSystem set DefaultValue='' +update ReadingQuestionTrial set DefaultValue='' + +----------删除之后要重新同步器官数据 +delete OrganInfo where SystemCriterionId='B0450000-9B8E-98FA-6658-08DA4DAB1FAC' +delete OrganTrialInfo +    where (select count(1) as num from OrganInfo where OrganInfo.Id = OrganTrialInfo.OrganInfoId) = 0 + + +----2022-12-15 +Update Trial set BlindBaseLineName='Baseline',BlindFollowUpPrefix='Follow-up' +update DataInspection set VisitTaskId = GeneralId where EntityName='VisitTask' + +--2022-12-17 维护稽查数据 +update DataInspection set VisitTaskId= substring(JsonDetail,CHARINDEX('OriginalReReadingTaskId":"',JsonDetail)+26,36) where EntityName='VisitTaskReReading' +update DataInspection set VisitTaskId= substring(JsonDetail,CHARINDEX('VisitTaskId":"',JsonDetail)+14,36) where EntityName='TaskMedicalReview' +update DataInspection set VisitTaskId= substring(JsonDetail,CHARINDEX('VisitTaskId":"',JsonDetail)+14,36) where EntityName='ReadingOncologyTaskInfo' +update DataInspection set VisitTaskId= substring(JsonDetail,CHARINDEX('VisitTaskId":"',JsonDetail)+14,36) where EntityName='ReadingMedicalReviewDialog' +update DataInspection set VisitTaskId= substring(JsonDetail,CHARINDEX('VisitTaskId":"',JsonDetail)+14,36) where EntityName='ReadingTaskQuestionAnswer' +update DataInspection set VisitTaskId= substring(JsonDetail,CHARINDEX('VisitTaskId":"',JsonDetail)+14,36) where EntityName='ReadingTableAnswerRowInfo' +update DataInspection set VisitTaskId= substring(JsonDetail,CHARINDEX('VisitTaskId":"',JsonDetail)+14,36) where EntityName='ReadingMedicineQuestionAnswer' + + +update DataInspection set SubjectVisitId=null where EntityName='ReadModule' +update DataInspection set TrialReadingCriterionId=(select TrialReadingCriterionId from VisitTask where Id =VisitTaskId) where VisitTaskId is not null and TrialReadingCriterionId is null + + + +--稽查bug 数据维护 + +update DataInspection set TrialReadingCriterionId= substring(JsonDetail,CHARINDEX('TrialCriterionId":"',JsonDetail)+19 + +( DATALENGTH( left( cast([JsonDetail] as VARCHAR(2500)) ,charindex('TrialCriterionId":"',JsonDetail)) ) + -LEN ( left( cast([JsonDetail] as VARCHAR(2500)) ,charindex('TrialCriterionId":"',JsonDetail)) ) +),36) where EntityName='ReadingTableQuestionTrial' +update DataInspection set TrialReadingCriterionId= substring(JsonDetail,CHARINDEX('ReadingQuestionCriterionTrialId":"',JsonDetail)+34,36) where EntityName='ReadingQuestionTrial' +update DataInspection set TrialReadingCriterionId= GeneralId where EntityName='ReadingQuestionCriterionTrial' + +--维护临床数据状态 +update ReadingClinicalData set IsSign=1 where EXISTS(select * from SubjectVisit where Id=ReadingId and IsBaseLine=1 and IsConfirmedClinicalData=1) and IsSign=0 +update ReadingClinicalData set ReadingClinicalDataState=3 where IsSign=1 and ReadingClinicalDataState!=3 + + + +--20221223--发布生产开始 + + + +update DataInspection set Identification='ReadingMedicineQuestionAnswer/Add' , ObjectTypeId='E8250000-3E2C-0016-8166-08DA7E8DFF90',OptTypeId='02FCF476-4089-4B2C-B9BE-08DA0E391693',ModuleTypeId='A4460000-3E2C-0016-F305-08DA7E8C40BB' where EntityName='ReadingMedicineQuestionAnswer' +update DataInspection set ObjectTypeId='A4100000-3E2C-0016-A716-08DA7E8DA9BD',OptTypeId='02FCF476-4089-4B2C-B9BE-08DA0E391693',ModuleTypeId='A4460000-3E2C-0016-F305-08DA7E8C40BB' where Identification='ReadingMedicalReview/saveMedicalReviewInfo/TaskMedicalReview/Update' +update DataInspection set Identification='ReadingMedicalReviewDialog/Add' , ObjectTypeId='D8270000-3E2C-0016-2A14-08DA3EDB77FD',OptTypeId='02FCF476-4089-4B2C-B9BE-08DA0E391693',ModuleTypeId='A4460000-3E2C-0016-F305-08DA7E8C40BB' where EntityName='ReadingMedicalReviewDialog' + +update DataInspection set Identification='ReadingMedicalReviewDialog/Add/IR' from DataInspection INNER JOIN [User] on DataInspection.CreateUserId = [User].Id where DataInspection.EntityName='ReadingMedicalReviewDialog' and [User].UserTypeEnum=13 +update DataInspection set Identification='ReadingMedicalReviewDialog/Add/MIM' from DataInspection INNER JOIN [User] on DataInspection.CreateUserId = [User].Id where DataInspection.EntityName='ReadingMedicalReviewDialog' and [User].UserTypeEnum=14 + + + +---------修改 ReadingMedicineQuestionAnswer的VisitTaskId +update ReadingMedicineQuestionAnswer set ReadingMedicineQuestionAnswer.VisitTaskId=TaskMedicalReview.VisitTaskId from TaskMedicalReview +inner join ReadingMedicineQuestionAnswer on ReadingMedicineQuestionAnswer.TaskMedicalReviewId=TaskMedicalReview.id + + + + +update frontAuditConfig set DictionaryCode ='' where Code='AuditState' and DictionaryCode='AuditState' + +----正式环境 修改TrialCode 错误的 + + + + + +----------------------------2023-1-09 + +-- 修改Hospital 的SiteId +update Hospital set Hospital.SiteId=Site.Id from Site +inner join Hospital on Hospital.HospitalName=Site.SiteName + + +---2023 -1-10 +--处理检查批次 + --基线 +update VisitTask set IsNeedClinicalDataSign =1 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join SubjectVisit on VisitTask.SourceSubjectVisitId=SubjectVisit.Id + + where VisitTask.ReadingCategory=1 and SubjectVisit.IsBaseLine=1 and EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and (ClinicalDataLevel=0 or ClinicalDataLevel=1) and IsConfirm=1) and IsNeedClinicalDataSign=0 + + + update VisitTask set IsNeedClinicalDataSign =0 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join SubjectVisit on VisitTask.SourceSubjectVisitId=SubjectVisit.Id + + where VisitTask.ReadingCategory=1 and SubjectVisit.IsBaseLine=1 and not EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and (ClinicalDataLevel=0 or ClinicalDataLevel=1) and IsConfirm=1) and IsNeedClinicalDataSign=1 + + --非基线 +update VisitTask set IsNeedClinicalDataSign =1 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join SubjectVisit on VisitTask.SourceSubjectVisitId=SubjectVisit.Id + where VisitTask.ReadingCategory=1 and SubjectVisit.IsBaseLine=0 and EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=1 and IsConfirm=1) and IsNeedClinicalDataSign =0 + + + update VisitTask set IsNeedClinicalDataSign =0 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join SubjectVisit on VisitTask.SourceSubjectVisitId=SubjectVisit.Id + where VisitTask.ReadingCategory=1 and SubjectVisit.IsBaseLine=0 and not EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=1 and IsConfirm=1) and IsNeedClinicalDataSign =1 + +--处理裁判 + +update VisitTask set IsNeedClinicalDataSign =0,IsClinicalDataSign=0 where VisitTask.ReadingCategory=4 + +--处理肿瘤学 + +update VisitTask set IsNeedClinicalDataSign =1 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join ReadModule on VisitTask.SouceReadModuleId=ReadModule.Id + where VisitTask.ReadingCategory=5 and EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=3 and IsConfirm=1) + and IsNeedClinicalDataSign =0 + + update VisitTask set IsNeedClinicalDataSign =0 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join ReadModule on VisitTask.SouceReadModuleId=ReadModule.Id + where VisitTask.ReadingCategory=5 and not EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=3 and IsConfirm=1) + and IsNeedClinicalDataSign =1 + +--处理全局 + +update VisitTask set IsNeedClinicalDataSign =1 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join ReadModule on VisitTask.SouceReadModuleId=ReadModule.Id + where VisitTask.ReadingCategory=2 and EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=2 and IsConfirm=1) + and IsNeedClinicalDataSign =0 + + +update VisitTask set IsNeedClinicalDataSign =0 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join ReadModule on VisitTask.SouceReadModuleId=ReadModule.Id + where VisitTask.ReadingCategory=2 and not EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=2 and IsConfirm=1 ) + and IsNeedClinicalDataSign=1 + + + -----处理 是否所有临床数据都签名的字段 +--处理检查批次 + --基线 +update VisitTask set IsClinicalDataSign =1 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join SubjectVisit on VisitTask.SourceSubjectVisitId=SubjectVisit.Id + + where VisitTask.ReadingCategory=1 and SubjectVisit.IsBaseLine=1 and VisitTask.IsClinicalDataSign =0 and VisitTask.IsNeedClinicalDataSign=1 + and EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and (ClinicalDataLevel=0 or ClinicalDataLevel=1) and IsConfirm=1) + and (select count(*) from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ( ClinicalDataLevel=0 or ClinicalDataLevel=1) and IsConfirm=1 and UploadRole=1 + ) + =(select count(*) from ReadingClinicalData inner join ClinicalDataTrialSet on ReadingClinicalData.ClinicalDataTrialSetId =ClinicalDataTrialSet.Id + where ReadingClinicalData.TrialId=VisitTask.TrialId and IsSign=1 and ReadingClinicalDataState=3 and ReadingId=VisitTask.SourceSubjectVisitId and ClinicalDataTrialSet.UploadRole=1 + -- 有可能仅仅IC Subject级别 需要签名 IC自动签名了 没有PM需要签名的文档 此时应该设置为已签署 + ) + +--处理基线脏数据 + update VisitTask set IsClinicalDataSign =0 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join SubjectVisit on VisitTask.SourceSubjectVisitId=SubjectVisit.Id + + where VisitTask.ReadingCategory=1 and SubjectVisit.IsBaseLine=1 and VisitTask.IsClinicalDataSign =1 and VisitTask.IsNeedClinicalDataSign=1 + and EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and (ClinicalDataLevel=0 or ClinicalDataLevel=1) and IsConfirm=1) + and ( + select count(*) from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ( ClinicalDataLevel=0 or ClinicalDataLevel=1) and IsConfirm=1 and UploadRole=1 + ) + !=(select count(*) from ReadingClinicalData inner join ClinicalDataTrialSet on ReadingClinicalData.ClinicalDataTrialSetId =ClinicalDataTrialSet.Id + where ReadingClinicalData.TrialId=VisitTask.TrialId and IsSign=1 and ReadingClinicalDataState=3 and ReadingId=VisitTask.SourceSubjectVisitId and ClinicalDataTrialSet.UploadRole=1 + -- 有可能仅仅IC Subject级别 需要签名 IC自动签名了 没有PM需要签名的文档 此时应该设置为已签署 + ) + + + + --非基线 +update VisitTask set IsClinicalDataSign =1 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join SubjectVisit on VisitTask.SourceSubjectVisitId=SubjectVisit.Id + where VisitTask.ReadingCategory=1 and SubjectVisit.IsBaseLine=0 and VisitTask.IsClinicalDataSign =0 and VisitTask.IsNeedClinicalDataSign=1 + + -- 非基线 可能只有IC 检查批次级别 PM 没有 + and EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=1 and IsConfirm=1) + and (select count(*) from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=1 and IsConfirm=1 and UploadRole=1 ) + =(select count(*) from ReadingClinicalData inner join ClinicalDataTrialSet on ReadingClinicalData.ClinicalDataTrialSetId =ClinicalDataTrialSet.Id + where ReadingClinicalData.TrialId=VisitTask.TrialId and IsSign=1 and ReadingClinicalDataState=3 and ReadingId=VisitTask.SourceSubjectVisitId and ClinicalDataTrialSet.UploadRole=1) + + --维护错误数据 + update VisitTask set IsClinicalDataSign =0 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join SubjectVisit on VisitTask.SourceSubjectVisitId=SubjectVisit.Id + where VisitTask.ReadingCategory=1 and SubjectVisit.IsBaseLine=0 and VisitTask.IsClinicalDataSign =1 and VisitTask.IsNeedClinicalDataSign=1 + + -- 非基线 可能只有IC 检查批次级别 PM 没有 + and EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=1 and IsConfirm=1) + and (select count(*) from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=1 and IsConfirm=1 and UploadRole=1 ) + !=(select count(*) from ReadingClinicalData inner join ClinicalDataTrialSet on ReadingClinicalData.ClinicalDataTrialSetId =ClinicalDataTrialSet.Id + where ReadingClinicalData.TrialId=VisitTask.TrialId and IsSign=1 and ReadingClinicalDataState=3 and ReadingId=VisitTask.SourceSubjectVisitId and ClinicalDataTrialSet.UploadRole=1) + + +--处理肿瘤学 + +update VisitTask set IsClinicalDataSign =1 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join ReadModule on VisitTask.SouceReadModuleId=ReadModule.Id + where VisitTask.ReadingCategory=5 and VisitTask.IsClinicalDataSign =0 and VisitTask.IsNeedClinicalDataSign=1 + and EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=3 and IsConfirm=1) + and (select count(*) from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=3 and IsConfirm=1) + =(select count(*) from ReadingClinicalData inner join ClinicalDataTrialSet on ReadingClinicalData.ClinicalDataTrialSetId =ClinicalDataTrialSet.Id + where ReadingClinicalData.TrialId=VisitTask.TrialId and IsSign=1 and ReadingClinicalDataState=3 and ReadingId=VisitTask.SouceReadModuleId and ClinicalDataTrialSet.UploadRole=1) + + + +--处理全局 +update VisitTask set IsClinicalDataSign =1 from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join ReadModule on VisitTask.SouceReadModuleId=ReadModule.Id + where VisitTask.ReadingCategory=2 and VisitTask.IsClinicalDataSign =0 and VisitTask.IsNeedClinicalDataSign=1 + and EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=2 and IsConfirm=1) + + and (select count(*) from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=2 and IsConfirm=1) + =(select count(*) from ReadingClinicalData inner join ClinicalDataTrialSet on ReadingClinicalData.ClinicalDataTrialSetId =ClinicalDataTrialSet.Id + where ReadingClinicalData.TrialId=VisitTask.TrialId and IsSign=1 and ReadingClinicalDataState=3 and ReadingId=VisitTask.SouceReadModuleId and ClinicalDataTrialSet.UploadRole=1) + + --维护脏数据 + update VisitTask set IsClinicalDataSign =0 from VisitTask + + --select VisitTask.Id as VisitTaskId ,Trial.Id, Trial.ExperimentName, VisitTask.TaskName,ReadingQuestionCriterionTrial.CriterionName,Subject.Code ,VisitTask.IsNeedClinicalDataSign,VisitTask.IsClinicalDataSign ,VisitTask.DoctorUserId from VisitTask +inner join ReadingQuestionCriterionTrial on ReadingQuestionCriterionTrial.Id=VisitTask.TrialReadingCriterionId +inner join ReadModule on VisitTask.SouceReadModuleId=ReadModule.Id +inner join Subject on VisitTask.SubjectId=Subject.Id +inner join Trial on VisitTask.TrialId=Trial.Id + where VisitTask.ReadingCategory=2 and VisitTask.IsClinicalDataSign =1 and VisitTask.IsNeedClinicalDataSign=1 + and EXISTS (select * from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=2 and IsConfirm=1) + + and (select count(*) from ClinicalDataTrialSet where TrialId=VisitTask.TrialId and CriterionEnumListStr LIKE '%|'+ CAST(ReadingQuestionCriterionTrial.CriterionType AS varchar(20))+'|%' and ClinicalDataLevel=2 and IsConfirm=1 ) + !=(select count(*) from ReadingClinicalData inner join ClinicalDataTrialSet on ReadingClinicalData.ClinicalDataTrialSetId =ClinicalDataTrialSet.Id + where ReadingClinicalData.TrialId=VisitTask.TrialId and IsSign=1 and ReadingClinicalDataState=3 and ReadingId=VisitTask.SouceReadModuleId and ClinicalDataTrialSet.UploadRole=1 and ClinicalDataLevel=2 ) + + + --处理字段 IsFrontTaskNeedSignButNotSign + + UPDATE v SET [v].[IsFrontTaskNeedSignButNotSign] =1 +FROM [VisitTask] AS [v] +WHERE EXISTS ( + + SELECT 1 + FROM [VisitTask] AS [v0] + where [v0].SubjectId=v.SubjectId and [v0].TrialReadingCriterionId=V.TrialReadingCriterionId and [v0].TrialId=V.TrialId and EXISTS( + SELECT 1 + FROM [VisitTask] AS [v1] + WHERE [v1].SubjectId=v.SubjectId and V1.TrialReadingCriterionId=V.TrialReadingCriterionId and V1.TrialId=V.TrialId and [v1].[IsNeedClinicalDataSign]=1 and V1.IsClinicalDataSign=0 and V1.VisitTaskNum< [v0].[VisitTaskNum] + + ) and [v0].[IsFrontTaskNeedSignButNotSign]=0 AND [v0].[Id] = [v].[Id] ) + + + UPDATE v SET [v].[IsFrontTaskNeedSignButNotSign] =0 +FROM [VisitTask] AS [v] +WHERE EXISTS ( + + SELECT 1 + FROM [VisitTask] AS [v0] + where [v0].SubjectId=v.SubjectId and [v0].TrialReadingCriterionId=V.TrialReadingCriterionId and [v0].TrialId=V.TrialId and not EXISTS( + SELECT 1 + FROM [VisitTask] AS [v1] + WHERE [v1].SubjectId=v.SubjectId and V1.TrialReadingCriterionId=V.TrialReadingCriterionId and V1.TrialId=V.TrialId and [v1].[IsNeedClinicalDataSign]=1 and V1.IsClinicalDataSign=0 and V1.VisitTaskNum< [v0].[VisitTaskNum] + + ) and [v0].[IsFrontTaskNeedSignButNotSign]=1 AND [v0].[Id] = [v].[Id] ) + + update ReadingClinicalData set IsVisit=1 from ReadingClinicalData INNER JOIN ClinicalDataTrialSet on ReadingClinicalData.ClinicalDataTrialSetId = ClinicalDataTrialSet.Id where ClinicalDataTrialSet.ClinicalDataLevel in (0,1) and ReadingClinicalData.IsVisit=0 + update VisitTask set IsClinicalDataSign=0 where IsNeedClinicalDataSign=0 and IsClinicalDataSign=1 + + -- 判断 检查批次 和阅片期 是否有脏数据吧 +--查询确认 +select V.SubjectId, V.SourceSubjectVisitId, V.TrialReadingCriterionId,V.IsSelfAnalysis, v.DoctorUserId,V.ArmEnum,Count(*) as EffectCount,max(v.CreateTime),max(v.Id) as Id from VisitTask V +join ReadingQuestionCriterionTrial on V.TrialReadingCriterionId = ReadingQuestionCriterionTrial.Id +where V.TaskState=0 and V.SourceSubjectVisitId is not null and ReadingQuestionCriterionTrial.ReadingType=2 +GROUP BY V.TrialId, V.SubjectId, V.SourceSubjectVisitId , V.TrialReadingCriterionId ,V.IsSelfAnalysis,V.ArmEnum ,v.DoctorUserId +HAVING Count(*)>1 + +--删除脏的多生成的任务 +delete VisitTask where Id in (select max(v.Id) as Id from VisitTask V +join ReadingQuestionCriterionTrial on V.TrialReadingCriterionId = ReadingQuestionCriterionTrial.Id +where V.TaskState=0 and V.SourceSubjectVisitId is not null and ReadingQuestionCriterionTrial.ReadingType=2 +GROUP BY V.TrialId, V.SubjectId, V.SourceSubjectVisitId , V.TrialReadingCriterionId ,V.IsSelfAnalysis,V.ArmEnum ,v.DoctorUserId +HAVING Count(*)>1) + +select V.TrialId, V.SubjectId, V.SouceReadModuleId, V.TrialReadingCriterionId,V.IsSelfAnalysis, V.ArmEnum,Count(*) as EffectCount,max(v.CreateTime),max(v.Id) as Id from VisitTask V +join ReadingQuestionCriterionTrial on V.TrialReadingCriterionId = ReadingQuestionCriterionTrial.Id +where V.TaskState=0 and V.SouceReadModuleId is not null and ReadingQuestionCriterionTrial.ReadingType=2 +GROUP BY V.TrialId, V.SubjectId, V.SouceReadModuleId , V.TrialReadingCriterionId ,V.IsSelfAnalysis,V.ArmEnum +HAVING Count(*)>2 + + +----2023年 开始 + -- 维护临床数据 先运行swagger 维护数据的方法 + +---------------------维护表格问题ID 2013-1-31 +update ReadingTableQuestionTrial set ReadingTableQuestionTrial.SystemTableQuestionId= ReadingTableQuestionSystem.Id from ReadingTableQuestionSystem +inner join ReadingTableQuestionTrial on ReadingTableQuestionSystem.QuestionName=ReadingTableQuestionTrial.QuestionName +inner join ReadingQuestionTrial on ReadingTableQuestionTrial.ReadingQuestionId=ReadingQuestionTrial.Id and ReadingQuestionTrial.ReadingQuestionSystemId=ReadingTableQuestionSystem.ReadingQuestionId + +where ReadingQuestionTrial.ReadingQuestionSystemId is not null and ReadingTableQuestionTrial.SystemTableQuestionId is null + + + +-------------------维护检查批次任务 IsSelfAnalysis 为 null 的 ------------------------ + + update VisitTask + set RelatedVisitTaskIds= iif(STUFF((SELECT ',' + '"'+ CONVERT(nvarchar(80),Id) +'"' FROM VisitTask vt where + vt.TrialId=VisitTask.TrialId and + vt.SubjectId =VisitTask.SubjectId and + vt.ArmEnum =VisitTask.ArmEnum and + vt.DoctorUserId =VisitTask.DoctorUserId and + vt.ReadingTaskState =2 and + vt.TrialReadingCriterionId =VisitTask.TrialReadingCriterionId and + vt.TaskState =0 and + vt.VisitTaskNum <=VisitTask.VisitTaskNum and + vt.IsAnalysisCreate =VisitTask.IsAnalysisCreate and + vt.IsSelfAnalysis is null and + vt.ReadingCategory = VisitTask.ReadingCategory + FOR XML PATH('')),1,1,'') is null,'[]','['+ STUFF((SELECT ',' + '"'+ CONVERT(nvarchar(80),Id) +'"' FROM VisitTask vt where + vt.TrialId=VisitTask.TrialId and + vt.SubjectId =VisitTask.SubjectId and + vt.ArmEnum =VisitTask.ArmEnum and + vt.DoctorUserId =VisitTask.DoctorUserId and + vt.ReadingTaskState =2 and + vt.TrialReadingCriterionId =VisitTask.TrialReadingCriterionId and + vt.TaskState =0 and + vt.VisitTaskNum <=VisitTask.VisitTaskNum and + vt.IsAnalysisCreate =VisitTask.IsAnalysisCreate and + vt.IsSelfAnalysis is null and + vt.ReadingCategory = VisitTask.ReadingCategory + FOR XML PATH('')),1,1,'') +']') , + + PastResultTaskIds=iif(STUFF((SELECT ',' + '"'+ CONVERT(nvarchar(80),Id) +'"' FROM VisitTask vt where + vt.TrialId=VisitTask.TrialId and + vt.SubjectId =VisitTask.SubjectId and + vt.ArmEnum =VisitTask.ArmEnum and + vt.DoctorUserId =VisitTask.DoctorUserId and + vt.ReadingTaskState =2 and + vt.TrialReadingCriterionId =VisitTask.TrialReadingCriterionId and + vt.TaskState =0 and + vt.VisitTaskNum + ///TrialSiteEquipmentSurvey + /// + [Table("TrialSiteEquipmentSurvey")] + public class TrialSiteEquipmentSurvey : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + [ForeignKey("TrialSiteSurveyId")] + public TrialSiteSurvey TrialSiteSurvey { get; set; } + + public Guid TrialSiteSurveyId { get; set; } + + [JsonIgnore] + [ForeignKey("EquipmentTypeId")] + public Dictionary EquipmentType { get; set; } + + public Guid EquipmentTypeId { get; set; } + + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + /// + /// Parameters + /// + [Required] + public string Parameters { get; set; } = string.Empty; + + /// + /// ManufacturerName + /// + [Required] + public string ManufacturerName { get; set; } = string.Empty; + + /// + /// ScannerType + /// + [Required] + public string ScannerType { get; set; } = string.Empty; + + /// + /// Note + /// + [Required] + public string Note { get; set; } = string.Empty; + + + + } + +} diff --git a/IRaCIS.Core.Domain/SiteSurvey/TrialSiteSurvey.cs b/IRaCIS.Core.Domain/SiteSurvey/TrialSiteSurvey.cs new file mode 100644 index 0000000..f31d878 --- /dev/null +++ b/IRaCIS.Core.Domain/SiteSurvey/TrialSiteSurvey.cs @@ -0,0 +1,153 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:16:57 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialSiteSurvey + /// + [Table("TrialSiteSurvey")] + public class TrialSiteSurvey : Entity, IAuditUpdate, IAuditAdd,ISoftDelete + { + public TrialSiteSurveyEnum State { get; set; } = TrialSiteSurveyEnum.ToSubmit; + + + // 必须 { get; set; } 否则 翻译出错 + [JsonIgnore] + public List TrialSiteUserSurveyList { get; set; } =new List(); + [JsonIgnore] + public List TrialSiteEquipmentSurveyList { get; set; } = new List(); + + [JsonIgnore] + public TrialSite TrialSite { get; set; } + [JsonIgnore] + public Trial Trial { get; set; } + [JsonIgnore] + public Site Site { get; set; } + + public DateTime? DeletedTime { get; set; } + + + public bool IsDeleted { get; set; } + + public Guid? DeleteUserId { get; set; } + + /// + /// TrialId + /// + [Required] + public Guid TrialId { get; set; } + + /// + /// SiteId + /// + [Required] + public Guid SiteId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + /// + /// UserName + /// + [Required] + public string UserName { get; set; } = string.Empty; + + /// + /// Phone + /// + [Required] + public string Phone { get; set; } = string.Empty; + + /// + /// Email + /// + [Required] + public string Email { get; set; } = string.Empty; + + /// + /// AverageEngravingCycle + /// + [Required] + public int AverageEngravingCycle { get; set; } + + /// + /// IsConfirmImagingTechnologist + /// + [Required] + public bool IsConfirmImagingTechnologist { get; set; } + + /// + /// NotConfirmReson + /// + [Required] + public string NotConfirmReson { get; set; } = string.Empty; + + /// + /// EfficacyEvaluatorType + /// + [Required] + public int EfficacyEvaluatorType { get; set; } + + /// + /// IsFollowStudyParameters + /// + [Required] + public bool IsFollowStudyParameters { get; set; } + + /// + /// NotFollowReson + /// + [Required] + public string NotFollowReson { get; set; } = string.Empty; + + + public string LatestBackReason { get; set; } = string.Empty; + + + + public Guid? PreliminaryUserId { get; set; } + + public Guid? ReviewerUserId { get; set; } + + public DateTime? PreliminaryTime { get; set; } + + public DateTime? ReviewerTime { get; set; } + + [JsonIgnore] + public User ReviewerUser { get; set; } + [JsonIgnore] + public User PreliminaryUser { get; set; } + + + } + +} diff --git a/IRaCIS.Core.Domain/SiteSurvey/TrialSiteUserSurvey.cs b/IRaCIS.Core.Domain/SiteSurvey/TrialSiteUserSurvey.cs new file mode 100644 index 0000000..6956365 --- /dev/null +++ b/IRaCIS.Core.Domain/SiteSurvey/TrialSiteUserSurvey.cs @@ -0,0 +1,112 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 13:16:57 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using IRaCIS.Core.Domain.Share; +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialSiteUserSurvey + /// + [Table("TrialSiteUserSurvey")] + public class TrialSiteUserSurvey : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + [ForeignKey("TrialSiteSurveyId")] + public TrialSiteSurvey TrialSiteSurvey { get; set; } + + public Guid TrialSiteSurveyId { get; set; } + + [JsonIgnore] + [ForeignKey("UserTypeId")] + public UserType UserTypeRole { get; set; } + + public Guid? UserTypeId { get; set; } + + + public Guid TrialRoleNameId { get; set; } + + public Dictionary TrialRoleName { get; set; } + + + + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + public string UserName { get; set; } = string.Empty; + + /// + /// Phone + /// + [Required] + public string Phone { get; set; } = string.Empty; + + /// + /// Email + /// + [Required] + public string Email { get; set; } = string.Empty; + + + + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + + + + public string OrganizationName { get; set; } = string.Empty; + + public Guid? SystemUserId { get; set; } + + + public DateTime? ExpireTime { get; set; } + + public bool IsJoin { get; set; } + + public DateTime? ConfirmTime { get; set; } + + public string RejectReason { get; set; } = string.Empty; + + /// + /// IsGenerateAccount + /// + [Required] + public bool IsGenerateAccount { get; set; } + + public bool IsGenerateSuccess { get; set; } + + + public TrialSiteUserStateEnum InviteState { get; set; } = TrialSiteUserStateEnum.WaitSent; + + + + + } + +} diff --git a/IRaCIS.Core.Domain/Trial/DataInspection.cs b/IRaCIS.Core.Domain/Trial/DataInspection.cs new file mode 100644 index 0000000..7931682 --- /dev/null +++ b/IRaCIS.Core.Domain/Trial/DataInspection.cs @@ -0,0 +1,165 @@ + +//-------------------------------------------------------------------- +// 生成时间 2022-03-21 17:13:43 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///DataInspection + /// + [Table("DataInspection")] + public class DataInspection : Entity, IAuditAdd + { + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建用户ID + /// + public Guid CreateUserId { get; set; } + + + + /// + /// 项目iD + /// + + public Guid? TrialId { get; set; } + + /// + /// 中心 + /// + public Guid? SiteId { get; set; } + + /// + /// 患者 + /// + public Guid? SubjectId { get; set; } + + /// + /// 检查批次 + /// + public Guid? SubjectVisitId { get; set; } + + + public Guid? VisitTaskId { get; set; } + + public VisitTask VisitTask { get; set; } + + /// + /// IP地址 + /// + public string IP { get; set; } = string.Empty; + + /// + /// 修改原因 + /// + [Required] + public string Reason { get; set; } = string.Empty; + + /// + /// 是否有签名 + /// + public bool IsSign { get; set; } + + /// + /// 签名ID + /// + public Guid? SignId { get; set; } + + /// + /// 父类ID + /// + + public Guid? ParentId { get; set; } + + /// + /// 子类 + /// + public Guid? ChildrenTypeId { get; set; } + /// + /// 对象类型 + /// + public Guid? ObjectTypeId { get; set; } + + /// + /// 操作类型 + /// + public Guid? OptTypeId { get; set; } + + /// + /// 功能模块 + /// + public Guid? ModuleTypeId { get; set; } + + + /// + /// Json 对象 + /// + + public string JsonDetail { get; set; } = string.Empty; + + /// + /// 创建人名称 + /// + public string CreateUserName { get; set; } = string.Empty; + + public string CreateUserRealName { get; set; } = string.Empty; + /// + /// 角色名称 + /// + public string RoleName { get; set; } = string.Empty; + + + + /// + /// 标识 + /// + public string Identification { get; set; } = string.Empty; + + + /// + /// 稽查的对象Id + /// + public Guid? GeneralId { get; set; } + + + + /// + /// 批次Id + /// + public Guid BatchId { get; set; } + + + /// + /// 稽查对象,关联的父对象Id + /// + public Guid? ObjectRelationParentId { get; set; } + + public Guid? ObjectRelationParentId2 { get; set; } + + public Guid? ObjectRelationParentId3 { get; set; } + + + public string EntityName { get; set; } + + + public Guid? TrialReadingCriterionId { get; set; } + + public Guid? DoctorUserId { get; set; } + + [ForeignKey("TrialReadingCriterionId")] + public ReadingQuestionCriterionTrial TrialReadingCriterion { get; set; } + + } + + + +} diff --git a/IRaCIS.Core.Domain/Trial/Enroll.cs b/IRaCIS.Core.Domain/Trial/Enroll.cs new file mode 100644 index 0000000..4146a91 --- /dev/null +++ b/IRaCIS.Core.Domain/Trial/Enroll.cs @@ -0,0 +1,73 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Enroll")] + public partial class Enroll : Entity,IAuditUpdate,IAuditAdd + { + + [JsonIgnore] + [ForeignKey("TrialId")] + public virtual Trial Trial { get; set; } + [JsonIgnore] + public virtual Doctor Doctor { get; set; } + + //public TrialPaymentPrice TrialPaymentPrice { get; set; } + + public Guid DoctorId { get; set; } + public Guid TrialId { get; set; } + public Guid AttachmentId { get; set; } = Guid.Empty; + public EnrollStatus EnrollStatus { get; set; } + + public decimal? AdjustmentMultiple { get; set; } + public DateTime? EnrollTime { get; set; } + public DateTime? OutEnrollTime { get; set; } + + public string Memo { get; set; } = string.Empty; + + public int ReviewerReadingType { get; set; } = 0; + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + + public int? Training { get; set; } + + public int? RefresherTraining { get; set; } + + public int? Timepoint { get; set; } + + public int? Timepoint48H { get; set; } + + public int? Timepoint24H { get; set; } + + public int? Adjudication { get; set; } + + public int? Adjudication48H { get; set; } + + public int? Adjudication24H { get; set; } + + public int? Global { get; set; } + + + public int? Downtime { get; set; } + + + + /// + /// ˺ 뵽Ŀк ֵ + /// + public Guid? DoctorUserId { get; set; } + [JsonIgnore] + public User DoctorUser { get; set; } + + + [JsonIgnore] + public List EnrollReadingCategoryList { get; set; } + + } +} diff --git a/IRaCIS.Core.Domain/Trial/EnrollDetail.cs b/IRaCIS.Core.Domain/Trial/EnrollDetail.cs new file mode 100644 index 0000000..c4f46c2 --- /dev/null +++ b/IRaCIS.Core.Domain/Trial/EnrollDetail.cs @@ -0,0 +1,31 @@ +using IRaCIS.Core.Domain.Share; +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("EnrollDetail")] + public partial class EnrollDetail : Entity, IAuditAdd + { + [JsonIgnore] + public virtual TrialStatusDetail TrialDetail { get; set; } + + public Guid DoctorId { get; set; } + public Guid TrialId { get; set; } + public EnrollStatus EnrollStatus { get; set; } + public Guid? EnrollId { get; set; } + public string Memo { get; set; } = string.Empty; + public Guid CreateUserId { get; set; } + public int OptUserType { get; set; } + public DateTime CreateTime { get; set; } + + + public Guid TrialDetailId { get; set; } + + [JsonIgnore] + [ForeignKey("CreateUserId")] + public User CreateUser { get; set; } + [JsonIgnore] + public Doctor Doctor { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Trial/EnrollReadingCategory.cs b/IRaCIS.Core.Domain/Trial/EnrollReadingCategory.cs new file mode 100644 index 0000000..6cf112e --- /dev/null +++ b/IRaCIS.Core.Domain/Trial/EnrollReadingCategory.cs @@ -0,0 +1,46 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-07-08 10:43:53 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///EnrollReadingCategory + /// + [Table("EnrollReadingCategory")] + public class EnrollReadingCategory : Entity, IAuditAdd + { + /// + /// EnrollId + /// + public Guid EnrollId { get; set; } + + /// + /// ReadingCategory + /// + public ReadingCategory ReadingCategory { get; set; } + + + public Guid TrialReadingCriterionId { get; set; } + + /// + /// CreateUserId + /// + public Guid CreateUserId { get; set; } + + /// + /// CreateTime + /// + public DateTime CreateTime { get; set; } + + [JsonIgnore] + [ForeignKey("EnrollId")] + public Enroll Enroll { get; set; } + } + +} diff --git a/IRaCIS.Core.Domain/Trial/Trial.cs b/IRaCIS.Core.Domain/Trial/Trial.cs new file mode 100644 index 0000000..059d320 --- /dev/null +++ b/IRaCIS.Core.Domain/Trial/Trial.cs @@ -0,0 +1,483 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("Trial")] + public partial class Trial : Entity, IAuditUpdate, IAuditAdd, ISoftDelete + { + public Trial() + { + ClinicalTrialProjectDetails = new HashSet(); + TrialDicList = new List(); + } + + [JsonIgnore] + public List TaskConsistentRuleList { get; set; } + [JsonIgnore] + public List SubjectDoctorUserList { get; set; } = new List(); + [JsonIgnore] + public List VisitTaskList { get; set; } = new List() { }; + [JsonIgnore] + public List TrialSiteSurveyList { get; set; } = new List(); + + [JsonIgnore] + public List TrialDocumentList { get; set; } + [JsonIgnore] + public List EnrollList { get; set; } = new List(); + [JsonIgnore] + public List WorkloadList { get; set; } = new List(); + [JsonIgnore] + public List TrialUserList { get; set; } = new List(); + [JsonIgnore] + public List ReadingQuestionCriterionTrialList { get; set; } = new List(); + [JsonIgnore] + public List SubjectList { get; set; } = new List(); + [JsonIgnore] + public List StudyList { get; set; } = new List(); + [JsonIgnore] + public List TrialSiteList { get; set; } = new List(); + [JsonIgnore] + public List TrialSiteUserList { get; set; } = new List(); + + [JsonIgnore] + [ForeignKey("DeclarationTypeId")] + public Dictionary DeclarationType { get; set; } + + public Guid DeclarationTypeId { get; set; } = Guid.Empty; + + public Guid IndicationTypeId { get; set; } = Guid.Empty; + public Guid? PhaseId { get; set; } = Guid.Empty; + + [JsonIgnore] + [ForeignKey("IndicationTypeId")] + public Dictionary IndicationType { get; set; } + + [JsonIgnore] + [ForeignKey("PhaseId")] + public Dictionary Phase { get; set; } + + [JsonIgnore] + [ForeignKey("SponsorId")] + public Sponsor Sponsor { get; set; } + [JsonIgnore] + [ForeignKey("CROId")] + public CRO CRO { get; set; } + [JsonIgnore] + [ForeignKey("ReviewModeId")] + public Dictionary ReviewMode { get; set; } + + [JsonIgnore] + + public List clinicalDataTrialSets { get; set; } = new List { }; + [JsonIgnore] + public virtual ICollection ClinicalTrialProjectDetails { get; set; } + [JsonIgnore] + public virtual ICollection TrialDicList { get; set; } + + + + [StringLength(100)] + public string TrialCode { get; set; } = string.Empty; + + public int Code { get; set; } + + [StringLength(512)] + public string Indication { get; set; } = string.Empty; + + //һ״̬ + //[ConcurrencyCheck] + public int TrialEnrollStatus { get; set; } + + + //״̬ + [StringLength(500)] + public string TrialStatusStr { get; set; } = StaticData.TrialState.TrialInitializing; + + [JsonIgnore] + public List TrialStateChangeList { get; set; } = new List(); + + + + public Guid? CROId { get; set; } = Guid.Empty; + + public Guid? SponsorId { get; set; } = Guid.Empty; + public Guid? ReviewModeId { get; set; } = Guid.Empty; + + + [StringLength(500)] + public string ProjectCycle { get; set; } = string.Empty; + + + public int ExpectedPatients { get; set; } + + public decimal TimePointsPerPatient { get; set; } + + public int GRRReviewers { get; set; } + + public int TotalReviewers { get; set; } + + [StringLength(500)] + public string ReviewProtocol { get; set; } = string.Empty; + + [StringLength(500)] + public string MessageFromClient { get; set; } = string.Empty; + + public string Note { get; set; } = string.Empty; + + + public string ReviewProtocolName { get; set; } = string.Empty; + public string MessageFromClientName { get; set; } = string.Empty; + public int Expedited { get; set; } + + public DateTime CreateTime { get; set; } + + public Guid CreateUserId { get; set; } + + public DateTime UpdateTime { get; set; } + + public Guid UpdateUserId { get; set; } + public AttendedReviewerType AttendedReviewerType { get; set; } = AttendedReviewerType.CN;//0ȫйҽ 1ҽ 2йҽҲҽ + + public bool VisitPlanConfirmed { get; set; } + + + /// + /// ߱Ź + /// + public bool IsNoticeSubjectCodeRule { get; set; } = false; + + /// + /// ߱Ÿʽ + /// + public string SubjectCodeRule { get; set; } = "5λɣǰ2λΪıţ3λΪ˳ţEDC¼ıűһ"; + + + /// + /// ߵڶ + /// + public bool IsSubjectSecondCodeView { get; set; } = false; + + + /// + /// ɨУ + /// + public bool IsVerifyVisitImageDate { get; set; } = false; + + /// + ///λ׼ Ƿ л׼ʱ䣨״θҩʱ䣩 + /// + public bool IsHaveFirstGiveMedicineDate { get; set; } = false; + + /// + /// Ƿ + /// + public bool IsHaveSubjectAge { get; set; } = true; + + public bool IsSubjectSexView { get; set; } = true; + + /// + /// ƻĩμ + /// + public string OutEnrollmentVisitName { get; set; } = "EOT"; + + /// + /// ӰĿ + /// + public bool IsImageReplicationAcrossTrial { get; set; } = false; + + + + public string BodyPartTypes { get; set; } = "ʲ|Բ||ز|/¸|ǻ|ȫ|"; + + public string Modalitys { get; set; } = "CT|MRI|BoneScan|Photograph|PET|X-ray|US"; + + [NotMapped] + public List ModalityList => Modalitys.Split('|', StringSplitOptions.RemoveEmptyEntries).Where(t => !string.IsNullOrEmpty(t)).ToList(); + + + public int ChangeDefalutDays { get; set; } = 5; + + + + + + + //Ƭʽ 1 ҽѧӰ 2 Ƶ 3 ĵ + public int ReadingMode { get; set; } = 1; + + /// + /// ٴϢ 1ϵͳ¼ 2ϵͳ¼+PDF 0 + /// + public int ClinicalInformationTransmissionEnum { get; set; } = 1; + + /// + /// QC 0 1 2˫ + /// + public TrialQCProcess QCProcessEnum { get; set; } = TrialQCProcess.DoubleAudit; + + /// + /// ӰһԺ˲ + /// + public bool IsImageConsistencyVerification { get; set; } = true; + + + + + + + + + /// + /// Ƿ ȷ + /// + public bool IsEnrollementQualificationConfirm { get; set; } = false; + + //PD չǷʾ ü Ƿʾ PDչ (Ӷ״̬) + public bool IsPDProgressView { get; set; } = false; + + + //ĿӼ + public bool IsUrgent { get; set; } = false; + + + //Suject Editҳ Ƿʾ Ӽ + public bool IsSubjectExpeditedView { get; set; } = false; + + + + + + + + + /// + /// Ƿ ٴϢ + /// + public bool IsCRAAuditClinicalInformation { get; set; } = false; + + /// + /// Ӱ񵼳 + /// + public bool IsImageExport { get; set; } = false; + + + public string PreliminaryAuditReuploadText { get; set; } = string.Empty; + + public string ReviewAuditReuploadText { get; set; } = string.Empty; + + + + + //о + public string ResearchProgramNo { get; set; } + + //ʵ + public string ExperimentName { get; set; } + + //еλ + public string MainResearchUnit { get; set; } + + // PI + public string HeadPI { get; set; } + + + /// + /// Ŀ 1 ʽĿ0 ʽĿ 2ѵĿ + /// time + public TrialType TrialType { get; set; } + + //public string TempCode { get; set; } + + + public int PlanSiteCount { get; set; } + + public int PlanVisitCount { get; set; } + + + + public DateTime? TrialFinishedTime { get; set; } + + + + + + public bool IsTrialStart { get; set; } = false; + public bool IsDeleted { get; set; } + + + //QC + + public User QCQuestionConfirmedUser { get; set; } + public Guid? QCQuestionConfirmedUserId { get; set; } + public DateTime? QCQuestionConfirmedTime { get; set; } + + /// + /// Ŀʱ + /// + public DateTime? TrialFinishTime { get; set; } + + + public int? DigitPlaces { get; set; } = 1; + + + public bool IsTrialProcessConfirmed { get; set; } + public bool IsTrialBasicLogicConfirmed { get; set; } + public bool IsTrialUrgentConfirmed { get; set; } + + public bool IsQCQuestionConfirmed { get; set; } + + + public DateTime? DeletedTime { get; set; } + public Guid? DeleteUserId { get; set; } + + /// + /// ͬٴʱ + /// + public DateTime? SyncClinicalDataTime { get; set; } + + + public string BlindBaseLineName { get; set; } = "Baseline"; + + public string BlindFollowUpPrefix { get; set; } = "Follow-up"; + + #region ʼ + /// + /// ˺ + /// + public string EmailFromEmail { get; set; } = string.Empty; + + /// + /// + /// + public string EmailFromName { get; set; } = string.Empty; + + /// + /// /Ȩ + /// + public string EmailAuthorizationCode { get; set; } = string.Empty; + + /// + /// SMTP + /// + public string EmailSMTPServerAddress { get; set; } = string.Empty; + + /// + /// SMTP˿ + /// + public int? EmailSMTPServerPort { get; set; } + + /// + /// Ƿù + /// + public bool IsConfigureEmail { get; set; } = false; + #endregion + + ///// + ///// ͼǷбע + ///// + //public bool IsImageIabeled { get; set; } + + + + ////Ƭ + //public ReadingMethod ReadingType { get; set; } = ReadingMethod.Double; + + + //public bool IsGlobalReading { get; set; } = true; + + ///// + ///// ٲƬ + ///// + //public bool? IsArbitrationReading { get; set; } = true; + + //public bool IsClinicalReading { get; set; } + + + ///// + ///// 1 Mint2 PACS + ///// + + //public int ImagePlatform { get; set; } = 1; + + + ///// + ///// ٲù + ///// + //public ArbitrationRule ArbitrationRule { get; set; } = ArbitrationRule.None; + + //// + //public TaskAllocateObj TaskAllocateObjEnum { get; set; } + + ////Զ + //public bool IsFollowVisitAutoAssign { get; set; } = true; + + ////ȫԶ + //public bool IsFollowGlobalVisitAutoAssign { get; set; } = true; + + //public bool IsFollowJudgeTaskAutoAssign { get; set; } = true; + + //public TaskAllocateDefaultState FollowJudgeTaskAutoAssignDefaultState { get; set; } = TaskAllocateDefaultState.Allocated; + + ////ԶĬ״̬ + //public TaskAllocateDefaultState FollowVisitAutoAssignDefaultState { get; set; } = TaskAllocateDefaultState.Allocated; + + ////ȫԶĬ״̬ + //public TaskAllocateDefaultState FollowGlobalVisitAutoAssignDefaultState { get; set; } = TaskAllocateDefaultState.Allocated; + + ////Ƭʾ + + //public ReadingTaskViewMethod ReadingTaskViewEnum { get; set; } + + ////ƬʾǷ˳ + //public bool IsReadingTaskViewInOrder { get; set; } = true; + + ///// + ///// ƬǷʾϢ + ///// + //public bool IsReadingShowSubjectInfo { get; set; } = false; + + ///// + ///// ƬǷʾ + ///// + //public bool IsReadingShowPreviousResults { get; set; } = false; + + + + + ///// + ///// ȷҽѧ + ///// + + //public bool IsConfirmMedicineQuestion { get; set; } = false; + + + + + ///// + ///// ƬϢǩʱ + ///// + //public DateTime? ReadingInfoSignTime { get; set; } + + ///// + ///// Ƭ + ///// + //public ReadingTool? ReadingTool { get; set; } + + //public Guid? ReviewTypeId { get; set; } = Guid.Empty; + + //[ForeignKey("ReviewTypeId")] + //public Dictionary ReviewType { get; set; } + + + //public Guid? QCSecondConfirmedUserId { get; set; } + //public DateTime? QCSecondConfirmedTime { get; set; } + + //public int QCQuestionConfirmState { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Trial/TrialDictionary.cs b/IRaCIS.Core.Domain/Trial/TrialDictionary.cs new file mode 100644 index 0000000..2c87c9c --- /dev/null +++ b/IRaCIS.Core.Domain/Trial/TrialDictionary.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("TrialDictionary")] + public partial class TrialDictionary : Entity + { + //public Guid Id { get; set; } + [JsonIgnore] + public virtual Trial Trial { get; set; } + [JsonIgnore] + public virtual Dictionary Dictionary { get; set; } + + [StringLength(50)] + public string KeyName { get; set; } = string.Empty; + + public Guid TrialId { get; set; } + + public Guid DictionaryId { get; set; } + } +} diff --git a/IRaCIS.Core.Domain/Trial/TrialSign.cs b/IRaCIS.Core.Domain/Trial/TrialSign.cs new file mode 100644 index 0000000..e0156cd --- /dev/null +++ b/IRaCIS.Core.Domain/Trial/TrialSign.cs @@ -0,0 +1,54 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-02-16 16:23:57 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using IRaCIS.Core.Domain.Share; +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialSign + /// + [Table("TrialSign")] + public class TrialSign : Entity, IAuditAdd + { + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// TrialId + /// + public Guid? TrialId { get; set; } + + /// + /// SubjectVisitId + /// + public Guid? SubjectVisitId { get; set; } + + + //关联基础数据 + public Guid? SignCodeId { get; set; } + + public string SignCode { get; set; } + + public string SignText { get; set; } + + public bool IsCompleted { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Trial/TrialStateChange.cs b/IRaCIS.Core.Domain/Trial/TrialStateChange.cs new file mode 100644 index 0000000..a7a0cbb --- /dev/null +++ b/IRaCIS.Core.Domain/Trial/TrialStateChange.cs @@ -0,0 +1,43 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-02-25 14:21:48 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialStateChange + /// + [Table("TrialStateChange")] + public class TrialStateChange : Entity, IAuditAdd + { + [JsonIgnore] + [ForeignKey("TrialId")] + public Trial Trial { get; set; } + + public Guid TrialId { get; set; } + + + public string OriginState { get; set; } = String.Empty; + + + public string NowState { get; set; } = String.Empty; + + + public string Reason { get; set; }=String.Empty; + + + public DateTime CreateTime { get; set; } + + + public Guid CreateUserId { get; set; } + + [JsonIgnore] + [ForeignKey("CreateUserId")] + public User User { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Trial/TrialStatusDetail.cs b/IRaCIS.Core.Domain/Trial/TrialStatusDetail.cs new file mode 100644 index 0000000..d90d797 --- /dev/null +++ b/IRaCIS.Core.Domain/Trial/TrialStatusDetail.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("TrialStatus")] + public partial class TrialStatusDetail : Entity, IAuditAdd + { + public virtual ICollection IntoGroupDetails { get; set; } + public TrialStatusDetail() + { + IntoGroupDetails = new HashSet(); + } + [JsonIgnore] + public Trial Trial { get; set; } + + public Guid TrialId { get; set; } + + + + public int TrialStatus { get; set; } + + [StringLength(100)] + public string Memo { get; set; } = string.Empty; + + public int OptUserType { get; set; } + + public Guid CreateUserId { get; set; } + + public DateTime CreateTime { get; set; } + + + } +} diff --git a/IRaCIS.Core.Domain/TrialSiteUser/TrialAudit.cs b/IRaCIS.Core.Domain/TrialSiteUser/TrialAudit.cs new file mode 100644 index 0000000..cba781e --- /dev/null +++ b/IRaCIS.Core.Domain/TrialSiteUser/TrialAudit.cs @@ -0,0 +1,29 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + public class TrialAudit:Entity + { + public int AuditType { get; set; } + + public Guid TrialId { get; set; } + + public Guid StudyId { get; set; } = Guid.Empty; + + public Guid? SubjectId { get; set; } + + public Guid OptUserId { get; set; } + + public string OptUser { get; set; } + + public DateTime OptTime { get; set; }=DateTime.Now; + public string Note { get; set; } + + public string Detail { get; set; } + + [ForeignKey("SubjectId")] + public Subject Subject { get; set; } + public Trial Trial { get; set; } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/TrialSiteUser/TrialExternalUser.cs b/IRaCIS.Core.Domain/TrialSiteUser/TrialExternalUser.cs new file mode 100644 index 0000000..297b372 --- /dev/null +++ b/IRaCIS.Core.Domain/TrialSiteUser/TrialExternalUser.cs @@ -0,0 +1,116 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-04 13:33:37 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using IRaCIS.Core.Domain.Share; +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialExternalUser + /// + [Table("TrialExternalUser")] + public class TrialExternalUser : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + public Trial Trial { get; set; } + + /// + /// TrialId + /// + [Required] + public Guid TrialId { get; set; } + + + public Guid UserTypeId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + /// + /// Phone + /// + [Required] + public string Phone { get; set; } = String.Empty; + + /// + /// Email + /// + [Required] + public string Email { get; set; } + + /// + /// FirstName + /// + [Required] + public string FirstName { get; set; } + + /// + /// LastName + /// + [Required] + public string LastName { get; set; } + + + + ///// + ///// 是否存在系统用户表中 + ///// + //[Required] + //public bool IsExist { get; set; } + + + public string OrganizationName { get; set; }=String.Empty; + + + public bool IsSystemUser{ get; set; } + + + public Guid SystemUserId { get; set; } + + + + public bool IsJoin { get; set; } + + + + + public DateTime? ExpireTime { get; set; } + + public DateTime? ConfirmTime { get; set; } + + public string RejectReason { get; set; } + + /// + /// 邀请状态 + /// + [Required] + public TrialExternalUserStateEnum InviteState { get; set; } = TrialExternalUserStateEnum.WaitSent; + + } + +} diff --git a/IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs b/IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs new file mode 100644 index 0000000..03f12b2 --- /dev/null +++ b/IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + public class TrialSite : Entity, IAuditAdd, IAuditUpdate,ISoftDelete + { + + public Guid TrialId { get; set; } + + public Guid SiteId { get; set; } + + public string TrialSiteCode { get; set; } = String.Empty; + + public string TrialSiteAliasName { get; set; }=String.Empty; + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public bool IsDeleted { get; set; } + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + [JsonIgnore] + //导航属性 + [ForeignKey("SiteId")] + public Site Site { get; set; } + [JsonIgnore] + [ForeignKey("TrialId")] + public Trial Trial { get; set; } + + + + /// + /// Site 下面有多个检查批次记录 + /// + [JsonIgnore] + public List SubjectVisitList { get; set; } + [JsonIgnore] + public List TrialSiteSurveyList { get; set; } + + + //Site 由多个人负责 + [JsonIgnore] + public List CRCUserList { get; set; } + + [JsonIgnore] + public List SubjectList { get; set; } + [JsonIgnore] + public List StudyList { get; set; } + [JsonIgnore] + public List NoneDicomStudyList { get; set; } + + [JsonIgnore] + public List StudyMonitorList { get; set; } + + [JsonIgnore] + public List ReadingPeriodSites { get; set; } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteUser.cs b/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteUser.cs new file mode 100644 index 0000000..adee90f --- /dev/null +++ b/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteUser.cs @@ -0,0 +1,69 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-11-23 15:40:27 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///UserTrialSite + /// + [Table("TrialSiteUser")] + public class TrialSiteUser : Entity, IAuditUpdate, IAuditAdd,ISoftDelete + { + + [Required] + public Guid UserId { get; set; } + + + [Required] + public Guid TrialId { get; set; } + + + [Required] + public Guid SiteId { get; set; } + + public DateTime UpdateTime { get; set; } + + + public DateTime CreateTime { get; set; } + + public Guid CreateUserId { get; set; } + + public Guid UpdateUserId { get; set; } + + + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + [JsonIgnore] + [ForeignKey("UserId")] + public User User { get; set; } + [JsonIgnore] + [ForeignKey("TrialId")] + public Trial Trial { get; set; } + [JsonIgnore] + [ForeignKey("SiteId")] + public Site Site { get; set; } + + + + [JsonIgnore] + public TrialSite TrialSite { get; set; } + [JsonIgnore] + public TrialUser TrialUser { get; set; } + + + } + + + +} diff --git a/IRaCIS.Core.Domain/TrialSiteUser/TrialUser.cs b/IRaCIS.Core.Domain/TrialSiteUser/TrialUser.cs new file mode 100644 index 0000000..d1c5de4 --- /dev/null +++ b/IRaCIS.Core.Domain/TrialSiteUser/TrialUser.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using IRaCIS.Core.Domain.Share; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// άԱĿϵ - ʵ + /// + [Table("TrialUser")] + public partial class TrialUser : Entity, IAuditUpdate, IAuditAdd ,ISoftDelete + { + public Guid UserId { get; set; } + public Guid TrialId { get; set; } + [JsonIgnore] + public Trial Trial { get; set; } + [JsonIgnore] + [ForeignKey("UserId")] + public User User { get; set; } + + [JsonIgnore] + public List SiteList { get; set; } + + + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + + public DateTime? RemoveTime { get; set; } + + public DateTime? JoinTime { get; set; } + + + + } +} diff --git a/IRaCIS.Core.Domain/TrialSiteUser/TrialUserPreparation .cs b/IRaCIS.Core.Domain/TrialSiteUser/TrialUserPreparation .cs new file mode 100644 index 0000000..71dab10 --- /dev/null +++ b/IRaCIS.Core.Domain/TrialSiteUser/TrialUserPreparation .cs @@ -0,0 +1,74 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-03-24 13:22:08 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialUserPreparation + /// + [Table("TrialUserPreparation ")] + public class TrialUserPreparation : Entity, IAuditUpdate, IAuditAdd + { + + + + /// + /// UserId + /// + [Required] + public Guid UserId { get; set; } + + /// + /// TrialId + /// + [Required] + public Guid TrialId { get; set; } + + /// + /// UpdateTime + /// + [Required] + public DateTime UpdateTime { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// UpdateUserId + /// + [Required] + public Guid UpdateUserId { get; set; } + + + public DateTime? ExpireTime { get; set; } + + + public bool? IsJoin { get; set; } + + public DateTime? JoinTime { get; set; } + + public string RejectReason { get; set; } + + [JsonIgnore] + public User User { get; set; } + [JsonIgnore] + public Trial Trial { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Visit/Subject.cs b/IRaCIS.Core.Domain/Visit/Subject.cs new file mode 100644 index 0000000..4a64345 --- /dev/null +++ b/IRaCIS.Core.Domain/Visit/Subject.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 患者 + /// + [Table("Subject")] + public class Subject : Entity, IAuditAdd, IAuditUpdate, ISoftDelete + { + [JsonIgnore] + public List SubjectVisitList { get; set; } = new List(); + [JsonIgnore] + public List SubjectDoctorList { get; set; } = new List(); + [JsonIgnore] + public List SubjectVisitTaskList { get; set; } = new List(); + [JsonIgnore] + public List ReadModuleList { get; set; } + [JsonIgnore] + public List SubjectCanceDoctorList { get; set; } + + [JsonIgnore] + public List ClinicalDataList { get; set; } + + + + [JsonIgnore] + [ForeignKey("FinalSubjectVisitId")] + public SubjectVisit FinalSubjectVisit { get; set; } + public Guid? FinalSubjectVisitId { get; set; } + + + //需要配置是两个键连接 + [JsonIgnore] + public TrialSite TrialSite { get; set; } + + [JsonIgnore] + [ForeignKey("TrialId")] + public Trial Trial { get; set; } + + [JsonIgnore] + [ForeignKey("SiteId")] + public Site Site { get; set; } + [JsonIgnore] + public List StudyList { get; set; } = new List(); + + public string Code { get; set; } + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public int? Age { get; set; } + public string Sex { get; set; } = string.Empty; + public Guid SiteId { get; set; } = Guid.Empty; + + [JsonIgnore] + [ForeignKey("LatestSubjectVisitId")] + public SubjectVisit LatestSubjectVisit { get; set; } + public Guid? LatestSubjectVisitId { get; set; } + + //public bool IsMissingImages { get; set; } = false; + + + public Guid TrialId { get; set; } = Guid.Empty; + public string MedicalNo { get; set; } = string.Empty; + + public SubjectStatus Status { get; set; } = SubjectStatus.OnVisit;//1 检查批次中,2 出组 3 检查批次结束 + public string Reason { get; set; } = string.Empty; + public bool IsEnrollment { get; set; } + + + public DateTime? OutEnrollmentTime { get; set; } + + public DateTime? VisitOverTime { get; set; } + + + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public Guid UpdateUserId { get; set; } + + + public string ShortName { get; set; } = String.Empty; + + public string Height { get; set; } = String.Empty; + + public string Weight { get; set; } = String.Empty; + + public DateTime? BirthDate { get; set; } + public DateTime? SignDate { get; set; } + + public int StudyCount { get; set; } = 0; + public string Modalities { get; set; } = string.Empty; + + public DateTime? FirstGiveMedicineTime { get; set; } + + public bool IsUrgent { get; set; } + + + public bool IsDeleted { get; set; } + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + public bool IsReReadingOrBackInfluenceAnalysis { get; set; } + + + //是否分配了读片医生 + //public bool IsAssignDoctorUser{get;set;} + } +} diff --git a/IRaCIS.Core.Domain/Visit/SubjectCanceDoctor.cs b/IRaCIS.Core.Domain/Visit/SubjectCanceDoctor.cs new file mode 100644 index 0000000..7be5054 --- /dev/null +++ b/IRaCIS.Core.Domain/Visit/SubjectCanceDoctor.cs @@ -0,0 +1,31 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2022-07-29 10:43:49 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SubjectCanceDoctor + /// + [Table("SubjectCanceDoctor")] + public class SubjectCanceDoctor : Entity, IAuditAdd + { + + public Subject Subject { get; set; } + public Guid CreateUserId { get; set; } + + public DateTime CreateTime { get; set; } + + + public Guid SubjectId { get; set; } + + public string Note { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Visit/SubjectVisit.cs b/IRaCIS.Core.Domain/Visit/SubjectVisit.cs new file mode 100644 index 0000000..cf320e0 --- /dev/null +++ b/IRaCIS.Core.Domain/Visit/SubjectVisit.cs @@ -0,0 +1,238 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using IRaCIS.Core.Domain.Share; +using Microsoft.EntityFrameworkCore.Metadata.Internal; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("SubjectVisit")] + public class SubjectVisit : Entity, IAuditUpdate, IAuditAdd, ISoftDelete + { + //一个检查批次 对应有对应Site的 TrialSiteCode 所以 fluentApi中配置 TrialSite 连表用TrialId SiteId 双字段 + [JsonIgnore] + public TrialSite TrialSite { get; set; } + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + public Guid SiteId { get; set; } + + [JsonIgnore] + public VisitStage VisitStage { get; set; } + public Guid? VisitStageId { get; set; } + public int VisitDay { get; set; } + public string VisitName { get; set; } = string.Empty; + public int VisitWindowLeft { get; set; } + public int VisitWindowRight { get; set; } + + [Column(TypeName = "decimal(18,1)")] + public decimal VisitNum { get; set; } + //public string AnonymousVisitName { get; set; } = string.Empty; + public string BlindName { get; set; } = string.Empty; + + + + public string SVUPDES { get; set; } = string.Empty; + public DateTime? SVSTDTC { get; set; } + public DateTime? SVENDTC { get; set; } + public bool InPlan { get; set; } = true; + public bool IsBaseLine { get; set; } = false; + + + //0 未执行 1 执行了 2 不可用 + public VisitExecutedEnum VisitExecuted { get; set; } = VisitExecutedEnum.UnExecuted; + + public DateTime? EarliestScanDate { get; set; } + + public DateTime? LatestScanDate { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public Guid? Auditor { get; set; } + + + //public SubjectVisitStateEnum VisitState { get; set; } + + //核查状态 + public CheckStateEnum CheckState { get; set; } + + //提交状态 + public SubmitStateEnum SubmitState { get; set; } + //审核状态 + public AuditStateEnum AuditState { get; set; } + public ForwardStateEnum ForwardState { get; set; } + + /// + /// 单审通过人 + /// + public Guid? PreliminaryAuditUserId { get; set; } + + /// + /// 双审通过人 + /// + public Guid? ReviewAuditUserId { get; set; } + + /// + /// 一致性核查人Id + /// + public Guid? CheckUserId { get; set; } + + public DateTime? ReviewAuditTime { get; set; } + public DateTime? PreliminaryAuditTime { get; set; } + public Guid? ForwardUserId { get; set; } + public DateTime? ForwardTime { get; set; } + public Guid? CurrentActionUserId { get; set; } + public DateTime? CurrentActionUserExpireTime { get; set; } + + public DateTime? SubmitTime { get; set; } + public DateTime? CheckTime { get; set; } + + /// + /// 通过原因 + /// + public string ManualPassReason { get; set; } + + public bool IsUrgent { get; set; } + + public bool IsTake { get; set; } + + public bool IsFinalVisit { get; set; } + + public ChallengeStateEnum ChallengeState { get; set; } + + public string CheckResult { get; set; } = String.Empty; + + //是否一致性核查回退 + public bool? IsCheckBack { get; set; } + + public DateTime? CheckBackTime { get; set; } + + public CheckChanllengeTypeEnum CheckChallengeState { get; set; } + public PDStateEnum PDState { get; set; } = PDStateEnum.None; + + //public bool IsOutEnromentVisit { get; set; } = false; + + public DateTime? CheckPassedTime { get; set; } + + /// + /// 上一检查批次 + /// + public Guid? OutPlanPreviousVisitId { get; set; } + + [JsonIgnore] + [ForeignKey("OutPlanPreviousVisitId")] + public SubjectVisit OutPlanPreviousVisit { get; set; } + + //public Guid? ClinicalDataSignUserId { get; set; } + + //public DateTime? ClinicalDataSignTime { get; set; } + + [JsonIgnore] + [ForeignKey("ClinicalDataSignUserId")] + public User ClinicalDataSignUser { get; set; } + + [ForeignKey("ForwardUserId")] + public User ForwardUser { get; set; } + + + [JsonIgnore] + [ForeignKey("PreliminaryAuditUserId")] + public User PreliminaryAuditUser { get; set; } + + [JsonIgnore] + [ForeignKey("ReviewAuditUserId")] + public User ReviewAuditUser { get; set; } + + [JsonIgnore] + [ForeignKey("CurrentActionUserId")] + public User CurrentActionUser { get; set; } + + public RequestBackStateEnum RequestBackState { get; set; } + + + public bool IsQCConfirmedReupload { get; set; } + + public bool IsLostVisit { get; set; } + + + //是否确认了、签名了 临床数据完整性 + public bool? IsConfirmedClinicalData { get; set; } + + + public bool IsEnrollmentConfirm { get; set; } + + public bool IsVisitTaskGenerated { get; set; } + + public bool IsPMBackOrReReading { get; set; } + + /// + /// 关闭一致性质疑原因 + /// + public string CloseTheReason { get; set; } + + + public bool IsDeleted { get; set; } + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + + public Guid? SubmitUserId { get; set; } + + [JsonIgnore] + [ForeignKey("SubmitUserId")] + public User SubmitUser { get; set; } + + //导航属性 + [JsonIgnore] + [ForeignKey("TrialId")] + public Trial Trial { get; set; } + + [JsonIgnore] + [ForeignKey("SiteId")] + public Site Site { get; set; } + + [JsonIgnore] + [ForeignKey("SubjectId")] + public Subject Subject { get; set; } + + + //// 一个检查批次可以被多个参与者 查看 + //public List TrialUsers { get; set; } + + ////一个检查批次 对应该Site下的多个IC管理 必须加这个 不然生成的sql 会报 TrialSiteUserId 不存在该列名 + + //public List TrialSiteUserList { get; set; } + [JsonIgnore] + public List PreviousHistoryList { get; set; } + [JsonIgnore] + public List PreviousOtherList { get; set; } + [JsonIgnore] + public List PreviousSurgeryList { get; set; } + [JsonIgnore] + public List PreviousPDFList { get; set; } + [JsonIgnore] + public List ReadingClinicalDataList { get; set; } + [JsonIgnore] + public List CheckChallengeDialogList { get; set; } = new List(); + [JsonIgnore] + public List StudyList { get; set; } = new List(); + [JsonIgnore] + public List NoneDicomStudyList { get; set; } = new List(); + + [JsonIgnore] + public List QCChallengeList { get; set; } = new List(); + [JsonIgnore] + public List QCChallengeDialogList { get; set; } = new List(); + [JsonIgnore] + public List VisitTaskList { get; set; } = new List(); + + + + public ReadingStatusEnum ReadingStatus { get; set; } + + + } +} diff --git a/IRaCIS.Core.Domain/Visit/VisitPlanInfluenceStat.cs b/IRaCIS.Core.Domain/Visit/VisitPlanInfluenceStat.cs new file mode 100644 index 0000000..286717b --- /dev/null +++ b/IRaCIS.Core.Domain/Visit/VisitPlanInfluenceStat.cs @@ -0,0 +1,47 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-23 15:37:48 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///VisitPlanInfluenceStudystat + /// + [Table("VisitPlanInfluenceStat")] + public class VisitPlanInfluenceStat : Entity, IAuditAdd + { + [JsonIgnore] + public List InfluenceStudyList { get; set; }=new List(); + + public Guid TrialId { get; set; } + + /// + /// CreateTime + /// + [Required] + public DateTime CreateTime { get; set; } + + /// + /// CreateUserId + /// + [Required] + public Guid CreateUserId { get; set; } + + /// + /// InfluenceCount + /// + [Required] + public int InconsistentCount { get; set; } + + [JsonIgnore] + [ForeignKey("CreateUserId")] + public User CreateUser { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Visit/VisitPlanInfluenceStudy.cs b/IRaCIS.Core.Domain/Visit/VisitPlanInfluenceStudy.cs new file mode 100644 index 0000000..05e7bd0 --- /dev/null +++ b/IRaCIS.Core.Domain/Visit/VisitPlanInfluenceStudy.cs @@ -0,0 +1,50 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2021-12-20 16:42:26 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///VisitPlanInfluenceSubjectVisit + /// + [Table("VisitPlanInfluenceStudy")] + public class VisitPlanInfluenceStudy : Entity, IAuditAdd + { + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } + public Guid SubjectVisitId { get; set; } + + public Guid StudyId { get; set; } + + public Guid TrialId { get; set; } + + public bool IsDicomStudy { get; set; } + + public string Modality { get; set; } + + public bool IsOverWindowNowNotOverWindow { get; set; } + + public DateTime? StudyTime { get; set; } + + public string HistoryWindow { get; set; } + + public string NowWindow { get; set; } + public DateTime CreateTime { get; set; } + + public Guid CreateUserId { get; set; } + + [JsonIgnore] + [ForeignKey("CreateUserId")] + public User CreateUser { get; set; } + [JsonIgnore] + [ForeignKey("VisitPlanInfluenceStatId")] + public VisitPlanInfluenceStat VisitPlanInfluenceStat { get; set; } + + public Guid VisitPlanInfluenceStatId { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Visit/VisitStage.cs b/IRaCIS.Core.Domain/Visit/VisitStage.cs new file mode 100644 index 0000000..2504f9c --- /dev/null +++ b/IRaCIS.Core.Domain/Visit/VisitStage.cs @@ -0,0 +1,47 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("VisitStage")] + public class VisitStage : Entity, IAuditUpdate, IAuditAdd,ISoftDelete + { + [JsonIgnore] + public Trial Trial { get; set; } + public Guid TrialId { get; set; } + + public string BlindName { get; set; } = string.Empty; + + + + [Column(TypeName = "decimal(18,1)")] + public decimal VisitNum { get; set; } + public string VisitName { get; set; } = string.Empty; + public int VisitDay { get; set; } + public string Description { get; set; } = string.Empty; + + public bool IsConfirmed { get; set; }=false; + + public bool NeedGlobal { get; set; } = false; + + public bool IsBaseLine { get; set; } = false; + + + public int VisitWindowLeft { get; set; } + public int VisitWindowRight { get; set; } + + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public bool IsHaveFirstConfirmed { get; set; } + + public bool IsDeleted { get; set; } + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + } +} diff --git a/IRaCIS.Core.Domain/_Config/_AppSettings.cs b/IRaCIS.Core.Domain/_Config/_AppSettings.cs new file mode 100644 index 0000000..a2d329b --- /dev/null +++ b/IRaCIS.Core.Domain/_Config/_AppSettings.cs @@ -0,0 +1,134 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Json; + +namespace IRaCIS.Core.Domain.Share +{ + /// + /// 多环境 配置环境实体 + /// + public class ServiceVerifyConfigOption + { + public bool OpenUserComplexPassword { get; set; } + + public bool OpenSignDocumentBeforeWork { get; set; } + + public bool OpenTrialRelationDelete { get; set; } + + public bool OpenLoginLimit { get; set; } + } + + public class SystemEmailSendConfig + { + public int Port { get; set; } + + public string Host { get; set; } + + + public string FromEmail { get; set; } + + public string FromName { get; set; } + + public string AuthorizationCode { get; set; } + } + + + /// + /// 项目基础配置规则 + /// + public class AppSettings + { + public static string DoctorCodePrefix { get; set; } + public static string UserCodePrefix { get; set; } + + + public static string QCChallengeCodePrefix { get; set; } + + public static string DicomStudyCodePrefix { get; set; } + + public static string NoneDicomStudyCodePrefix { get; set; } + + public static string DefaultInternalOrganizationName { get; set; } + + public static int ImageShareExpireDays { get; set; } = 7; + + + public static string SystemSiteCodePrefix { get; set; } + + public static string BlindTaskPrefix { get; set; } + + /// + /// 用户默认密码 + /// + public static readonly string DefaultPassword = "123456"; + + + static AppSettings() + { + var configuration = new ConfigurationBuilder() + .Add(new JsonConfigurationSource + { + Path = "appsettings.json", + ReloadOnChange = true + }) + .Build(); + + DoctorCodePrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("DoctorCodePrefix"); + UserCodePrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("UserCodePrefix"); + QCChallengeCodePrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("QCChallengeCodePrefix"); + NoneDicomStudyCodePrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("NoneDicomStudyCodePrefix"); + DicomStudyCodePrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("DicomStudyCodePrefix"); + DefaultPassword= configuration.GetSection("IRaCISBasicConfig").GetValue("DefaultPassword"); + SystemSiteCodePrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("SystemSiteCodePrefix"); + + DefaultInternalOrganizationName = configuration.GetSection("IRaCISBasicConfig").GetValue("DefaultInternalOrganizationName"); + + ImageShareExpireDays = configuration.GetSection("IRaCISBasicConfig").GetValue("ImageShareExpireDays"); + + BlindTaskPrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("BlindTaskPrefix"); + + } + + + //获取实体编码字符串 + public static string GetCodeStr(int codeInt ,string typeStr) + { + switch (typeStr) + { + case nameof(Doctor): + + return DoctorCodePrefix + codeInt.ToString("D4"); + + case nameof(User): + + return UserCodePrefix + codeInt.ToString("D4"); + + case nameof(QCChallenge): + + return QCChallengeCodePrefix+ codeInt.ToString("D5"); + + case nameof(NoneDicomStudy): + + return NoneDicomStudyCodePrefix + codeInt.ToString("D5"); + + case nameof(DicomStudy): + + return DicomStudyCodePrefix + codeInt.ToString("D5"); + + case nameof(VisitTask): + + return "W" + codeInt.ToString("D5"); + + case nameof(Site): + + return SystemSiteCodePrefix + codeInt.ToString("D4"); + + default: + return string.Empty; + } + } + + } + + +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/_Config/_StaticData.cs b/IRaCIS.Core.Domain/_Config/_StaticData.cs new file mode 100644 index 0000000..94b3ef3 --- /dev/null +++ b/IRaCIS.Core.Domain/_Config/_StaticData.cs @@ -0,0 +1,196 @@ +namespace IRaCIS.Core.Domain.Share; + +public static class StaticData +{ + + + + #region 字典表项固定值 + public static readonly string Title = "Title"; + public static readonly string ReadingType = "ReadingType"; + public static readonly string Subspeciality = "Subspeciality"; + + public static readonly string Modality = "Modality"; + public static readonly string Criterion = "Criterion"; + public static readonly string ReviewType = "ReviewType"; + public static readonly string ReadingStandard = "ReadingStandard"; + #endregion + + + public static class Folder + { + public static readonly string IRaCISDataFolder = "EIImageViewerData"; + + public static readonly string TrialDataFolder = "TrialData"; + + public static readonly string SystemDataFolder = "SystemData"; + + + public static readonly string SignDocumentFolder = "SignDocument"; + + public static readonly string DataTemplate = "DataTemplate"; + + public static readonly string NoticeAttachment = "NoticeAttachment"; + + public static readonly string DicomFolder = "Dicom"; + public static readonly string NoneDicomFolder = "NoneDicom"; + public static readonly string TreatmenthistoryFolder = "Treatmenthistory"; + + public static readonly string Reading = "Reading"; + + public static readonly string MedicalReview = "MedicalReview"; + + public static readonly string JudgeTask = "JudgeTask"; + + public static readonly string ReadingAnswer = "ReadingAnswer"; + + + public static readonly string UploadEDCData = "UploadEDCData"; + public static readonly string UploadFileFolder = "UploadFile"; + } + + + + public static class TrialOpt + { + //默认 + public static readonly string AfterStopCannNotOpt = "AfterStopCannNotOpt"; + + //ongoing 前能操作 但是Stop后 也不能操作 + public static readonly string BeforeOngoingCantOpt = "BeforeOngoingCantOpt"; + + public static readonly string AddOrUpdateTrial = "AddOrUpdateTrial"; + + public static readonly string SignSystemDocNoTrialId = "SignSystemDocNoTrialId"; + + } + + + + public static class CacheKey + { + public static string StudyMaxCode = "StudyMaxCode"; + + public static string TaskMaxCode = "TaskMaxCode"; + + public const string UserTypeId = "UserTypeId"; + + } + + + + /// + /// 医生学位等级 + /// + public static class ReviewerDegree + { + public const string Bachelor = "Bachelor"; + public const string Master = "Master"; + public const string Doctorate = "Doctorate"; + } + + + + /// + /// 项目状态 + /// + public static class TrialState + { + public const string TrialInitializing = "Initializing"; + + public const string TrialOngoing = "Ongoing"; + + public const string TrialCompleted = "Completed"; + + public const string TrialStopped = "Stopped"; + } + + + + + + + /// + /// 匿名化配置 key + /// + public static class Anonymize + { + public const string Anonymize_FixedField = "Anonymize_FixedField"; + + public const string Anonymize_IRCInfoField = "Anonymize_IRCInfoField"; + + public const string Anonymize_AddFixedFiled = "Anonymize_AddFixedFiled"; + + public const string Anonymize_AddIRCInfoFiled = "Anonymize_AddIRCInfoFiled"; + } + + + + + + + /// + /// 导出模板code + /// + public static class Export + { + public const string TrialSiteUserList_Export = "TrialSiteUserList_Export"; + + public const string TrialSiteUserSummary_Export = "TrialSiteUserSummary_Export"; + + public const string TrialUserList_Export = "TrialUserList_Export"; + + public const string TrialCRCUploadImageList_Export = "TrialCRCUploadImageList_Export"; + + public const string TrialQCImageChanllengeList_Export = "TrialQCImageChanllengeList_Export"; + + public const string TrialSubjectList_Export = "TrialSubjectList_Export"; + + public const string TrialSubjectProgressList_Export = "TrialSubjectProgressList_Export"; + + public const string TrialSubjectReadingPeriodList_Export = "TrialSubjectReadingPeriodList_Export"; + + public const string TrialStudyList_Export = "TrialStudyList_Export"; + + public const string TrialStudyUploadMonitor_Export = "TrialStudyUploadMonitor_Export"; + + public const string TrialSubjectVisitCheckList_Export = "TrialSubjectVisitCheckList_Export"; + + public const string TrialReadingTaskList_Export = "TrialReadingTaskList_Export"; + + public const string TrialReReadingTaskList_Export = "TrialReReadingTaskList_Export"; + + public const string TrialMedicalReviewList_Export = "TrialMedicalReviewList_Export"; + + + //public const string TrialRECIST1Point1SelfAnalysisList_Export = "TrialRECIST1Point1SelfAnalysisList_Export"; + + //public const string TrialRECIST1Point1GroupAnalysisList_Export = "TrialRECIST1Point1GroupAnalysisList_Export"; + + + public const string TrialSelfAnalysisList_Export = "TrialSelfAnalysisList_Export"; + + public const string TrialGroupAnalysisList_Export = "TrialGroupAnalysisList_Export"; + + + public const string OverallTumorEvaluation_Export = "OverallTumorEvaluation_Export"; + + + public const string RECIST1Point1EvaluationOfTumorEfficacy_Export = "RECIST1Point1EvaluationOfTumorEfficacy_Export"; + + public const string RECIST1Point1DetailedOfEvaluatedLesion_Export = "RECIST1Point1DetailedOfEvaluatedLesion_Export"; + + public const string PCWG3Point1DetailedOfEvaluatedLesion_Export = "PCWG3Point1DetailedOfEvaluatedLesion_Export"; + + + + + } + + + + +} + + + diff --git a/IRaCIS.Core.Infra.EFCore/Common/AuditingData.cs b/IRaCIS.Core.Infra.EFCore/Common/AuditingData.cs new file mode 100644 index 0000000..17283ad --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Common/AuditingData.cs @@ -0,0 +1,3026 @@ +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore.Common.Dto; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Infrastructure.Extention; +using MassTransit; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; +using Newtonsoft.Json; +using SharpCompress.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + + +namespace IRaCIS.Core.Infra.EFCore.Common +{ + public static class AuditOpt + { + public static readonly string Add = "Add"; + public static readonly string Update = "Update"; + public static readonly string Deleted = "Deleted"; + } + + /// + /// 添加稽查稽查数据 + /// + public class AuditingData : IAuditingData + { + + /// + /// 数据库对象o + /// + public IRaCISDBContext _dbContext { get; set; } + + /// + /// 用户信息 + /// + public IUserInfo _userInfo { get; set; } + + /// + /// 构造方法 + /// + /// + /// + public AuditingData(IRaCISDBContext dbContext, IUserInfo userInfo) + { + _dbContext = dbContext; + _userInfo = userInfo; + _userInfo.BatchId = _userInfo.BatchId == null ? NewId.NextGuid() : _userInfo.BatchId; + } + + + + /// + /// 特殊删除 + /// + private List NodeleteTableTypes + { + get + { + return new List() + { + typeof(TrialUser), + typeof(TrialSiteSurvey), + typeof(TrialSiteUser), + typeof(VisitStage), + typeof(TrialSite) + }; + } + } + + + public string GetEntityAuditOpt(EntityEntry entityEntry) + { + if (entityEntry.State == EntityState.Added) + { + return AuditOpt.Add; + } + + else if (entityEntry.State == EntityState.Deleted || + (entityEntry.State == EntityState.Modified + && typeof(ISoftDelete).IsAssignableFrom(entityEntry.Entity.GetType()) + && (bool)entityEntry.Entity.GetType().GetProperty(nameof(ISoftDelete.IsDeleted)).GetValue(entityEntry.Entity) + && entityEntry.State == EntityState.Modified + && !NodeleteTableTypes.Contains(entityEntry.Entity.GetType()) + ) + ) + { + return AuditOpt.Deleted; + } + else + { + return AuditOpt.Update; + } + } + + /// + /// 插入Add的实体 + /// + /// + public async Task InsertAddEntitys(List entitys) + { + + #region 区分 + + // 项目 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(Trial))) + { + + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as Trial; + + var extraIdentification = string.Empty; + + //阅片标准 + var criterionNameList = await _dbContext.ReadingQuestionCriterionTrial.Where(t => t.TrialId == entity.Id && t.IsConfirm).OrderBy(t => t.ShowOrder).Select(t => t.CriterionName).ToListAsync(); + + //临床数据配置 + var clinicalDataSetNameList = await _dbContext.ClinicalDataTrialSet.Where(t => t.TrialId == entity.Id && t.IsConfirm).Select(t => t.ClinicalDataSetName).ToListAsync(); + + + + //List trialDics = new List(); + //var dictionaryIds = new List(); + //if (entity.TrialDicList == null || entity.TrialDicList.Count == 0) + //{ + // dictionaryIds = await this._dbContext.TrialDictionary.Where(x => x.TrialId == entity.Id && x.KeyName == "Criterion").Select(x => x.DictionaryId).ToListAsync(); + //} + //else + //{ + // dictionaryIds = entity.TrialDicList.Select(x => x.DictionaryId).ToList(); + //} + //trialDics = await this._dbContext.Dictionary.Where(x => dictionaryIds.Contains(x.Id)).Select(x => x.ValueCN).ToListAsync(); + + + + Guid id = entity.Id; + var oldentity = await _dbContext.Trial.Where(x => x.Id == id).Select(t => new + { + t.IsTrialBasicLogicConfirmed, + t.IsTrialProcessConfirmed, + t.IsTrialUrgentConfirmed, + t.IsConfigureEmail + }).FirstOrDefaultAsync(); + switch (_userInfo.RequestUrl) + { + case "configTrialBasicInfo/ConfigTrialBasicInfoConfirm": + extraIdentification = $"/{oldentity.IsTrialBasicLogicConfirmed.ToString()}"; + break; + + case "configTrialBasicInfo/ConfigTrialUrgentInfoConfirm": + extraIdentification = $"/{oldentity.IsTrialUrgentConfirmed.ToString()}"; + break; + + case "TrialEmailNoticeConfig/setTrialEmail": + + extraIdentification = oldentity.IsConfigureEmail ? "/EmailUpdate" : "/EmailSave"; + break; + } + + + await InsertInspection(item.Entity as Trial, type, x => new InspectionConvertDTO() + { + TrialId = x.Id, + ExtraIndentification = extraIdentification, + }, new + { + //TrialDicList = string.Join(",", trialDics) + + CriterionNames = criterionNameList.Count() > 0 ? string.Join(",", criterionNameList) : string.Empty, + + ClinicalDataSetNames = clinicalDataSetNameList.Count() > 0 ? string.Join(",", clinicalDataSetNameList) : String.Empty, + + + }); ; + } + + + + + + #endregion + + + #region 已修改 + + #region 阅片单元配置 + + //系统阅片标准 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingQuestionCriterionSystem))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingQuestionCriterionSystem; + + + var extraIdentification = string.Empty; + + if (_dbContext.Entry(entity).Property(t => t.IsMustGlobalReading).IsModified == true) + { + extraIdentification = $"/IsGlobalReading"; + } + + if (_dbContext.Entry(entity).Property(t => t.IsOncologyReading).IsModified == true) + { + extraIdentification = $"/IsOncologyReading"; + } + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = /*type == AuditOpt.Update ? true :*/ false, + ExtraIndentification = extraIdentification + + }); + } + //项目阅片标准 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingQuestionCriterionTrial))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingQuestionCriterionTrial; + + var isDistinctionInterface = false; + + //设置项目配置 肿瘤学配置 和阅片标准配置 + if (_userInfo.RequestUrl == "TrialConfig/setOncologySet" + + //现在废弃 没用 + || _userInfo.RequestUrl == "TrialConfig/setTrialReadingCriterion" + + //项目流程确认 + || _userInfo.RequestUrl == "configTrialBasicInfo/ConfigTrialProcessInfoConfirm" + + //阅片单元 保存阅片规则 + || _userInfo.RequestUrl == "TrialConfig/setCriterionReadingInfo" + + || _userInfo.RequestUrl == "configTrialBasicInfo/TrialReadingInfoSign") + { + isDistinctionInterface = true; + } + + + //同步的数据 后面加"/Auto" 因为同步的地方可能会改 所以取反 + var extraIdentification = string.Empty; + if (_userInfo.RequestUrl != "ReadingQuestion/addOrUpdateReadingQuestionCriterionTrial" && type == AuditOpt.Add) + { + extraIdentification = "/Auto"; + } + + //保存肿瘤学配置哪里 强行要将 评估结果(中间字典表的多条数据)存到标准稽查上 + + var dicIdList = entitys.Where(x => x.Entity.GetType() == typeof(ReadingCriterionDictionary)).Select(t => t.Entity as ReadingCriterionDictionary).Where(t => t.CriterionId == entity.Id).Select(t => t.DictionaryId).ToList(); + + var dicValueList = new List(); + if (dicIdList.Count() == 0) + { + dicValueList = await _dbContext.ReadingCriterionDictionary.Where(t => t.CriterionId == entity.Id).Select(t => t.Dictionary.Value).ToListAsync(); + } + else + { + dicValueList = await _dbContext.Dictionary.Where(t => dicIdList.Contains(t.Id)).Select(t => t.Value).ToListAsync(); + } + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = isDistinctionInterface, + + TrialReadingCriterionId = entity.Id, + + ObjectRelationParentId = x.TrialId, + + ExtraIndentification = extraIdentification + + }, new + { + EvaluationResultTypes = dicValueList.Count > 0 ? string.Join(",", dicValueList) : string.Empty + }); + } + + //系统标准问题 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingQuestionSystem))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingQuestionSystem; + + + int? parentQuestionShowOrder = null; + int? relavantQuestionShowOrder = null; + + if (entity.ParentId != null) + { + parentQuestionShowOrder = await _dbContext.ReadingQuestionSystem.Where(t => t.Id == entity.ParentId).Select(t => t.ShowOrder).FirstOrDefaultAsync(); + + } + if (entity.RelevanceId != null) + { + relavantQuestionShowOrder = await _dbContext.ReadingQuestionSystem.Where(t => t.Id == entity.RelevanceId).Select(t => t.ShowOrder).FirstOrDefaultAsync(); + + } + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false + + }, new { ParentQuestionShowOrder = parentQuestionShowOrder, RelavantQuestionShowOrder = relavantQuestionShowOrder }); + } + //项目标准问题 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingQuestionTrial))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingQuestionTrial; + + + var isDistinctionInterface = false; + var extraIdentification = string.Empty; + + //同步的数据 后面加"/Auto" 因为同步的地方可能会改 所以取反 + if (_userInfo.RequestUrl != "ReadingQuestion/addOrUpdateReadingQuestionTrial" && type == AuditOpt.Add) + { + extraIdentification = "/Auto"; + } + + //重置仲裁规则 + + var cloneEntity = entity.Clone(); + + if (_userInfo.RequestUrl == "ReadingImageTask/setTrialCriterionJudgeQuestionAnswerGroup") + { + isDistinctionInterface = true; + + if (entity.JudgeType == JudgeTypeEnum.None) + { + extraIdentification = "/Reset"; + } + + //翻译 可能自己填写的 也有可能是从字典中获取的 + if (entity.QuestionGenre == TableQuestionType.Dictionary && !string.IsNullOrWhiteSpace(entity.DictionaryCode)) + { + var translateList = _dbContext.Dictionary.Where(t => t.Code == entity.DictionaryCode.Trim()).SelectMany(t => t.ChildList).Select(t => new { t.Code, t.Value, t.ValueCN }).ToList(); + + foreach (var translateItem in translateList) + { + cloneEntity.AnswerCombination = cloneEntity.AnswerCombination.Replace($"\"{translateItem.Code}\"", $"\"{translateItem.Value}\""); + + cloneEntity.AnswerGroup = cloneEntity.AnswerGroup.Replace($"|{translateItem.Code}|", $"|{translateItem.Value}|"); + } + } + } + + int? parentQuestionShowOrder = null; + int? relavantQuestionShowOrder = null; + + if (entity.ParentId != null) + { + parentQuestionShowOrder = await _dbContext.ReadingQuestionSystem.Where(t => t.Id == entity.ParentId).Select(t => t.ShowOrder).FirstOrDefaultAsync(); + + } + if (entity.RelevanceId != null) + { + relavantQuestionShowOrder = await _dbContext.ReadingQuestionSystem.Where(t => t.Id == entity.RelevanceId).Select(t => t.ShowOrder).FirstOrDefaultAsync(); + + } + + + await InsertInspection(cloneEntity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = isDistinctionInterface, + + TrialReadingCriterionId = entity.ReadingQuestionCriterionTrialId, + + ObjectRelationParentId = x.TrialId, + + ExtraIndentification = extraIdentification + + }, new { ParentQuestionShowOrder = parentQuestionShowOrder, RelavantQuestionShowOrder = relavantQuestionShowOrder }); + } + + //系统标准表格问题 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingTableQuestionSystem))) + { + + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingTableQuestionSystem; + + int? dependQuestionShowOrder = null; + int? parentQuestionShowOrder = null; + int? relavantQuestionShowOrder = null; + + if (entity.ParentId != null) + { + parentQuestionShowOrder = await _dbContext.ReadingQuestionSystem.Where(t => t.Id == entity.ParentId).Select(t => t.ShowOrder).FirstOrDefaultAsync(); + + } + if (entity.RelevanceId != null) + { + relavantQuestionShowOrder = await _dbContext.ReadingQuestionSystem.Where(t => t.Id == entity.RelevanceId).Select(t => t.ShowOrder).FirstOrDefaultAsync(); + + } + if (entity.DependParentId != null) + { + dependQuestionShowOrder = await _dbContext.ReadingQuestionSystem.Where(t => t.Id == entity.DependParentId).Select(t => t.ShowOrder).FirstOrDefaultAsync(); + + } + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false + + }, new { ParentQuestionShowOrder = parentQuestionShowOrder, RelavantQuestionShowOrder = relavantQuestionShowOrder, DependQuestionShowOrder = dependQuestionShowOrder }); + } + //项目标准表格问题 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingTableQuestionTrial))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingTableQuestionTrial; + + //同步的数据 后面加"/Auto" 因为同步的地方可能会改 所以取反 + var extraIdentification = string.Empty; + if (_userInfo.RequestUrl != "/ReadingQuestion/addOrUpdateReadingTableQuestionTrial" && type == AuditOpt.Add) + { + extraIdentification = "/Auto"; + } + + + int? dependQuestionShowOrder = null; + int? parentQuestionShowOrder = null; + int? relavantQuestionShowOrder = null; + + if (entity.ParentId != null) + { + parentQuestionShowOrder = await _dbContext.ReadingQuestionSystem.Where(t => t.Id == entity.ParentId).Select(t => t.ShowOrder).FirstOrDefaultAsync(); + + } + if (entity.RelevanceId != null) + { + relavantQuestionShowOrder = await _dbContext.ReadingQuestionSystem.Where(t => t.Id == entity.RelevanceId).Select(t => t.ShowOrder).FirstOrDefaultAsync(); + + } + if (entity.DependParentId != null) + { + dependQuestionShowOrder = await _dbContext.ReadingQuestionSystem.Where(t => t.Id == entity.DependParentId).Select(t => t.ShowOrder).FirstOrDefaultAsync(); + } + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + + TrialReadingCriterionId = entity.TrialCriterionId, + + ObjectRelationParentId = x.TrialId, + + ExtraIndentification = extraIdentification + + }, new { ParentQuestionShowOrder = parentQuestionShowOrder, RelavantQuestionShowOrder = relavantQuestionShowOrder, DependQuestionShowOrder = dependQuestionShowOrder }); + } + + + //系统器官 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(OrganInfo))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as OrganInfo; + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + + ObjectRelationParentId = entity.SystemCriterionId + + + }); + } + + //项目器官 + + if (entitys.Any(x => x.Entity.GetType() == typeof(OrganTrialInfo))) + { + + var organTrialEntityEnrtyList = entitys.Where(x => x.Entity.GetType() == typeof(OrganTrialInfo)); + + var extraIdentification = string.Empty; + + //同步添加 + if (_userInfo.RequestUrl != "OrganInfo/batchAddTrialOrgan" && organTrialEntityEnrtyList.Any(t => GetEntityAuditOpt(t) == AuditOpt.Add)) + { + extraIdentification = "/Auto"; + + + + var list = organTrialEntityEnrtyList.Select(t => t.Entity as OrganTrialInfo); + + var organIdList = list.Select(t => t.OrganInfoId).ToList(); + + var organList = await _dbContext.OrganInfo.Where(x => organIdList.Contains(x.Id)).ToListAsync(); + + + var firstEntity = list.FirstOrDefault(); + + var cloneEntity = firstEntity.Clone(); + + //保证Id 唯一 + cloneEntity.Id = IdentifierHelper.CreateGuid(firstEntity.TrialId.ToString(), firstEntity.TrialCriterionId.ToString()); + + await InsertInspection(cloneEntity, AuditOpt.Add, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + + TrialReadingCriterionId = cloneEntity.TrialCriterionId, + + ObjectRelationParentId = cloneEntity.TrialCriterionId, + + ObjectRelationParentId2 = cloneEntity.OrganInfoId, + + ExtraIndentification = extraIdentification + + }, new + { + QuestionAnswerList = list.Join(organList, t => t.OrganInfoId, u => u.Id, (t, u) => u).OrderBy(t => t.ShowOrder).ToList(), + }); + + } + else + { + //手动操作 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(OrganTrialInfo))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as OrganTrialInfo; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + + TrialReadingCriterionId = entity.TrialCriterionId, + + ObjectRelationParentId = entity.TrialCriterionId, + + ObjectRelationParentId2 = entity.OrganInfoId, + + ExtraIndentification = extraIdentification + + }); + } + } + } + + + + // 项目 系统公用表 + //病灶管理 区分项目 还是系统的 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(CriterionNidus))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as CriterionNidus; + + + var extraIdentification = string.Empty; + if (entity.IsSystemCriterion == false) + { + extraIdentification = "/IsTrial"; + + if (_userInfo.RequestUrl != "OrganInfo/addOrUpdateCriterionNidus" && type == AuditOpt.Add) + { + extraIdentification = "/IsTrial/Auto"; + } + } + + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + + ObjectRelationParentId = entity.CriterionId, + + TrialReadingCriterionId = entity.IsSystemCriterion == false ? entity.CriterionId : null, + + ExtraIndentification = extraIdentification + + + }); + } + + //疗效评估 + + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TumorAssessment))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TumorAssessment; + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + + ObjectRelationParentId = entity.CriterionId + + }); + } + + + // 项目 系统公用表 + //配置标准 病灶类型 自动计算标识 "LesionType", "QuestionType" + if (entitys.Any(x => x.Entity.GetType() == typeof(ReadingCriterionDictionary))) + { + + + + var type = AuditOpt.Add; + + var allList = entitys.Where(x => x.Entity.GetType() == typeof(ReadingCriterionDictionary)).Select(t => t.Entity as ReadingCriterionDictionary).ToList(); + + //查询出字典的Value ValueCN Des 保存 + var dicIdList = allList.Select(t => t.DictionaryId).ToList(); + + var selectList = await _dbContext.Dictionary.Where(x => dicIdList.Contains(x.Id)).Select(t => new { t.Id, t.Value, t.ValueCN, t.Description, t.ShowOrder }).ToListAsync(); + + + foreach (var list in allList.GroupBy(t => t.ParentCode)) + { + + var firstEntity = list.First(); + var cloneEntity = list.First().Clone(); + + + //保证Id 唯一 + cloneEntity.Id = IdentifierHelper.CreateGuid(firstEntity.CriterionId.ToString(), firstEntity.ParentCode.ToString()); + + + var extraIdentification = string.Empty; + + //系统标准 + if (cloneEntity.IsSystemCriterion) + { + extraIdentification = $"/{cloneEntity.ParentCode}"; + } + else + { + extraIdentification = $"/{cloneEntity.ParentCode}/IsTrial"; + } + + + await InsertInspection(cloneEntity, type, x => new InspectionConvertDTO() + { + + ObjectRelationParentId = x.CriterionId, + + TrialReadingCriterionId = x.IsSystemCriterion == false ? x.CriterionId : null, + + //不显示区分接口 通过是否是系统字典 以及字典Code 区分 + IsDistinctionInterface = false, + ExtraIndentification = extraIdentification + + }, + new + { + SelectList = selectList.Join(list, t => t.Id, u => u.DictionaryId, (t, u) => + new + { + t.Value, + t.ValueCN, + t.Description, + t.ShowOrder, + u.IsBaseLineUse, + u.IsFollowVisitUse + } + ).OrderBy(t => t.ShowOrder).ToList() + }); + } + + + + + + + + } + + + + #endregion + + + // 系统文件签署 父层级未记录稽查(系统文档初始数据) + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(SystemDocConfirmedUser))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as SystemDocConfirmedUser; + var systemDocument = await _dbContext.SystemDocument.Where(x => x.Id == entity.SystemDocumentId).FirstOrDefaultAsync(); + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + GeneralId = x.Id, + + ObjectRelationParentId = x.SystemDocumentId + + }, new + { + + FileTypeId = systemDocument.FileTypeId, + Name = systemDocument.Name, + UploadTime = systemDocument.CreateTime, + + CreateUserName = _userInfo.UserName, + UserType = _userInfo.UserTypeShortName, + IsSigned = true,// 是否签署 添加了就是签署了 + }); + } + + // 项目文件签署 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TrialDocConfirmedUser))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TrialDocConfirmedUser; + + var trialDoc = await _dbContext.TrialDocument.Where(x => x.Id == entity.TrialDocumentId).FirstOrDefaultAsync(); + + await InsertInspection(entity as TrialDocConfirmedUser, type, x => new InspectionConvertDTO() + { + TrialId = trialDoc.TrialId, + + + ObjectRelationParentId = x.TrialDocumentId + }, new + { + + UploadTime = trialDoc.CreateTime, + + CreateUserName = _userInfo.UserName, + UserType = _userInfo.UserTypeShortName, + IsSigned = true + }); + } + + + //医学审核 问题答案 + if (entitys.Any(x => x.Entity.GetType() == typeof(ReadingMedicineQuestionAnswer))) + { + + var type = AuditOpt.Add; + + var list = entitys.Where(x => x.Entity.GetType() == typeof(ReadingMedicineQuestionAnswer)).Select(t => t.Entity as ReadingMedicineQuestionAnswer); + + var questionIdList = list.Select(t => t.ReadingMedicineQuestionId).ToList(); + + var questionNameList = await _dbContext.ReadingMedicineTrialQuestion.Where(x => questionIdList.Contains(x.Id)).Select(t => new { t.QuestionName, ReadingMedicineQuestionId = t.Id, t.ShowOrder }).ToListAsync(); + + var firstEntity = list.FirstOrDefault(); + + var cloneEntity = firstEntity.Clone(); + + //保证Id 唯一 + cloneEntity.Id = IdentifierHelper.CreateGuid(firstEntity.VisitTaskId.ToString(), firstEntity.TaskMedicalReviewId.ToString()); + + await InsertInspection(cloneEntity, type, x => new InspectionConvertDTO() + { + VisitTaskId = x.VisitTaskId, + + //byzhouhang + ObjectRelationParentId = x.TaskMedicalReviewId, + + IsDistinctionInterface = false, + }, new + { + QuestionAnswerList = list.Join(questionNameList, t => t.ReadingMedicineQuestionId, u => u.ReadingMedicineQuestionId, (t, u) => new { t.Answer, u.QuestionName, u.ShowOrder }).OrderBy(t => t.ShowOrder).ToList(), + }); + } + + + //医学审核对话 + + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingMedicalReviewDialog))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingMedicalReviewDialog; + + var extraIdentification = string.Empty; + + //失效的时候 不区分标识 + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.MIM) + { + extraIdentification = $"/MIM"; + } + else + { + extraIdentification = $"/IR"; + } + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + VisitTaskId = entity.VisitTaskId, + + ObjectRelationParentId = entity.TaskMedicalReviewId, + + ExtraIndentification = extraIdentification + + }); + } + + + + //医学审核 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TaskMedicalReview))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TaskMedicalReview; + + var extraIdentification = string.Empty; + + //失效的时候 不区分标识 + if (_dbContext.Entry(entity).Property(t => t.IsInvalid).IsModified == true && entity.IsInvalid == true) + { + extraIdentification = $"/Invalid"; + } + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + VisitTaskId = entity.VisitTaskId, + + ObjectRelationParentId = entity.VisitTaskId, + + ObjectRelationParentId2 = entity.MedicalManagerUserId, + + ExtraIndentification = extraIdentification, + + IsDistinctionInterface = (type == AuditOpt.Add || extraIdentification != String.Empty) ? false : true + }); + } + + + + #region QC 质疑 一致性核查 + + + + // Qc 问题答案 + if (entitys.Any(x => x.Entity.GetType() == typeof(TrialQCQuestionAnswer))) + { + var entityEntryList = entitys.Where(x => x.Entity.GetType() == typeof(TrialQCQuestionAnswer)); + + var type = entityEntryList.All(t => t.State == EntityState.Added) ? AuditOpt.Add : AuditOpt.Update; + + + var list = entityEntryList.Select(t => t.Entity as TrialQCQuestionAnswer); + + + var firstEntity = list.FirstOrDefault(); + + var trialQuestionIdList = list.Select(t => t.TrialQCQuestionConfigureId); + + var trialQuestionNameList = await _dbContext.TrialQCQuestionConfigure.Where(x => x.TrialId == firstEntity.TrialId).Select(t => new { t.QuestionName, TrialQCQuestionConfigureId = t.Id, t.ShowOrder }).ToListAsync(); + + var beforeAnswerList = await _dbContext.TrialQCQuestionAnswer.Where(x => x.SubjectVisitId == firstEntity.SubjectVisitId && x.CurrentQCEnum == firstEntity.CurrentQCEnum && x.QCProcessEnum == firstEntity.QCProcessEnum) + .Select(u => new { u.TrialQCQuestionConfigureId, u.Answer }).ToListAsync(); + + var answerList = list.Select(u => new { u.TrialQCQuestionConfigureId, u.Answer }).Union(beforeAnswerList).DistinctBy(t => t.TrialQCQuestionConfigureId); + + var cloneEntity = firstEntity.Clone(); + + //保证Id 唯一 + cloneEntity.Id = IdentifierHelper.CreateGuid(firstEntity.SubjectVisitId.ToString(), ((int)firstEntity.QCProcessEnum).ToString(), ((int)firstEntity.CurrentQCEnum).ToString()); + + await InsertInspection(cloneEntity, type, x => new InspectionConvertDTO() + { + SubjectVisitId = x.SubjectVisitId, + + //byzhouhang + ObjectRelationParentId = x.SubjectVisitId, + }, new + { + QcQuestionAnswerList = answerList.Join(trialQuestionNameList, t => t.TrialQCQuestionConfigureId, u => u.TrialQCQuestionConfigureId, (t, u) => new + { + t.Answer, + u.QuestionName, + u.ShowOrder + }).OrderBy(t => t.ShowOrder).ToList(), + }); + + + + + #region OLd + //var entitylist = entitys.Where(x => x.Entity.GetType() == typeof(TrialQCQuestionAnswer)).Select(x => x.Entity as TrialQCQuestionAnswer).ToList(); + //var firstEntity = entitylist.FirstOrDefault(); + //var subjectVisit = await _dbContext.SubjectVisit.Where(x => x.Id == firstEntity.SubjectVisitId).FirstOrDefaultAsync(); + //subjectVisit = subjectVisit ?? new SubjectVisit(); + //if (type == "Add") + //{ + + + // await InsertInspection(firstEntity, type, answer => new InspectionConvertDTO() + // { + // SiteId = subjectVisit.SiteId, + // SubjectId = subjectVisit.SubjectId, + // SubjectVisitName = subjectVisit.VisitName, + // TrialId = subjectVisit.TrialId, + // SubjectVisitId = subjectVisit.Id, + + // //GeneralId = subjectVisit.Id, + // //byzhouhang + // ObjectRelationParentId = subjectVisit.Id, + // }, new + // { + // QcQuestionAnswerCommands = await Getdata(entitylist), + // }); + //} + //else if (type == "Update") + //{ + + // var questionIds = entitylist.Where(x => x.SubjectVisitId == subjectVisit.Id).Select(x => x.Id).ToList(); + // var createUserId = entitylist.Select(x => x.CreateUserId).FirstOrDefault(); + // var noUpdateData = _dbContext.TrialQCQuestionAnswer.Where(x => x.CreateUserId == createUserId && x.SubjectVisitId == subjectVisit.Id && !questionIds.Contains(x.Id)).ToList(); + // entitylist.AddRange(noUpdateData); + + // await InsertInspection(firstEntity, type, answer => new InspectionConvertDTO() + // { + // SiteId = subjectVisit.SiteId, + // SubjectId = subjectVisit.SubjectId, + // SubjectVisitName = subjectVisit.VisitName, + // TrialId = subjectVisit.TrialId, + // SubjectVisitId = subjectVisit.Id, + + // //GeneralId = subjectVisit.Id, + // //byzhouhang + // ObjectRelationParentId = subjectVisit.Id, + // }, new + // { + // QcQuestionAnswerCommands = await Getdata(entitylist), + // }); + + + //} + + //async Task> Getdata(List questionAnswers) + //{ + // var ids = questionAnswers.Select(x => x.TrialQCQuestionConfigureId).ToList(); + // var trialQCQuestionConfigureDatas = await _dbContext.TrialQCQuestionConfigure.Where(x => ids.Contains(x.Id)).ToListAsync(); + // var collect = questionAnswers.GroupJoin(trialQCQuestionConfigureDatas, one => one.TrialQCQuestionConfigureId, two => two.Id, (x, y) => new { one = x, two = y }) + // .SelectMany(a => a.two.DefaultIfEmpty(), (c, d) => new { c = c.one, d }) + // .OrderBy(x => x.d.ShowOrder) + // .Select(o => new AnswerDto() + // { + // QuestionName = o.d.QuestionName, + // Answer = o.c.Answer, + // }).ToList(); + // return collect; + //} + #endregion + + + + + + } + + //QC 质疑 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(QCChallenge))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as QCChallenge; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + SubjectVisitId = x.SubjectVisitId, + + ObjectRelationParentId = x.SubjectVisitId + }, new + { + + IsOverTime = entity.IsClosed ? entity.ClosedTime > entity.DeadlineTime : DateTime.Now > entity.DeadlineTime, + }); + } + + // 质疑 对话 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(QCChallengeDialog))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as QCChallengeDialog; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + + SubjectVisitId = x.SubjectVisitId, + + + //byzhouhang + ObjectRelationParentId = entity.QCChallengeId, + + IsDistinctionInterface = false + + }); + } + //一致性核查 对话 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(CheckChallengeDialog))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as CheckChallengeDialog; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + + SubjectVisitId = x.SubjectVisitId, + + //byzhouhang + IsDistinctionInterface = false, + + ObjectRelationParentId = entity.SubjectVisitId, + } + + ); + } + + + // 一致性核查文件 是否需要单独一个表记录? + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ConsistencyCheckFile))) + { + var type = GetEntityAuditOpt(item); + + await InsertInspection(item.Entity as ConsistencyCheckFile, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId = x.TrialId + + }); + } + #endregion + + // 中心调研表 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TrialSiteSurvey))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TrialSiteSurvey; + if (entity.TrialSite == null) + { + entity.TrialSite = await _dbContext.TrialSite.Where(x => x.SiteId == entity.SiteId && x.TrialId == entity.TrialId).FirstOrDefaultAsync(); + } + + if (entity.PreliminaryUser == null) + { + entity.PreliminaryUser = await _dbContext.User.Where(x => x.Id == entity.PreliminaryUserId).FirstOrDefaultAsync(); + } + + if (entity.ReviewerUser == null) + { + entity.ReviewerUser = await _dbContext.User.Where(x => x.Id == entity.ReviewerUserId).FirstOrDefaultAsync(); + } + + await InsertInspection(item.Entity as TrialSiteSurvey, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId = entity.TrialSite.Id, + }, new + { + + //TrialSiteCode = entity.TrialSite.TrialSiteCode, + //TrialSiteAliasName = entity.TrialSite.TrialSiteAliasName, + //Phone = entity.Phone, + //Email = entity.Email, + + + PreliminaryUser = entity.PreliminaryUser == null ? "" : entity.PreliminaryUser.FullName, + ReviewerUser = entity.ReviewerUser == null ? "" : entity.ReviewerUser.FullName, + + }); + } + + + + // 既往手术史 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(PreviousSurgery))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as PreviousSurgery; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + + SubjectVisitId = x.SubjectVisitId, + + ObjectRelationParentId = x.SubjectVisitId, + + ObjectRelationParentId2 = x.ClinicalDataTrialSetId, + + }, new + { + Type = ClinicalFileType.PreviousSurgery + }); + } + + // 既往放疗史 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(PreviousHistory))) + { + + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as PreviousHistory; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + + + IsDistinctionInterface = false, + + SubjectVisitId = x.SubjectVisitId, + + ObjectRelationParentId2 = x.ClinicalDataTrialSetId, + + ObjectRelationParentId = x.SubjectVisitId, + }, new + { + Type = ClinicalFileType.PreviousHistory + }); + } + + // 其他治疗史 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(PreviousOther))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as PreviousOther; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + + IsDistinctionInterface = false, + + SubjectVisitId = x.SubjectVisitId, + + ObjectRelationParentId2 = x.ClinicalDataTrialSetId, + + ObjectRelationParentId = x.SubjectVisitId, + }, new + { + Type = ClinicalFileType.PreviousOther + }); + } + + + + + + //系统文件 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(SystemDocument))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as SystemDocument; + + List needConfirmedUserTypeIdList = new List(); + + if (entity.NeedConfirmedUserTypeList == null) + { + needConfirmedUserTypeIdList = await _dbContext.SystemDocNeedConfirmedUserType.Where(x => x.SystemDocumentId == entity.Id).Select(t => t.NeedConfirmUserTypeId).ToListAsync(); + } + else + { + needConfirmedUserTypeIdList = entity.NeedConfirmedUserTypeList.Select(t => t.NeedConfirmUserTypeId).ToList(); + } + + + var userTypeNameList = await _dbContext.UserType.Where(x => needConfirmedUserTypeIdList.Contains(x.Id)).Select(x => x.UserTypeShortName).ToListAsync(); + var userTypeName = string.Join(",", userTypeNameList); + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false + }, new + { + NeedConfirmedUserType = userTypeName, + }); + } + + // 项目文档 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TrialDocument))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TrialDocument; + + List needConfirmedUserTypeIdList = new List(); + + if (entity.NeedConfirmedUserTypeList == null) + { + needConfirmedUserTypeIdList = await _dbContext.TrialDocNeedConfirmedUserType.Where(x => x.TrialDocumentId == entity.Id).Select(t => t.NeedConfirmUserTypeId).ToListAsync(); + } + else + { + needConfirmedUserTypeIdList = entity.NeedConfirmedUserTypeList.Select(t => t.NeedConfirmUserTypeId).ToList(); + } + + var usertypeNames = await _dbContext.UserType.Where(x => needConfirmedUserTypeIdList.Contains(x.Id)).Select(x => x.UserTypeShortName).ToListAsync(); + var usertypeName = string.Join(",", usertypeNames); + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + ObjectRelationParentId = x.TrialId + }, + new + { + NeedConfirmedUserType = usertypeName, + }); + + + } + + //系统 Qc 问题 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(QCQuestion))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as QCQuestion; + + int? parentQuestionOrder = null; + string parentQuestionName = string.Empty; + + if (entity.ParentId != null) + { + var question = await _dbContext.QCQuestionConfigure.Where(x => x.Id == entity.ParentId).Select(x => new { x.Id, x.ShowOrder, x.QuestionName }).FirstOrDefaultAsync(); + + parentQuestionOrder = question?.ShowOrder; + parentQuestionName = question?.QuestionName; + + } + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false + + }, new + { + ParentQuestionName = parentQuestionName, + ParentQuestionOrder = parentQuestionOrder + }); + } + + // 项目QC问题 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TrialQCQuestion))) + { + + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TrialQCQuestion; + + int? parentQuestionOrder = null; + string parentQuestionName = string.Empty; + + if (entity.ParentId != null) + { + var question = entitys.Where(x => x.Entity.GetType() == typeof(TrialQCQuestion)).Select(t => t.Entity as TrialQCQuestion).Select(x => new { x.Id, x.ShowOrder, x.QuestionName }).FirstOrDefault(t => t.Id == entity.ParentId); + + question = question ?? await _dbContext.TrialQCQuestionConfigure.Where(x => x.Id == entity.ParentId).Select(x => new { x.Id, x.ShowOrder, x.QuestionName }).FirstOrDefaultAsync(); + + + parentQuestionOrder = question?.ShowOrder; + parentQuestionName = question?.QuestionName; + + } + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId = x.TrialId, + IsDistinctionInterface = false + }, new + { + ParentQuestionName = parentQuestionName, + ParentQuestionOrder = parentQuestionOrder + }); + } + + //系统 医学审核问题 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingMedicineSystemQuestion))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingMedicineSystemQuestion; + + var parentQuestionName = string.Empty; + if (entity.ParentId != null) + { + parentQuestionName = await _dbContext.ReadingMedicineSystemQuestion.Where(x => x.Id == entity.ParentId).Select(x => x.QuestionName).FirstOrDefaultAsync(); + } + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + }, new { ParentQuestionName = parentQuestionName }); + } + + //项目医学审核问题 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingMedicineTrialQuestion))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingMedicineTrialQuestion; + + var parentQuestionName = string.Empty; + if (entity.ParentId != null) + { + parentQuestionName = await _dbContext.ReadingMedicineTrialQuestion.Where(x => x.Id == entity.ParentId).Select(x => x.QuestionName).FirstOrDefaultAsync(); + } + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + ObjectRelationParentId = x.TrialId + + }, new { ParentQuestionName = parentQuestionName }); + } + + + // 签名模板 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(SystemBasicData))) + { + var type = GetEntityAuditOpt(item); + + await InsertInspection(item.Entity as SystemBasicData, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false + }); + } + + + + // 项目中心 Site未稽查 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TrialSite))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TrialSite; + if (entity.Site == null) + { + entity.Site = await _dbContext.Site.Where(x => x.Id == entity.SiteId).FirstOrDefaultAsync(); + } + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + TrialId = x.TrialId, + + ObjectRelationParentId = x.TrialId + }, new + { + //兼容之前的配置 冗余该字段 + SiteCode = entity.TrialSiteCode, + + //Site 没记录稽查 必须查询存 + SiteName = entity.Site.SiteName, + City = entity.Site.City, + Country = entity.Site.Country, + }); + } + + + + // 项目人员 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TrialUser))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TrialUser; + //var user = await _dbContext.Users.Include(x => x.UserTypeRole).FirstOrDefaultAsync(x => x.Id == entity.UserId); + await InsertInspection(entity, type, x => new InspectionConvertDTO + { + IsDistinctionInterface = type == AuditOpt.Update ? true : false, + TrialId = x.TrialId, + ObjectRelationParentId = x.TrialId, + ObjectRelationParentId2 = x.UserId, + }); + } + + + // 项目中心人员 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TrialSiteUser))) + { + + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TrialSiteUser; + + + if (entity.TrialSite == null) + { + entity.TrialSite = await _dbContext.TrialSite.Where(x => x.TrialId == entity.TrialId && x.SiteId == entity.SiteId).IgnoreQueryFilters().FirstOrDefaultAsync(); + } + + await InsertInspection(item.Entity as TrialSiteUser, type, x => new InspectionConvertDTO + { + TrialId = x.TrialId, + ObjectRelationParentId = entity.TrialSite.Id, + ObjectRelationParentId2 = x.UserId, + }); + } + + + // 患者 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(Subject))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as Subject; + var finalSubjectVisitName = await _dbContext.SubjectVisit.AsNoTracking().Where(x => x.Id == entity.FinalSubjectVisitId && entity.FinalSubjectVisitId != null).Select(x => x.VisitName).FirstOrDefaultAsync(); + + //var trialSiteCode = await _dbContext.TrialSite.Where(t => t.TrialId == entity.TrialId && t.SiteId == entity.SiteId).Select(t => t.TrialSiteCode).FirstOrDefaultAsync(); + + var trialSiteId = await _dbContext.TrialSite.Where(t => t.TrialId == entity.TrialId && t.SiteId == entity.SiteId).Select(t => t.Id).FirstOrDefaultAsync(); + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + SubjectId = x.Id, + SiteId = x.SiteId, + + //项目的信息 找离的最近的项目稽查信息 + ObjectRelationParentId = trialSiteId, + }, new + { + FinalSubjectVisitName = finalSubjectVisitName, + + //变换名称 兼容之前做的稽查 冗余 + SubjectCode = entity.Code, + }); + } + + // 检查批次计划 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(VisitStage))) + { + + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as VisitStage; + + await InsertInspection(item.Entity as VisitStage, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId = x.TrialId + + }); + + #region Old + //var visitPlanStatus = await this._dbContext.Trial.Where(x => x.Id == entity.TrialId).Select(x => x.VisitPlanConfirmed).FirstOrDefaultAsync(); + //if (type == "Add") + //{ + // visitPlanStatus = false; + //} + //await InsertInspection(item.Entity as VisitStage, type, x => new InspectionConvertDTO() + //{ + // ObjectRelationParentId = x.TrialId + + //}, new + //{ + // VisitPlanStatus = visitPlanStatus, + //}); + #endregion + + } + + // 检查批次 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(SubjectVisit))) + { + + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as SubjectVisit; + + await InsertInspection(item.Entity as SubjectVisit, type, x => new InspectionConvertDTO() + { + //Subject的信息 找离的最近的Subject稽查信息 + ObjectRelationParentId = x.SubjectId, + + SubjectId = x.SubjectId, + SubjectVisitId = x.Id, + SiteId = x.SiteId, + + }, + //兼容之前的配置名 + new { SubjectVisitName = entity.VisitName } + ); + } + + // Dicom + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(DicomStudy))) + { + var type = GetEntityAuditOpt(item); + + await InsertInspection(item.Entity as DicomStudy, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId = x.SubjectVisitId + }); + } + + // 序列 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(DicomSeries))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as DicomSeries; + + await InsertInspection(item.Entity as DicomSeries, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId = x.StudyId + } + ); + } + + + // 非Dicom + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(NoneDicomStudy))) + { + + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as NoneDicomStudy; + + await InsertInspection(item.Entity as NoneDicomStudy, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId = x.SubjectVisitId, + + }); + } + + + #region 阅片人入组 + + + //阅片人入组 父层级未记录稽查(医生) + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(Enroll))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as Enroll; + var doctor = await _dbContext.Doctor.FirstOrDefaultAsync(x => x.Id == entity.DoctorId); + var readingCategoryList = await _dbContext.EnrollReadingCategory.Where(x => x.EnrollId == entity.Id).Select(t => t.ReadingCategory).ToListAsync(); + await InsertInspection(item.Entity as Enroll, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId = x.TrialId + }, new + { + //父层级的数据 暂时没有记录稽查 所以这里必须查 + Name = doctor?.FullName, + ChineseName = doctor?.ChineseName, + Email = doctor?.EMail, + IsUploadedACKSOW = entity.AttachmentId != Guid.Empty, + + //子层级的数据 记录到父层级 必须查询 不然找上一条时数据不准 + ReadingCategoryList = readingCategoryList + }); + } + + //独立阅片人 设置阅片类型 这里对于操作人 不区分 添加 编辑 删除 只有设置(相当于更新) + + if (entitys.Any(x => x.Entity.GetType() == typeof(EnrollReadingCategory))) + { + var type = AuditOpt.Update; + + var addList = entitys.Where(x => x.Entity.GetType() == typeof(EnrollReadingCategory) && x.State == EntityState.Added).Select(t => t.Entity as EnrollReadingCategory).ToList(); + var deleteList = entitys.Where(x => x.Entity.GetType() == typeof(EnrollReadingCategory) && x.State == EntityState.Deleted).Select(t => t.Entity as EnrollReadingCategory).ToList(); + + var first = addList.Union(deleteList).FirstOrDefault(); + + var enrollId = first.EnrollId; + + Guid? trialId = Guid.Empty; + + if (first.Enroll != null) + { + trialId = first.Enroll.TrialId; + } + else + { + trialId = await _dbContext.Enroll.Where(x => x.Id == enrollId).Select(t => t.TrialId).FirstOrDefaultAsync(); + } + + + await InsertInspection(first, type, x => new InspectionConvertDTO() + { + //GeneralId 和ObjectRelationParentId 一样 会成环 所以查询的时候 需要排除自身 + GeneralId = enrollId, + + TrialId = trialId, + + ObjectRelationParentId = enrollId, + + IsDistinctionInterface = false + }, new + { + ReadingCategoryList = addList.Select(t => t.ReadingCategory).ToList(), + }); + } + + #endregion + + + #region 阅片期临床数据 + + + + //系统临床数据配置 + + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ClinicalDataSystemSet))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ClinicalDataSystemSet; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + }); + } + + + + //项目临床数据配置 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ClinicalDataTrialSet))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ClinicalDataTrialSet; + + var extraIdentification = string.Empty; + if (_userInfo.RequestUrl == "configTrialBasicInfo/ConfigTrialProcessInfoConfirm") + { + extraIdentification = "/ConfirmSelect"; + } + + //获取配置的标准名称 + + //List trialDics = new List(); + //var dictionaryIds = new List(); + //if (entity.TrialDicList == null || entity.TrialDicList.Count == 0) + //{ + // dictionaryIds = await this._dbContext.TrialDictionary.Where(x => x.TrialId == entity.Id && x.KeyName == "Criterion").Select(x => x.DictionaryId).ToListAsync(); + //} + //else + //{ + // dictionaryIds = entity.TrialDicList.Select(x => x.DictionaryId).ToList(); + //} + //trialDics = await this._dbContext.Dictionary.Where(x => dictionaryIds.Contains(x.Id)).Select(x => x.ValueCN).ToListAsync(); + + var trialReadingCritiralIdList = new List(); + + var criterionNameList = new List(); + + if (entity.TrialClinicalDataSetCriteriaList == null || entity.TrialClinicalDataSetCriteriaList.Count() == 0) + { + criterionNameList = await _dbContext.TrialClinicalDataSetCriterion.Where(t => t.TrialClinicalDataSetId == entity.Id).Select(t => t.TrialReadingCriterion.CriterionName).ToListAsync(); + } + else + { + var ids = entity.TrialClinicalDataSetCriteriaList.Select(t => t.TrialReadingCriterionId).ToList(); + criterionNameList = await _dbContext.ReadingQuestionCriterionTrial.Where(t => ids.Contains(t.Id)).Select(t => t.CriterionName).ToListAsync(); + } + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + ObjectRelationParentId = entity.TrialId, + ExtraIndentification = extraIdentification, + }, new + { + CriterionNames = criterionNameList?.Count() > 0 ? string.Join(",", criterionNameList) : string.Empty + }); + } + + // CRC PM 临床数据 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingClinicalData))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingClinicalData; + + var config= await _dbContext.ClinicalDataTrialSet.FindAsync(entity.ClinicalDataTrialSetId); + + await InsertInspection(item.Entity as ReadingClinicalData, type, x => new InspectionConvertDTO() + { + + IsDistinctionInterface = type == AuditOpt.Update ? true : false, + + SubjectVisitId = x.IsVisit ? x.ReadingId : null, + + ObjectRelationParentId = entity.ClinicalDataTrialSetId, + + //ObjectRelationParentId2 = x.IsVisit == false?x.ReadingId:null + },new {FileCountViewStr= config?.ClinicalDataLevel==ClinicalLevel.Subject && config?.ClinicalUploadType==ClinicalUploadType.Table?"NA": entity.FileCount.ToString() }); + } + + //阅片期计划 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingPeriodSet))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingPeriodSet; + + var siteCodes = string.Empty; + if (entity.ReadingScope == ReadingScopeEnum.Site) + { + var siteIds = entity.ReadingPeriodSites.Select(t => t.SiteId).ToList(); + var nameList = _dbContext.TrialSite.Where(c => c.TrialId == entity.TrialId && siteIds.Contains(c.SiteId)).Select(t => t.TrialSiteCode).ToList(); + + siteCodes = String.Join(',', nameList); + } + + await InsertInspection(item.Entity as ReadingPeriodSet, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId = x.VisitStageId, + ObjectRelationParentId2 = entity.TrialReadingCriterionId, + + + }, new + { + SiteCodes = siteCodes + }); + } + + #region 阅片期临时数据不记录 + //foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingPeriodPlan))) + //{ + // var entity = item.Entity as ReadingPeriodPlan; + + // if (entity.ReadingPeriodSet == null) + // { + // entity.ReadingPeriodSet = _dbContext.ReadingPeriodSet.Find(entity.ReadingPeriodSetId); + // } + + // await InsertInspection(item.Entity as ReadingPeriodPlan, type, x => new InspectionConvertDTO() + // { + // GeneralId = x.Id, + // SubjectVisitId = x.SubjectVisitId, + // }, new + // { + // entity.ReadingPeriodSet.ReadingPeriodName, + // entity.ReadingPeriodSet.EffectOfTime, + // entity.ReadingPeriodSet.ExpirationDate + // }); + //} + + #endregion + + + //阅片期 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadModule))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadModule; + + await InsertInspection(item.Entity as ReadModule, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId = entity.SubjectVisitId, + + ObjectRelationParentId2 = entity.TrialReadingCriterionId, + + //SubjectVisitId = x.SubjectVisitId, + IsDistinctionInterface = false, + }); + } + + + #endregion + + + #region 阅片 + + //用户添加 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(User))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as User; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false + }, new + { + UserRealName = entity.FullName, + } + ); + } + + + + //分配规则 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TaskAllocationRule))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TaskAllocationRule; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + + ObjectRelationParentId = entity.EnrollId, + + ObjectRelationParentId2 = entity.DoctorUserId + + }); + } + + // suject 医生绑定关系 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(SubjectUser))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as SubjectUser; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + IsDistinctionInterface = false, + + ObjectRelationParentId = entity.SubjectId, + + ObjectRelationParentId2 = entity.DoctorUserId, + + ObjectRelationParentId3 = entity.TrialReadingCriterionId, + + }, new { ArmToTask = entity.ArmEnum }); + } + + + + //申请重阅记录表 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(VisitTaskReReading))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as VisitTaskReReading; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + VisitTaskId = entity.OriginalReReadingTaskId, + + Reason = entity.RequestReReadingReason, + + ObjectRelationParentId = entity.OriginalReReadingTaskId, + + }); + } + + + //一致性分析规则 + + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TaskConsistentRule))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TaskConsistentRule; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId = entity.TrialId, + + ObjectRelationParentId2 = entity.TrialReadingCriterionId + }); + } + + + + #endregion + + + + #region 阅片结果 + + + #region 暂时不区分标准 + + //保存影像质量 多条记录,只记录一条稽查 + if (entitys.Any(x => x.Entity.GetType() == typeof(ReadingTaskQuestionAnswer))) + { + // 保存影像质量 、 修改整体肿瘤评估结果 、 非dicom 保存检查批次阅片结果 + if (_userInfo.RequestUrl == "ReadingImageTask/changeDicomReadingQuestionAnswer" + || _userInfo.RequestUrl == "ReadingImageTask/saveImageQuality" + || _userInfo.RequestUrl == "ReadingImageTask/saveVisitTaskQuestions" + || _userInfo.RequestUrl == "ReadingImageTask/changeCalculationAnswer") + { + var type = AuditOpt.Add; + + var extraIdentification = string.Empty; + + //具体的答案 + var taskQuestionAnswerList = entitys.Where(x => x.Entity.GetType() == typeof(ReadingTaskQuestionAnswer)).Select(t => t.Entity as ReadingTaskQuestionAnswer).ToList(); + + //获取问题名称 组合成数组 + var quesionList = await _dbContext.ReadingQuestionTrial.Where(t => taskQuestionAnswerList.Select(k => k.ReadingQuestionTrialId).Contains(t.Id)).Select(t => new + { + t.QuestionName, + QuestionId = t.Id, + t.DictionaryCode, + t.ShowOrder, + AnswerType = t.Type, + }).OrderBy(t => t.ShowOrder).ToListAsync(); + + + var firstEntity = taskQuestionAnswerList.First(); + + var cloneEntity = firstEntity.Clone(); + + + + //保证Id 唯一 + cloneEntity.Id = IdentifierHelper.CreateGuid(firstEntity.ReadingQuestionCriterionTrialId.ToString(), firstEntity.ReadingQuestionTrialId.ToString(), firstEntity.VisitTaskId.ToString()); + + + dynamic tableQuesionAndAnswerList = null; + //自定义特有标识 + if (await _dbContext.ReadingQuestionCriterionTrial.AnyAsync(t => t.Id == firstEntity.ReadingQuestionCriterionTrialId && t.CriterionType == CriterionType.SelfDefine)) + { + extraIdentification = "/Self"; + + //还会把病灶问题答案更新 + + var tableQuestionAnswerList = entitys.Where(x => x.Entity.GetType() == typeof(ReadingTableQuestionAnswer)).Select(t => t.Entity as ReadingTableQuestionAnswer).ToList(); + + + //获取表格问题名称 组合成数组 + var tableQuesionList = await _dbContext.ReadingTableQuestionTrial.Where(t => tableQuestionAnswerList.Select(k => k.TableQuestionId).Contains(t.Id)).Select(t => + new + { + TrialReadingCriterionId = t.ReadingQuestionTrial.ReadingQuestionCriterionTrialId, //标准Id + Type = t.ReadingQuestionTrial.QuestionName, //病灶类型 + t.ReadingQuestionTrial.Unit, + t.ReadingQuestionTrial.CustomUnit, + t.DictionaryCode, + t.QuestionName, + QuestionId = t.Id, + t.ShowOrder, + AnswerType= t.Type, + }) + .OrderBy(t => t.ShowOrder).ToListAsync(); + + tableQuesionAndAnswerList = tableQuestionAnswerList.Join(tableQuesionList, t => t.TableQuestionId, u => u.QuestionId, (t, u) => + new + { + //如果问题类型是附件 特殊处理 方便前端解析 + Answer= u.AnswerType== "upload"? "❄❅❆❇❈❉❊" +t.Answer:t.Answer, + u.QuestionName, + u.DictionaryCode, + u.ShowOrder, + t.RowId + } + ).OrderBy(t => t.RowId).ThenBy(t=>t.ShowOrder).ToList(); + + + + } + + await InsertInspection(cloneEntity, type, x => new InspectionConvertDTO() + { + VisitTaskId = x.VisitTaskId, + + ObjectRelationParentId = x.VisitTaskId, + + TrialReadingCriterionId = x.ReadingQuestionCriterionTrialId, + + ExtraIndentification = extraIdentification, + + }, new { QuestionAnswerList = taskQuestionAnswerList.Join(quesionList, t => t.ReadingQuestionTrialId, u => u.QuestionId, (t, u) => + new { Answer = u.AnswerType == "upload" ? "❄❅❆❇❈❉❊" + t.Answer : t.Answer, u.DictionaryCode, u.QuestionName, u.ShowOrder }).OrderBy(t => t.ShowOrder).ToList() + , + TableQuestionAndAnswerList = tableQuesionAndAnswerList + } + ); + + + } + + + } + + //病灶这里操作 ReadingTableAnswerRowInfo ReadingTableQuestionAnswer + + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingTableAnswerRowInfo))) + { + + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingTableAnswerRowInfo; + + var tableQuestionAnswerList = entitys.Where(x => x.Entity.GetType() == typeof(ReadingTableQuestionAnswer)).Select(t => t.Entity as ReadingTableQuestionAnswer).Where(t => t.RowId == entity.Id).ToList(); + + + //获取表格问题名称 组合成数组 + var tableQuesionList = await _dbContext.ReadingTableQuestionTrial.Where(t => tableQuestionAnswerList.Select(k => k.TableQuestionId).Contains(t.Id)).Select(t => + new + { + TrialReadingCriterionId = t.ReadingQuestionTrial.ReadingQuestionCriterionTrialId, //标准Id + Type = t.ReadingQuestionTrial.QuestionName, //病灶类型 + t.ReadingQuestionTrial.Unit, + t.ReadingQuestionTrial.CustomUnit, + t.DictionaryCode, + t.QuestionName, + QuestionId = t.Id, + t.ShowOrder, + AnswerType = t.Type, + + }) + .OrderBy(t => t.ShowOrder).ToListAsync(); + + var trialReadingCriterionId = tableQuesionList.FirstOrDefault()?.TrialReadingCriterionId; + + //获取表名称 类型名称(病灶类型) 不用查 从项目问题的稽查记录里面去取 + //var tableName = await _dbContext.ReadingQuestionTrial.Where(t => t.Id == entity.QuestionId).Select(t => t.QuestionName).FirstOrDefaultAsync(); + + //分裂病灶 需要原病灶的标识 + + //if (_userInfo.RequestUrl == " ReadingImageTask/splitLesion") + var originalRowMark = string.Empty; + if (entity.SplitRowId != null) + { + + originalRowMark = await _dbContext.ReadingTableAnswerRowInfo.Where(t => t.Id == entity.SplitRowId).Select(t => t.RowMark).FirstOrDefaultAsync(); + } + + //处理标识 因为触发器在稽查后才进行操作 + + entity.RowMark = entity.OrderMark + entity.RowIndex.GetLesionMark(); + + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + VisitTaskId = x.VisitTaskId, + + ObjectRelationParentId = x.VisitTaskId, + + TrialReadingCriterionId = trialReadingCriterionId, + + ObjectRelationParentId2 = x.QuestionId + + }, new + { + OriginalRowMark = originalRowMark, + //TableName = tableName, + QuestionAnswerList = + + //需要手动添加病灶类型 + tableQuestionAnswerList.Join(tableQuesionList, t => t.TableQuestionId, u => u.QuestionId, (t, u) => + new + { + //如果问题类型是附件 特殊处理 方便前端解析 + Answer = u.AnswerType == "upload" ? "❄❅❆❇❈❉❊" + t.Answer : t.Answer, + //t.Answer /*u.Unit==ValueUnit.Custom? t.Answer+u.CustomUnit:(u.Unit != ValueUnit.None|| u.Unit != null)*/, + u.QuestionName, + u.DictionaryCode, + u.ShowOrder + } + ).OrderBy(t => t.ShowOrder).ToList() + }); + + + ////添加/修改病灶接口 只会对单个病灶进行操作 + //if (_userInfo.RequestUrl == "ReadingImageTask/submitTableQuestion") + //{ + //} + + ////删除病灶接口 里面也有更新 + //if (_userInfo.RequestUrl == "ReadingImageTask/deleteReadingRowAnswer") + //{ + // //有删除 有修改 多条稽查记录 + + // var deleteList = entitys.Where(x => x.Entity.GetType() == typeof(ReadingTableQuestionAnswer) && GetEntityAuditOpt(x) == AuditOpt.Deleted).Select(t => t.Entity as ReadingTableQuestionAnswer).ToList(); + + // var updateList = entitys.Where(x => x.Entity.GetType() == typeof(ReadingTableQuestionAnswer) && GetEntityAuditOpt(x) == AuditOpt.Update).Select(t => t.Entity as ReadingTableQuestionAnswer).ToList(); + //} + + } + + + #endregion + + + + #region 肿瘤学阅片结果 记录表格 废弃 + + //if (entitys.Any(x => x.entity.gettype() == typeof(readingoncologytaskinfo))) + //{ + // var type = auditopt.add; + + // var oncologyanswerlist = entitys.where(x => x.entity.gettype() == typeof(readingoncologytaskinfo)).select(t => t.entity as readingoncologytaskinfo); + + // var visittaskidlist = oncologyanswerlist.select(t => t.visittaskid).tolist(); + + // var visittasklist = await _dbcontext.visittask.where(t => visittaskidlist.contains(t.id)).select(t => new { visittaskid = t.id, t.taskname, t.taskblindname, t.visittasknum, t.subjectid }).tolistasync(); + + + + // var modifyvisitlist = await _dbcontext.readingglobaltaskinfo.where(t => visittaskidlist.contains(t.taskid) && t.visittask.taskstate == taskstate.effect).groupby(t => t.taskid).select(g => new + // { + // visittaskid = g.key, + // globalresult = g.select(c => new { c.answer, c.questionid }) + // }).tolistasync(); + + + // //var oncologyanswervisitlist = visittasklist.join(oncologyanswerlist, t => t.visittaskid, u => u.visittaskid, (t, u) => new { t.visittaskid, t.taskblindname, t.taskname, u.evaluationreason, u.evaluationresult }).tolist(); + + // var query = from answer in oncologyanswerlist + // join visittask in visittasklist on answer.visittaskid equals visittask.visittaskid + // join modifyvisit in modifyvisitlist on answer.visittaskid equals modifyvisit.visittaskid into cc + // from modifyvisit in cc.defaultifempty() + // select new + // { + // visittask.taskblindname, + // visittask.taskname, + // answer.evaluationreason, + // answer.evaluationresult, + // isglobalmodify = modifyvisit != null, + // reason = modifyvisit != null ? modifyvisit.globalresult.where(t => t.questionid == null).firstordefault()?.answer : string.empty + // }; + // var result = query.tolist(); + + + // var oncologyanswer = oncologyanswerlist.first(); + + // var cloneentity = oncologyanswer.clone(); + + // cloneentity.id = newid.nextguid(); + + // await insertinspection(oncologyanswer, type, x => new inspectionconvertdto() + // { + // visittaskid = oncologyanswer.visittaskid, + + // objectrelationparentid = oncologyanswer.visittaskid, + + // }, new { oncologyanswerlist = result }); + //} + + #endregion + + + + + // 全局 阅片结果 + if (entitys.Any(x => x.Entity.GetType() == typeof(ReadingGlobalTaskInfo))) + { + var type = AuditOpt.Add; + + var list = entitys.Where(x => x.Entity.GetType() == typeof(ReadingGlobalTaskInfo)).Select(t => t.Entity as ReadingGlobalTaskInfo); + + //不是固定问题的问题 + var firstEntity = list.Where(t => t.QuestionId != null).First(); + + var cloneEntity = firstEntity.Clone(); + + var criterion = await _dbContext.VisitTask.Where(t => t.Id == cloneEntity.GlobalTaskId).Select(t => new { t.TrialReadingCriterionId,t.TrialReadingCriterion.CriterionType } ).FirstOrDefaultAsync(); + + //保证Id 唯一 + cloneEntity.Id = IdentifierHelper.CreateGuid(firstEntity.GlobalTaskId.ToString(), criterion.TrialReadingCriterionId.ToString()); + + var extraIdentification = string.Empty; + + + var objList = new List(); + + foreach (var group in list.GroupBy(t => t.TaskId)) + { + var questionAnswerList = group.Where(t => t.QuestionId != null).ToList(); + + + var questionIdList = questionAnswerList.Select(t => t.QuestionId).ToList(); + + var quesionList = await _dbContext.ReadingQuestionTrial.Where(t => questionIdList.Contains(t.Id)).Select(t => new + { + t.QuestionName, + QuestionId = t.Id, + t.DictionaryCode, + t.ShowOrder + }).OrderBy(t => t.ShowOrder).ToListAsync(); + + + //pcwg3 的问题没有提交过来,是通过固定的方式去查询出来 + if (criterion.CriterionType == CriterionType.PCWG3) + { + + extraIdentification = "/PCWG3"; + //获取检查批次的评估结果 也要记录稽查 + var visitAnswerList = await _dbContext.ReadingTaskQuestionAnswer.Where(t => t.VisitTaskId == group.Key &&t.ReadingQuestionTrial.GlobalReadingShowType != GlobalReadingShowType.NotShow).Select(u => new + { + TaskBlindName = u.VisitTask.TaskBlindName, + QuestionId = u.ReadingQuestionTrialId, + u.ReadingQuestionTrial.QuestionName, + u.ReadingQuestionTrial.DictionaryCode, + u.ReadingQuestionTrial.ShowOrder, + u.Answer + }).OrderBy(t=>t.ShowOrder).ToListAsync(); + + + var obj = new + { + TaskBlindName = visitAnswerList.Select(t => t.TaskBlindName).FirstOrDefault(), + VisitQuestionAnswerList = visitAnswerList, + + + Reason = group.Where(t => t.QuestionId == null && t.GlobalAnswerType == GlobalAnswerType.Reason).FirstOrDefault()?.Answer, + + QuestionAnswerList = questionAnswerList.Join(quesionList, t => t.QuestionId, u => u.QuestionId, (t, u) => new { t.Answer, u.QuestionName, u.DictionaryCode, u.ShowOrder }).OrderBy(t => t.ShowOrder).ToList() + }; + + objList.Add(obj); + + + + } + else + { + //获取检查批次的评估结果 也要记录稽查 + var visitAnswerList = await _dbContext.ReadingTaskQuestionAnswer.Where(t => t.VisitTaskId == group.Key && questionIdList.Contains(t.ReadingQuestionTrialId)).Select(u => new + { + TaskBlindName = u.VisitTask.TaskBlindName, + QuestionId = u.ReadingQuestionTrialId, + u.Answer + }).ToListAsync(); + + + var obj = new + { + TaskBlindName = visitAnswerList.Select(t => t.TaskBlindName).FirstOrDefault(), + VisitQuestionAnswerList = visitAnswerList.Join(quesionList, t => t.QuestionId, u => u.QuestionId, (t, u) => new { t.Answer, u.QuestionName, u.DictionaryCode, u.ShowOrder }).OrderBy(t => t.ShowOrder).ToList(), + Reason = group.Where(t => t.QuestionId == null && t.GlobalAnswerType == GlobalAnswerType.Reason).FirstOrDefault()?.Answer, + AgreeOrNot = group.Where(t => t.QuestionId == null && t.GlobalAnswerType == GlobalAnswerType.AgreeOrNot).FirstOrDefault()?.Answer, + UpdateType = group.Where(t => t.QuestionId == null && t.GlobalAnswerType == GlobalAnswerType.UpdateType).FirstOrDefault()?.Answer, + QuestionAnswerList = questionAnswerList.Join(quesionList, t => t.QuestionId, u => u.QuestionId, (t, u) => new { t.Answer, u.QuestionName, u.DictionaryCode, u.ShowOrder }).OrderBy(t => t.ShowOrder).ToList() + }; + + objList.Add(obj); + + } + + } + + + await InsertInspection(cloneEntity, type, x => new InspectionConvertDTO() + { + VisitTaskId = x.GlobalTaskId, + TrialReadingCriterionId = criterion.TrialReadingCriterionId, + ObjectRelationParentId = x.TaskId, + ExtraIndentification=extraIdentification + }, new { GlobalAnswerList = objList }); + + } + + + //肿瘤学阅片结果 + + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingOncologyTaskInfo))) + { + var type = AuditOpt.Add; + + var entity = item.Entity as ReadingOncologyTaskInfo; + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + VisitTaskId = entity.VisitTaskId, + + ObjectRelationParentId = entity.VisitTaskId, + + }); + } + + + #endregion + + + //任务 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(VisitTask))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as VisitTask; + + var obj = new object() { }; + + //检查批次任务-- 非Dicom 阅片 + if (_userInfo.RequestUrl == "ReadingImageTask/SubmitVisitTaskQuestions" && entity.ReadingTaskState != ReadingTaskState.HaveSigned && type == AuditOpt.Update) + { + //提交检查批次任务的时候 会多次更新同一个记录 只记录最后一次 + return; + } + + + + //Dicom 阅片 签名 + if (_userInfo.RequestUrl == "ReadingImageTask/SubmitDicomVisitTask") + { + //跳转阅片结果需要该参数 + var subjectCode = _dbContext.Subject.Where(t => t.Id == entity.SubjectId).Select(t => t.Code).First(); + obj = new { SubjectCode = subjectCode }; + } + + #region 裁判、肿瘤学、全局 都是通用的 + + //裁判任务 结果的保存 和签名提交 + if (entity.JudgeResultTaskId != null && (_userInfo.RequestUrl == "ReadingImageTask/SaveJudgeVisitTaskResult" || _userInfo.RequestUrl == "ReadingImageTask/SubmitJudgeVisitTaskResult")) + { + + var visitTaskNum = entity.VisitTaskNum - ReadingCommon.TaskNumDic[ReadingCategory.Judge]; + var list = await _dbContext.VisitTask.Where(t => t.TaskState == TaskState.Effect && t.SubjectId == entity.SubjectId && t.VisitTaskNum == visitTaskNum && t.JudgeVisitTaskId == entity.Id && t.TrialReadingCriterionId == entity.TrialReadingCriterionId).Select(t => new { t.Id, t.DoctorUser.FullName, t.ArmEnum }).OrderBy(t => t.ArmEnum).ToListAsync(); + + + var r1 = list.Where(t => t.ArmEnum == Arm.DoubleReadingArm1).FirstOrDefault(); + var r2 = list.Where(t => t.ArmEnum == Arm.DoubleReadingArm2).FirstOrDefault(); + + + obj = new { R1 = r1.FullName, R2 = r2.FullName, SelectResult = r1.Id == entity.JudgeResultTaskId ? "R1" : "R2" }; + } + + #region 通过链接跳转 2022 12-19 + + ////肿瘤学任务 + //if (entity.ReadingTaskState == ReadingTaskState.HaveSigned && _userInfo.RequestUrl == "ReadingImageTask/SubmitOncologyReadingInfo") + //{ + + + // var oncologyAnswerList = await _dbContext.ReadingOncologyTaskInfo.Where(t => t.OncologyTaskId == entity.Id).Select(t => new { t.VisitTask.TaskBlindName, t.VisitTask.TaskName, t.EvaluationReason, t.EvaluationResult, t.VisitTask.VisitTaskNum, VisitTaskId = t.VisitTask.Id }).ToListAsync(); + + // var golbalTaskInfo = await _dbContext.VisitTask.Where(t => t.SubjectId == entity.SubjectId && t.ReadingCategory == ReadingCategory.Global && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == entity.IsAnalysisCreate && t.VisitTaskNum == (entity.VisitTaskNum - ReadingCommon.TaskNumDic[ReadingCategory.Oncology] + ReadingCommon.TaskNumDic[ReadingCategory.Global])).Select(t => new { GlobalTaskId = t.Id }).FirstNotNullAsync(); + + + // var globalResultList = await _dbContext.ReadingGlobalTaskInfo.Where(t => t.GlobalTaskId == golbalTaskInfo.GlobalTaskId).ToListAsync(); + + // var modifyVisitList = globalResultList + // .GroupBy(t => t.TaskId).Select(g => new + // { + // VisitTaskId = g.Key, + // GlobalResult = g.Select(c => new { c.Answer, c.QuestionId }) + // }).ToList(); + + // var query = from answer in oncologyAnswerList + // join modifyVisit in modifyVisitList on answer.VisitTaskId equals modifyVisit.VisitTaskId into cc + // from modifyVisit in cc.DefaultIfEmpty() + // select new + // { + // answer.TaskBlindName, + // answer.TaskName, + // answer.EvaluationReason, + // answer.EvaluationResult, + // IsGlobalModify = modifyVisit != null, + // Reason = modifyVisit != null ? modifyVisit.GlobalResult.Where(t => t.QuestionId == null).FirstOrDefault()?.Answer : String.Empty + // }; + + // var result = query.ToList(); + + // obj = new { OncologyAnswerList = result }; + //} + + ////全局任务 + //if (entity.ReadingTaskState == ReadingTaskState.HaveSigned && _userInfo.RequestUrl == "ReadingImageTask/SubmitGlobalReadingInfo") + //{ + + // var globalResultList = await _dbContext.ReadingGlobalTaskInfo.Where(t => t.GlobalTaskId == entity.Id).Select(t => new { VisitTaskId = t.TaskId, t.QuestionId, t.Answer, t.VisitTask.TaskBlindName, t.VisitTask.TaskName, t.TrialReadingQuestion.QuestionName }).ToListAsync(); + + // var visitTaskIdList = globalResultList.Select(t => t.VisitTaskId).ToList(); + + // var visitResultList = await _dbContext.ReadingTaskQuestionAnswer.Where(t => visitTaskIdList.Contains(t.VisitTaskId) && t.ReadingQuestionTrial.IsJudgeQuestion).Select(t => new { t.VisitTaskId, t.VisitTask.TaskBlindName, t.VisitTask.TaskName, t.VisitTask.VisitTaskNum, t.ReadingQuestionTrial.QuestionName, t.ReadingQuestionTrial.ShowOrder, t.Answer, t.ReadingQuestionTrialId }) + // .OrderBy(t => t.VisitTaskNum).ToListAsync(); + + // var query = visitResultList.GroupBy(t => new { t.VisitTaskId, t.VisitTaskNum, t.TaskName, t.TaskBlindName }).Select(g => new + // { + // VisitTaskId = g.Key.VisitTaskId, + // VisitTaskNum = g.Key.VisitTaskNum, + // TaskName = g.Key.TaskName, + // TaskBlindName = g.Key.TaskBlindName, + + // VisitQuestionAnswerList = g.OrderBy(t => t.ShowOrder).Select(u => new { u.QuestionName, u.Answer }).ToList(), + + // GlobalQuestionAnswerList = g.OrderBy(t => t.ShowOrder).Select(u => new { u.QuestionName, Answer = globalResultList.Where(t => t.VisitTaskId == g.Key.VisitTaskId && t.QuestionId == u.ReadingQuestionTrialId).FirstOrDefault()?.Answer ?? String.Empty }).ToList(), + + // Reason = globalResultList.Where(t => t.VisitTaskId == g.Key.VisitTaskId && t.QuestionId == null).FirstOrDefault()?.Answer ?? String.Empty + // }); + + + // var result = query.ToList(); + + // obj = new { GlobalAnswerList = result }; + //} + + #endregion + + #endregion + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + VisitTaskId = x.Id, + + IsDistinctionInterface = type == AuditOpt.Update ? true : false, + + ObjectRelationParentId = entity.SourceSubjectVisitId != null ? entity.SourceSubjectVisitId : entity.SouceReadModuleId, + + ObjectRelationParentId2 = entity.DoctorUserId, + + ObjectRelationParentId3 = entity.TrialReadingCriterionId, + + }, obj); + } + + + + #endregion + + + } + + /// + /// 插入稽查实体 + /// + /// 泛型 + /// 数据 + /// 类型 + /// 表达式 + /// 其他对象 + /// + public async Task InsertInspection(T entityObj, string type, Expression> expression = null, object otherItem = null) where T : Entity + { + + InspectionConvertDTO inspection = new InspectionConvertDTO(); + + + if (expression != null) + { + var f = expression.Compile(); + + inspection = f(entityObj); + } + + //避免重复赋值 有些特殊的GeneralId + var generalId = (inspection.GeneralId != null && inspection.GeneralId != Guid.Empty) ? inspection.GeneralId : entityObj.Id; + inspection.GeneralId = generalId; + + inspection.Identification = await GetInspectionRecordIdentificationAsync(inspection, entityObj, type, inspection.IsDistinctionInterface) + inspection.ExtraIndentification; + + //将实体对象属性 映射到稽查实体 + MapEntityPropertyToAuditEntity(entityObj, inspection); + + //var generalData = await GetInspectionGeneralDataAsync(inspection); + + var generalData = await GetInspectionGeneralGuidAsync(inspection); + + //不可少 因为稽查实体可能某些Id没有 + MapEntityPropertyToAuditEntity(generalData, inspection); + + #region 处理标识 + + var from = await _dbContext.FrontAuditConfig.FirstOrDefaultAsync(x => x.Identification == inspection.Identification); + inspection.ObjectTypeId = from?.ObjectTypeId; + inspection.OptTypeId = from?.OptTypeId; + //inspection.ChildrenTypeId = from?.ChildrenTypeId; + inspection.ModuleTypeId = from?.ModuleTypeId; + + #endregion + + + + inspection.CreateUserName = _userInfo.UserName; + inspection.CreateUserRealName = _userInfo.RealName; + inspection.RoleName = _userInfo.UserTypeShortName; + inspection.CreateUserId = _userInfo.Id; + inspection.IP = _userInfo.IP; + inspection.CreateTime = inspection.CreateTime == default(DateTime) ? DateTime.Now : inspection.CreateTime; + + if (_userInfo.SignId != null) + { + inspection.SignId = _userInfo.SignId; + inspection.IsSign = true; + } + + var entityName = entityObj.GetType().Name; + + inspection.JsonDetail = new InspectionJsonDetail + { + //稽查实体,加上扩充的信息 + //Data = AddJsonItem(entityObj, otherItem), + + //通用信息 每条稽查必须记录的 + //CommonData = generalData + + EntityName = entityName, + + //Data 仅仅记录稽查实体的信息 CommonData 存放关联数据的信息(特殊情况下会单独赋值) + //后期通过关联 就不需要把 OtherItem 并入到Data中 + Data = AddJsonItem(entityObj, otherItem), + + //准确来讲 此处称之为 RelationData 更贴合 为了兼容之前的数据 + CommonData = new { SiteCode = generalData.SiteCode, SubjectCode = generalData.SubjectCode }, + + }.ToJsonStr(); + + inspection.BatchId = _userInfo.BatchId.Value; + + inspection.EntityName = entityName; + + await _dbContext.DataInspection.AddAsync(inspection); + + + //await AddInspectionRecordAsync( entityObj, inspection, otherItem); + } + + + /// + /// 将数据库实体属性,映射到稽查实体属性 避免重复赋值 + /// + /// + /// + public void MapEntityPropertyToAuditEntity(object data, DataInspection auditEntity) + { + foreach (var auditEntityPropertyInfo in auditEntity.GetType().GetProperties()) + { + var excepetNameList = typeof(Entity).GetProperties().Select(t => t.Name) + .Concat(typeof(ISoftDelete).GetProperties().Select(t => t.Name)) + .Concat(typeof(IAuditAdd<>).GetProperties().Select(t => t.Name)) + .Concat(typeof(IAuditUpdate<>).GetProperties().Select(t => t.Name)); + + var filterProperties = data.GetType().GetProperties().Where(t => !excepetNameList.Any(u => u == t.Name)); + + if (filterProperties.Any(t => t.Name == auditEntityPropertyInfo.Name)) + { + var value = data.GetType().GetProperty(auditEntityPropertyInfo.Name).GetValue(data); + + auditEntity.GetType().GetProperty(auditEntityPropertyInfo.Name).SetValue(auditEntity, value); + } + } + } + + + /// + /// 获取稽查通用Guid数据 (每条稽查记录必须查询的) + /// + /// + /// + public async Task GetInspectionGeneralGuidAsync(InspectionConvertDTO inspection) + { + + InspectionGeneralData generalData = new InspectionGeneralData() + { + SiteId = inspection.SiteId, + SubjectId = inspection.SubjectId, + SubjectVisitId = inspection.SubjectVisitId, + TrialId = inspection.TrialId, + TrialReadingCriterionId = inspection.TrialReadingCriterionId + }; + + + + + if (inspection.VisitTaskId != null) + { + + if (generalData.TrialId == null || generalData.SiteId == null || generalData.SubjectId == null || generalData.TrialReadingCriterionId == null) + { + var info = await _dbContext.VisitTask.Where(x => x.Id == inspection.VisitTaskId).Select(x => new { SubjectCode = x.Subject.Code, TrialSiteCode = x.Subject.TrialSite.TrialSiteCode, SubjectId = x.SubjectId, x.Subject.SiteId, x.TrialId, x.SourceSubjectVisitId, ReadModuleSubjectVisitId = (Guid?)x.ReadModule.SubjectVisitId, x.TrialReadingCriterionId }).FirstOrDefaultAsync(); + + generalData.TrialReadingCriterionId = info?.TrialReadingCriterionId ?? generalData.TrialReadingCriterionId; + generalData.TrialId = info?.TrialId ?? generalData.TrialId; + generalData.SubjectId = info?.SubjectId ?? generalData.SubjectId; + generalData.SiteId = info?.SiteId ?? generalData.SiteId; + generalData.SubjectVisitId = info?.SourceSubjectVisitId != null ? info?.SourceSubjectVisitId : (info?.ReadModuleSubjectVisitId != null ? info?.ReadModuleSubjectVisitId : null); + + //以后移除 + generalData.SiteCode = info?.TrialSiteCode ?? generalData.SiteCode; + generalData.SubjectCode = info?.SubjectCode ?? generalData.SubjectCode; + } + + } + + if (inspection.TrialReadingCriterionId != null) + { + if (generalData.TrialId == null) + { + var info = await _dbContext.ReadingQuestionCriterionTrial.Where(x => x.Id == inspection.TrialReadingCriterionId).Select(x => new { x.TrialId }).FirstOrDefaultAsync(); + + generalData.TrialId = info?.TrialId ?? generalData.TrialId; + } + } + + // Suject visit 都进行了设置 不用处理 + + if (inspection.SubjectVisitId != null) + { + if (generalData.TrialId == null || generalData.SiteId == null || generalData.SubjectId == null) + { + var info = await _dbContext.SubjectVisit.Where(x => x.Id == generalData.SubjectVisitId).Select(x => + new { x.SubjectId, x.SiteId, x.TrialId, SubjectCode = x.Subject.Code, TrialSiteCode = x.Subject.TrialSite.TrialSiteCode }).FirstOrDefaultAsync(); + + + generalData.TrialId = info?.TrialId ?? generalData.TrialId; + generalData.SiteId = info?.SiteId ?? generalData.SiteId; + generalData.SubjectId = info?.SubjectId ?? generalData.SubjectId; + + //以后移除 + generalData.SiteCode = info?.TrialSiteCode ?? generalData.SiteCode; + generalData.SubjectCode = info?.SubjectCode ?? generalData.SubjectCode; + } + + + } + + + if (generalData.SubjectId != null) + { + if (generalData.TrialId == null || generalData.SiteId == null) + { + var info = await _dbContext.Subject.Where(x => x.Id == generalData.SubjectId).Select(x => new { SubjectId = x.Id, x.SiteId, x.TrialId, SubjectCode = x.Code, TrialSiteCode = x.TrialSite.TrialSiteCode }).FirstOrDefaultAsync(); + + if (info == null) + { + var subject= _dbContext.Subject.Find(generalData.SubjectId); + + generalData.TrialId = subject?.TrialId; + generalData.SiteId = subject?.SiteId; + } + else + { + generalData.TrialId = info?.TrialId ?? generalData.TrialId; + generalData.SiteId = info?.SiteId ?? generalData.SiteId; + } + + + + + + + //以后移除 + generalData.SiteCode = info?.TrialSiteCode ?? generalData.SiteCode; + generalData.SubjectCode = info?.SubjectCode ?? generalData.SubjectCode; + } + + + } + + return generalData; + } + + + + /// + /// 获取稽查记录的标识符 部分业务会进行特殊处理 + /// + /// + public async Task GetInspectionRecordIdentificationAsync(InspectionConvertDTO inspection, T entityObj, string type, bool IsDistinctionInterface = true) + { + var entityTypeName = entityObj.GetType().Name; + + #region 标识符特殊处理 + + //文档签署这块,不区分系统和项目的 需要处理为同一个标识 + if (typeof(T) == typeof(TrialDocConfirmedUser) || typeof(T) == typeof(SystemDocConfirmedUser)) + { + entityTypeName = "New/" + "UserSigned"; + } + + switch (entityObj.GetType().Name) + { + case nameof(TrialDocConfirmedUser): + case nameof(SystemDocConfirmedUser): + + var softDelete = entityObj as ISoftDelete; + + if (type == AuditOpt.Update) + { + if (softDelete.IsDeleted == true) + { + type = type + "/" + 2; + } + else + { + type = type + "/" + 1; + } + } + + break; + + case nameof(SystemBasicData): + var basicData = entityObj as SystemBasicData; + type = type + (basicData.ParentId == null ? "/parent" : string.Empty); + break; + + //case nameof(Trial): + // var trial = entityObj as Trial; + // Guid id = trial.Id; + // var oldentity = await _dbContext.Trial.Where(x => x.Id == id).Select(t => new { t.IsTrialBasicLogicConfirmed, t.IsTrialProcessConfirmed, t.IsTrialUrgentConfirmed }).FirstOrDefaultAsync(); + // switch (_userInfo.RequestUrl.ToLower()) + // { + // case "configtrialbasicinfo/configtrialbasicinfoconfirm": + // type = type + "/" + oldentity.IsTrialBasicLogicConfirmed.ToString(); + // break; + // //case "configtrialbasicinfo/configtrialprocessinfoconfirm": + // // type = type + "/" + oldentity.IsTrialProcessConfirmed.ToString(); + // // break; + // case "configtrialbasicinfo/configtrialurgentinfoconfirm": + // type = type + "/" + oldentity.IsTrialUrgentConfirmed.ToString(); + // break; + // } + // break; + + //case nameof(ReadingQuestionTrial): + + // var trialReadingQuestion = entityObj as ReadingQuestionTrial; + + // switch (_userInfo.RequestUrl) + // { + // case "ReadingImageTask/setTrialCriterionJudgeQuestionAnswerGroup": + + // if (trialReadingQuestion.JudgeType == JudgeTypeEnum.None) + // { + // type = type + "/" + "Reset"; + // } + + // break; + // } + // break; + + + + + #region 检查批次相关 + + // 对话消息区分用户类型 + case nameof(CheckChallengeDialog): + type = type + "/(" + _userInfo.UserTypeShortName + ")"; + + var checkDialog = entityObj as CheckChallengeDialog; + + switch (_userInfo.RequestUrl.ToLower()) + { + case "qcoperation/closecheckchallenge": + + inspection.Reason = checkDialog.TalkContent.Substring(checkDialog.TalkContent.LastIndexOf(':') + 1); + break; + } + + break; + + // 对话消息区分用户类型 + case nameof(QCChallengeDialog): + type = type + "/(" + _userInfo.UserTypeShortName + ")"; + + var dialog = entityObj as QCChallengeDialog; + switch (_userInfo.RequestUrl.ToLower()) + { + case "qcoperation/closeqcchallenge": + + inspection.Reason = dialog.TalkContent.Substring(dialog.TalkContent.LastIndexOf(':') + 1); + break; + } + + break; + + + case nameof(SubjectVisit): + var sv = entityObj as SubjectVisit; + switch (_userInfo.RequestUrl.ToLower()) + { + //待处理? + case "qcoperation/qcpassedorfailed": + + type = type + "/" + (40 % (int)sv.AuditState).ToString(); + + break; + + //设置核查通过 + case "qcoperation/setcheckpass": + + inspection.Reason = sv.ManualPassReason; + break; + + //领取或者取消QC任务 + case "qcoperation/obtainorcancelqctask": + type = type + "/" + sv.IsTake.ToString(); + break; + + //确认重阅 区分用户类型 + case "visittask/confirmrereading": + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM) + { + type = type + "/" + 1; + + } + else + { + //SPM + type = type + "/" + 2; + } + break; + } + + if (sv.CheckChallengeState == CheckChanllengeTypeEnum.CRCWaitPMReply || sv.CheckChallengeState == CheckChanllengeTypeEnum.PMWaitCRCReply) + { + //发送对话 修改质疑状态 不需要区分接口 + IsDistinctionInterface = false; + + type = type + "/ModifyCheckChallengeState"; + } + break; + + case nameof(NoneDicomStudy): + switch (_userInfo.RequestUrl.ToLower()) + { + case "nonedicomstudy/addorupdatenonedicomstudy": + type = type + "/(" + _userInfo.UserTypeShortName + ")"; + break; + } + break; + #endregion + + + #region 阅片任务相关 + + //任务表 + case nameof(VisitTask): + + var visitTask = entityObj as VisitTask; + + + if (type == AuditOpt.Add) + { + //生成一致性分析任务 + if (visitTask.IsSelfAnalysis == true) + { + type = type + "/" + "SelfAnalysis"; + } + else if (visitTask.IsSelfAnalysis == false) + { + type = type + "/" + "GroupAnalysis"; + } + else + { + type = type + "/" + "NotAnalysis"; + } + + //区分任务类型 + type = type + "/" + (int)visitTask.ReadingCategory; + + + } + else + { + switch (_userInfo.RequestUrl) + { + //申请重阅 + case "VisitTask/applyReReading": + type = type + "/" + (int)visitTask.ReReadingApplyState; + break; + + + //同意重阅 + case "VisitTask/ConfirmReReading": + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM) + { + type = type + "/" + 1; + + } + else + { + type = type + "/" + 2; + } + + break; + } + + } + + + + break; + + //重阅记录表 + case nameof(VisitTaskReReading): + + var visitTaskReReading = entityObj as VisitTaskReReading; + + switch (_userInfo.RequestUrl) + { + case "VisitTask/applyReReading": + type = type + "/" + (int)visitTaskReReading.RequestReReadingType; + break; + + case "VisitTask/ConfirmReReading": + + if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM) + { + type = type + "/" + 1; + + } + else + { + type = type + "/" + 2; + } + + if (visitTaskReReading.RequestReReadingResultEnum == RequestReReadingResult.Agree) + { + type = type + "/" + 1; + + } + else if (visitTaskReReading.RequestReReadingResultEnum == RequestReReadingResult.Reject) + { + type = type + "/" + 2; + } + break; + } + + break; + + //一致性分析规则 + case nameof(TaskConsistentRule): + + var taskConsistentRule = entityObj as TaskConsistentRule; + + //自身一致性分析 + if (taskConsistentRule.IsSelfAnalysis == true) + { + type = type + "/" + 1; + + } + //组件一致性分析 + else + { + type = type + "/" + 2; + } + + break; + #endregion + + + ////标准 器官病灶表 + //case nameof(CriterionNidus): + + // var criterionNidus = entityObj as CriterionNidus; + + // if (criterionNidus.IsSystemCriterion == false) + // { + // type = type + "/IsTrial"; + // } + + // break; + + ////系统 项目公用 + //case nameof(ReadingCriterionDictionary): + + // var readingCriterionDictionary = entityObj as ReadingCriterionDictionary; + + // if (readingCriterionDictionary.IsSystemCriterion) + // { + // type = $"{type}/{readingCriterionDictionary.ParentCode}"; + // } + + // break; + + + } + + #endregion + + if (IsDistinctionInterface) + { + return $"{_userInfo.RequestUrl}/{entityTypeName}/{type}"; + + } + else + { + return $"{entityTypeName}/{type}"; + } + } + + + /// 往json里面添加属性 + /// + /// json + /// 添加对象 + /// + public IDictionary AddJsonItem(object jsonObj, object otherItem = null) + { + + + var JsonData = JsonConvert.DeserializeObject>((jsonObj).ToJsonStr()); + + if (otherItem == null) + { + return JsonData; + } + var other = JsonConvert.DeserializeObject>(otherItem.ToJsonStr()); + + foreach (var item in other) + { + if (JsonData.ContainsKey(item.Key)) + { + JsonData[item.Key] = item.Value; + } + else + { + JsonData.Add(item.Key, item.Value); + } + + } + return JsonData; + + } + + + + } +} diff --git a/IRaCIS.Core.Infra.EFCore/Common/Dto/AnswerDto.cs b/IRaCIS.Core.Infra.EFCore/Common/Dto/AnswerDto.cs new file mode 100644 index 0000000..0467f5c --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Common/Dto/AnswerDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace IRaCIS.Core.Infra.EFCore.Common.Dto +{ + public class AnswerDto + { + public string QuestionName { get; set; } + + public string Answer { get; set; } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Infra.EFCore/Common/Dto/AuditingDto.cs b/IRaCIS.Core.Infra.EFCore/Common/Dto/AuditingDto.cs new file mode 100644 index 0000000..4e43a00 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Common/Dto/AuditingDto.cs @@ -0,0 +1,17 @@ +using IRaCIS.Core.Domain.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore.Common.Dto +{ + public class AuditingDto + { + public Type EntityType { get; set; } + + public Expression> Expression { get; set; } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/Common/Dto/DateDto.cs b/IRaCIS.Core.Infra.EFCore/Common/Dto/DateDto.cs new file mode 100644 index 0000000..9bd9e5a --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Common/Dto/DateDto.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore.Common.Dto +{ + public class DateDto + { + public string Code { get; set; } + + public string DateType { get; set; } + } + + public class TypeNameDto + { + public string TypeName { get; set; } + + + public List Types { get; set; } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/Common/Dto/ForeignKey.cs b/IRaCIS.Core.Infra.EFCore/Common/Dto/ForeignKey.cs new file mode 100644 index 0000000..54fe5eb --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Common/Dto/ForeignKey.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore.Common.Dto +{ + public class ForeignKey + { + public string Text { get; set; } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/Common/Dto/SetDictionaryValueDto.cs b/IRaCIS.Core.Infra.EFCore/Common/Dto/SetDictionaryValueDto.cs new file mode 100644 index 0000000..4f589b9 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Common/Dto/SetDictionaryValueDto.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.ComponentModel.DataAnnotations; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Infra.EFCore.Common.Dto +{ + public class SetInspectionEnumValueDto + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public List AuditDataIds { get; set; } + + + + } + + public class SetInspectionEnumDataDto + { + public Guid Id { get; set; } + + public string Identification { get; set; } + + public string JsonStr { get; set; } + + public Guid? ObjectRelationParentId { get; set; } + + public Guid? ObjectRelationParentId2 { get; set; } + + public Guid? ObjectRelationParentId3 { get; set; } + + + /// + /// 批次Id + /// + public Guid BatchId { get; set; } + + public DateTime CreateTime { get; set; } + } + + + + + public class AddInterface + { + public Guid ParentId { get; set; } + + public List Names { get; set; } + } + + public class AccessToDialogueInDto + { + public Guid Id { get; set; } + + public AccessToDialogueEnum Type { get; set; } + + public DateTime Createtime { get; set; } + } + + + public class AccessToDialogueOutDto + { + public string CreateUserName { get; set; } + + public string TalkContent { get; set; } + + public DateTime CreateTime { get; set; } + + public bool IsTitle { get; set; } + } + + public enum AccessToDialogueEnum + { + /// + /// 质疑 + /// + Question = 0, + + /// + /// 一致性核查 + /// + Consistency = 1, + } + + /// + /// 复制 + /// + public class CopyFrontAuditConfigItemDto + { + public Guid ParentId { get; set; } + + public Guid ChildId { get; set; } + } + + /// + /// 稽查数据 + /// + public class InspectionJsonDetail + { + public string EntityName { get; set; } + public object Data { get; set; } + + public object CommonData { get; set; } + } + + + public class InspectionConvertDTO : DataInspection + { + /// + /// 项目名称 + /// + public string TrialName { get; set; } + + /// + /// 中心Code + /// + public string SiteCode { get; set; } + + /// + /// 项目编码 + /// + public string ResearchProgramNo { get; set; } + + /// + /// 中心名称 + /// + public string SiteName { get; set; } + + /// + /// 患者名称 + /// + public string SubjectCode { get; set; } + + /// + /// 检查批次名称 + /// + public string SubjectVisitName { get; set; } + + /// + /// 盲态检查批次名 + /// + public string BlindName { get; set; } = string.Empty; + + + //标识操作 是否区分接口 + public bool IsDistinctionInterface=true; + + public string ExtraIndentification = string.Empty; + + + } + + /// + /// 稽查外层数据 + /// + public class InspectionGeneralData + { + /// + /// 项目iD + /// + public Guid? TrialId { get; set; } + + /// + /// 中心 + /// + public Guid? SiteId { get; set; } + + /// + /// 患者 + /// + public Guid? SubjectId { get; set; } + + /// + /// 检查批次 + /// + public Guid? SubjectVisitId { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + + /// + /// 项目名称 + /// + public string TrialName { get; set; } + + /// + /// 中心Code + /// + public string SiteCode { get; set; } + + /// + /// 项目编码 + /// + public string ResearchProgramNo { get; set; } + + + /// + /// 患者名称 + /// + public string SubjectCode { get; set; } + + /// + /// 检查批次名称 + /// + public string SubjectVisitName { get; set; } + + + + // /// + ///// 创建人名称 + ///// + //public string CreateUserName { get; set; } + + + // /// + // /// 角色名称 + // /// + // public string RoleName { get; set; } + + + } + + public class SetInspectionEnum + { + public string Identification { get; set; } + + public string Json { get; set; } + } + + + public class TableList + { + public string Name { get; set; } + + public string Remake { get; set; } + } + + + + + +} diff --git a/IRaCIS.Core.Infra.EFCore/Common/EFSqlQuery.cs b/IRaCIS.Core.Infra.EFCore/Common/EFSqlQuery.cs new file mode 100644 index 0000000..38c84a9 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Common/EFSqlQuery.cs @@ -0,0 +1,82 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using Microsoft.Data.SqlClient; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore +{ + public static class EFSqlQuery + { + public static IEnumerable SqlQuery(this DatabaseFacade facade, string sql, params object[] parameters) where T : class, new() + { + DataTable dt = SqlQuery(facade, sql, parameters); + return dt.ToEnumerable(); + } + + public static IEnumerable ToEnumerable(this DataTable dt) where T : class, new() + { + PropertyInfo[] propertyInfos = typeof(T).GetProperties(); + T[] ts = new T[dt.Rows.Count]; + int i = 0; + foreach (DataRow row in dt.Rows) + { + T t = new T(); + foreach (PropertyInfo p in propertyInfos) + { + if (dt.Columns.IndexOf(p.Name) != -1 && row[p.Name] != DBNull.Value) + p.SetValue(t, row[p.Name], null); + } + ts[i] = t; + i++; + } + return ts; + } + + public static DataTable SqlQuery(this DatabaseFacade facade, string sql, params object[] parameters) + { + DbCommand cmd = CreateCommand(facade, sql, out DbConnection conn, parameters); + DbDataReader reader = cmd.ExecuteReader(); + DataTable dt = new DataTable(); + dt.Load(reader); + reader.Close(); + conn.Close(); + return dt; + } + + private static DbCommand CreateCommand(DatabaseFacade facade, string sql, out DbConnection dbConn, params object[] parameters) + { + DbConnection conn = facade.GetDbConnection(); + dbConn = conn; + conn.Open(); + DbCommand cmd = conn.CreateCommand(); + if (facade.IsSqlServer()) + { + cmd.CommandText = sql; + CombineParams(ref cmd, parameters); + } + return cmd; + } + + private static void CombineParams(ref DbCommand command, params object[] parameters) + { + if (parameters != null) + { + foreach (SqlParameter parameter in parameters) + { + if (!parameter.ParameterName.Contains("@")) + parameter.ParameterName = $"@{parameter.ParameterName}"; + command.Parameters.Add(parameter); + } + + + } + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/Common/IAuditingData.cs b/IRaCIS.Core.Infra.EFCore/Common/IAuditingData.cs new file mode 100644 index 0000000..4c4deba --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Common/IAuditingData.cs @@ -0,0 +1,16 @@ +using Microsoft.EntityFrameworkCore.ChangeTracking; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore.Common +{ + public interface IAuditingData + { + //Task IncomingEntitys(List entitys); + + Task InsertAddEntitys(List entitys); + } +} diff --git a/IRaCIS.Core.Infra.EFCore/Common/ReadingCommon.cs b/IRaCIS.Core.Infra.EFCore/Common/ReadingCommon.cs new file mode 100644 index 0000000..7d5d018 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Common/ReadingCommon.cs @@ -0,0 +1,219 @@ +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore.Common +{ + public static class ReadingCommon + { + /// + /// 标准字典设置 + /// + public static class CriterionDictionary + { + /// + /// 全局评估类型 + /// + public const string GlobalAssess = "GlobalAssessType"; + + /// + /// 肿瘤学评估类型 + /// + public const string OncologyAssess = "OncologyAssessType"; + + + + + ///// + ///// 标准字典需要配置的相 + ///// + //public static List CriterionDictionaryCodeList = new List() + //{ + // "LesionType", + // "QuestionType" + //}; + } + + /// + /// 获取语言名称 + /// + /// + /// + /// + /// + public static string LanguageName(this string Name, string englishName, bool isEN) + { + return isEN ? englishName : Name; + } + + /// + /// 阅片问题类型 + /// + public static class QuestionType + { + + /// + /// 类型自动计算 + /// + public const string Calculation = "calculation"; + } + + + + + public static Dictionary SplitLesionDic = new Dictionary() + { + {1, "a" }, + {2, "b" }, + {3, "c" }, + {4, "d" }, + {5, "e" }, + {6, "f" }, + {7, "g" }, + {8, "h" }, + {9, "i" }, + {10, "j" }, + {11, "k" }, + {12, "l" }, + {13, "m" }, + {14, "n" }, + {15, "o" }, + {16, "p" }, + {17, "q" }, + {18, "r" }, + {19, "s" }, + {20, "t" }, + {21, "u" }, + {22, "v" }, + {23, "w" }, + {24, "x" }, + {25, "y" }, + {26, "z" }, + + }; + + public static string GetLesionMark(this decimal value) + { + if (value % 1 == 0) + { + return decimal.ToInt32(value).ToString().PadLeft(2, '0'); + } + else + { + return Math.Floor(value).ToString().PadLeft(2, '0') + SplitLesionDic[decimal.ToInt32((value % 1) * 100)]; + } + } + + public static Dictionary TaskNumDic = new Dictionary() + { + {ReadingCategory.Visit, 0 }, + {ReadingCategory.Global,(decimal) 0.03 }, + {ReadingCategory.Judge,(decimal) 0.02 }, + {ReadingCategory.Oncology, (decimal)0.06 }, + + }; + + public const string EvaluationReason = "肿瘤学阅片评估原因请依据临床数据填写,在与影像学结果不一致时必填。"; + + public static bool IsNullOrEmpty(this string value) + { + if (value == null || value == string.Empty) + { + return true; + } + else + { + return false; + } + } + + + /// + /// 获取枚举Int值 + /// + /// + /// + /// + public static string GetEnumInt(this T value) where T : Enum + { + + return ((int)(object)value).ToString(); + } + + + /// + /// 字符匹配枚举 + /// + /// + /// + /// + /// + public static bool EqEnum(this string value, T enumValue) where T:Enum + { + try + { + return int.Parse(value) == (int)(object)enumValue; + } + catch (Exception) + { + + return false; + } + } + public static decimal NullChange0(this decimal? value) + { + if (value == null) + { + return 0; + + } + else + { + return value.Value; + } + } + + public static decimal IsNullOrEmptyReturn0(this string value) + { + try + { + if (value == null || value == string.Empty || value == "NA") + { + return 0; + + } + else + { + return decimal.Parse(value); + } + } + catch (Exception) + { + + return 0; + } + + } + + + + /// + /// 获取DisplayName + /// + /// + /// + public static string GetDisplayName(this Enum enumName) + { + var type = enumName.GetType();//先获取这个枚举的类型 + var field = type.GetField(enumName.ToString());//通过这个类型获取到值 + var obj = (DisplayAttribute)field.GetCustomAttribute(typeof(DisplayAttribute));//得到特性 + return obj.Name ?? ""; + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/Context/AuditContext.cs b/IRaCIS.Core.Infra.EFCore/Context/AuditContext.cs new file mode 100644 index 0000000..6bfc50c --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Context/AuditContext.cs @@ -0,0 +1,56 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Infra.EFCore +{ + #region AuditContext + public class AuditContext : DbContext + { + //传递委托进来写日志 + + private readonly string _connectionString; + + public AuditContext(string connectionString) + { + _connectionString = connectionString; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseSqlServer(_connectionString); + + //public DbSet SaveChangesAudits { get; set; } + } + + public class SaveChangesAudit + { + public Guid Id { get; set; } + //public Guid AuditId { get; set; } + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } + public bool Succeeded { get; set; } = false; + public string ErrorMessage { get; set; } = string.Empty; + + public ICollection Entities { get; } = new List(); + } + + public class EntityAudit: IAuditAddWithUserName + { + public Guid Id { get; set; } + public Guid AlterId { get; set; }=Guid.Empty; + public Guid SaveChangesAuditId { get; set; } + public EntityState State { get; set; } + public string AuditMessage { get; set; } + + + + + public SaveChangesAudit SaveChangesAudit { get; set; } + + public string CreateUser { get; set; } = string.Empty; + public Guid CreateUserId { get; set; }=Guid.Empty; + public DateTime CreateTime { get; set; }=DateTime.Now; + } + #endregion +} diff --git a/IRaCIS.Core.Infra.EFCore/Context/DbContextExt.cs b/IRaCIS.Core.Infra.EFCore/Context/DbContextExt.cs new file mode 100644 index 0000000..0eb9ee8 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Context/DbContextExt.cs @@ -0,0 +1,250 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace IRaCIS.Core.Infra.EFCore +{ + public static class DbContextExt + { + + /// + /// 获取变化的实体信息 + /// + /// + /// + /// + public static IEnumerable GetChanges(this DbContext db) + { + return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified && e.Entity is T).Select(e => + { + var originalObject = e.OriginalValues.ToObject(); + var currentObject = e.CurrentValues.ToObject(); + return new ChangeEntry + { + EntityState = e.State, + Entity = e.Entity, + EntityType = e.OriginalValues.EntityType.ClrType, + ChangeProperties = e.OriginalValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(currentObject))), (t1, t2) => new ChangePropertyInfo() + { + PropertyInfo = t1.Property.PropertyInfo, + OriginalValue = t1.Value, + CurrentValue = t2.Value, + IsPrimaryKey = t1.Property.IsPrimaryKey(), + IsForeignKey = t1.Property.IsForeignKey() + }).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList() + }; + }); + } + + + + /// + /// 获取变化的实体信息 + /// + /// + /// + public static IEnumerable GetChanges(this DbContext db) + { + return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified).Select(e => + { + var originalObject = e.OriginalValues.ToObject(); + var currentObject = e.CurrentValues.ToObject(); + return new ChangeEntry() + { + EntityState = e.State, + Entity = e.Entity, + EntityType = e.OriginalValues.EntityType.ClrType, + ChangeProperties = e.OriginalValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(currentObject))), (t1, t2) => new ChangePropertyInfo() + { + PropertyInfo = t1.Property.PropertyInfo, + OriginalValue = t1.Value, + CurrentValue = t2.Value, + IsPrimaryKey = t1.Property.IsPrimaryKey(), + IsForeignKey = t1.Property.IsForeignKey(), + }).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList() + }; + }); + } + + /// + /// 获取添加的实体信息 + /// + /// + /// + /// + public static IEnumerable GetAdded(this DbContext db) + { + return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added && e.Entity is T).Select(e => + { + var currentObject = e.CurrentValues.ToObject(); + return new ChangeEntry + { + EntityState = e.State, + Entity = e.Entity, + EntityType = e.CurrentValues.EntityType.ClrType, + ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo() + { + PropertyInfo = p.PropertyInfo, + CurrentValue = p.PropertyInfo.GetValue(currentObject), + IsPrimaryKey = p.IsPrimaryKey(), + IsForeignKey = p.IsForeignKey(), + }).ToList() + }; + }); + } + + /// + /// 获取添加的实体信息 + /// + /// + /// + public static IEnumerable GetAdded(this DbContext db) + { + return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added).Select(e => + { + var currentObject = e.CurrentValues.ToObject(); + return new ChangeEntry + { + EntityState = e.State, + Entity = e.Entity, + EntityType = e.CurrentValues.EntityType.ClrType, + ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo() + { + PropertyInfo = p.PropertyInfo, + CurrentValue = p.PropertyInfo.GetValue(currentObject), + IsPrimaryKey = p.IsPrimaryKey(), + IsForeignKey = p.IsForeignKey(), + }).ToList() + }; + }); + } + + /// + /// 获取移除的实体信息 + /// + /// + /// + /// + public static IEnumerable GetRemoved(this DbContext db) + { + return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted && e.Entity is T).Select(e => + { + var originalObject = e.OriginalValues.ToObject(); + return new ChangeEntry + { + EntityState = e.State, + Entity = e.Entity, + EntityType = e.OriginalValues.EntityType.ClrType, + ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo() + { + PropertyInfo = p.PropertyInfo, + OriginalValue = p.PropertyInfo.GetValue(originalObject), + IsPrimaryKey = p.IsPrimaryKey(), + IsForeignKey = p.IsForeignKey(), + }).ToList() + }; + }); + } + + /// + /// 获取移除的实体信息 + /// + /// + /// + public static IEnumerable GetRemoved(this DbContext db) + { + return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted).Select(e => + { + var originalObject = e.OriginalValues.ToObject(); + return new ChangeEntry + { + EntityState = e.State, + Entity = e.Entity, + EntityType = e.OriginalValues.EntityType.ClrType, + ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo() + { + PropertyInfo = p.PropertyInfo, + OriginalValue = p.PropertyInfo.GetValue(originalObject), + IsPrimaryKey = p.IsPrimaryKey(), + IsForeignKey = p.IsForeignKey(), + }).ToList() + }; + }); + } + + /// + /// 获取所有的变更信息 + /// + /// + /// + /// + public static IEnumerable GetAllChanges(this DbContext db) + { + return GetChanges(db).Union(GetAdded(db)).Union(GetRemoved(db)); + } + + /// + /// 获取所有的变更信息 + /// + /// + /// + public static IEnumerable GetAllChanges(this DbContext db) + { + return GetChanges(db).Union(GetAdded(db)).Union(GetRemoved(db)); + } + } + + public class ChangePropertyInfo + { + /// + /// 属性 + /// + public PropertyInfo PropertyInfo { get; set; } + + /// + /// 原始值 + /// + public object OriginalValue { get; set; } + + /// + /// 新值 + /// + public object CurrentValue { get; set; } + + /// + /// 是否是主键 + /// + public bool IsPrimaryKey { get; set; } + + /// + /// 是否是外键 + /// + public bool IsForeignKey { get; set; } + } + + public class ChangeEntry + { + /// + /// 所属实体 + /// + public object Entity { get; set; } + + /// + /// 实体类型 + /// + public Type EntityType { get; set; } + + /// + /// 变更类型 + /// + public EntityState EntityState { get; set; } + + /// + /// 字段变更信息 + /// + public List ChangeProperties { get; set; } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs new file mode 100644 index 0000000..cfb59af --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs @@ -0,0 +1,754 @@ +using IRaCIS.Core.Domain.Models; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using System.Reflection; +using EntityFramework.Exceptions.SqlServer; +using IRaCIS.Core.Domain.Share; +using MassTransit; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.ValueGeneration; +using UserTypeGroup = IRaCIS.Core.Domain.Models.UserTypeGroup; +using IRaCIS.Core.Infra.EFCore.Common; +using IRaCIS.Core.Infra.EFCore.Common.Dto; +using EntityFramework.Exceptions.Common; +using IRaCIS.Core.Infrastructure; +using System.Data; + +namespace IRaCIS.Core.Infra.EFCore +{ + public class IRaCISDBContext : DbContext + { + public readonly IUserInfo _userInfo; + + public readonly ILogger _logger; + + // 在控制台 + //public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); }); + // 调试窗口 + public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddDebug(); }); + + public IRaCISDBContext(DbContextOptions options, IUserInfo userInfo, ILogger _Logger + + ) : base(options) + { + _userInfo = userInfo; + + _logger = _Logger; + + } + + + //比数据库上下文构造函数先执行 不能构造函数注入的方式使用配置文件 + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseLoggerFactory(MyLoggerFactory) + + .ReplaceService(); + + optionsBuilder.UseExceptionProcessor(); + + //var config = new ConfigurationBuilder() + // .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build(); + //connectionString = config.GetSection("ConnectionStrings:RemoteNew").Value; + //optionsBuilder.AddInterceptors(new AuditingInterceptor(connectionString)); + } + + #region IModelCacheKeyFactory + + + public class DynamicModelCacheKeyFactoryDesignTimeSupport : IModelCacheKeyFactory + { + public object Create(DbContext context, bool designTime) + => context is IRaCISDBContext dynamicContext + ? (context.GetType(), dynamicContext._userInfo.IsEn_Us, designTime) + : (object)context.GetType(); + + public object Create(DbContext context) + => Create(context, false); + } + + #endregion + + + + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(builder => + { + builder.HasBaseType((Type)null); + builder.ToView(null); + builder.HasNoKey(); + }); + + //modelBuilder.HasDbFunction(typeof(DbContext).GetMethod(nameof(GetTableList))); + + + + modelBuilder.Entity().HasMany(t => t.VisitTaskList).WithOne(t => t.DoctorUser).HasForeignKey(t => t.DoctorUserId).IsRequired(false); + + + modelBuilder.Entity().HasMany(t => t.DoctorTrialVisitTaskList).WithOne(t => t.DoctorTaskMedicalReviewRule).HasForeignKey(t => new { t.DoctorUserId, t.TrialId }).HasPrincipalKey(u => new { u.DoctorUserId, u.TrialId }); + + modelBuilder.Entity().HasMany(t => t.TaskMedicalReviewList).WithOne(t => t.TaskMedicalReviewRule).HasForeignKey(t => new { t.DoctorUserId, t.TrialId }).HasPrincipalKey(u => new { u.DoctorUserId, u.TrialId }); + + //会导致级联删除 + //modelBuilder.Entity().HasMany(t => t.SubjectArmVisitTaskList).WithOne(t => t.SujectArm).HasForeignKey(t => new { t.SubjectId, t.ArmEnum }).HasPrincipalKey(u => new { u.SubjectId, u.ArmEnum }) + // ./*IsRequired(false)*/OnDelete(DeleteBehavior.NoAction); + + // modelBuilder.Entity().HasOne(t => t.SujectArm).WithMany(s => s.SubjectArmVisitTaskList).HasForeignKey(t => new { t.SubjectId, t.ArmEnum }).HasPrincipalKey(u => new { u.SubjectId, u.ArmEnum }); + + modelBuilder.Entity().HasMany(t => t.JudgeVisitList).WithOne(t => t.JudgeVisitTask); + + + modelBuilder.Entity().HasMany(t => t.TaskMedicalReviewList).WithOne(t => t.VisitTask).HasForeignKey(t => t.VisitTaskId); + + modelBuilder.Entity().HasOne(t => t.Subject).WithMany(s => s.SubjectVisitTaskList).HasForeignKey(t => t.SubjectId); + + modelBuilder.Entity().HasMany(t => t.TaskInfluenceList).WithOne(s => s.OriginalTask).HasForeignKey(t => t.OriginalTaskId); + + + modelBuilder.Entity().HasMany(t => t.GlobalVisitResultList).WithOne(s => s.GlobalVisitTask).HasForeignKey(t => t.GlobalTaskId); + + + modelBuilder.Entity().HasMany(t => t.ChildList).WithOne(t => t.Parent); + modelBuilder.Entity().HasMany(t => t.EarlierSubjectUserList).WithOne(t => t.OrignalSubjectUser); + if (_userInfo.IsEn_Us) + { + modelBuilder.Entity().Property(t => t.MappedValue).HasColumnName(nameof(Domain.Models.Dictionary.Value)); + } + else + { + modelBuilder.Entity().Property(t => t.MappedValue).HasColumnName(nameof(Domain.Models.Dictionary.ValueCN)); + } + + + //遍历实体模型手动配置 + var typesToRegister = Assembly.GetExecutingAssembly().GetTypes().Where(q => q.GetInterface(typeof(IEntityTypeConfiguration<>).FullName) != null); + foreach (var type in typesToRegister) + { + dynamic configurationInstance = Activator.CreateInstance(type); + modelBuilder.ApplyConfiguration(configurationInstance); + } + + base.OnModelCreating(modelBuilder); + + + foreach (var entityType in modelBuilder.Model.GetEntityTypes()) + { + // 软删除配置 + if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType)) + { + entityType.AddSoftDeleteQueryFilter(); + + } + + if (typeof(Entity).IsAssignableFrom(entityType.ClrType)) + { + modelBuilder.Entity(entityType.ClrType).Property(nameof(Entity.Id)).HasValueGenerator(); + } + + + } + + } + + #region + public IQueryable GetTableList() + { + return Set().FromSqlRaw("EXEC dbo.procGetTableList"); + } + + public IQueryable GetTableColumn(string tableName) + { + return Set().FromSqlRaw($"EXEC dbo.procGetTableColumn {tableName}"); + } + #endregion + + #region Doctor + public virtual DbSet Dictionary { get; set; } + public virtual DbSet Doctor { get; set; } + + public virtual DbSet DoctorCriterionFile { get; set; } + public virtual DbSet DoctorDictionary { get; set; } + public virtual DbSet Postgraduate { get; set; } + public virtual DbSet Education { get; set; } + public virtual DbSet ResearchPublications { get; set; } + public virtual DbSet TrialExperience { get; set; } + + public virtual DbSet UserDoctor { get; set; } + public virtual DbSet Vacation { get; set; } + + public virtual DbSet Attachment { get; set; } + public virtual DbSet TrialExperienceCriteria { get; set; } + + + + + #endregion + + #region Enroll + + public virtual DbSet DoctorWorkload { get; set; } + public virtual DbSet Enroll { get; set; } + + public virtual DbSet EnrollReadingCategory { get; set; } + + + public virtual DbSet EnrollDetails { get; set; } + + + + + #endregion + + + #region Reading + public virtual DbSet TrialCriterionDictionaryCode { get; set; } + + public virtual DbSet SystemCriterionDictionaryCode { get; set; } + public virtual DbSet ReadingTaskRelation { get; set; } + public virtual DbSet OrganInfo { get; set; } + + public virtual DbSet ReadingCriterionDictionary { get; set; } + public virtual DbSet ReadingTableAnswerRowInfo { get; set; } + public virtual DbSet OrganTrialInfo { get; set; } + public virtual DbSet ReadingTableQuestionSystem { get; set; } + public virtual DbSet ReadingPeriodSet { get; set; } + + public virtual DbSet ReadingTaskQuestionAnswer { get; set; } + public virtual DbSet ReadingPeriodPlan { get; set; } + + public virtual DbSet ReadingClinicalData { get; set; } + + public virtual DbSet ReadingOncologyTaskInfo { get; set; } + public virtual DbSet ReadingGlobalTaskInfo { get; set; } + public virtual DbSet ReadingQuestionCriterionSystem { get; set; } + + public virtual DbSet ReadingQuestionCriterionTrial { get; set; } + + public virtual DbSet ReadingQuestionSystem { get; set; } + + public virtual DbSet ReadingQuestionTrial { get; set; } + + //public virtual DbSet ReadingClinicalDataView { get; set; } + + public virtual DbSet ReadingClinicalDataPDF { get; set; } + + public virtual DbSet ReadingJudgeInfo { get; set; } + + public virtual DbSet ReadModule { get; set; } + + public virtual DbSet ReadModuleView { get; set; } + + public virtual DbSet ClinicalDataTrialSet { get; set; } + + public virtual DbSet ClinicalDataSystemSet { get; set; } + + public virtual DbSet ReadingMedicineSystemQuestion { get; set; } + + public virtual DbSet ReadingMedicineTrialQuestion { get; set; } + + public virtual DbSet ReadingMedicineQuestionAnswer { get; set; } + + public virtual DbSet ReadingMedicalReviewDialog { get; set; } + + public virtual DbSet CriterionNidus { get; set; } + + public virtual DbSet ReadingTableQuestionTrial { get; set; } + + + public virtual DbSet TumorAssessment { get; set; } + + public virtual DbSet TrialClinicalDataSetCriterion { get; set; } + + + //public virtual DbSet TrialClinicalDataCriterion { get; set; } + //public virtual DbSet SystemClinicalDataCriterion { get; set; } + + #endregion + + #region Subject and Visit and study + public virtual DbSet StudyMonitor { get; set; } + + public virtual DbSet Subject { get; set; } + public virtual DbSet VisitPlans { get; set; } + public virtual DbSet VisitPlanInfluenceStudy { get; set; } + + public virtual DbSet VisitPlanInfluenceStat { get; set; } + + public virtual DbSet NoneDicomStudyFile { get; set; } + public virtual DbSet NoneDicomStudy { get; set; } + public virtual DbSet PreviousPDF { get; set; } + public virtual DbSet PreviousSurgery { get; set; } + public virtual DbSet PreviousOther { get; set; } + public virtual DbSet PreviousHistory { get; set; } + + public virtual DbSet StudyDTF { get; set; } + public virtual DbSet DicomStudys { get; set; } + public virtual DbSet DicomSeries { get; set; } + public virtual DbSet DicomInstances { get; set; } + public virtual DbSet ImageShare { get; set; } + + + #endregion + + #region Management + public virtual DbSet VerificationCodes { get; set; } + public virtual DbSet MenuFunctions { get; set; } + public virtual DbSet Roles { get; set; } + public virtual DbSet UserTypeMenuFunction { get; set; } + public virtual DbSet Users { get; set; } + public virtual DbSet TrialAudit { get; set; } + public virtual DbSet UserType { get; set; } + + public virtual DbSet SaveChangesAudits { get; set; } + + + + #endregion + + #region Institution + + + + public virtual DbSet ResearchCenter { get; set; } + public virtual DbSet Hospitals { get; set; } + public virtual DbSet CROCompany { get; set; } + public virtual DbSet Sponsor { get; set; } + + #endregion + + #region Trial + public virtual DbSet Trial { get; set; } + + + public virtual DbSet TrialDictionary { get; set; } + public virtual DbSet TrialDetail { get; set; } + public virtual DbSet UserTrial { get; set; } + + public virtual DbSet ProjectDictionary { get; set; } + + public virtual DbSet UserTrialSite { get; set; } + public virtual DbSet TrialSite { get; set; } + + public virtual DbSet Site { get; set; } + + public virtual DbSet User { get; set; } + + public virtual DbSet TrialSiteUserSurvey { get; set; } + public virtual DbSet TrialSiteEquipmentSurvey { get; set; } + public virtual DbSet TrialSiteSurvey { get; set; } + + public virtual DbSet StudyStatusDetails { get; set; } + + + + #endregion + + #region Financial + public virtual DbSet ReviewerPayInformation { get; set; } + public virtual DbSet RankPrice { get; set; } + public virtual DbSet TrialPaymentPrice { get; set; } + public virtual DbSet AwardPrice { get; set; } + public virtual DbSet Payment { get; set; } + public virtual DbSet PaymentDetail { get; set; } + public virtual DbSet ExchangeRate { get; set; } + public virtual DbSet TrialRevenuesPrice { get; set; } + public virtual DbSet PaymentAdjustment { get; set; } + public virtual DbSet TrialRevenuesPriceVerification { get; set; } + + + #endregion + + #region QC + + public virtual DbSet TrialQCQuestionConfigure { get; set; } + public virtual DbSet QCQuestionConfigure { get; set; } + public virtual DbSet TrialQCQuestionAnswer { get; set; } + public virtual DbSet CheckChallengeDialog { get; set; } + #endregion + + + #region QA + + public virtual DbSet QCChallengeDialog { get; set; } + //public virtual DbSet QATemplateDictionary { get; set; } + public virtual DbSet QCChallenge { get; set; } + public virtual DbSet SubjectVisit { get; set; } + #endregion + + + + + #region Document + public virtual DbSet SystemDocument { get; set; } + public virtual DbSet TrialDocument { get; set; } + public virtual DbSet TrialDocUserTypeConfirm { get; set; } + public virtual DbSet SystemDocConfirmedUser { get; set; } + public virtual DbSet SystemDocNeedConfirmedUserType { get; set; } + + public virtual DbSet TrialDocNeedConfirmedUserType { get; set; } + + public virtual DbSet TrialDocConfirmedUser { get; set; } + #endregion + + #region 暂时未用 + + #region 工作量分配 + //public virtual DbSet WorkloadTPs { get; set; } + //public virtual DbSet WorkloadGlobals { get; set; } + //public virtual DbSet WorkloadADs { get; set; } + //public virtual DbSet WorkloadDetails { get; set; } + #endregion + //public virtual DbSet SysMessages { get; set; } + //public virtual DbSet TrialAttachment { get; set; } + //public virtual DbSet SystemLogs { get; set; } + //public virtual DbSet TU { get; set; } + //public virtual DbSet TR { get; set; } + //public virtual DbSet RS { get; set; } + //public virtual DbSet Reports { get; set; } + //public virtual DbSet StudyReviewer { get; set; } + + //public virtual DbSet KeyInstances { get; set; } + //public virtual DbSet GlobalRS { get; set; } + //public virtual DbSet GlobalResult { get; set; } + //public virtual DbSet ImageLabels { get; set; } + + //public virtual DbSet QaTemplateItem { get; set; } + //public virtual DbSet QaTemplateItemDictionary { get; set; } + //public virtual DbSet QaTemplateTemplateItem { get; set; } + //public virtual DbSet QATrailTemplate { get; set; } + //public virtual DbSet QATrialTemplateItem { get; set; } + //public virtual DbSet QARecordTrialTemplateItem { get; set; } + //public virtual DbSet QARecordTemplateItemDetail { get; set; } + //public virtual DbSet QATemplate { get; set; } + //public virtual DbSet QANoticeUser { get; set; } + //public virtual DbSet QANotice { get; set; } + //public virtual DbSet TrialUserPreparation { get; set; } + + #endregion + + public virtual DbSet ShortcutKey { get; set; } + public virtual DbSet EmailNoticeConfig { get; set; } + public virtual DbSet SystemBasicData { get; set; } + + public virtual DbSet TrialSign { get; set; } + + public virtual DbSet TrialStateChange { get; set; } + + public virtual DbSet SystemAnonymization { get; set; } + + public virtual DbSet TrialExternalUser { get; set; } + + public virtual DbSet UserTypeGroup { get; set; } + + public virtual DbSet DataInspection { get; set; } + + + + public virtual DbSet FrontAuditConfig { get; set; } + + public virtual DbSet ConsistencyCheckFile { get; set; } + + public virtual DbSet CommonDocument { get; set; } + + public virtual DbSet SystemNotice { get; set; } + + public virtual DbSet SystemNoticeUserRead { get; set; } + + public virtual DbSet SystemNoticeUserType { get; set; } + + public virtual DbSet ReadingTableQuestionAnswer { get; set; } + + + + #region 废弃 + + public override int SaveChanges() + { + + SetCommonEntityAuditInfo(); + AddAudit().GetAwaiter(); + return base.SaveChanges(); + } + #endregion + public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) + { + // 采用触发器的方式 设置 CreateUserId CreateTime UpdateTime UpdateUserId 稽查实体里面没有这四个字段的值 因为先后顺序的原因 + SetCommonEntityAuditInfo(); + await AddAudit(); + + + var result = 0; + + try + { + + result = await base.SaveChangesAsync(cancellationToken); + } + catch (UniqueConstraintException ex) + { + _logger.LogError(ex.Message); + + throw new DBSaveFailedException("该唯一键已经存在于数据库中。"); + + } + catch (TimeoutException ex) + { + _logger.LogError(ex.Message); + + throw new DBSaveFailedException("数据库操作已经超时,请稍后重试。"); + + } + catch (CannotInsertNullException ex) + { + _logger.LogError(ex.Message); + + throw new DBSaveFailedException("无法在非空列上插入空值。"); + } + catch (MaxLengthExceededException ex) + { + _logger.LogError(ex.Message); + + throw new DBSaveFailedException("字符串超过了数据库列的最大长度。"); + + + + } + catch (NumericOverflowException ex) + { + _logger.LogError(ex.Message); + + throw new DBSaveFailedException("数值超过了数据类型的范围。"); + } + catch (SyntaxErrorException ex) + { + _logger.LogError(ex.Message); + + throw new DBSaveFailedException("SQL 查询中存在语法错误。"); + } + catch (ReferenceConstraintException ex) + { + _logger.LogError(ex.Message); + throw new DBSaveFailedException("无法进行当前操作,当前数据不符合外键约束。"); + } + catch (DbUpdateConcurrencyException ex) + { + _logger.LogError(ex.Message); + + throw new DBSaveFailedException("SQL 事务失败,请检查环境。"); + } + return result; + } + + + public async Task AddAudit() + { + + //触发器里面提交事务 业务方法里面提交事务 会记录两次 + var inspectionGeneralIdList = ChangeTracker.Entries().Where(t => typeof(DataInspection).IsAssignableFrom(t.Entity.GetType())).Select(t => ((DataInspection)t.Entity).GeneralId).ToList(); + + var entities = ChangeTracker.Entries().Where(u => (u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Added)) + .Where(t => !typeof(DataInspection).IsAssignableFrom(t.Entity.GetType()) && !inspectionGeneralIdList.Contains(((Entity)t.Entity).Id)) + .ToList(); + AuditingData auditingData = new AuditingData(this, _userInfo); + + //await auditingData.IncomingEntitys(entities); + + + if (entities.Count > 0) + { + await auditingData.InsertAddEntitys(entities); + + } + + + } + + /// + /// 重写savechange方式 统一增加审计信息 CreateUserId CreateTime UpdateTime Update UserId + /// + private void SetCommonEntityAuditInfo() + { + + ChangeTracker.DetectChanges(); // Important! + + // 获取所有更改,删除,新增的实体,但排除审计实体(避免死循环) + var entities = ChangeTracker.Entries() + .Where(u => (u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Added)).Where(x => !typeof(DataInspection).IsAssignableFrom(x.Entity.GetType())).ToList(); + + foreach (var t in entities) + { + switch (t.State) + { + + case EntityState.Deleted: + + break; + case EntityState.Modified: + + if (t.Entity is IAuditUpdate updateEntity1) + { + updateEntity1.UpdateTime = DateTime.Now; + updateEntity1.UpdateUserId = _userInfo.Id; + } + + if (t.Entity is ISoftDelete softDelete) + { + if (softDelete.IsDeleted) + { + softDelete.DeleteUserId = _userInfo.Id; + softDelete.DeletedTime = DateTime.Now; + } + else + { + softDelete.DeletedTime = null; + } + } + + break; + //添加的时候,更新审计字段也赋值 + case EntityState.Added: + + + if (t.Entity is IAuditAdd addEntity) + { + if (addEntity.CreateTime == default(DateTime)) + { + addEntity.CreateTime = DateTime.Now; + } + + addEntity.CreateUserId = _userInfo.Id; + } + + if (t.Entity is IAuditUpdate updateEntity) + { + updateEntity.UpdateTime = DateTime.Now; + updateEntity.UpdateUserId = _userInfo.Id; + } + + if (t.Entity is IAuditAddWithUserName addEntity3) + { + if (addEntity3.CreateTime == default(DateTime)) + { + addEntity3.CreateTime = DateTime.Now; + + } + + addEntity3.CreateUserId = _userInfo.Id; + addEntity3.CreateUser = _userInfo.RealName; + } + break; + } + } + + + } + + + + #region 更新审计信息 废弃 + + /// + /// 事件绑定的方式 更新审计信息 废弃 + /// + /// + /// + private void UpdateAuitUser(object sender, EntityEntryEventArgs e) + { + if (e.Entry.Entity is IAuditUpdate updateEntity) + { + switch (e.Entry.State) + { + //case EntityState.Deleted: + // entityWithTimestamps.UpdateTime = DateTime.UtcNow; + // Console.WriteLine($"Stamped for delete: {e.Entry.Entity}"); + // break; + case EntityState.Modified: + updateEntity.UpdateTime = DateTime.UtcNow; + updateEntity.UpdateUserId = _userInfo.Id; + break; + //添加的时候,更新审计字段也赋值 + case EntityState.Added: + updateEntity.UpdateTime = DateTime.UtcNow; + updateEntity.UpdateUserId = _userInfo.Id; + break; + } + } + if (e.Entry.Entity is IAuditAdd addEntity) + { + switch (e.Entry.State) + { + case EntityState.Added: + addEntity.CreateTime = DateTime.UtcNow; + addEntity.CreateUserId = _userInfo.Id; + break; + } + } + + if (e.Entry.Entity is IAuditAddWithUserName addEntity2) + { + switch (e.Entry.State) + { + case EntityState.Added: + addEntity2.CreateTime = DateTime.UtcNow; + addEntity2.CreateUserId = _userInfo.Id; + addEntity2.CreateUser = _userInfo.RealName; + break; + case EntityState.Deleted: + addEntity2.CreateTime = DateTime.UtcNow; + addEntity2.CreateUserId = _userInfo.Id; + addEntity2.CreateUser = _userInfo.RealName; + break; + } + } + } + + #endregion + + + + + public virtual DbSet TaskAllocationRule { get; set; } + + public virtual DbSet VisitTask { get; set; } + + public virtual DbSet SubjectUser { get; set; } + + public virtual DbSet VisitTaskReReading { get; set; } + + public virtual DbSet TaskMedicalReview { get; set; } + + public virtual DbSet TaskMedicalReviewRule { get; set; } + + public virtual DbSet TaskConsistentRule { get; set; } + + + public virtual DbSet TaskInfluence { get; set; } + + public virtual DbSet SubjectCanceDoctor { get; set; } + + public virtual DbSet TrialEmailNoticeConfig { get; set; } + + public virtual DbSet TrialEmailNoticeUser { get; set; } + + + } + + public class MySequentialGuidValueGenerator : ValueGenerator + { + public override Guid Next(EntityEntry entry) + { + return NewId.NextGuid(); + } + public override bool GeneratesTemporaryValues => false; + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Infra.EFCore/Context/Triggers/AuditAddTrigger.cs b/IRaCIS.Core.Infra.EFCore/Context/Triggers/AuditAddTrigger.cs new file mode 100644 index 0000000..6b29784 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Context/Triggers/AuditAddTrigger.cs @@ -0,0 +1,66 @@ +//using System; +//using System.Threading; +//using System.Threading.Tasks; +//using EntityFrameworkCore.Triggered; +//using IRaCIS.Core.Domain.Models; +//using IRaCIS.Core.Domain.Share; + +//namespace IRaCIS.Core.Application.Triggers +//{ +// public class AuditAddTrigger: IBeforeSaveTrigger +// { +// private readonly IUserInfo _userInfo; + +// public AuditAddTrigger(IUserInfo userInfo) +// { +// _userInfo = userInfo; +// } + +// public Task BeforeSave(ITriggerContext context, CancellationToken cancellationToken) +// { +// if (context.ChangeType == ChangeType.Added) +// { +// context.Entity.CreateUserId = _userInfo.Id; + +// if (context.Entity.CreateTime == default(DateTime)) +// { +// context.Entity.CreateTime = DateTime.Now; + +// } + +// } + +// return Task.CompletedTask; + +// } +// } + +// public class AuditAddWithUsernameTrigger : IBeforeSaveTrigger +// { +// private readonly IUserInfo _userInfo; + +// public AuditAddWithUsernameTrigger(IUserInfo userInfo) +// { +// _userInfo = userInfo; +// } + +// public Task BeforeSave(ITriggerContext context, CancellationToken cancellationToken) +// { +// if (context.ChangeType == ChangeType.Added) +// { +// context.Entity.CreateUserId = _userInfo.Id; +// context.Entity.CreateUser = _userInfo.UserName; + +// if (context.Entity.CreateTime == default(DateTime)) +// { +// context.Entity.CreateTime = DateTime.Now; +// } +// } + + + +// return Task.CompletedTask; + +// } +// } +//} \ No newline at end of file diff --git a/IRaCIS.Core.Infra.EFCore/Context/Triggers/AuditUpdateTrigger.cs b/IRaCIS.Core.Infra.EFCore/Context/Triggers/AuditUpdateTrigger.cs new file mode 100644 index 0000000..5b0d732 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Context/Triggers/AuditUpdateTrigger.cs @@ -0,0 +1,33 @@ +//using System; +//using System.Threading; +//using System.Threading.Tasks; +//using EntityFrameworkCore.Triggered; +//using IRaCIS.Core.Domain.Models; +//using IRaCIS.Core.Domain.Share; + +//namespace IRaCIS.Core.Application.Triggers +//{ +// public class AuditUpdateTrigger : IBeforeSaveTrigger +// { +// private readonly IUserInfo _userInfo; + +// public AuditUpdateTrigger(IUserInfo userInfo) +// { +// _userInfo = userInfo; +// } + +// public Task BeforeSave(ITriggerContext context, CancellationToken cancellationToken) +// { +// if (context.ChangeType == ChangeType.Modified || context.ChangeType == ChangeType.Added) +// { +// context.Entity.UpdateTime = DateTime.Now; + +// context.Entity.UpdateUserId = _userInfo.Id; + +// } + +// return Task.CompletedTask; + +// } +// } +//} \ No newline at end of file diff --git a/IRaCIS.Core.Infra.EFCore/Context/Triggers/SoftDeleteTrigger.cs b/IRaCIS.Core.Infra.EFCore/Context/Triggers/SoftDeleteTrigger.cs new file mode 100644 index 0000000..27c0367 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Context/Triggers/SoftDeleteTrigger.cs @@ -0,0 +1,41 @@ +//using System; +//using System.Threading; +//using System.Threading.Tasks; +//using EntityFrameworkCore.Triggered; +//using IRaCIS.Core.Domain.Models; +//using IRaCIS.Core.Domain.Share; + +//namespace IRaCIS.Core.Application.Triggers +//{ + +// public class SoftDeleteTrigger : IBeforeSaveTrigger +// { +// private readonly IUserInfo _userInfo; + +// public SoftDeleteTrigger(IUserInfo userInfo) +// { +// _userInfo = userInfo; +// } + +// //Generator Detached 状态才会进去 误用 +// //modelBuilder.Entity(entityType.ClrType).Property(nameof(ISoftDelete.DeletedTime)).HasValueGenerator().ValueGeneratedOnAddOrUpdate(); +// public Task BeforeSave(ITriggerContext context, CancellationToken cancellationToken) +// { +// if (context.ChangeType == ChangeType.Modified) +// { +// if (context.Entity.IsDeleted) +// { +// context.Entity.DeleteUserId = _userInfo.Id; +// context.Entity.DeletedTime = DateTime.Now; +// } +// else +// { +// context.Entity.DeletedTime = null; +// } +// } + +// return Task.CompletedTask; + +// } +// } +//} \ No newline at end of file diff --git a/IRaCIS.Core.Infra.EFCore/Context/ValueGenerator/UploadTotalMillisecondsIntervalGenerator.cs b/IRaCIS.Core.Infra.EFCore/Context/ValueGenerator/UploadTotalMillisecondsIntervalGenerator.cs new file mode 100644 index 0000000..a509648 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Context/ValueGenerator/UploadTotalMillisecondsIntervalGenerator.cs @@ -0,0 +1,34 @@ +//using System; +//using IRaCIS.Core.Domain.Models; +//using Microsoft.EntityFrameworkCore.ChangeTracking; +//using Microsoft.EntityFrameworkCore.ValueGeneration; + +//namespace IRaCIS.Core.Infra.EFCore.ValueGenerator +//{ +// /// +// /// 上传监控 时间间隔存储 需要按照这个字段进行排序 +// /// +// public class UploadTotalMillisecondsInterval : ValueGenerator +// { + +// //code first must migration dbfirst must config in db and also need config in code +// //modelBuilder.Entity().Property(e => e.TotalMillisecondsInterval).HasComputedColumnSql("datediff(MS,UploadStartTime,UploadFinishedTime)"); + +// //modelBuilder.Entity().Property(e => e.TestInterval).ValueGeneratedOnAddOrUpdate().HasDefaultValueSql("datediff(MS,UploadStartTime,UploadFinishedTime)"); + +// public override int Next(EntityEntry entry) +// { +// if (entry.Entity is StudyMonitor entity) +// { +// return (entity.UploadFinishedTime - entity.UploadStartTime)?.Milliseconds; +// } +// else +// { +// throw new ArgumentException("ValueGenerator用在了不适合的实体"); + +// } +// } +// public override bool GeneratesTemporaryValues => false; +// } + +//} \ No newline at end of file diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/DoctorConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/DoctorConfigration.cs new file mode 100644 index 0000000..bac1411 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/DoctorConfigration.cs @@ -0,0 +1,57 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + + +namespace IRaCIS.Core.Infra.EFCore.EntityConfigration +{ + public class DoctorConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + builder + .HasOne(dd => dd.Doctor) + .WithMany(p => p.DoctorDicRelationList) + .HasForeignKey(dd => dd.DoctorId); + + builder + .HasOne(dd => dd.Dictionary) + .WithMany(d => d.DoctorDicRelationList) + .HasForeignKey(dd => dd.DictionaryId); + } + + + } + + //public class DoctorTaskConfigration : IEntityTypeConfiguration + //{ + + + // public void Configure(EntityTypeBuilder builder) + // { + // builder + // .HasOne(dd => dd.DoctorUser) + // .WithMany(p => p.VisitTaskList) + // .HasForeignKey(dd => dd.DoctorUserId); + + + // } + + + //} + + public class DictionaryConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + builder.Property(e => e.MappedValue).Metadata.SetBeforeSaveBehavior(PropertySaveBehavior.Ignore); + builder.Property(e => e.MappedValue).Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); + + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/NoneDicomStudyConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/NoneDicomStudyConfigration.cs new file mode 100644 index 0000000..8d4c9eb --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/NoneDicomStudyConfigration.cs @@ -0,0 +1,29 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + + +namespace IRaCIS.Core.Infra.EFCore.EntityConfigration +{ + public class NoneDicomStudyConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + // builder + // .HasMany(s => s.TrialSiteUserList) + //.WithOne(c => c.DicomStudy) + //.HasForeignKey(s => new { s.TrialId, s.SiteId }) + //.HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + + builder + .HasOne(s => s.TrialSite) + .WithMany(c => c.NoneDicomStudyList) + .HasForeignKey(s => new { s.TrialId, s.SiteId }) + .HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/ReadingPeriodSiteConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/ReadingPeriodSiteConfigration.cs new file mode 100644 index 0000000..14fe438 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/ReadingPeriodSiteConfigration.cs @@ -0,0 +1,22 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + + +namespace IRaCIS.Core.Infra.EFCore.EntityConfigration +{ + public class ReadingPeriodSiteConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + + builder + .HasOne(s => s.TrialSite) + .WithMany(c => c.ReadingPeriodSites) + .HasForeignKey(s => new { s.TrialId, s.SiteId }) + .HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/StudyConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/StudyConfigration.cs new file mode 100644 index 0000000..78c26fc --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/StudyConfigration.cs @@ -0,0 +1,29 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + + +namespace IRaCIS.Core.Infra.EFCore.EntityConfigration +{ + public class StudyConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + // builder + // .HasMany(s => s.TrialSiteUserList) + // .WithOne(c => c.DicomStudy) + //.HasForeignKey(s => new { s.TrialId, s.SiteId }) + //.HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + + builder + .HasOne(s => s.TrialSite) + .WithMany(c => c.StudyList) + .HasForeignKey(s => new { s.TrialId, s.SiteId }) + .HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/StudyMonitorConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/StudyMonitorConfigration.cs new file mode 100644 index 0000000..b60f29a --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/StudyMonitorConfigration.cs @@ -0,0 +1,32 @@ +using IRaCIS.Core.Domain.Models; +//using IRaCIS.Core.Infra.EFCore.ValueGenerator; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + + +namespace IRaCIS.Core.Infra.EFCore.EntityConfigration +{ + public class StudyMonitorConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + // builder + // .HasMany(s => s.TrialSiteUserList) + //.WithOne(c => c.DicomStudy) + //.HasForeignKey(s => new { s.TrialId, s.SiteId }) + //.HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + //builder.Property(e => e.TotalMillisecondsInterval).HasValueGenerator().ValueGeneratedOnAdd(); + + + builder + .HasOne(s => s.TrialSite) + .WithMany(c => c.StudyMonitorList) + .HasForeignKey(s => new { s.TrialId, s.SiteId }) + .HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/SubjectConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/SubjectConfigration.cs new file mode 100644 index 0000000..d10f2f3 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/SubjectConfigration.cs @@ -0,0 +1,34 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + + +namespace IRaCIS.Core.Infra.EFCore.EntityConfigration +{ + public class SubjectConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + // 从 Subject 到 TrialSiteUserList 会用两个建 + //builder + //.HasMany(s => s.TrialSiteUserList) + //.WithOne(c => c.Subject) + //.HasForeignKey(s => new { s.TrialId, s.SiteId }) + //.HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + // 患者 在项目中只有一个site 获取 TrialSiteCode 用两个键 + builder + .HasOne(s => s.TrialSite) + .WithMany(c => c.SubjectList) + .HasForeignKey(s => new { s.TrialId, s.SiteId }) + .HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + //不能同时配置一对多 和一对一 但是有时表要存储多的最新的 比如患者 最新的检查批次 在这里要显示配置 + builder.HasOne(s => s.LatestSubjectVisit); + builder.HasOne(s => s.FinalSubjectVisit); + builder.HasMany(s => s.SubjectVisitList).WithOne(sv => sv.Subject); + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/SubjectVisitConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/SubjectVisitConfigration.cs new file mode 100644 index 0000000..6348b99 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/SubjectVisitConfigration.cs @@ -0,0 +1,33 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + + +namespace IRaCIS.Core.Infra.EFCore.EntityConfigration +{ + public class SubjectVisitConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + // SubjectVisit 从TrialSite 导航获取 TrialSiteCode 用两个键连接 + builder + .HasOne(s => s.TrialSite) + .WithMany(c => c.SubjectVisitList) + .HasForeignKey(s => new { s.TrialId, s.SiteId }) + .HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + + // 从 SubjectVisit 到 TrialSiteUserList 会用两个建 这里不行,添加患者的时候,批量添加检查批次的时候,siteId trialId 会重复 所以不能这样搞 + // modelBuilder.Entity() + //.HasMany(s => s.TrialSiteUserList) + //.WithOne(c => c.SubjectVisit) + //.HasForeignKey(s => new { s.TrialId, s.SiteId }) + //.HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + + builder.HasOne(s => s.Subject).WithMany(sv => sv.SubjectVisitList); + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialDocument.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialDocument.cs new file mode 100644 index 0000000..ff8d813 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialDocument.cs @@ -0,0 +1,38 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + + +namespace IRaCIS.Core.Infra.EFCore.EntityConfigration +{ + public class TrialDocUserTypeConfirmConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + ////项目文档 需要确认的用户类型 找到项目下对应用户类型的用户 用UserTypeId 关联 + //builder + // .HasMany(dd => dd.TrialUserList) + // .WithOne(d => d.TrialDocNeedConfirmedUserType) + // .HasForeignKey(s => new {/* s.TrialId,*/ s.User.UserTypeId }) + // .HasPrincipalKey(c => new { /*c.TrialDocument.TrialId,*/ c.NeedConfirmUserTypeId }); + } + } + + public class TrialDocConfirmUserConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + + + //builder + // .HasOne(dd => dd.TrialUser) + // .WithMany(d => d.TrialDocUserConfirmedList) + // .HasForeignKey(s => new { s.TrialDocument.TrialId, s.ConfirmUserId }) + // .HasPrincipalKey(c => new { c.TrialId, c.UserId }); + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialSiteConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialSiteConfigration.cs new file mode 100644 index 0000000..1d7ebd3 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialSiteConfigration.cs @@ -0,0 +1,23 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + + +namespace IRaCIS.Core.Infra.EFCore.EntityConfigration +{ + public class TrialSiteConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + //从 TrialSite 导航到TrialUserSite (Site下负责的CRC用户 )会用两个建 trial Site列表会用到 + builder + .HasMany(s => s.CRCUserList) + .WithOne(c => c.TrialSite) + .HasForeignKey(s => new { s.TrialId, s.SiteId }) + .HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialSiteSurveyConfigration .cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialSiteSurveyConfigration .cs new file mode 100644 index 0000000..4b4babd --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialSiteSurveyConfigration .cs @@ -0,0 +1,31 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + + +namespace IRaCIS.Core.Infra.EFCore.EntityConfigration +{ + public class TrialSiteSurveyConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + // SubjectVisit 从TrialSite 导航获取 TrialSiteCode 用两个键连接 + builder + .HasOne(s => s.TrialSite) + .WithMany(c => c.TrialSiteSurveyList) + .HasForeignKey(s => new { s.TrialId, s.SiteId }) + .HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + + // 从 SubjectVisit 到 TrialSiteUserList 会用两个建 这里不行,添加患者的时候,批量添加检查批次的时候,siteId trialId 会重复 所以不能这样搞 + // modelBuilder.Entity() + //.HasMany(s => s.TrialSiteUserList) + //.WithOne(c => c.SubjectVisit) + //.HasForeignKey(s => new { s.TrialId, s.SiteId }) + //.HasPrincipalKey(c => new { c.TrialId, c.SiteId }); + + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialUserConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialUserConfigration.cs new file mode 100644 index 0000000..9601c8f --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/TrialUserConfigration.cs @@ -0,0 +1,23 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace IRaCIS.Core.Infra.EFCore.EntityConfigration +{ + + + public class TrialUserConfigration : IEntityTypeConfiguration + { + + + public void Configure(EntityTypeBuilder builder) + { + builder + .HasMany(s => s.SiteList) + .WithOne(c => c.TrialUser) + .HasForeignKey(s => new { s.TrialId, s.UserId }) + .HasPrincipalKey(c => new { c.TrialId, c.UserId }); + + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj b/IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj new file mode 100644 index 0000000..5325a1f --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/IRaCIS.Core.Infra.EFCore.csproj @@ -0,0 +1,38 @@ + + + + net6.0 + AnyCPU;x64 + + + + ..\bin + + + + ..\bin + + + + ..\bin + + + + + + + + + + + + + + + + + + + + + diff --git a/IRaCIS.Core.Infra.EFCore/Interceptor/AuditInterceptor.cs b/IRaCIS.Core.Infra.EFCore/Interceptor/AuditInterceptor.cs new file mode 100644 index 0000000..75a4ee3 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Interceptor/AuditInterceptor.cs @@ -0,0 +1,161 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Diagnostics; + +public class AuditingInterceptor : ISaveChangesInterceptor +{ + private readonly string _connectionString; + private SaveChangesAudit _audit; + + public AuditingInterceptor(string connectionString) + { + _connectionString = connectionString; + } + + #region SavingChanges + public async ValueTask> SavingChangesAsync( + DbContextEventData eventData, + InterceptionResult result, + CancellationToken cancellationToken = default) + { + _audit = CreateAudit(eventData.Context); + + await Task.CompletedTask; + return result; + + + } + + public InterceptionResult SavingChanges( + DbContextEventData eventData, + InterceptionResult result) + { + _audit = CreateAudit(eventData.Context); + + return result; + } + #endregion + + #region SavedChanges + public int SavedChanges(SaveChangesCompletedEventData eventData, int result) + { + if (_audit.Entities.Count > 0) + { + var auditContext = eventData.Context as IRaCISDBContext; + _audit.Succeeded = true; + _audit.EndTime = DateTime.Now; + auditContext.SaveChangesAudits.Add(_audit); + auditContext.SaveChanges(); + } + + return result; + } + + public async ValueTask SavedChangesAsync( + SaveChangesCompletedEventData eventData, + int result, + CancellationToken cancellationToken = default) + { + if (_audit.Entities.Count > 0) + { + var auditContext = eventData.Context as IRaCISDBContext; + _audit.Succeeded = true; + auditContext.SaveChangesAudits.Add(_audit); + _audit.EndTime = DateTime.Now; + + await auditContext.SaveChangesAsync(); + } + + return result; + } + #endregion + + #region SaveChangesFailed + public void SaveChangesFailed(DbContextErrorEventData eventData) + { + using (var auditContext = new AuditContext(_connectionString)) + { + auditContext.Attach(_audit); + _audit.Succeeded = false; + _audit.EndTime = DateTime.Now; + _audit.ErrorMessage = eventData.Exception.Message; + + auditContext.SaveChanges(); + } + } + + public async Task SaveChangesFailedAsync( + DbContextErrorEventData eventData, + CancellationToken cancellationToken = default) + { + using (var auditContext = new AuditContext(_connectionString)) + { + auditContext.Attach(_audit); + _audit.Succeeded = false; + _audit.EndTime = DateTime.Now; + _audit.ErrorMessage = eventData.Exception.InnerException?.Message; + + await auditContext.SaveChangesAsync(cancellationToken); + } + } + #endregion + + + + #region CreateAudit + private static bool NeedAudit(EntityEntry entityEntry) + { + var type = entityEntry.Entity.GetType(); + return type != typeof(EntityAudit) && type != typeof(SaveChangesAudit) && type != typeof(DicomSeries) && type != typeof(DicomInstance) && type != typeof(TrialAudit); + } + + private static SaveChangesAudit CreateAudit(DbContext context) + { + context.ChangeTracker.DetectChanges(); + + var audit = new SaveChangesAudit { StartTime = DateTime.Now }; + + foreach (var entry in context.ChangeTracker.Entries().Where(t => NeedAudit(t))) + { + var auditMessage = entry.State switch + { + EntityState.Deleted => CreateDeletedMessage(entry), + EntityState.Modified => CreateModifiedMessage(entry), + EntityState.Added => CreateAddedMessage(entry), + _ => null + }; + + if (auditMessage != null) + { + var alterIdStr = entry.CurrentValues["Id"].ToString(); + audit.Entities.Add(new EntityAudit { State = entry.State, AuditMessage = auditMessage, AlterId = Guid.Parse(alterIdStr) }); + } + } + + return audit; + + string CreateAddedMessage(EntityEntry entry) + => + entry.Properties.Aggregate( + $"Inserting {entry.Metadata.DisplayName()} with ", + (auditString, property) => auditString + $"{property.Metadata.Name}: '{property.CurrentValue}' "); + + string CreateModifiedMessage(EntityEntry entry) + => entry.Properties.Where(property => property.IsModified || property.Metadata.IsPrimaryKey()).Aggregate( + $"Updating {entry.Metadata.DisplayName()} with ", + (auditString, property) => auditString + $"{property.Metadata.Name}: '{property.CurrentValue}' "); + + string CreateDeletedMessage(EntityEntry entry) + => entry.Properties.Where(property => property.Metadata.IsPrimaryKey()).Aggregate( + $"Deleting {entry.Metadata.DisplayName()} with ", + (auditString, property) => auditString + $"{property.Metadata.Name}: '{property.CurrentValue}' "); + } + #endregion + +} \ No newline at end of file diff --git a/IRaCIS.Core.Infra.EFCore/Interceptor/QueryWithNoLockDbCommandInterceptor.cs b/IRaCIS.Core.Infra.EFCore/Interceptor/QueryWithNoLockDbCommandInterceptor.cs new file mode 100644 index 0000000..cca322b --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Interceptor/QueryWithNoLockDbCommandInterceptor.cs @@ -0,0 +1,56 @@ +using Microsoft.EntityFrameworkCore.Diagnostics; +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore +{ + public class QueryWithNoLockDbCommandInterceptor : DbCommandInterceptor + { + private static readonly Regex TableAliasRegex = + new Regex(@"(?(FROM|JOIN) \[[a-zA-Z]\w*\] AS \[[a-zA-Z]\w*\](?! WITH \(NOLOCK\)))", + RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.IgnoreCase); + + public override InterceptionResult ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult result) + { + command.CommandText = TableAliasRegex.Replace( + command.CommandText, + "${tableAlias} WITH (NOLOCK)" + ); + return base.ScalarExecuting(command, eventData, result); + } + + public override ValueTask> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult result, + CancellationToken cancellationToken = new CancellationToken()) + { + command.CommandText = TableAliasRegex.Replace( + command.CommandText, + "${tableAlias} WITH (NOLOCK)" + ); + return base.ScalarExecutingAsync(command, eventData, result, cancellationToken); + } + + public override InterceptionResult ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult result) + { + command.CommandText = TableAliasRegex.Replace( + command.CommandText, + "${tableAlias} WITH (NOLOCK)" + ); + return result; + } + + public override ValueTask> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult result, + CancellationToken cancellationToken = new CancellationToken()) + { + command.CommandText = TableAliasRegex.Replace( + command.CommandText, + "${tableAlias} WITH (NOLOCK)" + ); + return base.ReaderExecutingAsync(command, eventData, result, cancellationToken); + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/Interceptor/SoftDeleteQueryExtension.cs b/IRaCIS.Core.Infra.EFCore/Interceptor/SoftDeleteQueryExtension.cs new file mode 100644 index 0000000..5f51789 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Interceptor/SoftDeleteQueryExtension.cs @@ -0,0 +1,34 @@ +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore.Metadata; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore +{ + public static class SoftDeleteQueryExtension + { + public static void AddSoftDeleteQueryFilter( + this IMutableEntityType entityData) + { + var methodToCall = typeof(SoftDeleteQueryExtension) + .GetMethod(nameof(GetSoftDeleteFilter), + BindingFlags.NonPublic | BindingFlags.Static) + .MakeGenericMethod(entityData.ClrType); + var filter = methodToCall.Invoke(null, new object[] { }); + entityData.SetQueryFilter((LambdaExpression)filter); + + } + + private static LambdaExpression GetSoftDeleteFilter() + where TEntity : class, ISoftDelete + { + Expression> filter = x => !x.IsDeleted; + return filter; + } + } +} diff --git a/IRaCIS.Core.Infra.EFCore/Report/ITU_TR_RSRepository.cs b/IRaCIS.Core.Infra.EFCore/Report/ITU_TR_RSRepository.cs new file mode 100644 index 0000000..98f0bff --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Report/ITU_TR_RSRepository.cs @@ -0,0 +1,17 @@ +using IRaCIS.Core.Domain.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace IRaCIS.Core.Infra.EFCore +{ + public interface ITURepository : IRepository + { + } + public interface ITRRepository : IRepository + { + } + public interface IRSRepository : IRepository + { + } +} diff --git a/IRaCIS.Core.Infra.EFCore/Report/IWorkloadDistributionRepository.cs b/IRaCIS.Core.Infra.EFCore/Report/IWorkloadDistributionRepository.cs new file mode 100644 index 0000000..8be8641 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Report/IWorkloadDistributionRepository.cs @@ -0,0 +1,19 @@ +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain; + +namespace IRaCIS.Core.Infra.EFCore +{ + public interface IWorkloadTPRepository : IRepository + { + } + public interface IWorkloadGlobalRepository : IRepository + { + } + public interface IWorkloadADRepository : IRepository + { + } + + public interface IWorkloadDetailRepository : IRepository + { + } +} diff --git a/IRaCIS.Core.Infra.EFCore/Report/TU_TR_RSRepository.cs b/IRaCIS.Core.Infra.EFCore/Report/TU_TR_RSRepository.cs new file mode 100644 index 0000000..14200d8 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Report/TU_TR_RSRepository.cs @@ -0,0 +1,27 @@ +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Infra.EFCore +{ + //public class TURepository : Repository, ITURepository + //{ + // public TURepository(IRaCISDBContext db) : base(db) + // { + + // } + //} + //public class TRRepository : Repository, ITRRepository + //{ + // public TRRepository(IRaCISDBContext db) : base(db) + // { + + // } + //} + //public class RSRepository : Repository, IRSRepository + //{ + // public RSRepository(IRaCISDBContext db) : base(db) + // { + + // } + //} +} diff --git a/IRaCIS.Core.Infra.EFCore/Report/WorkloadDistributionRepository.cs b/IRaCIS.Core.Infra.EFCore/Report/WorkloadDistributionRepository.cs new file mode 100644 index 0000000..a63d120 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Report/WorkloadDistributionRepository.cs @@ -0,0 +1,35 @@ +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.Infra.EFCore +{ + //public class WorkloadTPRepository : Repository, IWorkloadTPRepository + //{ + // public WorkloadTPRepository(IRaCISDBContext db) : base(db) + // { + + // } + //} + //public class WorkloadGlobalRepository : Repository, IWorkloadGlobalRepository + //{ + // public WorkloadGlobalRepository(IRaCISDBContext db) : base(db) + // { + + // } + //} + //public class WorkloadADRepository : Repository, IWorkloadADRepository + //{ + // public WorkloadADRepository(IRaCISDBContext db) : base(db) + // { + + // } + //} + + //public class WorkloadDetailRepository : Repository, IWorkloadDetailRepository + //{ + // public WorkloadDetailRepository(IRaCISDBContext db) : base(db) + // { + + // } + //} +} diff --git a/IRaCIS.Core.Infra.EFCore/Repository/ICommandRepository.cs b/IRaCIS.Core.Infra.EFCore/Repository/ICommandRepository.cs new file mode 100644 index 0000000..7819e22 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Repository/ICommandRepository.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore.ChangeTracking; + +namespace IRaCIS.Core.Infra.EFCore +{ + public interface ICommandRepository : ICommandRepository where TEntity : Entity + { + + Task InsertOrUpdateAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify); + + Task InsertFromDTOAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify); + + /// EF 跟踪方式,先查询出完整的实体 + + Task UpdateFromDTOAsync(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp[] verify); + + + + #region EF 跟踪方式删除 (先查询完整实体,再删除) + + /// 批量删除,EF跟踪方式(所有查询出来,再删除 浪费性能,但是稽查 或者触发某些操作时,需要知道数据库实体信息 不可避免用这种) + Task> DeleteFromQueryAsync(Expression> deleteFilter, bool autoSave = false, bool ignoreQueryFilter = false); + + Task> SoftDeleteFromQueryAsync(Expression> deleteFilter, bool autoSave = false, bool ignoreQueryFilter = false); + + Task DeleteFromQueryAsync(Guid id, bool autoSave = false, bool ignoreQueryFilter = false); + + + #endregion + + + #region EF跟踪方式 部分字段更新 会查询出来完整的实体 + + /// EF跟踪方式 已有查询好的,再更新部分字段 + Task UpdateAsync(TEntity entity, Expression> updateFactory, + bool autoSave = false, CancellationToken cancellationToken = default); + + /// EF跟踪方式 会去数据库查询完整的实体,再更新部分字段 + Task UpdatePartialFromQueryAsync(Guid id, Expression> updateFactory, + bool autoSave = false, CancellationToken cancellationToken = default); + + /// 稽查用这个 EF跟踪方式 先查询出来所有实体,再更新部分字段 + Task UpdatePartialFromQueryAsync(Expression> updateFilter, + Expression> updateFactory, + bool autoSave = false, bool ignoreQueryFilter = false, CancellationToken cancellationToken = default); + + #endregion + + #region EF跟踪方式 部分字段更新,不会去数据库查询完整实体 + + /// EF跟踪方式 生成 部分字段立即更新,不会去数据库查询完整的实体 + + Task UpdatePartialNowNoQueryAsync(Guid id, Expression> updateFactory, params EntityVerifyExp[] verify); + + /// EF跟踪方式 生成 部分字段更新,不会去数据库查询完整的实体 + Task UpdatePartialNoQueryAsync(Guid id, Expression> updateFactory, bool autoSave = false, params EntityVerifyExp[] verify); + #endregion + + #region 不走EF 跟踪机制,直接生成sql 批量更新或者删除 + + /// 批量删除,相当于原生sql, 没用EF跟踪方式(所有查询出来,再删除 浪费性能) + Task BatchDeleteNoTrackingAsync(Expression> deleteFilter); + + /// 批量更新,相当于原生sql, 没用EF跟踪方式(所有查询出来,再更新 浪费性能) + Task BatchUpdateNoTrackingAsync(Expression> where, Expression> updateFactory); + + #endregion + + + + + /// 不常用 暂时废弃 + Task UpdatePartialFieldsAsync(TEntity entity, string[] propertyNames, bool autoSave = false, bool ignoreEntityNullProperty = true, params EntityVerifyExp[] verify); + + + } + + public interface ICommandRepository where TEntity : class + { + EntityEntry Attach(TEntity entity); + + EntityEntry Entry(TEntity t); + + /// + ///不跟踪 查询单个实体,不会出现NUll + /// + /// + /// + /// + Task FirstAsync(Expression> exp = null, bool isTracking = false,bool ignoreQueryFilters = false); + + /// + ///跟踪 查询单个实体,会出现NUll + /// + /// + /// + /// + Task FirstOrDefaultAsync(Expression> exp = null, bool ignoreQueryFilters = false); + + Task FirstOrDefaultNoTrackingAsync(Expression> exp = null, bool ignoreQueryFilters = false); + + Task AnyAsync(Expression> exp, bool ignoreQueryFilters = false); + + bool Any(Expression> exp, bool ignoreQueryFilters = false); + + Task MaxAsync(Expression> selector); + + Task CountAsync(Expression> whereLambda = null, bool ignoreQueryFilters = false); + + ValueTask AddAsync(TEntity entity, bool autoSave = false); + + Task> AddRangeAsync(IEnumerable entities, bool autoSave = false); + + Task SaveChangesAsync(CancellationToken cancellationToken = default); + Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default); + + + + + + } + + + +} diff --git a/IRaCIS.Core.Infra.EFCore/Repository/IQueryRepository.cs b/IRaCIS.Core.Infra.EFCore/Repository/IQueryRepository.cs new file mode 100644 index 0000000..8b9a723 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Repository/IQueryRepository.cs @@ -0,0 +1,36 @@ +using AutoMapper; +using IRaCIS.Core.Domain.Models; +//using AutoMapper.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore +{ + public interface IQueryRepository where TEntity : Entity + { + IQueryable Select( Expression> selector); + + ValueTask FindAsync(TKey id, CancellationToken cancellationToken = default); + + ValueTask FindAsync(object[] keyValues, CancellationToken cancellationToken); + + } + + public interface IQueryRepository : IQueryRepository where TEntity : Entity + { + TEntity ImageFind(Guid id, Type type); + IQueryable Where(Expression> exp = null, bool isTraking = false, bool ignoreQueryFilters = false); + IQueryable AsQueryable( bool ignoreQueryFilters = false); + + IQueryable WhereIf(bool condition, Expression> filter); + IQueryable ProjectTo(IConfigurationProvider configuration, params Expression>[] membersToExpand); + IQueryable ProjectTo(IConfigurationProvider configuration, IDictionary parameters, params string[] membersToExpand); + IQueryable ProjectTo(IConfigurationProvider configuration, object parameters, params Expression>[] membersToExpand); + + } + +} \ No newline at end of file diff --git a/IRaCIS.Core.Infra.EFCore/Repository/IRaCISContextExtension.cs b/IRaCIS.Core.Infra.EFCore/Repository/IRaCISContextExtension.cs new file mode 100644 index 0000000..80bb27d --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Repository/IRaCISContextExtension.cs @@ -0,0 +1,177 @@ +using EFCore.BulkExtensions; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore +{ + public class EntityVerifyExp where TEntity : Entity + { + //验证表达式树 + public Expression> VerifyExp { get; set; } + + //验证提示错误信息 + public string VerifyMsg { get; set; } + + public VerifyEnum verifyType { get; set; } = VerifyEnum.Both; + + public bool IsVerify { get; set; } = true; + } + + public enum VerifyEnum + { + OnlyAdd = 1, + + OnlyUpdate = 2, + + Both = 3, + } + + + + + public static class EntityAction + { + /// + ///添加和更新的时候,通常需要与数据库已存在的数据进行校验,添加更新的区分在于是否需要排除自己 + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task EntityVerifyAsync(this IRaCISDBContext _dbContext, bool isAdd, EntityVerifyExp[] verify, Guid? entitydId = null) where T : Entity + { + + if (isAdd) + { + foreach (var verifyItem in verify.Where(t => t.verifyType != VerifyEnum.OnlyUpdate && t.IsVerify)) + { + if (await _dbContext.Set().AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false)) + { + throw new BusinessValidationFailedException(verifyItem.VerifyMsg); + } + } + } + else + { + foreach (var verifyItem in verify.Where(t => t.verifyType != VerifyEnum.OnlyAdd && t.IsVerify)) + { + if (verifyItem.verifyType == VerifyEnum.OnlyUpdate) + { + if (await _dbContext.Set().AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false)) + { + throw new BusinessValidationFailedException(verifyItem.VerifyMsg); + } + } + else if (verifyItem.verifyType == VerifyEnum.Both) + { + if (await _dbContext.Set().AnyAsync(verifyItem.VerifyExp.And(t => t.Id != entitydId)).ConfigureAwait(false)) + { + throw new BusinessValidationFailedException(verifyItem.VerifyMsg); + } + } + } + } + } + + ///注意 模型标注了 ConcurrencyCheck的属性,这样的实体,不适合用部分字段更新,ef生成的更新sql会自动带上ConcurrencyCheck的属性条件 + /// EntityState.Detached的实体 修改 部分字段 + public static void EntityModifyPartialFiled(this IRaCISDBContext _dbContext,T waitModifyEntity, Expression> updateFactory) where T : Entity + { + var entityEntry = _dbContext.Entry(waitModifyEntity); + //entityEntry.State = EntityState.Detached; + + var list = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name) + .Select(propName => typeof(T).GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)).ToList(); + + Func func = updateFactory.Compile(); + + T applyObj = func(waitModifyEntity); + + //深拷贝更新之前实体信息 + //var copyObj = waitModifyEntity.Clone(); + + foreach (PropertyInfo prop in list) + { + _dbContext.Entry(waitModifyEntity).Property(prop.Name).IsModified = true; + + object value = prop.GetValue(applyObj); + prop.SetValue(waitModifyEntity, value); + + } + + + } + + + #region 不走EF 跟踪机制的删除 更新 以及批量操作 + + /// 批量删除,相当于原生sql, 没用EF跟踪方式(所有查询出来,再删除 浪费性能) + public static async Task BatchDeleteNoTrackingAsync(this IRaCISDBContext _dbContext,Expression> deleteFilter) where T : Entity + { + if (deleteFilter == null) throw new ArgumentNullException(nameof(deleteFilter)); + + return await _dbContext.Set().IgnoreQueryFilters().Where(deleteFilter).BatchDeleteAsync() > 0; + } + + + /// 批量更新,相当于原生sql, 没用EF跟踪方式(所有查询出来,再更新 浪费性能) + public static async Task BatchUpdateNoTrackingAsync(this IRaCISDBContext _dbContext,Expression> where, Expression> updateFactory,Guid updateUserId) where T : Entity + { + if (where == null) throw new ArgumentNullException(nameof(where)); + + var bindings = ((MemberInitExpression)updateFactory.Body).Bindings.ToList(); + + var hasPropNameList = bindings.Select(t => t.Member.Name).ToList(); + + + if (typeof(IAuditUpdate).IsAssignableFrom(typeof(T))) + { + + if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateTime))) + { + bindings.Add(Expression.Bind(typeof(T).GetMember(nameof(IAuditUpdate.UpdateTime))[0], Expression.Constant(DateTime.Now))); + + } + + if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateUserId))) + { + bindings.Add(Expression.Bind(typeof(T).GetMember(nameof(IAuditUpdate.UpdateUserId))[0], Expression.Constant(updateUserId))); + + } + } + + + var member = Expression.MemberInit(Expression.New(typeof(T)), bindings); + + var factory = Expression.Lambda>(member, Expression.Parameter(typeof(T), "x")); + + + return await _dbContext.Set().IgnoreQueryFilters().Where(where).BatchUpdateAsync(factory).ConfigureAwait(false) > 0; + + + + } + + #endregion + } + + + + + + +} + + + + diff --git a/IRaCIS.Core.Infra.EFCore/Repository/IRepository.cs b/IRaCIS.Core.Infra.EFCore/Repository/IRepository.cs new file mode 100644 index 0000000..251a746 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Repository/IRepository.cs @@ -0,0 +1,463 @@ + + +using AutoMapper; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using IRaCIS.Core.Domain.Share; +using EFCore.BulkExtensions; +using Microsoft.Extensions.Localization; + +namespace IRaCIS.Core.Infra.EFCore +{ + + #region 泛型通用版本 + + + public interface IRepository + { + IQueryable GetQueryable(bool ignoreQueryFilters = false) where T : Entity; + DbSet Set() where T : Entity; + EntityEntry Entry(T t) where T : Entity; + + Task SaveChangesAsync(); + + IQueryable WhereIf(bool condition, Expression> filter) where T : Entity; + + + Task AnyAsync(Expression> filter, bool ignoreQueryFilters = false) where T : Entity; + + /// + ///不跟踪 查询单个实体,不会出现NUll + /// + /// + /// + /// + Task FirstAsync(Expression> exp = null, bool isTracking = false, bool ignoreQueryFilters = false) where T : Entity; + + Task FirstOrDefaultAsync(Expression> exp = null, bool ignoreQueryFilters = false) where T : Entity; + Task CountAsync(Expression> exp = null, bool ignoreQueryFilters = false) where T : Entity; + + IQueryable Where(Expression> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) where T : Entity; + + ValueTask FindAsync(Guid id) where T : Entity; + + + + Task InsertOrUpdateAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify) where T : Entity; + + Task InsertFromDTOAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify) where T : Entity; + + Task UpdateFromDTOAsync(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp[] verify) where T : Entity; + + + + ValueTask AddAsync(T entity, bool autoSave = false) where T : Entity; + + Task UpdateRange(IEnumerable entities, bool autoSave = false) where T : Entity; + Task AddRangeAsync(IEnumerable entities, bool autoSave = false) where T : Entity; + + Task UpdateAsync(T entity, bool autoSave = false) where T : Entity; + + Task DeleteAsync(T entity, bool autoSave = false) where T : Entity; + + + Task BatchDeleteAsync(Expression> deleteFilter) where T : Entity; + + Task BatchUpdateAsync(Expression> where, Expression> updateFactory) where T : Entity; + + Task UpdatePartialFromQueryAsync(Expression> updateFilter, + Expression> updateFactory, + bool autoSave = false, bool ignoreQueryFilter = false) where T : Entity; + } + + public class Repository : IRepository + { + #region 构造 基本 + private IRaCISDBContext _dbContext { get; } + public IMapper _mapper { get; set; } + + public IUserInfo _userInfo { get; set; } + + public IStringLocalizer _localizer { get; set; } + + public Repository(IRaCISDBContext dbContext, IMapper mapper, IUserInfo userInfo, IStringLocalizer localizer) + { + _localizer = localizer; + _dbContext = dbContext; + _mapper = mapper; + _userInfo = userInfo; + } + + /// + /// 设置是使用哪个仓储 默认不跟踪 + /// + /// + /// + /// + + public IQueryable GetQueryable(bool ignoreQueryFilters = false) where T : Entity + { + IQueryable query = _dbContext.Set(); + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + return query.AsNoTracking(); + } + public DbSet Set() where T : Entity + { + return _dbContext.Set(); + } + + public EntityEntry Entry(T t) where T : Entity + { + return _dbContext.Entry(t); + } + public IQueryable WhereIf(bool condition, Expression> filter) where T : Entity + { + IQueryable query = _dbContext.Set().AsNoTracking(); + return condition ? query.Where(filter) : query; + } + + private async Task SaveChangesAsync(bool autoSave) + { + if (autoSave) + { + return await SaveChangesAsync(); + } + else + { + return false; + } + } + + public async Task SaveChangesAsync() + { + + return await _dbContext.SaveChangesAsync().ConfigureAwait(false) > 0; + + } + + #endregion + + + + + public async Task InsertFromDTOAsync(TFrom from, bool autoSave = false, + params EntityVerifyExp[] verify) where T : Entity + { + + var entity = _mapper.Map(from); + + await _dbContext.EntityVerifyAsync(true, verify); + + entity = await AddAsync(entity, autoSave); + + + return entity; + } + + public async Task UpdateFromDTOAsync(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp[] verify) where T : Entity + { + var entity = _mapper.Map(from); + + + await _dbContext.EntityVerifyAsync(false, verify, entity.Id); + + var dbEntity = await _dbContext.Set().IgnoreQueryFilters().FirstOrDefaultAsync(t => t.Id == entity.Id).ConfigureAwait(false); + + if (dbEntity == null) + { + + throw new BusinessValidationFailedException(_localizer["Repository_UpdateError"]); + } + + var dbBeforEntity = dbEntity.Clone(); + + + _mapper.Map(from, dbEntity); + + + //DTO null 属性不更新 防止意外操作,导致保存数据错误,或者 add 和update 用一个模型,更新的时候,只传递了部分字段,导致,不想更新的字段,因为没传递值,用null覆盖了 + // Guid?属性 为null 时 映射到 Guid 时 默认会变成 Guid.Empty + if (ignoreDtoNullProperty) + { + var dbEntityProp = typeof(T).GetProperties(); + foreach (var propertyInfo in from.GetType().GetProperties()) + { + if (propertyInfo.GetValue(from) == null && dbEntityProp.Any(t => t.Name == propertyInfo.Name)) + { + _dbContext.Entry(dbEntity).Property(propertyInfo.Name).IsModified = false; + } + } + } + + await SaveChangesAsync(autoSave); + + + return dbBeforEntity; + } + + + + public async Task InsertOrUpdateAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify) where T : Entity + { + var entity = _mapper.Map(from); + + + if (entity.Id == Guid.Empty) + { + return await InsertFromDTOAsync(from, autoSave, verify); + } + else + { + return await UpdateFromDTOAsync(from, autoSave, false, verify); + } + } + + + public async Task AnyAsync(Expression> filter, bool ignoreQueryFilters = false) where T : Entity + { + + var query = _dbContext.Set().AsQueryable(); + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + return await query.AsNoTracking().AnyAsync(filter).ConfigureAwait(false); + + } + + + + + #region 基本查询 异步 + + /// + /// 跟踪查询某个实体 --异步 默认是跟踪查询 + /// + /// + /// + /// + /// + public async Task CountAsync(Expression> exp = null, bool ignoreQueryFilters = false) where T : Entity + { + var query = _dbContext.Set().AsQueryable(); + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + if (exp != null) + { + query = query.Where(exp); + } + + return await query.CountAsync().ConfigureAwait(false); + + + } + + public async Task FirstAsync(Expression> exp = null, bool isTracking = false, bool ignoreQueryFilters = false) where T : Entity + { + + var query = _dbContext.Set().AsQueryable(); + + if (!isTracking) + { + query = query.AsNoTracking(); + } + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + var entity = await query.FirstOrDefaultAsync(); + + if (entity is null) + { + throw new QueryBusinessObjectNotExistException($"The query object {typeof(T).Name} does not exist in database, Please check the query parameters"); + } + else + { + return entity; + } + + } + public async Task FirstOrDefaultAsync(Expression> exp = null, bool ignoreQueryFilters = false) where T : Entity + { + + var query = _dbContext.Set().AsQueryable(); + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + if (exp != null) + { + query = query.Where(exp); + } + + return await query.FirstOrDefaultAsync().ConfigureAwait(false); + } + + /// + /// 过滤 默认不跟踪 + /// + /// + /// + /// + /// + public IQueryable Where(Expression> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) where T : Entity + { + IQueryable query = _dbContext.Set(); + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + if (!isTraking) + { + query = query.AsNoTracking(); + } + + if (exp != null) + { + query = query.Where(exp); + } + return query; + } + + /// + /// 首先在内存中查找,其次再找数据库 + /// + /// + /// + /// + public async ValueTask FindAsync(Guid id) where T : Entity + { + return await _dbContext.Set().FindAsync(id).ConfigureAwait(false); + } + + #endregion + + #region 基本添加、更新、删除 异步 + + public async ValueTask AddAsync(T entity, bool autoSave = false) where T : Entity + { + + await _dbContext.Set().AddAsync(entity).ConfigureAwait(false); + + await SaveChangesAsync(autoSave); + + return entity; + } + + public async Task AddRangeAsync(IEnumerable entities, bool autoSave = false) where T : Entity + { + await _dbContext.Set().AddRangeAsync(entities).ConfigureAwait(false); + + return await SaveChangesAsync(autoSave); + } + + public async Task UpdateRange(IEnumerable entities, bool autoSave = false) where T : Entity + { + _dbContext.Set().UpdateRange(entities); + + await SaveChangesAsync(autoSave); + + } + + public async Task UpdateAsync(T entity, bool autoSave = false) where T : Entity + { + _dbContext.Set().Update(entity); + + return await SaveChangesAsync(autoSave); + } + + + public async Task DeleteAsync(T entity, bool autoSave = false) where T : Entity + { + _dbContext.Set().Remove(entity); + + return await SaveChangesAsync(autoSave); + + } + + public async Task DeleteManyAsync(IEnumerable entities, bool autoSave = false) where T : Entity + { + _dbContext.Set().RemoveRange(entities); + + return await SaveChangesAsync(autoSave); + } + + #endregion + + public async Task BatchDeleteAsync(Expression> deleteFilter) where T : Entity + { + return await _dbContext.BatchDeleteNoTrackingAsync(deleteFilter); + } + + public async Task BatchUpdateAsync(Expression> whereFilter, Expression> updateFactory) where T : Entity + { + + return await _dbContext.BatchUpdateNoTrackingAsync(whereFilter, updateFactory, _userInfo.Id); + + //return await _dbContext.Set().IgnoreQueryFilters().Where(whereFilter).BatchUpdateAsync(updateFactory).ConfigureAwait(false) > 0; + + } + + + public async Task UpdatePartialFromQueryAsync(Expression> updateFilter, + Expression> updateFactory, + bool autoSave = false, bool ignoreQueryFilter = false) where T : Entity + { + if (updateFilter == null) + { + throw new ArgumentException("更新过滤条件不允许为空", nameof(updateFilter)); + } + var query = ignoreQueryFilter ? _dbContext.Set().AsNoTracking().IgnoreQueryFilters() : _dbContext.Set().AsNoTracking(); + + var searchEntityList = await query.Where(updateFilter).ToListAsync(); + + foreach (var needUpdateEntity in searchEntityList) + { + _dbContext.EntityModifyPartialFiled(needUpdateEntity, updateFactory); + } + + await SaveChangesAsync(autoSave); + } + + public async Task UpdateAsync(T waitModifyEntity, Expression> updateFactory, bool autoSave = false) where T : Entity + { + + _dbContext.EntityModifyPartialFiled(waitModifyEntity, updateFactory); + + return await SaveChangesAsync(autoSave); + + } + + + } + + #endregion + + + + +} diff --git a/IRaCIS.Core.Infra.EFCore/Repository/Repository.cs b/IRaCIS.Core.Infra.EFCore/Repository/Repository.cs new file mode 100644 index 0000000..586e9ea --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Repository/Repository.cs @@ -0,0 +1,692 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using AutoMapper; +using IRaCIS.Core.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using AutoMapper.QueryableExtensions; +using EFCore.BulkExtensions; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.Extensions.Localization; + +namespace IRaCIS.Core.Infra.EFCore +{ + public interface IRepository : ICommandRepository, IQueryRepository where TEntity : Entity + { + IRaCISDBContext _dbContext { get; set; } + + } + + + public class Repository : IRepository + where TEntity : Entity, new() + { + + public IMapper _mapper { get; set; } + public IRaCISDBContext _dbContext { get; set; } + + public IStringLocalizer _localizer { get; set; } + + public DbSet _dbSet => _dbContext.Set(); + + public IUserInfo _userInfo { get; set; } + + public Repository(IRaCISDBContext dbContext, IMapper mapper, IUserInfo userInfo, IStringLocalizer localizer) + { + _localizer = localizer; + _dbContext = dbContext; + _mapper = mapper; + _userInfo = userInfo; + + } + + + + + #region 异步 EF 跟踪 添加 + public async Task> AddRangeAsync(IEnumerable entities, bool autoSave = false) + { + + await _dbSet.AddRangeAsync(entities).ConfigureAwait(false); + + await SaveChangesAsync(autoSave); + + return entities; + } + + /// EF跟踪方式 添加 + public async ValueTask AddAsync(TEntity entity, bool autoSave = false) + { + + await _dbSet.AddAsync(entity).ConfigureAwait(false); + + await SaveChangesAsync(autoSave); + + return entity; + } + + + + public async Task InsertFromDTOAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify) + { + + await _dbContext.EntityVerifyAsync(true, verify); + + var entity = _mapper.Map(from); + + entity = await AddAsync(entity, autoSave); + + + return entity; + + } + #endregion + + + #region 异步 EF 跟踪 部分字段更新 + + + /// 用前端传递的视图模型字段,更新,同时返回数据库该条记录的原始信息,方便对比某些字段是否更改,进行相应的逻辑操作 + + public async Task UpdateFromDTOAsync(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp[] verify) + { + + var entity = _mapper.Map(from); + + //await EntityVerifyAsync(false, verify, entity.Id); + + await _dbContext.EntityVerifyAsync(false, verify, entity.Id); + + var dbEntity = await _dbSet.IgnoreQueryFilters().FirstOrDefaultAsync(t => t.Id == entity.Id).ConfigureAwait(false); + + if (dbEntity == null) + { + + throw new BusinessValidationFailedException(_localizer["Repository_UpdateError"]); + } + + var dbBeforEntity = dbEntity.Clone(); + + + _mapper.Map(from, dbEntity); + + + //DTO null 属性不更新 防止意外操作,导致保存数据错误,或者 add 和update 用一个模型,更新的时候,只传递了部分字段,导致,不想更新的字段,因为没传递值,用null覆盖了 + // Guid?属性 为null 时 映射到 Guid 时 默认会变成 Guid.Empty + if (ignoreDtoNullProperty) + { + var dbEntityProp = typeof(TEntity).GetProperties(); + foreach (var propertyInfo in from.GetType().GetProperties()) + { + if (propertyInfo.GetValue(from) == null && dbEntityProp.Any(t => t.Name == propertyInfo.Name)) + { + _dbContext.Entry(dbEntity).Property(propertyInfo.Name).IsModified = false; + } + } + } + + await SaveChangesAsync(autoSave); + + + return dbBeforEntity; + } + + + + /// EF跟踪方式 生成 部分字段更新, 跟踪的实体仅有修改的属性的值有具体意义,没有从数据库查询完整的实体 + + public async Task UpdatePartialNoQueryAsync(Guid id, Expression> updateFactory, bool autoSave = false, params EntityVerifyExp[] verify) + { + + await _dbContext.EntityVerifyAsync(false, verify, id); + + var entity = new TEntity() { Id = id }; + + _dbContext.EntityModifyPartialFiled(entity, updateFactory); + + + + await SaveChangesAsync(autoSave); + + } + + + /// EF跟踪方式 生成 部分字段立即更新,默认会去处理更新更新人 更新时间 + public async Task UpdatePartialNowNoQueryAsync(Guid id, Expression> updateFactory, + params EntityVerifyExp[] verify) + { + await _dbContext.EntityVerifyAsync(false, verify, id); + + var entity = new TEntity() { Id = id }; + + _dbContext.EntityModifyPartialFiled(entity, updateFactory); + + return await SaveChangesAsync(true); + } + + + + + #endregion + + + #region 异步 EF 跟踪 自动生成 更新 和删除语句 + + + /// EF跟踪方式 更新,全字段更新 不好 + public async Task UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) + { + _dbSet.Update(entity); + + return await SaveChangesAsync(autoSave); + } + + + /// EF跟踪方式 外层先有查询好的完成实体,再更新部分字段 稽查的时候需要完整的实体信息 + public async Task UpdateAsync(TEntity waitModifyEntity, Expression> updateFactory, bool autoSave = false, CancellationToken cancellationToken = default) + { + + _dbContext.EntityModifyPartialFiled(waitModifyEntity, updateFactory); + + return await SaveChangesAsync(autoSave); + + } + + + /// EF跟踪方式 先查询出来,再更新部分字段 当在同一个事务里面需要更新同一个实体两次,请使用该方法,否则会因为重复跟踪同一个实体报错 (稽查的时候需要完整的实体信息) + public async Task UpdatePartialFromQueryAsync(Guid id, Expression> updateFactory, + bool autoSave = false, CancellationToken cancellationToken = default) + { + + //var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking(); + + + //不跟踪 查询出来的实体就是Detached + var searchEntity = await _dbSet.FindAsync( id); + + if (searchEntity == null) + { + throw new BusinessValidationFailedException(_localizer["Repository_UpdateError"]); + } + + _dbContext.EntityModifyPartialFiled(searchEntity, updateFactory); + + await SaveChangesAsync(autoSave); + + return searchEntity; + } + + public async Task UpdatePartialFromQueryAsync(Expression> updateFilter, + Expression> updateFactory, + bool autoSave = false, bool ignoreQueryFilter = false, CancellationToken cancellationToken = default) + { + if (updateFilter == null) + { + throw new ArgumentException("更新过滤条件不允许为空", nameof(updateFilter)); + } + var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking(); + + var searchEntityList = await query.Where(updateFilter).ToListAsync(); + + foreach (var needUpdateEntity in searchEntityList) + { + await UpdateAsync(needUpdateEntity, updateFactory, false); + } + + await SaveChangesAsync(autoSave); + + } + + + + + /// EF跟踪方式 删除 + public async Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) + { + _dbSet.Remove(entity); + + return await SaveChangesAsync(autoSave); + } + + + /// EF跟踪方式(查询出来,再删除 浪费性能,但是稽查 或者触发某些操作时,需要知道数据库实体信息 不可避免用这种) + public async Task DeleteFromQueryAsync(Guid id, bool autoSave = false, bool ignoreQueryFilter = false) + { + var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking(); + + var waitDelete = await query.Where(t => t.Id == id).FirstOrDefaultAsync(); + + if (waitDelete == null) + { + throw new BusinessValidationFailedException(_localizer["Repository_DeleteError"]); + } + + await DeleteAsync(waitDelete, autoSave); + + return waitDelete; + } + + + /// 批量删除,EF跟踪方式(所有查询出来,再删除 浪费性能,但是稽查 或者触发某些操作时,需要知道数据库实体信息 不可避免用这种) + public async Task> DeleteFromQueryAsync(Expression> deleteFilter, bool autoSave = false, bool ignoreQueryFilter = false) + { + var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking(); + var waitDeleteList = await query.Where(deleteFilter).ToListAsync(); + + _dbSet.RemoveRange(waitDeleteList); + await SaveChangesAsync(autoSave); + + return waitDeleteList; + + } + + public async Task> SoftDeleteFromQueryAsync(Expression> deleteFilter, bool autoSave = false, bool ignoreQueryFilter = false) + { + var query = ignoreQueryFilter ? _dbSet.IgnoreQueryFilters() : _dbSet; + var waitDeleteList = await query.Where(deleteFilter).ToListAsync(); + + foreach (var deleteItem in waitDeleteList) + { + if (deleteItem is ISoftDelete softDeleteItem) + { + softDeleteItem.IsDeleted = true; + } + } + + await SaveChangesAsync(autoSave); + + return waitDeleteList; + } + + #endregion + + + + #region 不走EF 跟踪机制的删除 更新 以及批量操作 + + /// 批量删除,相当于原生sql, 没用EF跟踪方式(所有查询出来,再删除 浪费性能) + public async Task BatchDeleteNoTrackingAsync(Expression> deleteFilter) + { + + return await _dbContext.BatchDeleteNoTrackingAsync(deleteFilter); + + } + + + /// 批量更新,相当于原生sql, 没用EF跟踪方式(所有查询出来,再更新 浪费性能) + public async Task BatchUpdateNoTrackingAsync(Expression> where, + Expression> updateFactory) + { + + return await _dbContext.BatchUpdateNoTrackingAsync(where, updateFactory,_userInfo.Id); + + } + + #endregion + + + + + #region 保存 、忽略 、验证 + public async Task InsertOrUpdateAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify) + { + var entity = _mapper.Map(from); + + + if (entity.Id == Guid.Empty) + { + return await InsertFromDTOAsync(from, autoSave, verify); + } + else + { + return await UpdateFromDTOAsync(from, autoSave, false, verify); + } + } + + + private async Task SaveChangesAsync(bool autoSave) + { + if (autoSave) + { + return await SaveChangesAsync(); + } + else + { + return false; + } + } + + /// + /// 忽略空值属性 + /// + /// + /// + private void IgnoreNullValues(ref TEntity entity, bool? ignoreNullValues = null) + { + var isIgnore = ignoreNullValues; + if (isIgnore == false) return; + + // 获取所有的属性 + var properties = _dbSet.EntityType.GetProperties(); + if (properties == null) return; + + foreach (var propety in properties) + { + var entityProperty = _dbContext.Entry(entity).Property(propety.Name); + var propertyValue = entityProperty?.CurrentValue; + var propertyType = entityProperty?.Metadata?.PropertyInfo?.PropertyType; + + // 判断是否是无效的值,比如为 null,默认时间,以及空 Guid 值 + var isInvalid = propertyValue == null + || (propertyType == typeof(DateTime) && propertyValue?.ToString() == new DateTime().ToString()) + || (propertyType == typeof(DateTimeOffset) && propertyValue?.ToString() == new DateTimeOffset().ToString()) + || (propertyType == typeof(Guid) && propertyValue?.ToString() == Guid.Empty.ToString()); + + if (isInvalid && entityProperty != null) + { + entityProperty.IsModified = false; + } + } + } + + + public async Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + + return await _dbContext.SaveChangesAsync(cancellationToken) > 0; + } + + + #endregion + + #region 不常用 + /// EF跟踪方式 生成 部分字段更新 (只更新传递的字段名 new[] {nameof(User.Name), nameof(User.Age)) + public async Task UpdatePartialFieldsAsync(TEntity entity, string[] propertyNames, + bool autoSave = false, bool ignoreEntityNullProperty = true, params EntityVerifyExp[] verify) + { + + await _dbContext.EntityVerifyAsync(false, verify, entity.Id); + + var entityEntry = _dbContext.Entry(entity); + entityEntry.State = EntityState.Detached; + + + foreach (var propertyName in propertyNames) + { + _dbContext.Entry(entity).Property(propertyName).IsModified = true; + } + + // 忽略空值 + IgnoreNullValues(ref entity, ignoreEntityNullProperty); + + return entityEntry.Entity; + } + + /// 更新 排除某些字段的更新 排除方式: new[] {nameof(User.Name), nameof(User.Age) + public async Task UpdateExcludeFields(TEntity entity, string[] propertyNames, bool autoSave = false, bool ignoreEntityNullProperty = true, params EntityVerifyExp[] verify) + { + await _dbContext.EntityVerifyAsync(false, verify, entity.Id); + + var entityEntry = _dbContext.Entry(entity); + entityEntry.State = EntityState.Modified; + + + foreach (var propertyName in propertyNames) + { + _dbContext.Entry(entity).Property(propertyName).IsModified = false; + } + + // 忽略空值 + IgnoreNullValues(ref entity, ignoreEntityNullProperty); + + return entityEntry.Entity; + } + + + #endregion + + + #region 异步查询 + + + + public async Task MaxAsync(Expression> selector) + { + + return await _dbSet.AsNoTracking().MaxAsync(selector); + } + + public async Task AnyAsync(Expression> exp, bool ignoreQueryFilters = false) + { + var query = _dbSet.AsQueryable(); + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + return await query.AsNoTracking().AnyAsync(exp); + } + + + public bool Any(Expression> exp, bool ignoreQueryFilters = false) + { + var query = _dbSet.AsQueryable(); + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + return query.AsNoTracking().Any(exp); + } + + public async Task CountAsync(Expression> whereLambda = null, bool ignoreQueryFilters = false) + { + var query = _dbSet.AsQueryable(); + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + return whereLambda == null ? await query.AsNoTracking().CountAsync() : await query.AsNoTracking().CountAsync(whereLambda); + } + + public async ValueTask FindAsync(Guid id, CancellationToken cancellationToken = default) + { + return await _dbContext.FindAsync(id); + } + + //有可能是联合主键,本项目没用到,用Guid + public async ValueTask FindAsync(object[] keyValues, CancellationToken cancellationToken) + { + return await _dbContext.FindAsync(keyValues); + } + + + public async Task FirstAsync(Expression> exp = null, bool isTracking = false, bool ignoreQueryFilters = false) + { + + var query = _dbSet.AsQueryable(); + + if (!isTracking) + { + query = query.AsNoTracking(); + } + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + var entity = await query.FirstOrDefaultAsync(exp); + + if (entity is null) + { + throw new QueryBusinessObjectNotExistException($"The query object {typeof(TEntity).Name} does not exist in database, Please check the query parameters"); + } + else + { + return entity; + } + + } + + public async Task FirstOrDefaultAsync(Expression> exp = null, bool ignoreQueryFilters = false) + { + + var query = _dbSet.AsQueryable(); + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + + if (exp != null) + { + query = query.Where(exp); + } + + return await query.FirstOrDefaultAsync().ConfigureAwait(false); + + } + + /// + /// 不跟踪 + /// + /// + /// + /// + public async Task FirstOrDefaultNoTrackingAsync(Expression> exp = null, bool ignoreQueryFilters = false) + { + + var query = _dbSet.AsNoTracking().AsQueryable(); + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + + if (exp != null) + { + query = query.Where(exp); + } + + return await query.AsNoTracking().FirstOrDefaultAsync().ConfigureAwait(false); + + } + + #endregion + + + #region 非异步部分 + + public TEntity ImageFind(Guid id, Type type) + { + //重传的时候 状态为修改 上传的时候状态为添加 内存中有,就不要重复查询数据库了 + var list = _dbContext.ChangeTracker.Entries() + .Where(u => (u.State == EntityState.Added || u.State == EntityState.Modified) && (u.Entity.GetType() == type)).Select(t => t.Entity as TEntity); + + var entity = list.FirstOrDefault(t => t.Id == id); + + if (entity == null) + { + return _dbSet.FirstOrDefault(t => t.Id == id); + } + else + { + return entity; + } + + } + + public IQueryable AsQueryable(bool ignoreQueryFilters = false) + { + var query = _dbSet.AsQueryable(); + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + return query.AsNoTracking(); + } + + public IQueryable Select(Expression> selector) + { + return _dbSet.AsNoTracking().Select(selector); + } + + public IQueryable WhereIf(bool condition, Expression> filter) + { + return condition ? _dbSet.AsNoTracking().Where(filter) : _dbSet.AsNoTracking(); + } + + public IQueryable Where(Expression> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) + { + IQueryable query = _dbSet; + + if (!isTraking) + { + query = query.AsNoTracking(); + } + + if (ignoreQueryFilters) + { + query = query.IgnoreQueryFilters(); + } + + if (exp != null) + { + query = query.Where(exp); + } + return query; + } + + public EntityEntry Entry(TEntity t) + { + return _dbContext.Entry(t); + } + + public EntityEntry Attach(TEntity entity) + { + return _dbSet.Attach(entity); + } + + public void Detached(TEntity t) + { + _dbContext.Entry(t).State = EntityState.Detached; + } + + + // automapper 相关 + public IQueryable ProjectTo(IConfigurationProvider configuration, object parameters, params Expression>[] membersToExpand) + { + return _dbSet.AsNoTracking().ProjectTo(configuration, parameters, membersToExpand); + } + + public IQueryable ProjectTo(IConfigurationProvider configuration, params Expression>[] membersToExpand) + { + return _dbSet.AsNoTracking().ProjectTo(configuration, membersToExpand); + } + + public IQueryable ProjectTo(IConfigurationProvider configuration, IDictionary parameters, params string[] membersToExpand) + { + return _dbSet.AsNoTracking().ProjectTo(configuration, parameters, membersToExpand); + } + + + #endregion + } + +} diff --git a/IRaCIS.Core.Infra.EFCore/UnitOfWork/EFUnitOfWork.cs b/IRaCIS.Core.Infra.EFCore/UnitOfWork/EFUnitOfWork.cs new file mode 100644 index 0000000..d12af4b --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/UnitOfWork/EFUnitOfWork.cs @@ -0,0 +1,62 @@ +using System; +using System.Data; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; + +namespace IRaCIS.Core.Infra.EFCore +{ + public class EFUnitOfWork : + IEFUnitOfWork where TDbContext : DbContext + , IDisposable + { + public DbContext DbContext { get; } + // https://docs.microsoft.com/en-us/ef/core/saving/transactions + private IDbContextTransaction _transaction; + + public EFUnitOfWork(TDbContext dbContext) + { + DbContext = dbContext; + } + + + public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted) + { + if (DbContext.Database.IsRelational()) + { + _transaction = DbContext.Database.BeginTransaction(isolationLevel); + } + } + + public async Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted) + { + if (DbContext.Database.IsRelational()) + { + _transaction = await DbContext.Database.BeginTransactionAsync(isolationLevel); + } + } + + //失败 自动回退 + public virtual void Commit() + { + DbContext.SaveChanges(); + _transaction?.Commit(); + } + + public virtual async Task CommitAsync(CancellationToken cancellationToken) + { + await DbContext.SaveChangesAsync(cancellationToken); + + await _transaction?.CommitAsync(cancellationToken); + + } + + public virtual void Dispose() + { + _transaction?.Dispose(); + } + + + } +} diff --git a/IRaCIS.Core.Infra.EFCore/UnitOfWork/EFUnitOfWorkFilter.cs b/IRaCIS.Core.Infra.EFCore/UnitOfWork/EFUnitOfWorkFilter.cs new file mode 100644 index 0000000..fd464e4 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/UnitOfWork/EFUnitOfWorkFilter.cs @@ -0,0 +1,118 @@ +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.EntityFrameworkCore; +using System; +using System.Data; +using System.Reflection; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore +{ + public sealed class UnitOfWorkAttribute : Attribute + { + public IsolationLevel _isolationLevel { get; set; } + + public bool IsCommitted { get; set; } + + public UnitOfWorkAttribute(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted) + { + _isolationLevel = isolationLevel; + } + + } + + public sealed class ManualCommitAttribute : Attribute + { + + } + + + public sealed class UnitOfWorkFilter : IAsyncActionFilter, IOrderedFilter + { + + /// + /// 过滤器排序 + /// + internal const int FilterOrder = 9999; + + public int Order => FilterOrder; + + /// + /// 数据库上下文 + /// + public IRaCISDBContext _dbContext { get; } + + public UnitOfWorkFilter(IRaCISDBContext dbContext) + { + _dbContext = dbContext; + } + + /// + /// 拦截请求 + /// + /// 动作方法上下文 + /// 中间件委托 + /// + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + // 获取动作方法描述器 + var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor; + var method = actionDescriptor.MethodInfo; + + // 判断是否贴有工作单元特性 + if (!method.IsDefined(typeof(UnitOfWorkAttribute), true)) + { + var resultContext = await next(); + } + //手动提交事务 + else if (method.IsDefined(typeof(UnitOfWorkAttribute), true) && method.IsDefined(typeof(ManualCommitAttribute), true)) + { + var resultContext = await next(); + } + else + { + //判断是否已经开启了事务 + var currentTransaction = _dbContext.Database.CurrentTransaction; + + + // 获取工作单元特性 + var unitOfWorkAttribute = method.GetCustomAttribute(); + + var strategy = _dbContext.Database.CreateExecutionStrategy(); + + await strategy.Execute(async () => + { + // 开启事务 + var _transaction = currentTransaction ?? _dbContext.Database.BeginTransaction(unitOfWorkAttribute._isolationLevel); + + // 调用 其他过滤器和Action业务 方法 + var resultContext = await next(); + + //await _dbContext.SaveChangesAsync(); + + var currentConnectionState = _dbContext.Database.GetDbConnection().State; + + + //之前的事务已经提交 连接已经关闭 + if (currentConnectionState != ConnectionState.Closed) + { + if (resultContext.Exception == null) + { + //提交事务 + await _transaction?.CommitAsync(); + } + else + { + //回退事务 + await _transaction?.RollbackAsync(); + } + } + + + }); + + } + } +} + +} diff --git a/IRaCIS.Core.Infra.EFCore/UnitOfWork/IEFUnitOfWork.cs b/IRaCIS.Core.Infra.EFCore/UnitOfWork/IEFUnitOfWork.cs new file mode 100644 index 0000000..6d04c78 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/UnitOfWork/IEFUnitOfWork.cs @@ -0,0 +1,22 @@ +using System; +using System.Data; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; + +namespace IRaCIS.Core.Infra.EFCore +{ + public interface IEFUnitOfWork :IDisposable where TDbContext : DbContext + { + DbContext DbContext { get; } + + + void BeginTransaction(IsolationLevel isolationLevel= IsolationLevel.ReadCommitted); + + Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted); + + void Commit(); + Task CommitAsync(CancellationToken cancellationToken); + + } +} diff --git a/IRaCIS.Core.Infrastructure/Cache/CacheType.cs b/IRaCIS.Core.Infrastructure/Cache/CacheType.cs new file mode 100644 index 0000000..5f64f0c --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Cache/CacheType.cs @@ -0,0 +1,18 @@ + +namespace IRaCIS.Core.Infra.Common.Cache +{ + /// + /// 缓存类型 + /// + public enum CacheType + { + /// + /// 内存缓存 + /// + Memory, + /// + /// Redis缓存 + /// + Redis + } +} diff --git a/IRaCIS.Core.Infrastructure/Cache/ICache.cs b/IRaCIS.Core.Infrastructure/Cache/ICache.cs new file mode 100644 index 0000000..defd2dd --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Cache/ICache.cs @@ -0,0 +1,107 @@ +using System; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.Common.Cache +{ + /// + /// 缓存接口 + /// + public interface ICache + { + /// + /// 用于在 key 存在时删除 key + /// + /// 键 + long Del(params string[] key); + + /// + /// 用于在 key 存在时删除 key + /// + /// 键 + /// + Task DelAsync(params string[] key); + + /// + /// 用于在 key 模板存在时删除 + /// + /// key模板 + /// + Task DelByPatternAsync(string pattern); + + /// + /// 检查给定 key 是否存在 + /// + /// 键 + /// + bool Exists(string key); + + /// + /// 检查给定 key 是否存在 + /// + /// 键 + /// + Task ExistsAsync(string key); + + /// + /// 获取指定 key 的值 + /// + /// 键 + /// + string Get(string key); + + /// + /// 获取指定 key 的值 + /// + /// byte[] 或其他类型 + /// 键 + /// + T Get(string key); + + /// + /// 获取指定 key 的值 + /// + /// 键 + /// + Task GetAsync(string key); + + /// + /// 获取指定 key 的值 + /// + /// byte[] 或其他类型 + /// 键 + /// + Task GetAsync(string key); + + /// + /// 设置指定 key 的值,所有写入参数object都支持string | byte[] | 数值 | 对象 + /// + /// 键 + /// 值 + bool Set(string key, object value); + + /// + /// 设置指定 key 的值,所有写入参数object都支持string | byte[] | 数值 | 对象 + /// + /// 键 + /// 值 + /// 有效期 + bool Set(string key, object value, TimeSpan expire); + + /// + /// 设置指定 key 的值,所有写入参数object都支持string | byte[] | 数值 | 对象 + /// + /// 键 + /// 值 + /// + Task SetAsync(string key, object value); + + /// + /// 设置指定 key 的值,所有写入参数object都支持string | byte[] | 数值 | 对象 + /// + /// 键 + /// 值 + /// 有效期 + /// + Task SetAsync(string key, object value, TimeSpan expire); + } +} diff --git a/IRaCIS.Core.Infrastructure/Cache/MemoryCache.cs b/IRaCIS.Core.Infrastructure/Cache/MemoryCache.cs new file mode 100644 index 0000000..16ffd57 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Cache/MemoryCache.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Memory; + +namespace IRaCIS.Core.Infra.Common.Cache +{ + /// + /// 内存缓存 + /// + public class MemoryCache : ICache + { + private readonly IMemoryCache _memoryCache; + public MemoryCache(IMemoryCache memoryCache) + { + _memoryCache = memoryCache; + } + + public long Del(params string[] key) + { + foreach(var k in key) + { + _memoryCache.Remove(k); + } + return key.Length; + } + + public Task DelAsync(params string[] key) + { + foreach (var k in key) + { + _memoryCache.Remove(k); + } + + return Task.FromResult((long)key.Length); + } + + public async Task DelByPatternAsync(string pattern) + { + if (string.IsNullOrEmpty(pattern)) + return default; + + pattern = Regex.Replace(pattern, @"\{.*\}", "(.*)"); + + var keys = GetAllKeys().Where(k => Regex.IsMatch(k, pattern)); + + if(keys != null && keys.Count() > 0) + { + return await DelAsync(keys.ToArray()); + } + + return default; + } + + public bool Exists(string key) + { + return _memoryCache.TryGetValue(key, out _); + } + + public Task ExistsAsync(string key) + { + return Task.FromResult(_memoryCache.TryGetValue(key, out _)); + } + + public string Get(string key) + { + return _memoryCache.Get(key)?.ToString(); + } + + public T Get(string key) + { + return _memoryCache.Get(key); + } + + public Task GetAsync(string key) + { + return Task.FromResult(Get(key)); + } + + public Task GetAsync(string key) + { + return Task.FromResult(Get(key)); + } + + public bool Set(string key, object value) + { + _memoryCache.Set(key, value); + return true; + } + + public bool Set(string key, object value, TimeSpan expire) + { + _memoryCache.Set(key, value, expire); + return true; + } + + public Task SetAsync(string key, object value) + { + Set(key, value); + return Task.FromResult(true); + } + + public Task SetAsync(string key, object value, TimeSpan expire) + { + Set(key, value, expire); + return Task.FromResult(true); + } + + private List GetAllKeys() + { + const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; + var entries = _memoryCache.GetType().GetField("_entries", flags).GetValue(_memoryCache); + var cacheItems = entries as IDictionary; + var keys = new List(); + if (cacheItems == null) return keys; + foreach (DictionaryEntry cacheItem in cacheItems) + { + keys.Add(cacheItem.Key.ToString()); + } + return keys; + } + } +} diff --git a/IRaCIS.Core.Infrastructure/Cache/RedisCache.cs b/IRaCIS.Core.Infrastructure/Cache/RedisCache.cs new file mode 100644 index 0000000..1c54017 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Cache/RedisCache.cs @@ -0,0 +1,88 @@ +using System; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.Common.Cache +{ + /// + /// Redis缓存 + /// + public class RedisCache : ICache + { + public long Del(params string[] key) + { + return RedisHelper.Del(key); + } + + public Task DelAsync(params string[] key) + { + return RedisHelper.DelAsync(key); + } + + public async Task DelByPatternAsync(string pattern) + { + if (string.IsNullOrEmpty(pattern)) + return default; + + pattern = Regex.Replace(pattern, @"\{.*\}", "*"); + + var keys = (await RedisHelper.KeysAsync(pattern)); + if(keys != null && keys.Length > 0) + { + return await RedisHelper.DelAsync(keys); + } + + return default; + } + + public bool Exists(string key) + { + return RedisHelper.Exists(key); + } + + public Task ExistsAsync(string key) + { + return RedisHelper.ExistsAsync(key); + } + + public string Get(string key) + { + return RedisHelper.Get(key); + } + + public T Get(string key) + { + return RedisHelper.Get(key); + } + + public Task GetAsync(string key) + { + return RedisHelper.GetAsync(key); + } + + public Task GetAsync(string key) + { + return RedisHelper.GetAsync(key); + } + + public bool Set(string key, object value) + { + return RedisHelper.Set(key, value); + } + + public bool Set(string key, object value, TimeSpan expire) + { + return RedisHelper.Set(key, value, expire); + } + + public Task SetAsync(string key, object value) + { + return RedisHelper.SetAsync(key, value); + } + + public Task SetAsync(string key, object value, TimeSpan expire) + { + return RedisHelper.SetAsync(key, value, expire); + } + } +} diff --git a/IRaCIS.Core.Infrastructure/Extention/ConvertJsonExtension.cs b/IRaCIS.Core.Infrastructure/Extention/ConvertJsonExtension.cs new file mode 100644 index 0000000..6da1715 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Extention/ConvertJsonExtension.cs @@ -0,0 +1,360 @@ +using Newtonsoft.Json; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using System.Text; +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static class ConvertJsonExtension + { + #region 私有方法 + /// + /// 过滤特殊字符 + /// + /// 字符串 + /// json字符串 + private static string String2Json(String s) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < s.Length; i++) + { + char c = s.ToCharArray()[i]; + switch (c) + { + case '\"': + sb.Append("\\\""); break; + case '\\': + sb.Append("\\\\"); break; + case '/': + sb.Append("\\/"); break; + case '\b': + sb.Append("\\b"); break; + case '\f': + sb.Append("\\f"); break; + case '\n': + sb.Append("\\n"); break; + case '\r': + sb.Append("\\r"); break; + case '\t': + sb.Append("\\t"); break; + default: + sb.Append(c); break; + } + } + return sb.ToString(); + } + /// + /// 格式化字符型、日期型、布尔型 + /// + /// + /// + /// + private static string StringFormat(string str, Type type) + { + if (type == typeof(string)) + { + str = String2Json(str); + str = "\"" + str + "\""; + } + else if (type == typeof(DateTime)) + { + str = "\"" + str + "\""; + } + else if (type == typeof(bool)) + { + str = str.ToLower(); + } + else if (type != typeof(string) && string.IsNullOrEmpty(str)) + { + str = "\"" + str + "\""; + } + return str; + } + + #endregion + + #region list转换成JSON + /// + /// list转换为Json + /// + /// + /// + /// + public static string ListToJson(this IList list) + { + object obj = list[0]; + return ListToJson(list, obj.GetType().Name); + } + /// + /// list转换为json + /// + /// + /// + /// + /// + private static string ListToJson(this IList list, string JsonName) + { + if (list.Count == 0) + { + return ""; + } + StringBuilder Json = new StringBuilder(); + if (string.IsNullOrEmpty(JsonName)) + JsonName = list[0].GetType().Name; + Json.Append("{\"" + JsonName + "\":["); + + for (int i = 0; i < list.Count; i++) + { + T obj = Activator.CreateInstance(); + PropertyInfo[] pi = obj.GetType().GetProperties(); + Json.Append("{"); + for (int j = 0; j < pi.Length; j++) + { + Type type = pi[j].GetValue(list[i], null).GetType(); + Json.Append("\"" + pi[j].Name.ToString() + "\":" + StringFormat(pi[j].GetValue(list[i], null).ToString(), type)); + if (j < pi.Length - 1) + { + Json.Append(","); + } + } + Json.Append("}"); + if (i < list.Count - 1) + { + Json.Append(","); + } + } + Json.Append("]}"); + return Json.ToString(); + } + #endregion + + #region 对象转换为Json + /// + /// 对象转换为json + /// + /// json对象 + /// json字符串 + public static string ToJson(this object jsonObject) + { + string jsonString = "{"; + PropertyInfo[] propertyInfo = jsonObject.GetType().GetProperties(); + for (int i = 0; i < propertyInfo.Length; i++) + { + object objectValue = propertyInfo[i].GetGetMethod().Invoke(jsonObject, null); + string value = string.Empty; + if (objectValue is DateTime || objectValue is Guid || objectValue is TimeSpan) + { + value = "'" + objectValue.ToString() + "'"; + } + else if (objectValue is string) + { + value = "'" + ToJson(objectValue.ToString()) + "'"; + } + else if (objectValue is IEnumerable) + { + value = ToJson((IEnumerable)objectValue); + } + else + { + value = ToJson(objectValue.ToString()); + } + jsonString += "\"" + ToJson(propertyInfo[i].Name) + "\":" + value + ","; + } + jsonString.Remove(jsonString.Length - 1, jsonString.Length); + return jsonString + "}"; + } + + #endregion + + #region 对象集合转换为json + /// + /// 对象集合转换为json + /// + /// 对象集合 + /// json字符串 + public static string ToJson(this IEnumerable array) + { + string jsonString = "{"; + foreach (object item in array) + { + jsonString += ToJson(item) + ","; + } + jsonString.Remove(jsonString.Length - 1, jsonString.Length); + return jsonString + "]"; + } + #endregion + + #region 普通集合转换Json + /// + /// 普通集合转换Json + /// + /// 集合对象 + /// Json字符串 + public static string ToArrayString(this IEnumerable array) + { + string jsonString = "["; + foreach (object item in array) + { + jsonString = ToJson(item.ToString()) + ","; + } + jsonString.Remove(jsonString.Length - 1, jsonString.Length); + return jsonString + "]"; + } + #endregion + + #region DataSet转换为Json + /// + /// DataSet转换为Json + /// + /// DataSet对象 + /// Json字符串 + public static string ToJson(this DataSet dataSet) + { + string jsonString = "{"; + foreach (DataTable table in dataSet.Tables) + { + jsonString += "\"" + table.TableName + "\":" + ToJson(table) + ","; + } + jsonString = jsonString.TrimEnd(','); + return jsonString + "}"; + } + #endregion + + #region Datatable转换为Json + /// + /// Datatable转换为Json + /// + /// Datatable对象 + /// Json字符串 + public static string ToJson(this DataTable dt) + { + StringBuilder jsonString = new StringBuilder(); + jsonString.Append("["); + DataRowCollection drc = dt.Rows; + for (int i = 0; i < drc.Count; i++) + { + jsonString.Append("{"); + for (int j = 0; j < dt.Columns.Count; j++) + { + string strKey = dt.Columns[j].ColumnName; + string strValue = drc[i][j].ToString(); + Type type = dt.Columns[j].DataType; + jsonString.Append("\"" + strKey + "\":"); + strValue = StringFormat(strValue, type); + if (j < dt.Columns.Count - 1) + { + jsonString.Append(strValue + ","); + } + else + { + jsonString.Append(strValue); + } + } + jsonString.Append("},"); + } + jsonString.Remove(jsonString.Length - 1, 1); + jsonString.Append("]"); + return jsonString.ToString(); + } + /// + /// DataTable转换为Json + /// + public static string ToJson(this DataTable dt, string jsonName) + { + StringBuilder Json = new StringBuilder(); + if (string.IsNullOrEmpty(jsonName)) + jsonName = dt.TableName; + Json.Append("{\"" + jsonName + "\":["); + if (dt.Rows.Count > 0) + { + for (int i = 0; i < dt.Rows.Count; i++) + { + Json.Append("{"); + for (int j = 0; j < dt.Columns.Count; j++) + { + Type type = dt.Rows[i][j].GetType(); + Json.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + StringFormat(dt.Rows[i][j].ToString(), type)); + if (j < dt.Columns.Count - 1) + { + Json.Append(","); + } + } + Json.Append("}"); + if (i < dt.Rows.Count - 1) + { + Json.Append(","); + } + } + } + Json.Append("]}"); + return Json.ToString(); + } + + #endregion + + #region DataReader转换为Json + /// + /// DataReader转换为Json + /// + /// DataReader对象 + /// Json字符串 + public static string ReaderJson(this IDataReader dataReader) + { + StringBuilder jsonString = new StringBuilder(); + Dictionary ModelField = new Dictionary(); + for (int i = 0; i < dataReader.FieldCount; i++) + { + ModelField.Add(dataReader.GetName(i), dataReader.GetFieldType(i)); + } + jsonString.Append("["); + while (dataReader.Read()) + { + jsonString.Append("{"); + foreach (KeyValuePair keyVal in ModelField) + { + Type type = keyVal.Value; + string strKey = keyVal.Key; + string strValue = dataReader[strKey].ToString(); + jsonString.Append("\"" + strKey + "\":"); + strValue = StringFormat(strValue, type); + jsonString.Append(strValue + ","); + } + jsonString.Remove(jsonString.Length - 1, 1); + jsonString.Append("},"); + } + dataReader.Close(); + jsonString.Remove(jsonString.Length - 1, 1); + jsonString.Append("]"); + return jsonString.ToString(); + } + #endregion + + + public static T DeserializeObject(this string entityString) + { + if (string.IsNullOrEmpty(entityString)) + { + return default(T); + } + if (entityString == "{}") + { + entityString = "[]"; + } + return JsonConvert.DeserializeObject(entityString); + } + + public static string Serialize(this object obj, JsonSerializerSettings formatDate = null) + { + if (obj == null) return null; + formatDate = formatDate ?? new JsonSerializerSettings + { + DateFormatString = "yyyy-MM-dd HH:mm:ss" + }; + return JsonConvert.SerializeObject(obj, formatDate); + } + + } +} + diff --git a/IRaCIS.Core.Infrastructure/Extention/EnumToSelectExtension.cs b/IRaCIS.Core.Infrastructure/Extention/EnumToSelectExtension.cs new file mode 100644 index 0000000..d64fe47 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Extention/EnumToSelectExtension.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static class EnumToSelectExtension + { + /// + /// 将枚举转换成字典(枚举,自定义描述) + /// + /// + /// 需要排除的枚举 + /// + public static Dictionary ToSelect( params TEnum[] exceptList) where TEnum : struct, Enum + { + var type = typeof(TEnum); + var dict = new Dictionary(); + foreach (var value in Enum.GetValues()) + //foreach (var value in type.GetEnumValues()) + { + var attr = type.GetField(value.ToString()) + .GetCustomAttribute(); + if (attr is null || exceptList.Contains(value)) continue; + + var key = type.GetFields().FirstOrDefault(t=>t.Name== value.ToString()).GetRawConstantValue(); + dict[key] = attr.Description; + } + return dict; + } + + /// + /// 扩展方法,获得枚举的Description + /// + /// 枚举值 + /// 当枚举值没有定义DescriptionAttribute,是否使用枚举名代替,默认是使用 + /// 枚举的Description + public static string GetDescription(this Enum value, Boolean nameInstead = true) + { + Type type = value.GetType(); + string name = Enum.GetName(type, value); + if (name == null) + { + return null; + } + + FieldInfo field = type.GetField(name); + DescriptionAttribute attribute = System.Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute; + + if (attribute == null && nameInstead == true) + { + return name; + } + return attribute?.Description; + } + + } + + + + +} diff --git a/IRaCIS.Core.Infrastructure/Extention/FileExt.cs b/IRaCIS.Core.Infrastructure/Extention/FileExt.cs new file mode 100644 index 0000000..95922a3 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Extention/FileExt.cs @@ -0,0 +1,63 @@ +using System.IO; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + /// + /// 大文件操作扩展类 + /// + public static class FileExt + { + /// + /// 以文件流的形式复制大文件 + /// + /// 源 + /// 目标地址 + /// 缓冲区大小,默认8MB + public static void CopyToFile(this Stream fs, string dest, int bufferSize = 1024 * 8 * 1024) + { + using var fsWrite = new FileStream(dest, FileMode.OpenOrCreate, FileAccess.ReadWrite); + byte[] buf = new byte[bufferSize]; + int len; + while ((len = fs.Read(buf, 0, buf.Length)) != 0) + { + fsWrite.Write(buf, 0, len); + } + } + + /// + /// 以文件流的形式复制大文件(异步方式) + /// + /// 源 + /// 目标地址 + /// 缓冲区大小,默认8MB + public static async void CopyToFileAsync(this Stream fs, string dest, int bufferSize = 1024 * 1024 * 8) + { + using var fsWrite = new FileStream(dest, FileMode.OpenOrCreate, FileAccess.ReadWrite); + byte[] buf = new byte[bufferSize]; + int len; + await Task.Run(() => + { + while ((len = fs.Read(buf, 0, buf.Length)) != 0) + { + fsWrite.Write(buf, 0, len); + } + }).ConfigureAwait(true); + } + + /// + /// 将内存流转储成文件 + /// + /// + /// + public static void SaveFile(this MemoryStream ms, string filename) + { + using var fs = new FileStream(filename, FileMode.Create, FileAccess.Write); + byte[] buffer = ms.ToArray(); // 转化为byte格式存储 + fs.Write(buffer, 0, buffer.Length); + fs.Flush(); + } + + + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Infrastructure/Extention/IDictionaryExtensions.cs b/IRaCIS.Core.Infrastructure/Extention/IDictionaryExtensions.cs new file mode 100644 index 0000000..83d7bbd --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Extention/IDictionaryExtensions.cs @@ -0,0 +1,437 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static class IDictionaryExtensions + { + /// + /// 添加或更新键值对 + /// + /// + /// + /// + /// 另一个字典集 + /// + public static void AddOrUpdate(this IDictionary @this, IDictionary that) + { + foreach (var item in that) + { + @this[item.Key] = item.Value; + } + } + + /// + /// 添加或更新键值对 + /// + /// + /// + /// + /// 另一个字典集 + /// + public static void AddOrUpdateTo(this IDictionary @this, IDictionary that) + { + foreach (var item in @this) + { + that[item.Key] = item.Value; + } + } + + /// + /// 添加或更新键值对 + /// + /// + /// + /// + /// 键 + /// 添加时的值 + /// 更新时的操作 + /// + public static TValue AddOrUpdate(this IDictionary @this, TKey key, TValue addValue, Func updateValueFactory) + { + if (!@this.ContainsKey(key)) + { + @this.Add(key, addValue); + } + else + { + @this[key] = updateValueFactory(key, @this[key]); + } + + return @this[key]; + } + + /// + /// 添加或更新键值对 + /// + /// + /// + /// + /// 键 + /// 添加时的值 + /// 更新时的操作 + /// + public static async Task AddOrUpdateAsync(this IDictionary @this, TKey key, TValue addValue, Func> updateValueFactory) + { + if (!@this.ContainsKey(key)) + { + @this.Add(key, addValue); + } + else + { + @this[key] = await updateValueFactory(key, @this[key]); + } + + return @this[key]; + } + + /// + /// 添加或更新键值对 + /// + /// + /// + /// + /// 键 + /// 添加时的值 + /// 更新时的值 + /// + public static TValue AddOrUpdate(this IDictionary @this, TKey key, TValue addValue, TValue updateValue) + { + if (!@this.ContainsKey(key)) + { + @this.Add(key, addValue); + } + else + { + @this[key] = updateValue; + } + + return @this[key]; + } + + /// + /// 添加或更新键值对 + /// + /// + /// + /// + /// 另一个字典集 + /// 更新时的操作 + /// + public static void AddOrUpdate(this IDictionary @this, IDictionary that, Func updateValueFactory) + { + foreach (var item in that) + { + AddOrUpdate(@this, item.Key, item.Value, updateValueFactory); + } + } + + /// + /// 添加或更新键值对 + /// + /// + /// + /// + /// 另一个字典集 + /// 更新时的操作 + /// + public static Task AddOrUpdateAsync(this IDictionary @this, IDictionary that, Func> updateValueFactory) + { + return that.ForeachAsync(item => AddOrUpdateAsync(@this, item.Key, item.Value, updateValueFactory)); + } + + /// + /// 添加或更新键值对 + /// + /// + /// + /// + /// 另一个字典集 + /// 更新时的操作 + /// + public static void AddOrUpdateTo(this IDictionary @this, IDictionary that, Func updateValueFactory) + { + foreach (var item in @this) + { + AddOrUpdate(that, item.Key, item.Value, updateValueFactory); + } + } + + /// + /// 添加或更新键值对 + /// + /// + /// + /// + /// 另一个字典集 + /// 更新时的操作 + /// + public static Task AddOrUpdateAsyncTo(this IDictionary @this, IDictionary that, Func> updateValueFactory) + { + return @this.ForeachAsync(item => AddOrUpdateAsync(that, item.Key, item.Value, updateValueFactory)); + } + + /// + /// 添加或更新键值对 + /// + /// + /// + /// + /// 键 + /// 添加时的操作 + /// 更新时的操作 + /// + public static TValue AddOrUpdate(this IDictionary @this, TKey key, Func addValueFactory, Func updateValueFactory) + { + if (!@this.ContainsKey(key)) + { + @this.Add(key, addValueFactory(key)); + } + else + { + @this[key] = updateValueFactory(key, @this[key]); + } + + return @this[key]; + } + + /// + /// 添加或更新键值对 + /// + /// + /// + /// + /// 键 + /// 添加时的操作 + /// 更新时的操作 + /// + public static async Task AddOrUpdateAsync(this IDictionary @this, TKey key, Func> addValueFactory, Func> updateValueFactory) + { + if (!@this.ContainsKey(key)) + { + @this.Add(key, await addValueFactory(key)); + } + else + { + @this[key] = await updateValueFactory(key, @this[key]); + } + + return @this[key]; + } + + /// + /// 获取或添加 + /// + /// + /// + /// + /// + /// + /// + public static TValue GetOrAdd(this IDictionary @this, TKey key, Func addValueFactory) + { + if (!@this.ContainsKey(key)) + { + @this.Add(key, addValueFactory()); + } + + return @this[key]; + } + + /// + /// 获取或添加 + /// + /// + /// + /// + /// + /// + /// + public static async Task GetOrAddAsync(this IDictionary @this, TKey key, Func> addValueFactory) + { + if (!@this.ContainsKey(key)) + { + @this.Add(key, await addValueFactory()); + } + + return @this[key]; + } + + /// + /// 获取或添加 + /// + /// + /// + /// + /// + /// + /// + public static TValue GetOrAdd(this IDictionary @this, TKey key, TValue addValue) + { + if (!@this.ContainsKey(key)) + { + @this.Add(key, addValue); + } + + return @this[key]; + } + + /// + /// 遍历IEnumerable + /// + /// + /// 回调方法 + public static void ForEach(this IDictionary dic, Action action) + { + foreach (var item in dic) + { + action(item.Key, item.Value); + } + } + + /// + /// 遍历IDictionary + /// + /// + /// 回调方法 + public static Task ForEachAsync(this IDictionary dic, Func action) + { + return dic.ForeachAsync(x => action(x.Key, x.Value)); + } + + /// + /// 安全的转换成字典集 + /// + /// + /// + /// + /// 键选择器 + /// + public static Dictionary ToDictionarySafety(this IEnumerable source, Func keySelector) + { + var dic = new Dictionary(); + foreach (var item in source) + { + dic[keySelector(item)] = item; + } + + return dic; + } + + /// + /// 安全的转换成字典集 + /// + /// + /// + /// + /// + /// 键选择器 + /// 值选择器 + /// + public static Dictionary ToDictionarySafety(this IEnumerable source, Func keySelector, Func elementSelector) + { + var dic = new Dictionary(); + foreach (var item in source) + { + dic[keySelector(item)] = elementSelector(item); + } + + return dic; + } + + /// + /// 安全的转换成字典集 + /// + /// + /// + /// + /// + /// 键选择器 + /// 值选择器 + /// + public static async Task> ToDictionarySafetyAsync(this IEnumerable source, Func keySelector, Func> elementSelector) + { + var dic = new ConcurrentDictionary(); + await source.ForeachAsync(async item => dic[keySelector(item)] = await elementSelector(item)); + return dic; + } + + /// + /// 安全的转换成字典集 + /// + /// + /// + /// + /// + /// 键选择器 + /// + public static ConcurrentDictionary ToConcurrentDictionary(this IEnumerable source, Func keySelector) + { + var dic = new ConcurrentDictionary(); + foreach (var item in source) + { + dic[keySelector(item)] = item; + } + + return dic; + } + + /// + /// 安全的转换成字典集 + /// + /// + /// + /// + /// + /// 键选择器 + /// 值选择器 + /// + public static ConcurrentDictionary ToConcurrentDictionary(this IEnumerable source, Func keySelector, Func elementSelector) + { + var dic = new ConcurrentDictionary(); + foreach (var item in source) + { + dic[keySelector(item)] = elementSelector(item); + } + + return dic; + } + + /// + /// 安全的转换成字典集 + /// + /// + /// + /// + /// + /// 键选择器 + /// 值选择器 + /// + public static async Task> ToConcurrentDictionaryAsync(this IEnumerable source, Func keySelector, Func> elementSelector) + { + var dic = new ConcurrentDictionary(); + await source.ForeachAsync(async item => dic[keySelector(item)] = await elementSelector(item)); + return dic; + } + + /// + /// 转换成并发字典集合 + /// + /// + /// + /// + /// + public static ConcurrentDictionary AsConcurrentDictionary(this Dictionary dic) => new(dic); + + /// + /// 转换成普通字典集合 + /// + /// + /// + /// + /// + public static Dictionary AsDictionary(this ConcurrentDictionary dic) => new(dic); + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Infrastructure/Extention/IEnumerableExtensions.cs b/IRaCIS.Core.Infrastructure/Extention/IEnumerableExtensions.cs new file mode 100644 index 0000000..f96f34c --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Extention/IEnumerableExtensions.cs @@ -0,0 +1,600 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static partial class IEnumerableExtensions + { + + ///// + ///// 按字段去重 + ///// + ///// + ///// + ///// + ///// + ///// + //public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) + //{ + // var hash = new HashSet(); + // return source.Where(p => hash.Add(keySelector(p))); + //} + + + /// + /// 添加多个元素 + /// + /// + /// + /// + public static void AddRange(this ICollection @this, params T[] values) + { + foreach (var obj in values) + { + @this.Add(obj); + } + } + + /// + /// 添加符合条件的多个元素 + /// + /// + /// + /// + /// + public static void AddRangeIf(this ICollection @this, Func predicate, params T[] values) + { + foreach (var obj in values) + { + if (predicate(obj)) + { + @this.Add(obj); + } + } + } + + /// + /// 添加不重复的元素 + /// + /// + /// + /// + public static void AddRangeIfNotContains(this ICollection @this, params T[] values) + { + foreach (T obj in values) + { + if (!@this.Contains(obj)) + { + @this.Add(obj); + } + } + } + + /// + /// 移除符合条件的元素 + /// + /// + /// + /// + public static void RemoveWhere(this ICollection @this, Func @where) + { + foreach (var obj in @this.Where(where).ToList()) + { + @this.Remove(obj); + } + } + + /// + /// 在元素之后添加元素 + /// + /// + /// + /// 条件 + /// 值 + public static void InsertAfter(this IList list, Func condition, T value) + { + foreach (var item in list.Select((item, index) => new { item, index }).Where(p => condition(p.item)).OrderByDescending(p => p.index)) + { + if (item.index + 1 == list.Count) + { + list.Add(value); + } + else + { + list.Insert(item.index + 1, value); + } + } + } + + /// + /// 在元素之后添加元素 + /// + /// + /// + /// 索引位置 + /// 值 + public static void InsertAfter(this IList list, int index, T value) + { + foreach (var item in list.Select((v, i) => new { Value = v, Index = i }).Where(p => p.Index == index).OrderByDescending(p => p.Index)) + { + if (item.Index + 1 == list.Count) + { + list.Add(value); + } + else + { + list.Insert(item.Index + 1, value); + } + } + } + + /// + /// 转HashSet + /// + /// + /// + /// + /// + /// + public static HashSet ToHashSet(this IEnumerable source, Func selector) + { + var set = new HashSet(); + set.UnionWith(source.Select(selector)); + return set; + } + + /// + /// 遍历IEnumerable + /// + /// + /// 回调方法 + /// + public static void ForEach(this IEnumerable objs, Action action) + { + foreach (var o in objs) + { + action(o); + } + } + + /// + /// 异步foreach + /// + /// + /// + /// 最大并行数 + /// + /// + /// + public static async Task ForeachAsync(this IEnumerable source, Func action, int maxParallelCount, CancellationToken cancellationToken = default) + { + var list = new List(); + foreach (var item in source) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + list.Add(action(item)); + if (list.Count >= maxParallelCount) + { + await Task.WhenAll(list); + list.Clear(); + } + } + + await Task.WhenAll(list); + } + + /// + /// 异步foreach + /// + /// + /// + /// + /// + public static Task ForeachAsync(this IEnumerable source, Func action, CancellationToken cancellationToken = default) + { + return ForeachAsync(source, action, source.Count(), cancellationToken); + } + + /// + /// 异步Select + /// + /// + /// + /// + /// + /// + public static Task SelectAsync(this IEnumerable source, Func> selector) + { + return Task.WhenAll(source.Select(selector)); + } + + /// + /// 异步Select + /// + /// + /// + /// + /// + /// + public static Task SelectAsync(this IEnumerable source, Func> selector) + { + return Task.WhenAll(source.Select(selector)); + } + + /// + /// 异步For + /// + /// + /// + /// + /// 最大并行数 + /// 取消口令 + /// + public static async Task ForAsync(this IEnumerable source, Func selector, int maxParallelCount, CancellationToken cancellationToken = default) + { + var list = new List(); + int index = 0; + foreach (var item in source) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + list.Add(selector(item, index++)); + if (list.Count >= maxParallelCount) + { + await Task.WhenAll(list); + list.Clear(); + } + } + + await Task.WhenAll(list); + } + + /// + /// 异步For + /// + /// + /// + /// + /// 取消口令 + /// + public static Task ForAsync(this IEnumerable source, Func selector, CancellationToken cancellationToken = default) + { + return ForAsync(source, selector, source.Count(), cancellationToken); + } + + /// + /// 取最大值 + /// + /// + /// + /// + /// + /// + public static TResult MaxOrDefault(this IQueryable source, Expression> selector) + => source.Select(selector).DefaultIfEmpty().Max(); + + /// + /// 取最大值 + /// + /// + /// + /// + /// + /// + /// + public static TResult MaxOrDefault(this IQueryable source, Expression> selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Max(); + + /// + /// 取最大值 + /// + /// + /// + /// + public static TSource MaxOrDefault(this IQueryable source) => source.DefaultIfEmpty().Max(); + + /// + /// 取最大值 + /// + /// + /// + /// + /// + public static TSource MaxOrDefault(this IQueryable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Max(); + + /// + /// 取最大值 + /// + /// + /// + /// + /// + /// + /// + public static TResult MaxOrDefault(this IEnumerable source, Func selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Max(); + + /// + /// 取最大值 + /// + /// + /// + /// + /// + /// + public static TResult MaxOrDefault(this IEnumerable source, Func selector) => source.Select(selector).DefaultIfEmpty().Max(); + + /// + /// 取最大值 + /// + /// + /// + /// + public static TSource MaxOrDefault(this IEnumerable source) => source.DefaultIfEmpty().Max(); + + /// + /// 取最大值 + /// + /// + /// + /// + /// + public static TSource MaxOrDefault(this IEnumerable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Max(); + + /// + /// 取最小值 + /// + /// + /// + /// + /// + /// + public static TResult MinOrDefault(this IQueryable source, Expression> selector) => source.Select(selector).DefaultIfEmpty().Min(); + + /// + /// 取最小值 + /// + /// + /// + /// + /// + /// + /// + public static TResult MinOrDefault(this IQueryable source, Expression> selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Min(); + + /// + /// 取最小值 + /// + /// + /// + /// + public static TSource MinOrDefault(this IQueryable source) => source.DefaultIfEmpty().Min(); + + /// + /// 取最小值 + /// + /// + /// + /// + /// + public static TSource MinOrDefault(this IQueryable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Min(); + + /// + /// 取最小值 + /// + /// + /// + /// + /// + /// + public static TResult MinOrDefault(this IEnumerable source, Func selector) => source.Select(selector).DefaultIfEmpty().Min(); + + /// + /// 取最小值 + /// + /// + /// + /// + /// + /// + /// + public static TResult MinOrDefault(this IEnumerable source, Func selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Min(); + + /// + /// 取最小值 + /// + /// + /// + /// + public static TSource MinOrDefault(this IEnumerable source) => source.DefaultIfEmpty().Min(); + + /// + /// 取最小值 + /// + /// + /// + /// + /// + public static TSource MinOrDefault(this IEnumerable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Min(); + + ///// + ///// 标准差 + ///// + ///// + ///// + ///// + ///// + //public static TResult StandardDeviation(this IEnumerable source, Func selector) where TResult : IConvertible + //{ + // return StandardDeviation(source.Select(t => selector(t).ConvertTo())).ConvertTo(); + //} + + ///// + ///// 标准差 + ///// + ///// + ///// + ///// + //public static T StandardDeviation(this IEnumerable source) where T : IConvertible + //{ + // return StandardDeviation(source.Select(t => t.ConvertTo())).ConvertTo(); + //} + + /// + /// 标准差 + /// + /// + /// + public static double StandardDeviation(this IEnumerable source) + { + double result = 0; + int count = source.Count(); + if (count > 1) + { + double avg = source.Average(); + double sum = source.Sum(d => (d - avg) * (d - avg)); + result = Math.Sqrt(sum / count); + } + + return result; + } + + /// + /// 随机排序 + /// + /// + /// + /// + public static IOrderedEnumerable OrderByRandom(this IEnumerable source) + { + return source.OrderBy(_ => Guid.NewGuid()); + } + + /// + /// 序列相等 + /// + /// + /// + /// + /// + /// + public static bool SequenceEqual(this IEnumerable first, IEnumerable second, Func condition) + { + if (first is ICollection source1 && second is ICollection source2) + { + if (source1.Count != source2.Count) + { + return false; + } + + if (source1 is IList list1 && source2 is IList list2) + { + int count = source1.Count; + for (int index = 0; index < count; ++index) + { + if (!condition(list1[index], list2[index])) + { + return false; + } + } + return true; + } + } + + using IEnumerator enumerator1 = first.GetEnumerator(); + using IEnumerator enumerator2 = second.GetEnumerator(); + while (enumerator1.MoveNext()) + { + if (!enumerator2.MoveNext() || !condition(enumerator1.Current, enumerator2.Current)) + { + return false; + } + } + + return !enumerator2.MoveNext(); + } + + /// + /// 序列相等 + /// + /// + /// + /// + /// + /// + /// + public static bool SequenceEqual(this IEnumerable first, IEnumerable second, Func condition) + { + if (first is ICollection source1 && second is ICollection source2) + { + if (source1.Count != source2.Count) + { + return false; + } + + if (source1 is IList list1 && source2 is IList list2) + { + int count = source1.Count; + for (int index = 0; index < count; ++index) + { + if (!condition(list1[index], list2[index])) + { + return false; + } + } + return true; + } + } + + using IEnumerator enumerator1 = first.GetEnumerator(); + using IEnumerator enumerator2 = second.GetEnumerator(); + while (enumerator1.MoveNext()) + { + if (!enumerator2.MoveNext() || !condition(enumerator1.Current, enumerator2.Current)) + { + return false; + } + } + + return !enumerator2.MoveNext(); + } + + /// + /// 对比两个集合哪些是新增的、删除的、修改的 + /// + /// + /// + /// + /// + /// 对比因素属性 + /// 对比因素属性 + /// + public static (List adds, List remove, List updates) CompareChanges(this IEnumerable olds, IEnumerable news, Func key1Selector, Func key2Selector) + { + return (news.Where(c => olds.All(m => key1Selector(m) != key2Selector(c))).ToList(), olds.Where(m => news.All(c => key2Selector(c) != key1Selector(m))).ToList(), olds.Where(m => news.Any(c => key1Selector(m) == key2Selector(c))).ToList()); + } + + /// + /// 对比两个集合哪些是新增的、删除的、修改的 + /// + /// + /// + /// + /// 对比因素属性 + /// + public static (List adds, List remove, List updates) CompareChanges(this IEnumerable olds, IEnumerable news, Func keySelector) + { + return (news.Where(c => olds.All(m => keySelector(m) != keySelector(c))).ToList(), olds.Where(m => news.All(c => keySelector(c) != keySelector(m))).ToList(), olds.Where(m => news.Any(c => keySelector(m) == keySelector(c))).ToList()); + } + } +} diff --git a/IRaCIS.Core.Infrastructure/Extention/RequestExtension.cs b/IRaCIS.Core.Infrastructure/Extention/RequestExtension.cs new file mode 100644 index 0000000..1aecdeb --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Extention/RequestExtension.cs @@ -0,0 +1,158 @@ +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static class HttpContextExtension + { + + public static T GetService(this HttpContext context) where T : class + { + return context.RequestServices.GetService(typeof(T)) as T; + } + + + public static string GetUserIp(this HttpContext context) + { + string realIP = null; + string forwarded = null; + string remoteIpAddress = context.Connection.RemoteIpAddress.ToString(); + if (context.Request.Headers.ContainsKey("X-Real-IP")) + { + realIP = context.Request.Headers["X-Real-IP"].ToString(); + if (realIP != remoteIpAddress) + { + remoteIpAddress = realIP; + } + } + if (context.Request.Headers.ContainsKey("X-Forwarded-For")) + { + forwarded = context.Request.Headers["X-Forwarded-For"].ToString(); + if (forwarded != remoteIpAddress) + { + remoteIpAddress = forwarded; + } + } + return remoteIpAddress; + } + + + + /// + /// 获取Request值 + /// + /// + /// + /// + public static string Request(this HttpContext context, string parameter) + { + try + { + if (context == null) + return null; + if (context.Request.Method == "POST") + return context.Request.Form[parameter].ToString(); + else + return context.Request.Query[parameter].ToString(); + } + catch (System.Exception ex) + { + Console.Write(ex.Message + ex.InnerException); + return context.RequestString(parameter); + } + } + + public static T Request(this HttpContext context, string parameter) where T : class + { + return context.RequestString(parameter)?.DeserializeObject(); + } + public static string RequestString(this HttpContext context, string parameter) + { + string requestParam = context.GetRequestParameters(); + if (string.IsNullOrEmpty(requestParam)) return null; + Dictionary keyValues = requestParam.DeserializeObject>(); + if (keyValues == null || keyValues.Count == 0) return null; + if (keyValues.TryGetValue(parameter, out object value)) + { + if (value == null) return null; + if (value.GetType() == typeof(string)) + { + return value?.ToString(); + } + return value.Serialize(); + } + return null; + } + /// + /// 是否为ajax请求 + /// + /// + /// + public static bool IsAjaxRequest(this HttpContext context) + { + return context.Request("X-Requested-With") == "XMLHttpRequest" + || (context.Request.Headers != null + && context.Request.Headers["X-Requested-With"] == "XMLHttpRequest"); + } + + public static UserAgent GetAgentType(this HttpContext context) + { + string agent = context.Request.Headers["User-Agent"].ToString().ToLower(); + + if (agent.Contains("ios") || agent.Contains("ipod") || agent.Contains("ipad")) + { + return UserAgent.IOS; + } + if (agent.Contains("windows")) + { + return UserAgent.Windows; + } + return UserAgent.Android; + + } + + /// + /// 获取请求的参数 + /// net core 2.0已增加回读方法 context.Request.EnableRewind(); + /// + /// + /// + /// + + public static string GetRequestParameters(this HttpContext context) + { + if (context.Request.Body == null || !context.Request.Body.CanRead || !context.Request.Body.CanSeek) + return null; + if (context.Request.Body.Length == 0) + return null; + if (context.Request.Body.Position > 0) + context.Request.Body.Position = 0; + + string prarameters = null; + var bodyStream = context.Request.Body; + + using (var buffer = new MemoryStream()) + { + bodyStream.CopyToAsync(buffer); + buffer.Position = 0L; + bodyStream.Position = 0L; + using (var reader = new StreamReader(buffer, Encoding.UTF8)) + { + buffer.Seek(0, SeekOrigin.Begin); + prarameters = reader.ReadToEnd(); + } + } + return prarameters; + } + } + public enum UserAgent + { + IOS = 0, + Android = 1, + Windows = 2, + Linux + } +} diff --git a/IRaCIS.Core.Infrastructure/Extention/SevenZipCompressor.cs b/IRaCIS.Core.Infrastructure/Extention/SevenZipCompressor.cs new file mode 100644 index 0000000..b703ae2 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Extention/SevenZipCompressor.cs @@ -0,0 +1,205 @@ +using SharpCompress.Archives; +using SharpCompress.Common; +using SharpCompress.Readers; +using SharpCompress.Writers; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + /// + /// 7z压缩 + /// + public static class SevenZipCompressor + { + /// + /// 将多个文件压缩到一个内存流中,可保存为zip文件,方便于web方式下载 + /// + /// 多个文件路径,文件或文件夹,或网络路径http/https + /// + /// 文件流 + public static MemoryStream ZipStream(List files, string rootdir = "") + { + using var archive = CreateZipArchive(files, rootdir); + var ms = new MemoryStream(); + archive.SaveTo(ms, new WriterOptions(CompressionType.Deflate) + { + LeaveStreamOpen = true, + ArchiveEncoding = new ArchiveEncoding() + { + Default = Encoding.UTF8 + } + }); + return ms; + } + + /// + /// 压缩多个文件 + /// + /// 多个文件路径,文件或文件夹 + /// 压缩到... + /// 压缩包内部根文件夹 + /// + public static void Zip(List files, string zipFile, string rootdir = "", ArchiveType archiveType = ArchiveType.SevenZip) + { + using var archive = CreateZipArchive(files, rootdir, archiveType); + archive.SaveTo(zipFile, new WriterOptions(CompressionType.Deflate) + { + LeaveStreamOpen = true, + ArchiveEncoding = new ArchiveEncoding() + { + Default = Encoding.UTF8 + } + }); + } + + /// + /// 解压文件,自动检测压缩包类型 + /// + /// rar文件 + /// 解压到... + /// 忽略空文件夹 + public static void Decompress(string compressedFile, string dir = "", bool ignoreEmptyDir = true) + { + if (string.IsNullOrEmpty(dir)) + { + dir = Path.GetDirectoryName(compressedFile); + } + + using Stream stream = File.OpenRead(compressedFile); + using var reader = ReaderFactory.Open(stream); + while (reader.MoveToNextEntry()) + { + if (ignoreEmptyDir) + { + reader.WriteEntryToDirectory(dir, new ExtractionOptions() + { + ExtractFullPath = true, + Overwrite = true + }); + } + else + { + if (!reader.Entry.IsDirectory) + { + reader.WriteEntryToDirectory(dir, new ExtractionOptions() + { + ExtractFullPath = true, + Overwrite = true + }); + } + } + } + } + + /// + /// 创建zip包 + /// + /// + /// + /// + /// + private static IWritableArchive CreateZipArchive(List files, string rootdir, ArchiveType archiveType = ArchiveType.SevenZip) + { + var archive = ArchiveFactory.Create(archiveType); + var dic = GetFileEntryMaps(files); + var remoteUrls = files.Distinct().Where(s => s.StartsWith("http")).Select(s => + { + try + { + return new Uri(s); + } + catch (UriFormatException) + { + return null; + } + }).Where(u => u != null).ToList(); + foreach (var pair in dic) + { + archive.AddEntry(Path.Combine(rootdir, pair.Value), pair.Key); + } + + if (!remoteUrls.Any()) + { + return archive; + } + + var streams = new ConcurrentDictionary(); + using var httpClient = new HttpClient(); + Parallel.ForEach(remoteUrls, url => + { + httpClient.GetAsync(url).ContinueWith(async t => + { + if (t.IsCompleted) + { + var res = await t; + if (res.IsSuccessStatusCode) + { + Stream stream = await res.Content.ReadAsStreamAsync(); + streams[Path.Combine(rootdir, Path.GetFileName(HttpUtility.UrlDecode(url.AbsolutePath)))] = stream; + } + } + }).Wait(); + }); + foreach (var kv in streams) + { + archive.AddEntry(kv.Key, kv.Value, true); + } + + return archive; + } + + /// + /// 获取文件路径和zip-entry的映射 + /// + /// + /// + private static Dictionary GetFileEntryMaps(List files) + { + var fileList = new List(); + void GetFilesRecurs(string path) + { + //遍历目标文件夹的所有文件 + fileList.AddRange(Directory.GetFiles(path)); + + //遍历目标文件夹的所有文件夹 + foreach (string directory in Directory.GetDirectories(path)) + { + GetFilesRecurs(directory); + } + } + + files.Where(s => !s.StartsWith("http")).ForEach(s => + { + if (Directory.Exists(s)) + { + GetFilesRecurs(s); + } + else + { + fileList.Add(s); + } + }); + if (!fileList.Any()) + { + return new Dictionary(); + } + + var dirname = new string(fileList.First().Substring(0, fileList.Min(s => s.Length)).TakeWhile((c, i) => fileList.All(s => s[i] == c)).ToArray()); + if (!Directory.Exists(dirname)) + { + dirname = Directory.GetParent(dirname).FullName; + } + + var dic = fileList.ToDictionary(s => s, s => s.Substring(dirname.Length)); + return dic; + } + } +} diff --git a/IRaCIS.Core.Infrastructure/Helper/IPHelper.cs b/IRaCIS.Core.Infrastructure/Helper/IPHelper.cs new file mode 100644 index 0000000..c8827ea --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Helper/IPHelper.cs @@ -0,0 +1,43 @@ +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; + +namespace IRaCIS.Core.Infrastructure +{ + public class IPHelper + { + /// + /// 是否为ip + /// + /// + /// + public static bool IsIP(string ip) + { + return Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"); + } + + public static string GetIP(HttpRequest request) + { + if (request == null) + { + return ""; + } + + string ip = request.Headers["X-Real-IP"].FirstOrDefault(); + if (string.IsNullOrEmpty(ip)) + { + ip = request.Headers["X-Forwarded-For"].FirstOrDefault(); + } + if (string.IsNullOrEmpty(ip)) + { + ip = request.HttpContext?.Connection?.RemoteIpAddress?.ToString(); + } + if (string.IsNullOrEmpty(ip) || !IsIP(ip)) + { + ip = "127.0.0.1"; + } + + return ip; + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Infrastructure/Helper/IdentifierHelper.cs b/IRaCIS.Core.Infrastructure/Helper/IdentifierHelper.cs new file mode 100644 index 0000000..5f9a54d --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Helper/IdentifierHelper.cs @@ -0,0 +1,22 @@ +using System; +using System.Security.Cryptography; +using System.Text; + +namespace IRaCIS.Core.Infrastructure +{ + public static class IdentifierHelper + { + private static MD5 md5 = MD5.Create(); + + private static object lockObj = new object(); + + public static Guid CreateGuid(params string[] parts) + { + lock (lockObj) + { + return new Guid(md5.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(parts)))); + } + + } + } +} diff --git a/IRaCIS.Core.Infrastructure/Helper/ZipHelper.cs b/IRaCIS.Core.Infrastructure/Helper/ZipHelper.cs new file mode 100644 index 0000000..017f7e1 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Helper/ZipHelper.cs @@ -0,0 +1,52 @@ +using System; +using System.IO; + +using ICSharpCode.SharpZipLib.Checksum; +using ICSharpCode.SharpZipLib.Zip; + +namespace IRaCIS.Core.Infrastructure +{ + public class ZipHelper + { + public static void CreateZip(string sourceFilePath, string destinationZipFilePath) + { + if (sourceFilePath[sourceFilePath.Length - 1] != Path.DirectorySeparatorChar) + sourceFilePath += Path.DirectorySeparatorChar; + + ZipOutputStream zipStream = new ZipOutputStream(File.Create(destinationZipFilePath)); + zipStream.SetLevel(6); // 压缩级别 0-9 + CreateZipFiles(sourceFilePath, zipStream); + zipStream.Finish(); + zipStream.Close(); + } + + private static void CreateZipFiles(string sourceFilePath, ZipOutputStream zipStream) + { + Crc32 crc = new Crc32(); + string[] filesArray = Directory.GetFileSystemEntries(sourceFilePath); + foreach (string file in filesArray) + { + if (Directory.Exists(file)) //如果当前是文件夹,递归 + { + CreateZipFiles(file, zipStream); + } + else //如果是文件,开始压缩 + { + FileStream fileStream = File.OpenRead(file); + byte[] buffer = new byte[fileStream.Length]; + fileStream.Read(buffer, 0, buffer.Length); + string tempFile = file.Substring(sourceFilePath.LastIndexOf("\\") + 1); + ZipEntry entry = new ZipEntry(tempFile); + entry.DateTime = DateTime.Now; + entry.Size = fileStream.Length; + fileStream.Close(); + crc.Reset(); + crc.Update(buffer); + entry.Crc = crc.Value; + zipStream.PutNextEntry(entry); + zipStream.Write(buffer, 0, buffer.Length); + } + } + } + } +} diff --git a/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj b/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj new file mode 100644 index 0000000..7e46568 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj @@ -0,0 +1,40 @@ + + + + net6.0 + AnyCPU;x64 + + + + ..\bin + + + + ..\bin + + + + ..\bin + + + + + + + + + + + + + + + + + + + + + + + diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Exception/BusinessValidationFailedException.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Exception/BusinessValidationFailedException.cs new file mode 100644 index 0000000..ccda662 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Exception/BusinessValidationFailedException.cs @@ -0,0 +1,17 @@ +using System; + +namespace IRaCIS.Core.Infrastructure +{ + public class BusinessValidationFailedException : Exception + { + + public BusinessValidationFailedException() + { + + } + + public BusinessValidationFailedException( string message) : base(message) + { + } + } +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Exception/DBSaveFailedException.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Exception/DBSaveFailedException.cs new file mode 100644 index 0000000..cd81f08 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Exception/DBSaveFailedException.cs @@ -0,0 +1,17 @@ +using System; + +namespace IRaCIS.Core.Infrastructure +{ + public class DBSaveFailedException : Exception + { + + public DBSaveFailedException() + { + + } + + public DBSaveFailedException( string message) : base(message) + { + } + } +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/IQueryableExtensions.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/IQueryableExtensions.cs new file mode 100644 index 0000000..f5e9f8b --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/IQueryableExtensions.cs @@ -0,0 +1,54 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using System.Linq.Dynamic.Core; +using System.Collections.Generic; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static class IQueryableExtensions + { + /// + /// 获取第一条 为null提示 + /// + /// + /// + /// + /// + public static async Task FirstNotNullAsync(this IQueryable source) + { + var result =await source.FirstOrDefaultAsync(); + + if (result == null) + { + throw new QueryBusinessObjectNotExistException($"The query object does not exist in database, Please check the query parameters"); + } + else + { + return result; + } + } + + /// + /// 查询字符串 为null 返回string.Empty + /// + /// + /// + /// + public static string FirstIsNullReturnEmpty(this IEnumerable source) + { + var result = source.FirstOrDefault(); + + if (result == null || result == string.Empty) + { + return string.Empty; + } + else + { + return result; + } + } + + } +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/IQueryablePageListExtensions.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/IQueryablePageListExtensions.cs new file mode 100644 index 0000000..10bd999 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/IQueryablePageListExtensions.cs @@ -0,0 +1,184 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using System.Linq.Dynamic.Core; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static class QueryablePageListExtensions + { + //单字段排序 + public static PageOutput ToPagedList(this IQueryable source, int pageIndex, int pageSize, string defaultSortFiled = "Id", bool isAsc = true) + { + if (pageIndex <= 0) + { + pageIndex = 1; + } + if (pageSize <= 0) + { + pageSize = 10; + } + var count = source.Count(); + + if (count == 0) + { + return new PageOutput() { CurrentPageData=new T[0] }; + } + + + var propName = string.IsNullOrWhiteSpace(defaultSortFiled) ? "Id" : defaultSortFiled; + + source = isAsc ? source.OrderBy(propName) : source.OrderBy(propName + " desc"); + + source = source.Skip((pageIndex - 1) * pageSize); + + var items = source + .Take(pageSize) + .ToArray(); + + var pagedList = new PageOutput() + { + PageIndex = pageIndex, + PageSize = pageSize, + TotalCount = count, + CurrentPageData = items + }; + + return pagedList; + } + + //单字段排序 异步 + public static async Task> ToPagedListAsync(this IQueryable source, int pageNumber, int pageSize, string defaultSortFiled = "Id", bool isAsc = true,bool isMultiSortFiled=false, string[] sortArray=default, CancellationToken cancellationToken = default) + { + + if (isMultiSortFiled&& sortArray==default) + { + throw new System.Exception("必须指定排序字段"); + } + + if (pageNumber <= 0) + { + pageNumber = 1; + } + if (pageSize <= 0) + { + pageSize = 10; + } + + var count = await source.CountAsync(cancellationToken).ConfigureAwait(false); + + if (count == 0) + { + return new PageOutput() { CurrentPageData = new T[0] }; + } + var propName = string.IsNullOrWhiteSpace(defaultSortFiled) ? "Id" : defaultSortFiled; + + if (!isMultiSortFiled) + { + source = isAsc ? source.OrderBy(propName) : source.OrderBy(propName + " desc"); + + } + else + { + var sortString = string.Join(',', sortArray); + + source= source.OrderBy(sortString); + } + source = source.Skip((pageNumber - 1) * pageSize); + var items = await source + .Take(pageSize) + .ToArrayAsync(cancellationToken) + .ConfigureAwait(false); + + var pagedList = new PageOutput() + { + PageIndex = pageNumber, + PageSize = pageSize, + TotalCount = count, + CurrentPageData = items + }; + + return pagedList; + } + + //多字段排序 ["a asc", "b desc", "c asc"] + public static PageOutput ToPagedList(this IQueryable source, int pageIndex, int pageSize,string[] sortArray) + { + if (pageIndex <= 0) + { + pageIndex = 1; + } + if (pageSize <= 0) + { + pageSize = 10; + } + var count = source.Count(); + + if (count == 0) + { + return new PageOutput() { CurrentPageData = new T[0] }; + } + + var sortString = string.Join(',', sortArray); + + source.OrderBy(sortString); + + source = source.Skip((pageIndex - 1) * pageSize); + + var items = source + .Take(pageSize) + .ToArray(); + + var pagedList = new PageOutput() + { + PageIndex = pageIndex, + PageSize = pageSize, + TotalCount = count, + CurrentPageData = items + }; + + return pagedList; + } + + //多字段排序异步 ["a asc", "b desc", "c asc"] + public static async Task> ToPagedListAsync(this IQueryable source, int pageNumber, int pageSize, string[] sortArray, CancellationToken cancellationToken = default) + { + if (pageNumber <= 0) + { + pageNumber = 1; + } + if (pageSize <= 0) + { + pageSize = 10; + } + + var count = await source.CountAsync(cancellationToken).ConfigureAwait(false); + + if (count == 0) + { + return new PageOutput() { CurrentPageData = new T[0] }; + } + + var sortString = string.Join(',', sortArray); + + source = source.OrderBy(sortString); + + source = source.Skip((pageNumber - 1) * pageSize); + var items = await source + .Take(pageSize) + .ToArrayAsync(cancellationToken) + .ConfigureAwait(false); + + var pagedList = new PageOutput() + { + PageIndex = pageNumber, + PageSize = pageSize, + TotalCount = count, + CurrentPageData = items + }; + + return pagedList; + } + } +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/IQueryableWhereExtension.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/IQueryableWhereExtension.cs new file mode 100644 index 0000000..3d583b8 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/IQueryableWhereExtension.cs @@ -0,0 +1,22 @@ +using System.Linq; +using System.Linq.Expressions; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static class QueryableWhereExtension + { + public static IQueryable WhereIf(this IQueryable query, [NotNullWhen(true)] bool condition, Expression> filter) where TEntity : class + { + return condition ? query.Where(filter) : query; + } + + public static IEnumerable WhereIf(this IEnumerable query, bool condition, Func filter) where TEntity : class + { + return condition ? query.Where(filter) : query; + } + + } +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Input/PageInput.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Input/PageInput.cs new file mode 100644 index 0000000..fb1b01f --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Input/PageInput.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + /// + /// 分页信息输入 + /// + public class PageInput + { + public int PageIndex { get; set; } = 1; + public int PageSize { set; get; } = 10; + public bool Asc { get; set; } = true; + public string SortField { get; set; } = ""; + } + + public class PageInputMultiSort + { + public int PageIndex { get; set; } = 1; + public int PageSize { set; get; } = 10; + + //["a asc", "b desc", "c asc"] + public string[] SortArray { get; set; } + } +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/LinqExtension.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/LinqExtension.cs new file mode 100644 index 0000000..7d23aa1 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/LinqExtension.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + /// + /// LINQ扩展方法 + /// + public static class LinqExtension + { + /// + /// 与连接 + /// + /// 类型 + /// 左条件 + /// 右条件 + /// 新表达式 + public static Expression> And(this Expression> left, Expression> right) + { + return CombineLambdas(left, right, ExpressionType.AndAlso); + } + + /// + /// 或连接 + /// + /// 类型 + /// 左条件 + /// 右条件 + /// 新表达式 + public static Expression> Or(this Expression> left, Expression> right) + { + return CombineLambdas(left, right, ExpressionType.OrElse); + } + + private static Expression> CombineLambdas(this Expression> left, Expression> right, ExpressionType expressionType) + { + var visitor = new SubstituteParameterVisitor + { + Sub = + { + [right.Parameters[0]] = left.Parameters[0] + } + }; + + Expression body = Expression.MakeBinary(expressionType, left.Body, visitor.Visit(right.Body)); + return Expression.Lambda>(body, left.Parameters[0]); + } + } + + internal class SubstituteParameterVisitor : ExpressionVisitor + { + public Dictionary Sub = new Dictionary(); + + protected override Expression VisitParameter(ParameterExpression node) + { + return Sub.TryGetValue(node, out var newValue) ? newValue : node; + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/ListTreeExtensions.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/ListTreeExtensions.cs new file mode 100644 index 0000000..e093195 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/ListTreeExtensions.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static class ListTreeExtensions + { + /// + /// 将列表转换为树形结构 + /// + /// 类型 + /// 数据 + /// 根条件 + /// 节点条件 + /// 添加子节点 + /// + /// + public static List ToTree(this List list, Func rootWhere, Func childsWhere, Action> addChilds, Func orderSelector, T childEntity = default) + { + if (rootWhere is null) + { + throw new ArgumentNullException(nameof(rootWhere)); + } + + if (childsWhere is null) + { + throw new ArgumentNullException(nameof(childsWhere)); + } + + if (addChilds is null) + { + throw new ArgumentNullException(nameof(addChilds)); + } + + if (orderSelector is null) + { + throw new ArgumentNullException(nameof(orderSelector)); + } + + var treelist = new List(); + //空树 + if (list == null || list.Count == 0) + { + return treelist; + } + if (!list.Any(e => rootWhere(childEntity, e))) + { + return treelist; + } + //树根 + if (list.Any(e => rootWhere(childEntity, e))) + { + treelist.AddRange(list.Where(e => rootWhere(childEntity, e)).OrderBy(orderSelector)); + } + //树叶 item 是根 + foreach (var item in treelist) + { + if (list.Any(e => childsWhere(item, e))) + { + var nodedata = list.Where(e => childsWhere(item, e)).OrderBy(orderSelector).ToList(); + + foreach (var child in nodedata) + { + //添加子集 + var data = list.ToTree(childsWhere, childsWhere, addChilds, orderSelector, child); + + addChilds(child, data); + } + addChilds(item, nodedata); + } + } + return treelist; + } + } +} \ No newline at end of file diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/MD5Helper.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/MD5Helper.cs new file mode 100644 index 0000000..aa3105f --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/MD5Helper.cs @@ -0,0 +1,20 @@ +using System.Security.Cryptography; +using System.Text; + +namespace IRaCIS.Core.Infrastructure +{ + public class MD5Helper + { + public static string Md5(string target) + { + using (MD5 md5 = MD5.Create()) + { // MD5非线程安全 + byte[] bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(target)); + StringBuilder sb = new StringBuilder(32); + for (int i = 0; i < bytes.Length; ++i) + sb.Append(bytes[i].ToString("x2")); + return sb.ToString(); + } + } + } +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/NUllCheckExtension.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/NUllCheckExtension.cs new file mode 100644 index 0000000..9023d70 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/NUllCheckExtension.cs @@ -0,0 +1,71 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static class NUllCheckExtension + { + + + [DoesNotReturn] + public static TEntity IfNullThrowException(this TEntity businessObject) where TEntity : class + { + if(businessObject == null) + { + throw new QueryBusinessObjectNotExistException($"The query object {typeof(TEntity).Name} does not exist in database, Please check the query parameters"); + } + else + { + return businessObject!; + } + } + + [DoesNotReturn] + public static TEntity IfNullThrowException(this TEntity? businessStruct) where TEntity : struct + { + if (businessStruct == null) + { + throw new QueryBusinessObjectNotExistException($"The query object {typeof(TEntity).Name} does not exist in database, Please check the query parameters"); + } + else + { + return (TEntity)businessStruct; + } + } + + [DoesNotReturn] + public static TEntity IfNullThrowConvertException(this TEntity businessObject) where TEntity : class + { + if (businessObject == null) + { + throw new QueryBusinessObjectNotExistException($" Can not Convert to {typeof(TEntity).Name} Type, Please check parameter"); + } + else + { + return businessObject!; + } + } + + } + + public class QueryBusinessObjectNotExistException : Exception + { + + public QueryBusinessObjectNotExistException() + { + + } + + public QueryBusinessObjectNotExistException(string message) : base(message) + { + } + } + + + public class ConversionException : Exception + { + public ConversionException(string message) : base(message) + { + } + } +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/ObjectExtension.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/ObjectExtension.cs new file mode 100644 index 0000000..adfc821 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/ObjectExtension.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json; +using System; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static class ObjectExtension + { + + public static T Clone(this T source) + { + // Don't serialize a null object, simply return the default for that object + if (Object.ReferenceEquals(source, null)) + { + return default(T); + } + + var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace }; + var serializeSettings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; + return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source, serializeSettings), deserializeSettings); + } + + /// + /// 将对象序列化成稽查需要的Json字符串 + /// + /// + /// + public static string ToJsonStr(this object obj) + { + + return JsonConvert.SerializeObject(obj, new JsonSerializerSettings { DateFormatString = "yyyy-MM-dd HH:mm:ss", ReferenceLoopHandling = ReferenceLoopHandling.Ignore, NullValueHandling = NullValueHandling.Ignore }); + } + + /// + /// 将对象序列化成稽查需要的Json字符串 + /// + /// + /// + public static string ToJsonNotIgnoreNull(this object obj) + { + + return JsonConvert.SerializeObject(obj, new JsonSerializerSettings { DateFormatString = "yyyy-MM-dd HH:mm:ss", ReferenceLoopHandling = ReferenceLoopHandling.Ignore, NullValueHandling = NullValueHandling.Include }); + } + } +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ApiResponseCodeEnum.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ApiResponseCodeEnum.cs new file mode 100644 index 0000000..0fb88df --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ApiResponseCodeEnum.cs @@ -0,0 +1,51 @@ + + +namespace IRaCIS.Core.Infrastructure.Extention +{ + /// + /// 此处是为了 后续代替 IsSuccess IsSuccess暂时还是保留,接下来的接口判断现在用Code 判断 不用IsSuccess就好了 + + /// + public enum ApiResponseCodeEnum + { + //正常的 相当于之前的 IsSuccess = true + OK = 0, + + //Api 输入参数有问题 相当于之前的 IsSuccess = false + ApiInputError = 1, + + //业务验证不通过 相当于之前的 IsSuccess = false + BusinessValidationFailed = 2, + + //数据不存在 + DataNotExist=3, + + //程序异常 相当于之前的 IsSuccess = false + ProgramException = 4, + + + + + //需要提示 ,需要提示 从Result 取数据 ( 0 可以继续处理提交 ,1 不能进行继续处理提交 ,2 刷新列表 ) + NeedTips = 5, + + + + + + + //在其他地方登陆,被迫下线 + LoginInOtherPlace = -1, + + //没有带token访问(未登陆) + NoToken=10, + + //带了Token,但是没有相应权限(该用户类型不能做) + HaveTokenNotAccess=11 + + } + + + + +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/IResponseOutput.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/IResponseOutput.cs new file mode 100644 index 0000000..3c1e273 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/IResponseOutput.cs @@ -0,0 +1,42 @@ +namespace IRaCIS.Core.Infrastructure.Extention +{ + /// + /// 响应数据输出接口 + /// + public interface IResponseOutput + { + /// + /// 是否成功 + /// + bool IsSuccess { get; } + + public ApiResponseCodeEnum Code { get; set; } + + /// + /// 消息 + /// + string ErrorMessage { get; } + } + + /// + /// 响应数据输出泛型接口 + /// + /// + public interface IResponseOutput : IResponseOutput + { + /// + /// 返回数据 + /// + T Data { get; } + } + + public interface IResponseOutput : IResponseOutput + { + /// + /// 返回数据 + /// + T Data { get; } + + T2 OtherInfo { get; } + } +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/PageOutput.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/PageOutput.cs new file mode 100644 index 0000000..b378061 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/PageOutput.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + /// + /// 分页信息输出 泛型 + /// + public class PageOutput + { + /// + /// 当前页索引 + /// + public int PageIndex { get; set; } + + /// + /// 每页的记录条数 + /// + public int PageSize { get; set; } + + /// + /// 数据总数 + /// + public long TotalCount { get; set; } = 0; + + /// + /// 数据 + /// + public IList CurrentPageData { get; set; } + + public PageOutput() + { + } + + public PageOutput(int pageIndex, int pageSize, long totalCount, IList data) + { + PageIndex = pageIndex; + PageSize = pageSize; + TotalCount = totalCount; + CurrentPageData = data; + } + + public PageOutput(int pageIndex, int pageSize, IQueryable list) + { + PageIndex = pageIndex; + PageSize = pageSize; + TotalCount = list.Count(); + CurrentPageData = list.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); + } + + + } +} diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ResponseOutput.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ResponseOutput.cs new file mode 100644 index 0000000..6b344f2 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ResponseOutput.cs @@ -0,0 +1,206 @@ +using Newtonsoft.Json; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + /// + /// 响应数据输出 泛型 + /// + public class ResponseOutput : IResponseOutput + { + /// + /// 是否成功标记 + /// + public bool IsSuccess { get; set; } + + + public ApiResponseCodeEnum Code { get; set; } = ApiResponseCodeEnum.OK; + + + /// + /// 消息 + /// + public string ErrorMessage { get; set; } + + /// + /// 数据 兼顾以前 Json序列化的时候返回属性名为“Result” + /// + [JsonProperty("Result")] + public T Data { get; set; } + + + + public object OtherData { get; set; } + + + /// + /// 成功 + /// + /// 数据 + /// 消息 + //public ResponseOutput Ok(T data, string msg = "", ApiResponseCodeEnum code = ApiResponseCodeEnum.OK) + //{ + // IsSuccess = true; + // Code = code; + // Data = data; + // ErrorMessage = msg; + + // return this; + //} + + public ResponseOutput Ok(T data, object otherData, string msg = "", ApiResponseCodeEnum code = ApiResponseCodeEnum.OK) + { + IsSuccess = true; + Code = code; + Data = data; + OtherData=otherData; + ErrorMessage = msg; + + return this; + } + + /// + /// 失败 + /// + /// 提示消息 + /// 数据 + /// + public ResponseOutput NotOk(string msg = "", T data = default, ApiResponseCodeEnum code = ApiResponseCodeEnum.OK) + { + IsSuccess = false; + Code = code; + ErrorMessage = msg; + Data = data; + return this; + } + + + } + + + public class ResponseOutput : IResponseOutput + { + [JsonProperty("Result")] + public T Data { get; private set; } + + public T2 OtherInfo { get; private set; } + + public bool IsSuccess { get; private set; } + + public ApiResponseCodeEnum Code { get; set; } + + + public string ErrorMessage { get; private set; } + + + public ResponseOutput Ok(T data, T2 otherInfo, string msg = "") + { + IsSuccess = true; + Data = data; + OtherInfo = otherInfo; + ErrorMessage = msg; + + return this; + } + } + + + /// + /// 响应数据静态输出 为了代码简洁 不用每处都New + /// + public static class ResponseOutput + { + + + public static IResponseOutput Ok(T data, T2 otherInfo, string msg = "") + { + return new ResponseOutput().Ok(data, otherInfo); + } + + + /// + /// 成功 -----适合查询 + /// + /// 数据 + /// 消息 + /// + //public static IResponseOutput Ok(T data = default, string msg = "") + //{ + // return new ResponseOutput().Ok(data, msg); + //} + + public static IResponseOutput Ok(T data = default, object otherData = default, string msg = "") + { + return new ResponseOutput().Ok(data, otherData, msg); + } + /// + /// 成功 + /// + /// + public static IResponseOutput Ok() + { + return Ok(); + } + + /// + /// 失败 + /// + /// 消息 + /// 数据 + /// + public static IResponseOutput NotOk(string msg = "", T data = default, ApiResponseCodeEnum code = ApiResponseCodeEnum.BusinessValidationFailed) + { + return new ResponseOutput().NotOk(msg, data, code); + } + + public static IResponseOutput NotOk( T data = default, ApiResponseCodeEnum code = ApiResponseCodeEnum.BusinessValidationFailed) + { + return new ResponseOutput().NotOk("", data, code); + } + + /// + /// 失败 + /// + /// 消息 + /// + public static IResponseOutput NotOk(string msg = "", ApiResponseCodeEnum code = ApiResponseCodeEnum.BusinessValidationFailed) + { + return new ResponseOutput().NotOk(msg,code:code); + } + + public static IResponseOutput DBNotExistIfNUll(object businessObject) + { + return new ResponseOutput().NotOk($"The business object{businessObject.GetType().Name} does not exist in the database, or was deleted by someone else, or an incorrect parameter query caused"); + } + + + /// + /// 根据布尔值返回结果 --适合删除 + /// + /// + /// + public static IResponseOutput Result(bool success) + { + return success ? Ok() : NotOk("Expect a change, but the database data has not changed"); + } + + /// + /// 根据布尔值返回结果 --适合添加和更新一起 + /// + /// + /// + public static IResponseOutput Result(bool success, T data = default) + { + return success ? Ok(data) : NotOk("Saved failed",data); + } + + ///// + ///// 根据布尔值返回结果 + ///// + ///// + ///// + //public static IResponseOutput Result(bool success) + //{ + // return success ? Ok() : NotOk(); + //} + } +} diff --git a/IRaCIS.Core.Test/DbHelper.ttinclude b/IRaCIS.Core.Test/DbHelper.ttinclude new file mode 100644 index 0000000..956e4eb --- /dev/null +++ b/IRaCIS.Core.Test/DbHelper.ttinclude @@ -0,0 +1,324 @@ +<#+ + public class config + { + public static readonly string ConnectionString = "Server=123.56.94.154,1433\\MSSQLSERVER;Database=IRaCIS_New_Tet;User ID=sa;Password=dev123456DEV;TrustServerCertificate=true"; + public static readonly string DbDatabase = "IRaCIS_New_Tet"; + //ַ,ƴ + public static readonly string TableName = "DefaultShortcutKey"; + //ļ service Ƿҳ + } +#> +<#+ + public class DbHelper + { + #region GetDbTables + + public static List GetDbTablesNew(string connectionString, string database,string tables = null) + { + if (!string.IsNullOrEmpty(tables)) + { + tables = string.Format(" and obj.name in ('{0}')", tables.Replace(",", "','")); + } + string sql = string.Format(@"SELECT + obj.name tablename + from {0}.sys.objects obj + inner join {0}.dbo.sysindexes idx on obj.object_id=idx.id and idx.indid<=1 + INNER JOIN {0}.sys.schemas schem ON obj.schema_id=schem.schema_id + left join {0}.sys.extended_properties g ON (obj.object_id = g.major_id AND g.minor_id = 0 AND g.name= 'MS_Description') + where type='U' {1} + order by obj.name", database,tables); + DataTable dt = GetDataTable(connectionString, sql); + return dt.Rows.Cast().Select(row =>row.Field("tablename")).ToList(); + } + + public static List GetDbTables(string connectionString, string database, string tables = null) + { + + if (!string.IsNullOrEmpty(tables)) + { + tables = string.Format(" and obj.name in ('{0}')", tables.Replace(",", "','")); + } + #region SQL + string sql = string.Format(@"SELECT + obj.name tablename, + schem.name schemname, + idx.rows, + CAST + ( + CASE + WHEN (SELECT COUNT(1) FROM sys.indexes WHERE object_id= obj.OBJECT_ID AND is_primary_key=1) >=1 THEN 1 + ELSE 0 + END + AS BIT) HasPrimaryKey + from {0}.sys.objects obj + inner join {0}.dbo.sysindexes idx on obj.object_id=idx.id and idx.indid<=1 + INNER JOIN {0}.sys.schemas schem ON obj.schema_id=schem.schema_id + where type='U' {1} + order by obj.name", database, tables); + #endregion + DataTable dt = GetDataTable(connectionString, sql); + return dt.Rows.Cast().Select(row => new DbTable + { + TableName = row.Field("tablename"), + SchemaName = row.Field("schemname"), + Rows = row.Field("rows"), + HasPrimaryKey = row.Field("HasPrimaryKey") + }).ToList(); + } + #endregion + + #region GetDbColumns + + public static List GetDbColumns(string connectionString, string database, string tableName, string schema = "dbo") + { + #region SQL + string sql = string.Format(@" + WITH indexCTE AS + ( + SELECT + ic.column_id, + ic.index_column_id, + ic.object_id + FROM {0}.sys.indexes idx + INNER JOIN {0}.sys.index_columns ic ON idx.index_id = ic.index_id AND idx.object_id = ic.object_id + WHERE idx.object_id =OBJECT_ID(@tableName) AND idx.is_primary_key=1 + ) + select + colm.column_id ColumnID, + CAST(CASE WHEN indexCTE.column_id IS NULL THEN 0 ELSE 1 END AS BIT) IsPrimaryKey, + colm.name ColumnName, + systype.name ColumnType, + colm.is_identity IsIdentity, + colm.is_nullable IsNullable, + cast(colm.max_length as int) ByteLength, + ( + case + when systype.name='nvarchar' and colm.max_length>0 then colm.max_length/2 + when systype.name='nchar' and colm.max_length>0 then colm.max_length/2 + when systype.name='ntext' and colm.max_length>0 then colm.max_length/2 + else colm.max_length + end + ) CharLength, + cast(colm.precision as int) Precision, + cast(colm.scale as int) Scale, + prop.value Remark + from {0}.sys.columns colm + inner join {0}.sys.types systype on colm.system_type_id=systype.system_type_id and colm.user_type_id=systype.user_type_id + left join {0}.sys.extended_properties prop on colm.object_id=prop.major_id and colm.column_id=prop.minor_id + LEFT JOIN indexCTE ON colm.column_id=indexCTE.column_id AND colm.object_id=indexCTE.object_id + where colm.object_id=OBJECT_ID(@tableName) + order by colm.column_id", database); + #endregion + SqlParameter param = new SqlParameter("@tableName", SqlDbType.NVarChar, 100) { Value = string.Format("{0}.{1}.{2}", database, schema, tableName) }; + DataTable dt = GetDataTable(connectionString, sql, param); + return dt.Rows.Cast().Select(row => new DbColumn() + { + ColumnID = row.Field("ColumnID"), + IsPrimaryKey = row.Field("IsPrimaryKey"), + ColumnName = row.Field("ColumnName"), + ColumnType = row.Field("ColumnType"), + IsIdentity = row.Field("IsIdentity"), + IsNullable = row.Field("IsNullable"), + ByteLength = row.Field("ByteLength"), + CharLength = row.Field("CharLength"), + Precision=row.Field("Precision"), + Scale = row.Field("Scale"), + Remark = row["Remark"].ToString() + }).ToList(); + } + + #endregion + + #region GetDataTable + + public static DataTable GetDataTable(string connectionString, string commandText, params SqlParameter[] parms) + { + using (SqlConnection connection = new SqlConnection(connectionString)) + { + SqlCommand command = connection.CreateCommand(); + command.CommandText = commandText; + command.Parameters.AddRange(parms); + SqlDataAdapter adapter = new SqlDataAdapter(command); + + DataTable dt = new DataTable(); + adapter.Fill(dt); + + return dt; + } + } + + #endregion + + #region GetPrimaryKey + public static string GetPrimaryKey(List dbColumns) + { + string primaryKey = string.Empty; + if (dbColumns!=null&&dbColumns.Count>0) + { + foreach (var item in dbColumns) + { + if (item.IsPrimaryKey==true) + { + primaryKey = item.ColumnName; + } + } + } + return primaryKey; + } + #endregion + } + + #region DbTable + public sealed class DbTable + { + public string TableName { get; set; } + public string SchemaName { get; set; } + public int Rows { get; set; } + + public bool HasPrimaryKey { get; set; } + } + #endregion + + #region DbColumn + + public sealed class DbColumn + { + + public int ColumnID { get; set; } + + + public bool IsPrimaryKey { get; set; } + + + public string ColumnName { get; set; } + + + public string ColumnType { get; set; } + + + public string CSharpType + { + get + { + return SqlServerDbTypeMap.MapCsharpType(ColumnType); + } + } + + /// + /// + /// + public Type CommonType + { + get + { + return SqlServerDbTypeMap.MapCommonType(ColumnType); + } + } + + public int ByteLength { get; set; } + + public int CharLength { get; set; } + + public int Precision{get;set;} + public int Scale { get; set; } + + public bool IsIdentity { get; set; } + + public bool IsNullable { get; set; } + + public string Remark { get; set; } + } + #endregion + + #region SqlServerDbTypeMap + + public class SqlServerDbTypeMap + { + public static string MapCsharpType(string dbtype) + { + if (string.IsNullOrEmpty(dbtype)) return dbtype; + dbtype = dbtype.ToLower(); + string csharpType = "object"; + switch (dbtype) + { + case "bigint": csharpType = "long"; break; + case "binary": csharpType = "byte[]"; break; + case "bit": csharpType = "bool"; break; + case "char": csharpType = "string"; break; + case "date": csharpType = "DateTime"; break; + case "datetime": csharpType = "DateTime"; break; + case "datetime2": csharpType = "DateTime"; break; + case "datetimeoffset": csharpType = "DateTimeOffset"; break; + case "decimal": csharpType = "decimal"; break; + case "float": csharpType = "double"; break; + case "image": csharpType = "byte[]"; break; + case "int": csharpType = "int"; break; + case "money": csharpType = "decimal"; break; + case "nchar": csharpType = "string"; break; + case "ntext": csharpType = "string"; break; + case "numeric": csharpType = "decimal"; break; + case "nvarchar": csharpType = "string"; break; + case "real": csharpType = "Single"; break; + case "smalldatetime": csharpType = "DateTime"; break; + case "smallint": csharpType = "short"; break; + case "smallmoney": csharpType = "decimal"; break; + case "sql_variant": csharpType = "object"; break; + case "sysname": csharpType = "object"; break; + case "text": csharpType = "string"; break; + case "time": csharpType = "TimeSpan"; break; + case "timestamp": csharpType = "byte[]"; break; + case "tinyint": csharpType = "byte"; break; + case "uniqueidentifier": csharpType = "Guid"; break; + case "varbinary": csharpType = "byte[]"; break; + case "varchar": csharpType = "string"; break; + case "xml": csharpType = "string"; break; + default: csharpType = "object"; break; + } + return csharpType; + } + + public static Type MapCommonType(string dbtype) + { + if (string.IsNullOrEmpty(dbtype)) return Type.Missing.GetType(); + dbtype = dbtype.ToLower(); + Type commonType = typeof(object); + switch (dbtype) + { + case "bigint": commonType = typeof(long); break; + case "binary": commonType = typeof(byte[]); break; + case "bit": commonType = typeof(bool); break; + case "char": commonType = typeof(string); break; + case "date": commonType = typeof(DateTime); break; + case "datetime": commonType = typeof(DateTime); break; + case "datetime2": commonType = typeof(DateTime); break; + case "datetimeoffset": commonType = typeof(DateTimeOffset); break; + case "decimal": commonType = typeof(decimal); break; + case "float": commonType = typeof(double); break; + case "image": commonType = typeof(byte[]); break; + case "int": commonType = typeof(int); break; + case "money": commonType = typeof(decimal); break; + case "nchar": commonType = typeof(string); break; + case "ntext": commonType = typeof(string); break; + case "numeric": commonType = typeof(decimal); break; + case "nvarchar": commonType = typeof(string); break; + case "real": commonType = typeof(Single); break; + case "smalldatetime": commonType = typeof(DateTime); break; + case "smallint": commonType = typeof(short); break; + case "smallmoney": commonType = typeof(decimal); break; + case "sql_variant": commonType = typeof(object); break; + case "sysname": commonType = typeof(object); break; + case "text": commonType = typeof(string); break; + case "time": commonType = typeof(TimeSpan); break; + case "timestamp": commonType = typeof(byte[]); break; + case "tinyint": commonType = typeof(byte); break; + case "uniqueidentifier": commonType = typeof(Guid); break; + case "varbinary": commonType = typeof(byte[]); break; + case "varchar": commonType = typeof(string); break; + case "xml": commonType = typeof(string); break; + default: commonType = typeof(object); break; + } + return commonType; + } + } + #endregion + #> \ No newline at end of file diff --git a/IRaCIS.Core.Test/IRaCIS.Core.Test.csproj b/IRaCIS.Core.Test/IRaCIS.Core.Test.csproj new file mode 100644 index 0000000..d9eb61f --- /dev/null +++ b/IRaCIS.Core.Test/IRaCIS.Core.Test.csproj @@ -0,0 +1,125 @@ + + + + net6.0 + + false + + default + + AnyCPU;x64 + + + + ..\bin + + + + ..\bin + + + + ..\bin + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + IRaCIS .Core.ServiceAsync.cs + TextTemplatingFileGenerator + + + IRaCIS .Core.IServiceAsync.cs + TextTemplatingFileGenerator + + + TextTemplatingFileGenerator + IRaCIS.Core.Dto.cs + + + TextTemplatingFileGenerator + IRaCIS.Core.Services.cs + + + TextTemplatingFileGenerator + IRaCIS.Core.IServices.cs + + + TextTemplatingFileGenerator + IRaCIS.Core.Repository.cs + + + TextTemplatingFileGenerator + IRaCIS.Core.IRepository.cs + + + TextTemplatingFileGenerator + IRaCIS.Core.Entity.cs + + + + + + + + + + IRaCIS .Core.IServiceAsync.tt + True + True + + + IRaCIS .Core.ServiceAsync.tt + True + True + + + True + True + IRaCIS.Core.Dto.tt + + + True + True + IRaCIS.Core.Entity.tt + + + True + True + IRaCIS.Core.IRepository.tt + + + True + True + IRaCIS.Core.IServices.tt + + + True + True + IRaCIS.Core.Repository.tt + + + True + True + IRaCIS.Core.Services.tt + + + + + + + + + + + diff --git a/IRaCIS.Core.Test/ModelAuto.ttinclude b/IRaCIS.Core.Test/ModelAuto.ttinclude new file mode 100644 index 0000000..b7ab0a3 --- /dev/null +++ b/IRaCIS.Core.Test/ModelAuto.ttinclude @@ -0,0 +1,115 @@ +<#@ assembly name="System.Core"#> +<#@ assembly name="Microsoft.VisualStudio.Interop"#> +<#@ import namespace="System.Collections.Generic"#> +<#@ import namespace="System.IO"#> +<#@ import namespace="System.Text"#> +<#@ import namespace="Microsoft.VisualStudio.TextTemplating"#> +<#+ +class Manager +{ + public struct Block { + public int Start, Length; + public String Name,OutputPath; + } + + public List blocks = new List(); + public Block currentBlock; + public Block footerBlock = new Block(); + public Block headerBlock = new Block(); + public ITextTemplatingEngineHost host; + public ManagementStrategy strategy; + public StringBuilder template; + public Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader) { + this.host = host; + this.template = template; + strategy = ManagementStrategy.Create(host); + } + public void StartBlock(String name,String outputPath) { + currentBlock = new Block { Name = name, Start = template.Length ,OutputPath=outputPath}; + } + + public void StartFooter() { + footerBlock.Start = template.Length; + } + + public void EndFooter() { + footerBlock.Length = template.Length - footerBlock.Start; + } + + public void StartHeader() { + headerBlock.Start = template.Length; + } + + public void EndHeader() { + headerBlock.Length = template.Length - headerBlock.Start; + } + + public void EndBlock() { + currentBlock.Length = template.Length - currentBlock.Start; + blocks.Add(currentBlock); + } + public void Process(bool split) { + String header = template.ToString(headerBlock.Start, headerBlock.Length); + String footer = template.ToString(footerBlock.Start, footerBlock.Length); + blocks.Reverse(); + foreach(Block block in blocks) { + String fileName = Path.Combine(block.OutputPath, block.Name); + if (split) { + String content = header + template.ToString(block.Start, block.Length) + footer; + strategy.CreateFile(fileName, content); + template.Remove(block.Start, block.Length); + } else { + strategy.DeleteFile(fileName); + } + } + } +} +class ManagementStrategy +{ + internal static ManagementStrategy Create(ITextTemplatingEngineHost host) { + return (host is IServiceProvider) ? new VSManagementStrategy(host) : new ManagementStrategy(host); + } + + internal ManagementStrategy(ITextTemplatingEngineHost host) { } + + internal virtual void CreateFile(String fileName, String content) { + File.WriteAllText(fileName, content); + } + + internal virtual void DeleteFile(String fileName) { + if (File.Exists(fileName)) + File.Delete(fileName); + } +} + +class VSManagementStrategy : ManagementStrategy +{ + private EnvDTE.ProjectItem templateProjectItem; + + internal VSManagementStrategy(ITextTemplatingEngineHost host) : base(host) { + IServiceProvider hostServiceProvider = (IServiceProvider)host; + if (hostServiceProvider == null) + throw new ArgumentNullException("Could not obtain hostServiceProvider"); + + EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetCOMService(typeof(EnvDTE.DTE)); + if (dte == null) + throw new ArgumentNullException("Could not obtain DTE from host"); + + templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile); + } + internal override void CreateFile(String fileName, String content) { + base.CreateFile(fileName, content); + //((EventHandler)delegate { templateProjectItem.ProjectItems.AddFromFile(fileName); }).BeginInvoke(null, null, null, null); + } + internal override void DeleteFile(String fileName) { + ((EventHandler)delegate { FindAndDeleteFile(fileName); }).BeginInvoke(null, null, null, null); + } + private void FindAndDeleteFile(String fileName) { + foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems) { + if (projectItem.get_FileNames(0) == fileName) { + projectItem.Delete(); + return; + } + } + } +}#> \ No newline at end of file diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.IServiceAsync.cs b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.IServiceAsync.cs new file mode 100644 index 0000000..fe5554c --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.IServiceAsync.cs @@ -0,0 +1,10 @@ + + +//在这里设置 isPage 是否生成分页的还是不分页 + + + + + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.IServiceAsync.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.IServiceAsync.tt new file mode 100644 index 0000000..9f0639f --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.IServiceAsync.tt @@ -0,0 +1,75 @@ +<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core.dll" #> +<#@ assembly name="System.Data.dll" #> +<#@ assembly name="System.Data.DataSetExtensions.dll" #> +<#@ assembly name="System.Xml.dll" #> +<#@ import namespace="System" #> +<#@ import namespace="System.Xml" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="System.Data.SqlClient" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.IO" #> +<#@ include file="$(ProjectDir)DbHelper.ttinclude" #> +<#@ include file="$(ProjectDir)ModelAuto.ttinclude" #> +<# var manager = new Manager(Host, GenerationEnvironment, true); #> + +//在这里设置 isPage 是否生成分页的还是不分页 + +<# + var isPage=false; + #> + +<# + var OutputPath1 =Path.GetDirectoryName(Host.TemplateFile+".."); + OutputPath1=Path.Combine(OutputPath1,"IServices_New"); + if (!Directory.Exists(OutputPath1)) + { + Directory.CreateDirectory(OutputPath1); + } +#> + + +<# foreach (var item in DbHelper.GetDbTablesNew(config.ConnectionString, config.DbDatabase,config.TableName)) + { + var tableName=item.ToString(); + manager.StartBlock("I"+tableName+"Service"+".cs",OutputPath1);//文件名 +#> +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// I<#=tableName#>Service + /// + public interface I<#=tableName#>Service + { + + <# if(isPage){#> + TaskView>> Get<#=tableName#>List(<#=tableName#>Query inQuery); + <# } else {#> + + TaskView>> Get<#=tableName#>List(<#=tableName#>Query inQuery); + <# }#> + + Task AddOrUpdate<#=tableName#>(<#=tableName#>AddOrEdit addOrEdit<#=tableName#>); + + Task Delete<#=tableName#>(Guid <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Id); + + + } +} +<# + manager.EndBlock(); + } + manager.Process(true); + #> + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.cs b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.cs new file mode 100644 index 0000000..2faa758 --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.cs @@ -0,0 +1,9 @@ + + + + +//在这里设置 isPage 是否生成分页的还是不分页 + + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.tt new file mode 100644 index 0000000..77388db --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.tt @@ -0,0 +1,126 @@ +<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core.dll" #> +<#@ assembly name="System.Data.dll" #> +<#@ assembly name="System.Data.DataSetExtensions.dll" #> +<#@ assembly name="System.Xml.dll" #> +<#@ import namespace="System" #> +<#@ import namespace="System.Xml" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="System.Data.SqlClient" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.IO" #> + +<#@ include file="$(ProjectDir)DbHelper.ttinclude" #> +<#@ include file="$(ProjectDir)ModelAuto.ttinclude" #> +<# var manager = new Manager(Host, GenerationEnvironment, true); #> + + +//在这里设置 isPage 是否生成分页的还是不分页 + +<# + var isPage=false; + #> + +<# + var OutputPath1 =Path.GetDirectoryName(Host.TemplateFile+".."); + OutputPath1=Path.Combine(OutputPath1,"Services_New"); + if (!Directory.Exists(OutputPath1)) + { + Directory.CreateDirectory(OutputPath1); + } +#> + + +<# foreach (var item in DbHelper.GetDbTablesNew(config.ConnectionString, config.DbDatabase,config.TableName)) + { + var tableName=item.ToString(); + manager.StartBlock(tableName+"Service"+".cs",OutputPath1);//文件名 +#> +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Service +{ + /// + /// <#=tableName#>Service + /// + [ ApiExplorerSettings(GroupName = "Test")] + public class <#=tableName#>Service: BaseService, I<#=tableName#>Service + { + + private readonly IRepository<<#=tableName#>> _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository; + + public <#=tableName#>Service(IRepository<<#=tableName#>> <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository) + { + _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository = <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository; + } + + <# if(isPage){#> + public async TaskView>> Get<#=tableName#>List(<#=tableName#>Query inQuery) + { + + var <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Queryable = + + _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository + .ProjectTo<<#=tableName#>View>(_mapper.ConfigurationProvider); + + var pageList= await <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Queryable. + .ToPagedListAsync(query<#=tableName#>.PageIndex, query<#=tableName#>.PageSize, string.IsNullOrWhiteSpace(query<#=tableName#>.SortField) ? "Id" : query<#=tableName#>.SortField, + hospitalSearchModel.Asc); + + return pageList; + } + <# } else {#> + + public async TaskView>> Get<#=tableName#>List(<#=tableName#>Query inQuery) + { + + + var <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Queryable = _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository + .ProjectTo<<#=tableName#>View>(_mapper.ConfigurationProvider); + + return await <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Queryable.ToListAsync(); + } + <# }#> + + + public async Task AddOrUpdate<#=tableName#>(<#=tableName#>AddOrEdit addOrEdit<#=tableName#>) + { + // 在此处拷贝automapper 映射 + + + CreateMap<<#=tableName#>, <#=tableName#>View>(); + // CreateMap< <#=tableName#>,<#=tableName#>AddOrEdit>().ReverseMap(); + + + var entity = await _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository.InsertOrUpdateAsync(addOrEdit<#=tableName#>, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + + [HttpDelete("{<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Id:guid}")] + public async Task Delete<#=tableName#>(Guid <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Id) + { + var success = await _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository.DeleteFromQueryAsync(t => t.Id == <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Id,true); + return ResponseOutput.Ok(); + } + + + } +} +<# + manager.EndBlock(); + } + manager.Process(true); + #> diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Dto.cs b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Dto.cs new file mode 100644 index 0000000..cb03b00 --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Dto.cs @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Dto.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Dto.tt new file mode 100644 index 0000000..bf59c18 --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Dto.tt @@ -0,0 +1,82 @@ +<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core.dll" #> +<#@ assembly name="System.Data.dll" #> +<#@ assembly name="System.Data.DataSetExtensions.dll" #> +<#@ assembly name="System.Xml.dll" #> +<#@ import namespace="System" #> +<#@ import namespace="System.Xml" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="System.Data.SqlClient" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.IO" #> +<#@ include file="$(ProjectDir)DbHelper.ttinclude" #> +<#@ include file="$(ProjectDir)ModelAuto.ttinclude" #> +<# var manager = new Manager(Host, GenerationEnvironment, true); #> + + + +<# + var OutputPath1 =Path.GetDirectoryName(Host.TemplateFile+".."); + OutputPath1=Path.Combine(OutputPath1,"Dto_New"); + if (!Directory.Exists(OutputPath1)) + { + Directory.CreateDirectory(OutputPath1); + } +#> + + +<# foreach (var item in DbHelper.GetDbTablesNew(config.ConnectionString, config.DbDatabase,config.TableName)) + { + var tableName=item.ToString(); + manager.StartBlock(tableName+"ViewModel"+".cs",OutputPath1);//文件名 +#> +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.ViewModel +{ + /// <#=tableName#>View 列表视图模型 + public class <#=tableName#>View + { + <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, tableName)){#> + public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } + <# }#> + } + + ///<#=tableName#>Query 列表查询参数模型 + public class <#=tableName#>Query + { + <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, tableName)){#><# if(column.CSharpType=="string"){#> + /// <#= column.Remark == "" ? column.ColumnName : column.Remark.Replace("\r\n","") #> + public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } + + <# }#> + <# }#> + } + + /// <#=tableName#>AddOrEdit 列表查询参数模型 + public class <#=tableName#>AddOrEdit + { + <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, tableName)){#> + public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } + <# }#> + } + + +} + + +<# + manager.EndBlock(); + } + manager.Process(true); + #> + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.cs b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.cs new file mode 100644 index 0000000..171fad3 --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.cs @@ -0,0 +1,3 @@ + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.tt new file mode 100644 index 0000000..c15229f --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.tt @@ -0,0 +1,68 @@ +<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core.dll" #> +<#@ assembly name="System.Data.dll" #> +<#@ assembly name="System.Data.DataSetExtensions.dll" #> +<#@ assembly name="System.Xml.dll" #> +<#@ import namespace="System" #> +<#@ import namespace="System.Xml" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="System.Data.SqlClient" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.IO" #> +<#@ include file="$(ProjectDir)DbHelper.ttinclude" #> +<#@ include file="$(ProjectDir)ModelAuto.ttinclude" #> +<# var manager = new Manager(Host, GenerationEnvironment, true); #> + +<# + var OutputPath1 =Path.GetDirectoryName(Host.TemplateFile+".."); + OutputPath1=Path.Combine(OutputPath1,"Models_New"); + if (!Directory.Exists(OutputPath1)) + { + Directory.CreateDirectory(OutputPath1); + } +#> + +<# foreach (var item in DbHelper.GetDbTablesNew(config.ConnectionString, config.DbDatabase,config.TableName)) + { + var tableName=item.ToString(); + manager.StartBlock(tableName+".cs",OutputPath1);//文件名 +#> + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///<#=tableName#> + /// + [Table("<#=tableName#>")] + public class <#=tableName#> : Entity, IAuditUpdate, IAuditAdd + { + <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, tableName)){#> + + /// + /// <#= column.Remark == "" ? column.ColumnName : column.Remark.Replace("\r\n"," ") #> + /// + <# + if(column.IsPrimaryKey) + {#>[Key] + <#}#><# if(!column.IsNullable) {#>[Required] + <# }#>public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } + <#}#> + } + + public virtual DbSet<<#=tableName#>> <#=tableName#> { get; set; } +} +<# + manager.EndBlock(); + } + manager.Process(true); + #> \ No newline at end of file diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IRepository.cs b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IRepository.cs new file mode 100644 index 0000000..aaed97b --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IRepository.cs @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IRepository.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IRepository.tt new file mode 100644 index 0000000..f328521 --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IRepository.tt @@ -0,0 +1,67 @@ +<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core.dll" #> +<#@ assembly name="System.Data.dll" #> +<#@ assembly name="System.Data.DataSetExtensions.dll" #> +<#@ assembly name="System.Xml.dll" #> +<#@ import namespace="System" #> +<#@ import namespace="System.Xml" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="System.Data.SqlClient" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.IO" #> +<#@ include file="$(ProjectDir)DbHelper.ttinclude" #> +<#@ include file="$(ProjectDir)ModelAuto.ttinclude" #> +<# var manager = new Manager(Host, GenerationEnvironment, true); #> + + +<# + var OutputPath1 =Path.GetDirectoryName(Host.TemplateFile+".."); + OutputPath1=Path.Combine(OutputPath1,"IRepositories_New"); + if (!Directory.Exists(OutputPath1)) + { + Directory.CreateDirectory(OutputPath1); + } +#> + + +<# foreach (var item in DbHelper.GetDbTablesNew(config.ConnectionString, config.DbDatabase,config.TableName)) + { + var tableName=item.ToString(); + manager.StartBlock("I"+tableName+"Repository"+".cs",OutputPath1);//文件名 +#> +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> +// 使用泛型仓储注册,现在不需要这个文件了,作为学习例子 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Domain.IRepository; +using IRaCIS.Core.Domain.Models; +namespace IRaCIS.Core.Domain.IRepository +{ + /// + /// I<#=tableName#>Repository + /// + public interface I<#=tableName#>Repository : IRepository<<#=tableName#>> + { + + + } +} +<# + manager.EndBlock(); + } + manager.Process(true); + #> + + + + + + + + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IServices.cs b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IServices.cs new file mode 100644 index 0000000..fe5554c --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IServices.cs @@ -0,0 +1,10 @@ + + +//在这里设置 isPage 是否生成分页的还是不分页 + + + + + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IServices.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IServices.tt new file mode 100644 index 0000000..4c91654 --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.IServices.tt @@ -0,0 +1,78 @@ +<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core.dll" #> +<#@ assembly name="System.Data.dll" #> +<#@ assembly name="System.Data.DataSetExtensions.dll" #> +<#@ assembly name="System.Xml.dll" #> +<#@ import namespace="System" #> +<#@ import namespace="System.Xml" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="System.Data.SqlClient" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.IO" #> +<#@ include file="$(ProjectDir)DbHelper.ttinclude" #> +<#@ include file="$(ProjectDir)ModelAuto.ttinclude" #> +<# var manager = new Manager(Host, GenerationEnvironment, true); #> + +//在这里设置 isPage 是否生成分页的还是不分页 + +<# + var isPage=false; + #> + +<# + var OutputPath1 =Path.GetDirectoryName(Host.TemplateFile+".."); + OutputPath1=Path.Combine(OutputPath1,"IServices_New"); + if (!Directory.Exists(OutputPath1)) + { + Directory.CreateDirectory(OutputPath1); + } +#> + + +<# foreach (var item in DbHelper.GetDbTablesNew(config.ConnectionString, config.DbDatabase,config.TableName)) + { + var tableName=item.ToString(); + manager.StartBlock("I"+tableName+"Service"+".cs",OutputPath1);//文件名 +#> +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using System.Collections.Generic; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure.Extention; +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// I<#=tableName#>Service + /// + public interface I<#=tableName#>Service + { + + <# if(isPage){#> + PageOutput<<#=tableName#>View> Get<#=tableName#>List(<#=tableName#>Query query<#=tableName#>); + <# } else {#> + + List<<#=tableName#>View> Get<#=tableName#>List(<#=tableName#>Query query<#=tableName#>); + <# }#> + + IResponseOutput AddOrUpdate<#=tableName#>(<#=tableName#>AddOrEdit addOrEdit<#=tableName#>); + + IResponseOutput Delete<#=tableName#>(Guid <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Id); + + + } +} +<# + manager.EndBlock(); + } + manager.Process(true); + #> + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Repository.cs b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Repository.cs new file mode 100644 index 0000000..b26f154 --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Repository.cs @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Repository.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Repository.tt new file mode 100644 index 0000000..0ba2092 --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Repository.tt @@ -0,0 +1,64 @@ +<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core.dll" #> +<#@ assembly name="System.Data.dll" #> +<#@ assembly name="System.Data.DataSetExtensions.dll" #> +<#@ assembly name="System.Xml.dll" #> +<#@ import namespace="System" #> +<#@ import namespace="System.Xml" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="System.Data.SqlClient" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.IO" #> +<#@ include file="$(ProjectDir)DbHelper.ttinclude" #> +<#@ include file="$(ProjectDir)ModelAuto.ttinclude" #> +<# var manager = new Manager(Host, GenerationEnvironment, true); #> + + + +<# + var OutputPath1 =Path.GetDirectoryName(Host.TemplateFile+".."); + OutputPath1=Path.Combine(OutputPath1,"Repositories_New"); + if (!Directory.Exists(OutputPath1)) + { + Directory.CreateDirectory(OutputPath1); + } +#> + + +<# foreach (var item in DbHelper.GetDbTablesNew(config.ConnectionString, config.DbDatabase,config.TableName)) + { + var tableName=item.ToString(); + manager.StartBlock(tableName+"Repository"+".cs",OutputPath1);//文件名 +#> +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> +// 使用泛型仓储注册,现在不需要这个文件了,作为学习例子 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using IRaCIS.Core.Domain.IRepository; +using IRaCIS.Core.Domain.Models; +namespace IRaCIS.Core.Domain.Repository; +{ + /// + /// <#=tableName#>Repository + /// + public class <#=tableName#>Repository : Repository<<#=tableName#>>, IRepository<<#=tableName#>> + { + public <#=tableName#>Repository( IRaCISDBContext db) : base( db) + { + + } + + } +} +<# + manager.EndBlock(); + } + manager.Process(true); + #> + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Services.cs b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Services.cs new file mode 100644 index 0000000..2faa758 --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Services.cs @@ -0,0 +1,9 @@ + + + + +//在这里设置 isPage 是否生成分页的还是不分页 + + + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Services.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Services.tt new file mode 100644 index 0000000..7b76254 --- /dev/null +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Services.tt @@ -0,0 +1,123 @@ +<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core.dll" #> +<#@ assembly name="System.Data.dll" #> +<#@ assembly name="System.Data.DataSetExtensions.dll" #> +<#@ assembly name="System.Xml.dll" #> +<#@ import namespace="System" #> +<#@ import namespace="System.Xml" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="System.Data.SqlClient" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.IO" #> + +<#@ include file="$(ProjectDir)DbHelper.ttinclude" #> +<#@ include file="$(ProjectDir)ModelAuto.ttinclude" #> +<# var manager = new Manager(Host, GenerationEnvironment, true); #> + + +//在这里设置 isPage 是否生成分页的还是不分页 + +<# + var isPage=false; + #> + +<# + var OutputPath1 =Path.GetDirectoryName(Host.TemplateFile+".."); + OutputPath1=Path.Combine(OutputPath1,"Services_New"); + if (!Directory.Exists(OutputPath1)) + { + Directory.CreateDirectory(OutputPath1); + } +#> + + +<# foreach (var item in DbHelper.GetDbTablesNew(config.ConnectionString, config.DbDatabase,config.TableName)) + { + var tableName=item.ToString(); + manager.StartBlock(tableName+"Service"+".cs",OutputPath1);//文件名 +#> +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Infrastructure.Extention; +namespace IRaCIS.Core.Application.Contracts +{ + /// + /// <#=tableName#>Service + /// + [ ApiExplorerSettings(GroupName = "Test")] + public class <#=tableName#>Service: BaseService, I<#=tableName#>Service + { + + private readonly IRepository<<#=tableName#>> _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository; + + public <#=tableName#>Service(IRepository<<#=tableName#>> <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository) + { + _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository = <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository; + } + + <# if(isPage){#> + public PageOutput<<#=tableName#>View> Get<#=tableName#>List(<#=tableName#>Query query<#=tableName#>) + { + + var <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Queryable = + + _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository.AsQueryable() + .ProjectTo<<#=tableName#>View>(_mapper.ConfigurationProvider); + + var pageList=<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Queryable. + .ToPagedList(query<#=tableName#>.PageIndex, query<#=tableName#>.PageSize, string.IsNullOrWhiteSpace(query<#=tableName#>.SortField) ? "Id" : query<#=tableName#>.SortField, + hospitalSearchModel.Asc); + + return pageList; + } + <# } else {#> + + public List<<#=tableName#>View> Get<#=tableName#>List(<#=tableName#>Query query<#=tableName#>) + { + + + var <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Queryable = _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository + .Where(<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Lambda).ProjectTo<<#=tableName#>View>(_mapper.ConfigurationProvider); + + return <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Queryable.ToList(); + } + <# }#> + + + public IResponseOutput AddOrUpdate<#=tableName#>(<#=tableName#>AddOrEdit addOrEdit<#=tableName#>) + { + // 在此处拷贝automapper 映射 + // CreateMap<<#=tableName#>AddOrEdit, <#=tableName#>>(); + // CreateMap< <#=tableName#>,<#=tableName#>AddOrEdit>(); + + var entity = _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository.UseMapper(_mapper).InsertOrUpdate(addOrEdit<#=tableName#>, true); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + + [HttpDelete("{<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Id:guid}")] + public IResponseOutput Delete<#=tableName#>(Guid <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Id) + { + var success = _<#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Repository.Delete(t => t.Id == <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Id); + return ResponseOutput.Result(success); + } + + + } +} +<# + manager.EndBlock(); + } + manager.Process(true); + #> diff --git a/IRaCIS.Core.Test/Test/Audit/AuditContext.cs b/IRaCIS.Core.Test/Test/Audit/AuditContext.cs new file mode 100644 index 0000000..2fc55e0 --- /dev/null +++ b/IRaCIS.Core.Test/Test/Audit/AuditContext.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; + +#region AuditContext +public class AuditContext : DbContext +{ + private readonly string _connectionString; + + public AuditContext(string connectionString) + { + _connectionString = connectionString; + } + + //protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + // => optionsBuilder.Usesq(_connectionString); + + public DbSet SaveChangesAudits { get; set; } +} + + +public class SaveChangesAudit +{ + public int Id { get; set; } + public Guid AuditId { get; set; } + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } + public bool Succeeded { get; set; } + public string ErrorMessage { get; set; } + + public ICollection Entities { get; } = new List(); +} + +public class EntityAudit +{ + public int Id { get; set; } + public EntityState State { get; set; } + public string AuditMessage { get; set; } + + public SaveChangesAudit SaveChangesAudit { get; set; } +} + +#endregion \ No newline at end of file diff --git a/IRaCIS.Core.Test/UnitTest1.cs b/IRaCIS.Core.Test/UnitTest1.cs new file mode 100644 index 0000000..1084bdf --- /dev/null +++ b/IRaCIS.Core.Test/UnitTest1.cs @@ -0,0 +1,18 @@ +//using NUnit.Framework; + +//namespace IRaCIS.Core.Test +//{ +// public class Tests +// { +// [SetUp] +// public void Setup() +// { +// } + +// [Test] +// public void Test1() +// { +// Assert.Pass(); +// } +// } +//} \ No newline at end of file diff --git a/ei_api.drone.yml b/ei_api.drone.yml new file mode 100644 index 0000000..d449333 --- /dev/null +++ b/ei_api.drone.yml @@ -0,0 +1,72 @@ +kind: pipeline +type: docker +name: irc-netcore-api + +steps: + - name: docker-build + image: docker + pull: if-not-exists + volumes: + - name: dockersock + path: /var/run/docker.sock + - name: cached_nuget_packages + path: /drone/nuget_packages + commands: + - date +%H:%M:%S + - pwd + - docker build -t Test.Study . + - date +%H:%M:%S + + - name: docker-deploy + image: docker + pull: if-not-exists + depends_on: + - docker-build + volumes: + - name: dockersock + path: /var/run/docker.sock + commands: + - date +%H:%M:%S + - docker rm -f test-study-container + - docker run -itd -e TZ=Asia/Shanghai -e ASPNETCORE_ENVIRONMENT=Test_Study --restart=always --name test-study-container -p 8030:80 Test.Study + - date +%H:%M:%S + +volumes: + - name: cached_nuget_packages + host: + path: /mnt/f/docker_publish/nuget_packages + - name: dockersock + host: + path: /var/run/docker.sock + +trigger: + branch: + - master + +--- + + +kind: pipeline +type: ssh +name: ssh-windows-publish + +platform: + os: windows + arch: amd64 + +clone: + disable: true #禁用默认克隆 + +server: + host: 123.56.94.154 + user: Administrator + password: WHxckj2019 + +steps: +- name: publish-test-EIImageViewer + commands: + - C:\CICD\Test.EIImageViewer\netcore_Publish.ps1 + +trigger: + branch: + - Test.EIImageViewer \ No newline at end of file