diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..d420007
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,321 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+
+# Default severity for analyzer diagnostics with category 'Interoperability'
+dotnet_analyzer_diagnostic.category-Interoperability.severity = none
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = tab
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Code Actions ####
+
+# Type members
+dotnet_hide_advanced_members = false
+dotnet_member_insertion_location = with_other_members_of_the_same_kind
+dotnet_property_generation_behavior = prefer_throwing_properties
+
+# Symbol search
+dotnet_search_reference_assemblies = true
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = false
+file_header_template = unset
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false
+dotnet_style_qualification_for_field = false
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_property = false
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true
+dotnet_style_predefined_type_for_member_access = true
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members
+
+# Expression-level preferences
+dotnet_prefer_system_hash_code = true
+dotnet_style_coalesce_expression = true
+dotnet_style_collection_initializer = true
+dotnet_style_explicit_tuple_names = true
+dotnet_style_namespace_match_folder = true
+dotnet_style_null_propagation = true
+dotnet_style_object_initializer = true
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true
+dotnet_style_prefer_collection_expression = when_types_loosely_match
+dotnet_style_prefer_compound_assignment = true
+dotnet_style_prefer_conditional_expression_over_assignment = true
+dotnet_style_prefer_conditional_expression_over_return = true
+dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
+dotnet_style_prefer_inferred_anonymous_type_member_names = true
+dotnet_style_prefer_inferred_tuple_names = true
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true
+dotnet_style_prefer_simplified_boolean_expressions = true
+dotnet_style_prefer_simplified_interpolation = true
+
+# Field preferences
+dotnet_style_readonly_field = true
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+# New line preferences
+dotnet_style_allow_multiple_blank_lines_experimental = true
+dotnet_style_allow_statement_immediately_after_block_experimental = true
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = false:silent
+csharp_style_var_for_built_in_types = false:silent
+csharp_style_var_when_type_is_apparent = false:silent
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:silent
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true
+csharp_style_pattern_matching_over_is_with_cast_check = true
+csharp_style_prefer_extended_property_pattern = true
+csharp_style_prefer_not_pattern = true
+csharp_style_prefer_pattern_matching = true
+csharp_style_prefer_switch_expression = true
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true:suggestion
+
+
+csharp_using_directive_placement = outside_namespace:silent
+csharp_prefer_simple_using_statement = true:suggestion
+# Modifier preferences
+csharp_prefer_static_anonymous_function = true
+csharp_prefer_static_local_function = true
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
+csharp_style_prefer_readonly_struct = true
+csharp_style_prefer_readonly_struct_member = true
+
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_prefer_system_threading_lock = true:suggestion
+csharp_style_namespace_declarations = file_scoped:silent
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_style_prefer_top_level_statements = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_prefer_system_threading_lock = true:suggestion
+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
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_deconstructed_variable_declaration = true
+csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
+csharp_style_inlined_variable_declaration = true
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_local_over_anonymous_function = true:suggestion
+csharp_style_prefer_null_check_over_type_check = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_prefer_tuple_swap = true
+csharp_style_prefer_unbound_generic_type_in_nameof = true
+csharp_style_prefer_utf8_string_literals = true
+csharp_style_throw_expression = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable
+csharp_style_unused_value_expression_statement_preference = discard_variable
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
+
+# New line preferences
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
+csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
+csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
+csharp_style_allow_embedded_statements_on_same_line_experimental = true
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = false
+csharp_indent_labels = no_change
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+[*.{cs,vb}]
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+dotnet_naming_rule.private_or_internal_field_should_be__开头.severity = suggestion
+dotnet_naming_rule.private_or_internal_field_should_be__开头.symbols = private_or_internal_field
+dotnet_naming_rule.private_or_internal_field_should_be__开头.style = _开头
+
+dotnet_naming_rule.private_or_internal_field_should_be__开头.severity = none
+dotnet_naming_rule.private_or_internal_field_should_be__开头.symbols = private_or_internal_field
+dotnet_naming_rule.private_or_internal_field_should_be__开头.style = _开头
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
+dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
+dotnet_naming_symbols.private_or_internal_field.required_modifiers =
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+dotnet_naming_style._开头.required_prefix = _
+dotnet_naming_style._开头.required_suffix =
+dotnet_naming_style._开头.word_separator =
+dotnet_naming_style._开头.capitalization = camel_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+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:silent
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:silent
+dotnet_style_prefer_compound_assignment = true:suggestion
+dotnet_style_prefer_simplified_interpolation = true:suggestion
+
+
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+tab_width = 4
+indent_size = 4
+end_of_line = crlf
+
+# IDE0305: Simplify collection initialization
+# dotnet_style_prefer_collection_expression = when_types_exactly_match
+# dotnet_style_prefer_collection_expression = when_types_loosely_match
+
+dotnet_diagnostic.IDE0305.severity = silent
+
+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
+
+# IDE1006: Naming Styles
+dotnet_diagnostic.IDE1006.severity = none
+
+# IDE0059: Unnecessary assignment of a value
+dotnet_diagnostic.IDE0059.severity = silent
+
+# CS0168: Variable is declared but never used
+dotnet_diagnostic.CS0168.severity = silent
+
+dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
+dotnet_style_namespace_match_folder = true:suggestion
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 5e2a23b..b8452d0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,16 +1,365 @@
-.vs
-.svn
-Bin
-Bin.Oem
-NotUse
-obj
-*.psd
-packages
-.user
-bin
-*.rar
-*.zip
-/Update/Update/*.user
+## 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
-IISProfile.pubxml
-*/**/logs
+*.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
+
+!Store.User
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..36df23c
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,23 @@
+
+
+
+
+ net472
+ $(DefaultTargetFramework)
+ 12.0
+ true
+ false
+ true
+ annotations
+ CS1591
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
diff --git a/KJD_WebService.sln b/KJD_WebService.sln
index 84af2e3..a8a48da 100644
--- a/KJD_WebService.sln
+++ b/KJD_WebService.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
-VisualStudioVersion = 18.9.12105.275 stable
+VisualStudioVersion = 18.9.12105.275
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KJD_WebService", "KJD_WebService\KJD_WebService.csproj", "{A6C03FE1-142A-4C97-99A9-67FD915836B6}"
EndProject
@@ -9,6 +9,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".test", ".test", "{AA240C1D
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebServiceTest", "test\WebServiceTest\WebServiceTest.csproj", "{348EB476-1813-419E-910F-0D27E58B3D19}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{15CB4E89-94B5-4851-A58F-0A56FBCD9527}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ .gitignore = .gitignore
+ Directory.Build.props = Directory.Build.props
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Store.User", "Store.User\Store.User.csproj", "{20BA615B-6EAC-47EA-87FE-DD740D6F3AD7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Store.Framework", "Store.Framework\Store.Framework.csproj", "{CD6FB773-0431-4FAE-9E30-65953118FEBF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -23,6 +34,14 @@ Global
{348EB476-1813-419E-910F-0D27E58B3D19}.Debug|Any CPU.Build.0 = Debug|Any CPU
{348EB476-1813-419E-910F-0D27E58B3D19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{348EB476-1813-419E-910F-0D27E58B3D19}.Release|Any CPU.Build.0 = Release|Any CPU
+ {20BA615B-6EAC-47EA-87FE-DD740D6F3AD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {20BA615B-6EAC-47EA-87FE-DD740D6F3AD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {20BA615B-6EAC-47EA-87FE-DD740D6F3AD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {20BA615B-6EAC-47EA-87FE-DD740D6F3AD7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CD6FB773-0431-4FAE-9E30-65953118FEBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD6FB773-0431-4FAE-9E30-65953118FEBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD6FB773-0431-4FAE-9E30-65953118FEBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD6FB773-0431-4FAE-9E30-65953118FEBF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/KJD_WebService/BLL/WXXD/WXOemManage.cs b/KJD_WebService/BLL/WXXD/WXOemManage.cs
index 89f29ab..df19a4c 100644
--- a/KJD_WebService/BLL/WXXD/WXOemManage.cs
+++ b/KJD_WebService/BLL/WXXD/WXOemManage.cs
@@ -17,6 +17,10 @@ namespace KJD_Web.BLL.WXXD
{
public class WXOemManage
{
+ ///
+ /// 店铺续费手续费 15
+ ///
+ const int ShopRenewFee = 15;
///
/// 手续费
///
@@ -603,7 +607,7 @@ namespace KJD_Web.BLL.WXXD
WHERE A.oem_id = '{MysqlUtils.ReplaceSqlSpecial(oem_id)}'";
var userDt = mySql.ExecuteDataTable(sql);
if (!string.IsNullOrEmpty(mySql.Message) || userDt == null || userDt.Rows.Count == 0)
- return 0;
+ return 0;//找不到用户或数据库错误返回
List userLst = userDt.Rows.Cast()
.Select(r => r["user_name"].ParseToString())
@@ -611,80 +615,130 @@ namespace KJD_Web.BLL.WXXD
.Distinct()
.ToList();
if (userLst.Count == 0)
- return 0;
+ return 0;//用户列表空 返回
+
+ decimal total_amount = 0m; // 总金额(元)
+ decimal rebate_amount = 0m; // 正数部分(累计返利)
+ string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+
+
+
// 2. 该oem所有账户的未结算流水,按id升序
sql = "SELECT * FROM `wxxd`.`user_payment_record` WHERE user_name IN ('" + string.Join("','", userLst) + "') AND type IN (20,23,26,16,31,34) AND settlement=0 AND is_deleted=0 ORDER BY id ASC";
var flowDt = mySql.ExecuteDataTable(sql);
- if (!string.IsNullOrEmpty(mySql.Message) || flowDt == null || flowDt.Rows.Count == 0)
+ if (!string.IsNullOrEmpty(mySql.Message))
return 0;
- decimal total_amount = 0m; // 总金额(元)
- decimal rebate_amount = 0m; // 正数部分(累计返利)
- HashSet deductedOrderSet = new HashSet(); // type=23 已扣减的订单
- HashSet giftsOrderSet = new HashSet(); // type=34 礼物已扣减的订单
- List settleIdLst = new List(); // 参与结算的流水id
- foreach (DataRow row in flowDt.Rows)
+
+
+ if (flowDt != null && flowDt.Rows.Count > 0)
{
- int type = row["type"].ParseToInt();
- decimal amount = row["amount"].ParseToDecimal();
- decimal oem_amount = row["oem_amount"].ParseToDecimal();
- long flowId = row["id"].ParseToLong();
+ HashSet deductedOrderSet = new HashSet(); // type=23 已扣减的订单
+ HashSet giftsOrderSet = new HashSet(); // type=34 礼物已扣减的订单
+ List settleIdLst = new List(); // 参与结算的流水id
- decimal itemAmount = 0m;
- if (type == 20)
- itemAmount = amount - 2.2m;
- else if (type == 26)
- itemAmount = amount - 2.5m;
- else if (type == 16)
- itemAmount = oem_amount;
- else if (type == 31)
- itemAmount = oem_amount;
- else if (type == 23)
+ foreach (DataRow row in flowDt.Rows)
{
- // 同一个订单只能减少一次
- string orderId = row["order_id"].ParseToString();
- if (orderId.IsNullOrEmpty() || deductedOrderSet.Contains(orderId))
- continue;
- deductedOrderSet.Add(orderId);
- itemAmount = -(amount - 2.2m);
- }
- else if (type == 34)
- {
- // 同一个订单只能减少一次
- string orderId = row["order_id"].ParseToString();
- if (orderId.IsNullOrEmpty() || giftsOrderSet.Contains(orderId))
- continue;
- giftsOrderSet.Add(orderId);
- itemAmount = -oem_amount;
- }
- else
- {
- continue; // 其他类型不计入
+ int type = row["type"].ParseToInt();
+ decimal amount = row["amount"].ParseToDecimal();
+ decimal oem_amount = row["oem_amount"].ParseToDecimal();
+ long flowId = row["id"].ParseToLong();
+
+ decimal itemAmount = 0m;
+ if (type == 20)
+ itemAmount = amount - 2.2m;
+ else if (type == 26)
+ itemAmount = amount - 2.5m;
+ else if (type == 16)
+ itemAmount = oem_amount;
+ else if (type == 31)
+ itemAmount = oem_amount;
+ else if (type == 23)
+ {
+ // 同一个订单只能减少一次
+ string orderId = row["order_id"].ParseToString();
+ if (orderId.IsNullOrEmpty() || deductedOrderSet.Contains(orderId))
+ continue;
+ deductedOrderSet.Add(orderId);
+ itemAmount = -(amount - 2.2m);
+ }
+ else if (type == 34)
+ {
+ // 同一个订单只能减少一次
+ string orderId = row["order_id"].ParseToString();
+ if (orderId.IsNullOrEmpty() || giftsOrderSet.Contains(orderId))
+ continue;
+ giftsOrderSet.Add(orderId);
+ itemAmount = -oem_amount;
+ }
+ else
+ {
+ continue; // 其他类型不计入
+ }
+
+ total_amount += itemAmount;
+ rebate_amount += itemAmount;
+
+ settleIdLst.Add(flowId);
}
- total_amount += itemAmount;
- rebate_amount += itemAmount;
+ if (settleIdLst.Count == 0)
+ return 0;
- settleIdLst.Add(flowId);
+ //更新用户流水记录表结算
+ sql = "UPDATE wxxd.user_payment_record SET settlement=1,settlement_time='" + now + "' WHERE id IN (" + string.Join(",", settleIdLst) + ")";
+ var flowRst = mySql.ExecuteAffectedRows(sql);
+ if (!string.IsNullOrEmpty(mySql.Message) || flowRst <= 0)
+ throw new Exception("标记流水结算失败");
}
- if (settleIdLst.Count == 0)
+
+
+
+
+
+ //读取用户店铺续费
+ sql = "SELECT id,delta_points FROM `wxxd`.`user_points_record` WHERE user_name IN ('" + string.Join("','", userLst) + "') AND option_type ='续费' AND settlement=0 ORDER BY id ASC";
+ var userPointsDt = mySql.ExecuteDataTable(sql);
+ if (!string.IsNullOrEmpty(mySql.Message))
return 0;
+ if (userPointsDt != null && userPointsDt.Rows.Count > 0) //更新用户流水记录表结算
+ {
+ int count = userPointsDt.Rows.Count;
+ //结算商铺续费
+ List settleIdLst = new List(); // 参与结算的流水id
+
+ foreach (DataRow row in userPointsDt.Rows)
+ {
+ long flowId = row["id"].ParseToLong();
+ decimal points = row["delta_points"].ParseToDecimal();
+
+ decimal itemAmount = points * 1;//比例1:1
+ itemAmount = itemAmount - ShopRenewFee;
+ total_amount += itemAmount;
+ rebate_amount += itemAmount;
+ settleIdLst.Add(flowId);
+ }
+
+ sql = "UPDATE wxxd.user_points_record SET settlement=1,settlement_time='" + now + "' WHERE id IN (" + string.Join(",", settleIdLst) + ")";
+ var flowRst = mySql.ExecuteAffectedRows(sql);
+ if (!string.IsNullOrEmpty(mySql.Message) || flowRst <= 0)
+ throw new Exception("商铺流水结算失败");
+ }
+
+
long total_fen = (long)Math.Round(total_amount * 100m);
long rebate_fen = (long)Math.Round(rebate_amount * 100m);
- string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
- // 3. 写oem金额 + 更新流水状态(同一事务)
- sql = "UPDATE wxxd.oem SET available_balance=available_balance+" + total_fen +
- ", total_rebate_amount=total_rebate_amount+" + rebate_fen +
- " WHERE oem_id='" + oem_id + "'";
- var oemRst = mySql.ExecuteAffectedRows(sql);
- if (!string.IsNullOrEmpty(mySql.Message) || oemRst <= 0)
- throw new Exception("结算流水到账户失败");
- sql = "UPDATE wxxd.user_payment_record SET settlement=1,settlement_time='" + now + "' WHERE id IN (" + string.Join(",", settleIdLst) + ")";
- var flowRst = mySql.ExecuteAffectedRows(sql);
-
- if (!string.IsNullOrEmpty(mySql.Message) || flowRst <= 0)
- throw new Exception("标记流水结算失败");
+ if (total_fen > 0 || rebate_fen > 0)
+ {
+ //写oem金额 + 更新流水状态
+ sql = "UPDATE wxxd.oem SET available_balance=available_balance+" + total_fen +
+ ", total_rebate_amount=total_rebate_amount+" + rebate_fen +
+ " WHERE oem_id='" + oem_id + "'";
+ var oemRst = mySql.ExecuteAffectedRows(sql);
+ if (!string.IsNullOrEmpty(mySql.Message) || oemRst <= 0)
+ throw new Exception("结算流水到账户失败");
+ }
return total_fen;
}
diff --git a/KJD_WebService/BLL/WXXD/WXUserManage.cs b/KJD_WebService/BLL/WXXD/WXUserManage.cs
index 1fc072a..24db4bb 100644
--- a/KJD_WebService/BLL/WXXD/WXUserManage.cs
+++ b/KJD_WebService/BLL/WXXD/WXUserManage.cs
@@ -902,9 +902,9 @@ namespace KJD_Web.BLL.WXXD
{
return "{\"code\":500,\"msg\":\"操作对象不能为空\"}";
}
- if (delta_points <= 0 && option_type == "续费")
+ if (option_type == "续费")
{
- return "{\"code\":500,\"msg\":\"续费金额错误,需大于0\"}";
+ return "{\"code\":500,\"msg\":\"续费金额错误\"}";
}
if (delta_points == 0)
{
diff --git a/KJD_WebService/Controllers/AsmxApiController.cs b/KJD_WebService/Controllers/AsmxApiController.cs
index e6aea10..ecd8520 100644
--- a/KJD_WebService/Controllers/AsmxApiController.cs
+++ b/KJD_WebService/Controllers/AsmxApiController.cs
@@ -1,4 +1,6 @@
using KJD_Web.BLL.WXXD;
+using KJD_Web.BLL.WXXD.Requests;
+using KJD_Web.Utils;
using System;
using System.Collections.Generic;
using System.IO;
@@ -26,6 +28,30 @@ namespace KJD_Web.Controllers
// return base.ExecuteAsync(controllerContext, cancellationToken);
//}
+ protected bool ValidateSign(SignRequestBase request, bool isUserLogin = false)
+ {
+ if (request == null || string.IsNullOrEmpty(request.time) || string.IsNullOrEmpty(request.code))
+ {
+ return false;
+ }
+
+ int time;
+ if (!int.TryParse(request.time, out time))
+ {
+ return false;
+ }
+ if (request.code != EncrypUtil.Encryption(time))
+ {
+ return false;
+ }
+ if (!isUserLogin && string.IsNullOrWhiteSpace(request.user_name))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
protected string GetRequestJsonString()
{
//Context.Response.ContentType = "text/html;charset=utf-8";
diff --git a/KJD_WebService/Controllers/WX/WXUserController.cs b/KJD_WebService/Controllers/WX/WXUserController.cs
index e830efb..2988125 100644
--- a/KJD_WebService/Controllers/WX/WXUserController.cs
+++ b/KJD_WebService/Controllers/WX/WXUserController.cs
@@ -17,7 +17,7 @@ namespace KJD_Web.Controllers
{
[RoutePrefix("wxxd_user")]
[Route("{action}")]
- public class WXUserController : AsmxApiController
+ public partial class WXUserController : AsmxApiController
{
//GetE57Authorization
diff --git a/KJD_WebService/Controllers/WX/WXUser_N_Controller.cs b/KJD_WebService/Controllers/WX/WXUser_N_Controller.cs
new file mode 100644
index 0000000..c6afe2b
--- /dev/null
+++ b/KJD_WebService/Controllers/WX/WXUser_N_Controller.cs
@@ -0,0 +1,51 @@
+using KJD_Web.Model;
+using KJD_Web.Requests.Users;
+using KJD_Web.Services.Users;
+using MAX.Core.Logger;
+using System;
+using System.Web.Http;
+
+namespace KJD_Web.Controllers
+{
+ public partial class WXUserController : AsmxApiController
+ {
+ private readonly UserService _userService;
+ private readonly ILoggerFacade _logger;
+
+ public WXUserController(UserService userService, ILoggerFacade logger)
+ {
+ _userService = userService ?? throw new ArgumentNullException(nameof(userService));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ ///
+ /// 用户续费商铺。
+ ///
+ [HttpPost]
+ public IHttpActionResult shop_renew([FromBody] ShopRenewRequest request)
+ {
+ if (!ValidateSign(request))
+ {
+ return Ok(ApiResult.Fail("校验失败", 999));
+ }
+ try
+ {
+ var response = _userService.ShopRenew(request);
+ return Ok(ApiResult.Success(response, "续费成功"));
+ }
+ catch (ArgumentException ex)
+ {
+ return Ok(ApiResult.Fail(ex.Message, ResultCode.ValidationError));
+ }
+ catch (InvalidOperationException ex)
+ {
+ return Ok(ApiResult.Fail(ex.Message, ResultCode.BusinessError));
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "商铺续费失败, UserName=" + (request == null ? null : request.UserName) + ", ShopId=" + (request == null ? null : request.ShopId));
+ return Ok(ApiResult.Fail("续费失败,请稍后重试", ResultCode.ServerError));
+ }
+ }
+ }
+}
diff --git a/KJD_WebService/Entitys/ShopEntity.cs b/KJD_WebService/Entitys/ShopEntity.cs
new file mode 100644
index 0000000..b5a4abd
--- /dev/null
+++ b/KJD_WebService/Entitys/ShopEntity.cs
@@ -0,0 +1,144 @@
+using FreeSql.DataAnnotations;
+using System;
+
+namespace YourNamespace
+{
+ ///
+ ///shop 表
+ ///
+ [Table(Name = "wxxd.shop")]
+ public class ShopEntity
+ {
+ ///
+ /// 表id
+ ///
+ [Column(Name = "id", IsPrimary = true, IsIdentity = true)]
+ public int Id { get; set; }
+
+ ///
+ /// login_name
+ ///
+ [Column(Name = "login_name")]
+ public string LoginName { get; set; }
+
+ ///
+ /// login_pass
+ ///
+ [Column(Name = "login_pass")]
+ public string LoginPass { get; set; }
+
+ ///
+ /// cookie
+ ///
+ [Column(Name = "cookie")]
+ public string Cookie { get; set; }
+
+ ///
+ /// cookie2
+ ///
+ [Column(Name = "cookie2")]
+ public string Cookie2 { get; set; }
+
+ ///
+ /// anti_contents
+ ///
+ [Column(Name = "anti_contents")]
+ public string AntiContents { get; set; }
+
+ ///
+ /// openid
+ ///
+ [Column(Name = "openid")]
+ public string Openid { get; set; }
+
+ ///
+ /// shop_id
+ ///
+ [Column(Name = "shop_id")]
+ public string ShopId { get; set; }
+
+ ///
+ /// shop_account
+ ///
+ [Column(Name = "shop_account")]
+ public string ShopAccount { get; set; }
+
+ ///
+ /// sub_user_name
+ ///
+ [Column(Name = "sub_user_name")]
+ public string SubUserName { get; set; }
+
+ ///
+ /// user_name
+ ///
+ [Column(Name = "user_name")]
+ public string UserName { get; set; }
+
+ ///
+ /// 店铺分组名称(非子账号分组)
+ ///
+ [Column(Name = "group_name")]
+ public string GroupName { get; set; }
+
+ ///
+ /// add_time
+ ///
+ [Column(Name = "add_time")]
+ public DateTime? AddTime { get; set; }
+
+ ///
+ /// mdy_time
+ ///
+ [Column(Name = "mdy_time")]
+ public DateTime? MdyTime { get; set; }
+
+ ///
+ /// 店铺所属公司
+ ///
+ [Column(Name = "company_name")]
+ public string CompanyName { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [Column(Name = "notes")]
+ public string Notes { get; set; }
+
+ ///
+ /// login_status
+ ///
+ [Column(Name = "login_status")]
+ public string LoginStatus { get; set; }
+
+ ///
+ /// 店铺每月商品上传限额剩余
+ ///
+ [Column(Name = "add_product_remaining")]
+ public int AddProductRemaining { get; set; }
+
+ ///
+ /// 上传限额所处月份
+ ///
+ [Column(Name = "add_product_month")]
+ public byte AddProductMonth { get; set; }
+
+ ///
+ /// 分配的二级超管的user_name
+ ///
+ [Column(Name = "operate_sub_sa_name")]
+ public string OperateSubSaName { get; set; }
+
+ ///
+ /// 分配二级超管的时间
+ ///
+ [Column(Name = "add_sub_sa_time")]
+ public DateTime? AddSubSaTime { get; set; }
+
+ ///
+ /// login_status2
+ ///
+ [Column(Name = "login_status2")]
+ public string LoginStatus2 { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/KJD_WebService/Entitys/UserEntity.cs b/KJD_WebService/Entitys/UserEntity.cs
new file mode 100644
index 0000000..e38387b
--- /dev/null
+++ b/KJD_WebService/Entitys/UserEntity.cs
@@ -0,0 +1,156 @@
+using FreeSql.DataAnnotations;
+using System;
+
+namespace KJD_Web.Entitys
+{
+ ///
+ /// 用户表
+ ///
+ [Table(Name = "wxxd.user")]
+ public class UserEntity
+ {
+ ///
+ /// ID
+ ///
+ [Column(Name = "id", IsPrimary = true, IsIdentity = true)]
+ public int Id { get; set; }
+
+ ///
+ /// 用户昵称
+ ///
+ [Column(Name = "nick_name")]
+ public string NickName { get; set; }
+
+ ///
+ /// 用户名称(登录账户)
+ ///
+ [Column(Name = "user_name")]
+ public string UserName { get; set; }
+
+ ///
+ /// 登录密码
+ ///
+ [Column(Name = "password")]
+ public string Password { get; set; }
+
+ ///
+ /// card_id
+ ///
+ [Column(Name = "card_id")]
+ public string CardId { get; set; }
+
+ ///
+ /// 用户分组 3主号 4客服 5加盟 8登录 9采购 10加盟 11上货
+ ///
+ [Column(Name = "group_id")]
+ public byte GroupId { get; set; }
+
+ ///
+ /// 父账户用户名
+ ///
+ [Column(Name = "parent_user_name")]
+ public string ParentUserName { get; set; }
+
+ ///
+ /// oem编号,仅oem用户有值
+ ///
+ [Column(Name = "oem_id")]
+ public string OemId { get; set; }
+
+ ///
+ /// 手机号
+ ///
+ [Column(Name = "mobile")]
+ public string Mobile { get; set; }
+
+ ///
+ /// 添加时间
+ ///
+ [Column(Name = "add_time")]
+ public DateTime? AddTime { get; set; }
+
+ ///
+ /// 修改时间
+ ///
+ [Column(Name = "mdy_time")]
+ public DateTime? MdyTime { get; set; }
+
+ ///
+ /// 公司名称
+ ///
+ [Column(Name = "company_name")]
+ public string CompanyName { get; set; }
+
+ ///
+ /// 备注
+ ///
+ [Column(Name = "notes")]
+ public string Notes { get; set; }
+
+ ///
+ /// 超管等级 0 普通用户 1 超级管理员
+ ///
+ [Column(Name = "sa_level")]
+ public short? SaLevel { get; set; }
+
+ ///
+ /// 账户权限
+ ///
+ [Column(Name = "authority")]
+ public string Authority { get; set; }
+
+ ///
+ /// 子账号被禁止展示的功能页面
+ ///
+ [Column(Name = "forbidden_page")]
+ public string ForbiddenPage { get; set; }
+
+ ///
+ /// 账户积分,1积分=1元
+ ///
+ [Column(Name = "points")]
+ public decimal Points { get; set; }
+
+ ///
+ /// 积分历史累计充值金额
+ ///
+ [Column(Name = "recharge_points_total")]
+ public decimal? RechargePointsTotal { get; set; }
+
+ ///
+ /// 店铺每续费一个月需要的积分,单位元
+ ///
+ [Column(Name = "shop_points_per_month")]
+ public int ShopPointsPerMonth { get; set; }
+
+ ///
+ /// 可用余额
+ ///
+ [Column(Name = "balance")]
+ public decimal? Balance { get; set; }
+
+ ///
+ /// 累计充值金额
+ ///
+ [Column(Name = "recharge_amount_total")]
+ public decimal? RechargeAmountTotal { get; set; }
+
+ ///
+ /// 每单中转仓快递费用,单位元,两位小数
+ ///
+ [Column(Name = "postage_per_order")]
+ public decimal? PostagePerOrder { get; set; }
+
+ ///
+ /// 每单中转仓退货快递费用,单位元,两位小数
+ ///
+ [Column(Name = "refund_postage_per_order")]
+ public decimal? RefundPostagePerOrder { get; set; }
+
+ ///
+ /// 礼物每单额外收取费用,单位元,两位小数
+ ///
+ [Column(Name = "gift_oem_amount_per_order")]
+ public decimal? GiftOemAmountPerOrder { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/KJD_WebService/Entitys/UserPointsRecordEntity.cs b/KJD_WebService/Entitys/UserPointsRecordEntity.cs
new file mode 100644
index 0000000..4e226fa
--- /dev/null
+++ b/KJD_WebService/Entitys/UserPointsRecordEntity.cs
@@ -0,0 +1,109 @@
+using FreeSql.DataAnnotations;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace KJD_Web.Entitys
+{
+
+ [Table(Name = "wxxd.user_points_record")]
+ public class UserPointsRecordEntity
+ {
+ ///
+ /// 主键(自增)
+ ///
+ [Column(Name = "id", IsPrimary = true, IsIdentity = true)]
+ public int Id { get; set; }
+
+ ///
+ /// 用户名(NOT NULL)
+ ///
+ [Column(Name = "user_name")]
+ public string UserName { get; set; }
+
+ ///
+ /// 用户昵称(NOT NULL)
+ ///
+ [Column(Name = "user_nick")]
+ public string UserNick { get; set; }
+
+ ///
+ /// 操作记录金额,单位元,最小1元(NOT NULL)
+ ///
+ [Column(Name = "delta_points")]
+ public decimal DeltaPoints { get; set; }
+
+ ///
+ /// 充值或消费后的当前金额(允许 NULL)
+ ///
+ [Column(Name = "new_points")]
+ public decimal? NewPoints { get; set; }
+
+ ///
+ /// 操作类型 消费 充值 转入 转出(NOT NULL)
+ ///
+ [Column(Name = "option_type")]
+ public string OptionType { get; set; }
+
+ ///
+ /// 操作者类型 管理员 用户(NOT NULL)
+ ///
+ [Column(Name = "operator_type")]
+ public string OperatorType { get; set; }
+
+ ///
+ /// 实际操作者用户名(NOT NULL)
+ ///
+ [Column(Name = "operator_name")]
+ public string OperatorName { get; set; }
+
+ ///
+ /// 消费店铺编号(消费才有值,充值为空,允许 NULL)
+ ///
+ [Column(Name = "shop_id")]
+ public string? ShopId { get; set; }
+
+ ///
+ /// 消费店铺名称(消费才有值,充值为空,允许 NULL)
+ ///
+ [Column(Name = "shop_account")]
+ public string? ShopAccount { get; set; }
+
+ ///
+ /// 备注(允许 NULL)
+ ///
+ [Column(Name = "remark")]
+ public string? Remark { get; set; }
+
+ ///
+ /// (点数转出时的)对方账户(允许 NULL)
+ ///
+ [Column(Name = "counterparty_account_name")]
+ public string? CounterpartyAccountName { get; set; }
+
+ ///
+ /// 支付宝订单的外部订单号(允许 NULL)
+ ///
+ [Column(Name = "out_trade_no")]
+ public string? OutTradeNo { get; set; }
+
+ ///
+ /// 添加时间(NOT NULL,默认 CURRENT_TIMESTAMP)
+ ///
+ [Column(Name = "add_time")]
+ public DateTime AddTime { get; set; }
+
+ ///
+ /// OEM是否结算 0未结算 1已结算
+ ///
+ [Column(Name = "settlement")]
+ public bool Settlement { get; set; }
+ ///
+ /// 结算时间
+ ///
+ [Column(Name = "settlement_time")]
+ public DateTime? SettlementTime { get; set; }
+ }
+
+}
\ No newline at end of file
diff --git a/KJD_WebService/Entitys/UserPointsShopExpire.cs b/KJD_WebService/Entitys/UserPointsShopExpire.cs
new file mode 100644
index 0000000..287abec
--- /dev/null
+++ b/KJD_WebService/Entitys/UserPointsShopExpire.cs
@@ -0,0 +1,43 @@
+using FreeSql.DataAnnotations;
+using System;
+
+namespace KJD_Web.Entitys
+{
+ ///
+ /// 用户积分商店过期
+ ///
+ [Table(Name = "wxxd.user_points_shop_expire")]
+ public class UserPointsShopExpireEntity
+ {
+ ///
+ /// ID
+ ///
+ [Column(Name = "id", IsPrimary = true, IsIdentity = true)]
+ public int Id { get; set; }
+
+ ///
+ /// 商店ID
+ ///
+ [Column(Name = "shop_id")]
+ public string ShopId { get; set; }
+
+ ///
+ /// 过期时间
+ ///
+ // 数据库列名末尾包含 U+200C,不能改成普通 expire_time。
+ [Column(Name = "expire_time\u200c")]
+ public int ExpireTime { get; set; }
+
+ ///
+ /// 添加时间
+ ///
+ [Column(Name = "add_time")]
+ public DateTime AddTime { get; set; }
+
+ ///
+ /// 修改时间
+ ///
+ [Column(Name = "mdy_time")]
+ public DateTime MdyTime { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/KJD_WebService/Global.asax.cs b/KJD_WebService/Global.asax.cs
index a0613f3..c744a28 100644
--- a/KJD_WebService/Global.asax.cs
+++ b/KJD_WebService/Global.asax.cs
@@ -1,6 +1,7 @@
using Autofac;
using Autofac.Integration.WebApi;
using FreeSql;
+using KJD_Web.Repository;
using KJD_Web.Utils;
using MAX.Core.DependencyInjection;
using MAX.Core.Logger;
@@ -37,14 +38,37 @@ namespace KJD_Web
var builder = new ContainerBuilder();
var appAssembly = Assembly.GetExecutingAssembly();
+ var connection = ConfigurationManager.ConnectionStrings["ConnKJD"];
+ if (connection == null || string.IsNullOrWhiteSpace(connection.ConnectionString))
+ {
+ throw new ConfigurationErrorsException("未配置连接字符串:ConnKJD");
+ }
+
// ImageCacheService 使用独立的 imagecache 数据库。
- builder.Register(_ => CreateImageCacheFreeSql())
- .As()
- .SingleInstance();
+ builder.Register(_ =>
+ {
+ return new FreeSqlBuilder()
+ .UseConnectionString(
+ DataType.MySql,
+ connection.ConnectionString)
+ .Build();
+ })
+ .As()
+ .SingleInstance();
+
+ builder.RegisterGeneric(typeof(AutofacRepository<>))
+ .As(typeof(IBaseRepository<>))
+ .UsingConstructor(typeof(IFreeSql))
+ .InstancePerRequest();
+
// 注册 Web API 所有 Controller
builder.RegisterApiControllers(appAssembly);
builder.RegisterInstance(NLogger.Instance).As().SingleInstance();
+
+
+
+
#region 注册所有IOC接口
Type typeSingleton = typeof(ISingletonDependency);
@@ -87,14 +111,13 @@ namespace KJD_Web
{
throw new ConfigurationErrorsException("未配置连接字符串:ConnKJD");
}
-
- var connectionBuilder = new MySqlConnectionStringBuilder(connection.ConnectionString)
- {
- Database = "imagecache"
- };
+ //var connectionBuilder = new MySqlConnectionStringBuilder(connection.ConnectionString)
+ //{
+ // Database = "imagecache"
+ //};
return new FreeSqlBuilder()
- .UseConnectionString(DataType.MySql, connectionBuilder.ConnectionString)
+ .UseConnectionString(DataType.MySql, connection.ConnectionString)
.UseAutoSyncStructure(false)
.Build();
}
diff --git a/KJD_WebService/KJD_WebService.csproj b/KJD_WebService/KJD_WebService.csproj
index 36f220f..caa9b92 100644
--- a/KJD_WebService/KJD_WebService.csproj
+++ b/KJD_WebService/KJD_WebService.csproj
@@ -26,18 +26,18 @@
- 12
- true
- full
- false
- bin\
- DEBUG;TRACE
- prompt
- 4
- false
+ 12
+ true
+ full
+ false
+ bin\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
- 12
+ 12
true
pdbonly
true
@@ -60,9 +60,15 @@
..\packages\FreeSql.3.5.311\lib\net451\FreeSql.dll
+
+ ..\packages\FreeSql.DbContext.3.5.311\lib\net45\FreeSql.DbContext.dll
+
..\packages\FreeSql.Provider.MySql.3.5.311\lib\net452\FreeSql.Provider.MySql.dll
+
+ ..\packages\FreeSql.Repository.3.5.311\lib\net45\FreeSql.Repository.dll
+
..\packages\Google.Protobuf.3.19.4\lib\net45\Google.Protobuf.dll
@@ -186,6 +192,7 @@
+
@@ -211,6 +218,11 @@
+
+
+
+
+
@@ -249,7 +261,11 @@
+
+
+
+
diff --git a/KJD_WebService/Model/ShopModel/ShopParam.cs b/KJD_WebService/Model/ShopModel/ShopParam.cs
index 12ea3c1..cfcd62f 100644
--- a/KJD_WebService/Model/ShopModel/ShopParam.cs
+++ b/KJD_WebService/Model/ShopModel/ShopParam.cs
@@ -1,10 +1,12 @@
-using System;
+using FreeSql.DataAnnotations;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace KJD_Web.Model.ShopModel
{
+ [Table(Name = "wxxd.shop")]
public class ShopParam
{
public int id { get; set; }
diff --git a/KJD_WebService/Model/UserLoginResultParam.cs b/KJD_WebService/Model/UserLoginResultParam.cs
index 8ac1038..83fe7a0 100644
--- a/KJD_WebService/Model/UserLoginResultParam.cs
+++ b/KJD_WebService/Model/UserLoginResultParam.cs
@@ -1,10 +1,12 @@
-using System;
+using FreeSql.DataAnnotations;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace KJD_Web.Model
{
+ [Table(Name = "wxxd.user")]
public class UserLoginResultParam
{
public int id { get; set; }
@@ -80,7 +82,7 @@ namespace KJD_Web.Model
///
/// 用户积分点数
///
- public int points { get; set; }
+ public decimal points { get; set; }
///
/// 单店铺续费1个月消耗的点数
///
diff --git a/KJD_WebService/NotFoundMessageFilter.cs b/KJD_WebService/NotFoundMessageFilter.cs
index 6fc8afd..89ede5c 100644
--- a/KJD_WebService/NotFoundMessageFilter.cs
+++ b/KJD_WebService/NotFoundMessageFilter.cs
@@ -35,7 +35,10 @@ namespace KJD_Web
HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
-
+ var originalBody = await response.Content.ReadAsStringAsync();
+ Console.WriteLine($"[HTTP] {request.Method} {request.RequestUri}");
+ Console.WriteLine($"[HTTP] Status: {(int)response.StatusCode} {response.StatusCode}");
+ Console.WriteLine($"[HTTP] Body: {originalBody}");
if (response.StatusCode != HttpStatusCode.OK)
{
response.Content = new StringContent(
diff --git a/KJD_WebService/Repository/AutofacRepository.cs b/KJD_WebService/Repository/AutofacRepository.cs
new file mode 100644
index 0000000..6254a2b
--- /dev/null
+++ b/KJD_WebService/Repository/AutofacRepository.cs
@@ -0,0 +1,17 @@
+using FreeSql;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace KJD_Web.Repository
+{
+ public class AutofacRepository : BaseRepository
+ where TEntity : class
+ {
+ public AutofacRepository(IFreeSql fsql)
+ : base(fsql)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/KJD_WebService/Repository/UserPointsRecordRepository.cs b/KJD_WebService/Repository/UserPointsRecordRepository.cs
new file mode 100644
index 0000000..f09341f
--- /dev/null
+++ b/KJD_WebService/Repository/UserPointsRecordRepository.cs
@@ -0,0 +1,19 @@
+//using FreeSql;
+//using KJD_Web.Entitys;
+//using MAX.Core.DependencyInjection;
+//using System;
+//using System.Collections.Generic;
+//using System.Linq;
+//using System.Web;
+
+//namespace KJD_Web.Repository
+//{
+
+// public class UserPointsRecordRepository : BaseRepository, IScopedDependency
+// {
+// public UserPointsRecordRepository(IFreeSql fsql) : base(fsql)
+// {
+
+// }
+// }
+//}
\ No newline at end of file
diff --git a/KJD_WebService/Requests/Users/ShopRenewRequest.cs b/KJD_WebService/Requests/Users/ShopRenewRequest.cs
new file mode 100644
index 0000000..194ace6
--- /dev/null
+++ b/KJD_WebService/Requests/Users/ShopRenewRequest.cs
@@ -0,0 +1,34 @@
+using KJD_Web.BLL.WXXD.Requests;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace KJD_Web.Requests.Users
+{
+ ///
+ /// 用户续费商铺
+ ///
+ public class ShopRenewRequest : SignRequestBase
+ {
+ public string UserName
+ {
+ get { return user_name; }
+ set { user_name = value; }
+ }
+ ///
+ /// 店铺ID
+ ///
+ public string ShopId { get; set; }
+
+ ///
+ /// 续费时长数值,例如 3;范围 1-120 个月
+ ///
+ public int RenewDuration { get; set; }
+
+ ///
+ /// 备注
+ ///
+ public string Remark { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/KJD_WebService/Responses/Users/ShopRenewResponse.cs b/KJD_WebService/Responses/Users/ShopRenewResponse.cs
new file mode 100644
index 0000000..ad16754
--- /dev/null
+++ b/KJD_WebService/Responses/Users/ShopRenewResponse.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace KJD_Web.Dtos.Users
+{
+ public class ShopRenewResponse
+ {
+ public string ShopId { get; set; }
+ public int ExpireTime { get; set; }
+ public int UserPoints { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/KJD_WebService/Services/Users/UserService.cs b/KJD_WebService/Services/Users/UserService.cs
new file mode 100644
index 0000000..cd0418c
--- /dev/null
+++ b/KJD_WebService/Services/Users/UserService.cs
@@ -0,0 +1,220 @@
+using FreeSql;
+using KJD_Web.Dtos.Users;
+using KJD_Web.Entitys;
+using KJD_Web.Model;
+using KJD_Web.Model.ShopModel;
+using KJD_Web.Requests.Users;
+using KJD_Web.Utils;
+using MAX.Core.DependencyInjection;
+using System;
+using YourNamespace;
+
+namespace KJD_Web.Services.Users
+{
+ public class UserService : ITransientDependency
+ {
+ private const int MinShopPointsPerMonth = 30;
+ private const int UserNameMaxLength = 20;
+ private const int ShopIdMaxLength = 50;
+ private const int RemarkMaxLength = 255;
+ private const int MaxRenewDuration = 120;
+
+ private readonly IFreeSql _freeSql;
+
+ public UserService(IFreeSql freeSql)
+ {
+ _freeSql = freeSql ?? throw new ArgumentNullException(nameof(freeSql));
+ }
+
+ ///
+ /// 商铺续费
+ ///
+ /// 续费请求
+ public ShopRenewResponse ShopRenew(ShopRenewRequest request)
+ {
+ ShopRenewResponse response = new ShopRenewResponse();
+ ValidateRequest(request);
+
+ string userName = request.UserName.Trim();
+ string shopId = request.ShopId.Trim();
+ string remark = string.IsNullOrWhiteSpace(request.Remark) ? null : request.Remark.Trim();
+
+ _freeSql.Transaction(() =>
+ {
+ UserEntity user = _freeSql.Select()
+ .Where(a => a.UserName == userName)
+ .ForUpdate()
+ .First();
+ if (user == null)
+ {
+ throw new InvalidOperationException("不存在的用户");
+ }
+
+ ShopEntity shop = _freeSql.Select()
+ .Where(a => a.ShopId == shopId && a.UserName == user.UserName)
+ .ForUpdate()
+ .First();
+ if (shop == null)
+ {
+ throw new InvalidOperationException("不存在的商铺");
+ }
+ if (!string.Equals(shop.UserName, userName, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new InvalidOperationException("该商铺不属于当前用户");
+ }
+
+ int shopPointsPerMonth = Math.Max(MinShopPointsPerMonth, user.ShopPointsPerMonth);
+ int deltaPoints = shopPointsPerMonth * request.RenewDuration;
+ if (user.Points < deltaPoints)
+ {
+ throw new InvalidOperationException("余额不足");
+ }
+
+ int newPoints = (int)user.Points - deltaPoints;
+ DateTime now = DateTime.Now;
+
+ UserPointsShopExpireEntity currentExpire = _freeSql.Select()
+ .Where(a => a.ShopId == shopId)
+ .ForUpdate()
+ .First();
+
+ DateTime expireBaseTime = now;
+ if (currentExpire != null && currentExpire.ExpireTime > 0)
+ {
+ DateTime currentExpireTime = TimeUtils.StampToDateTime(currentExpire.ExpireTime);
+ if (currentExpireTime > expireBaseTime)
+ {
+ expireBaseTime = currentExpireTime;
+ }
+ }
+
+ int newExpireTime = CalculateNewExpireTime(expireBaseTime, request.RenewDuration);
+
+ int affectedUser = _freeSql.Update()
+ .Set(a => a.Points, newPoints)
+ .Where(a => a.UserName == userName && a.Points == user.Points)
+ .ExecuteAffrows();
+ if (affectedUser != 1)
+ {
+ throw new InvalidOperationException("账户已发生改变,请刷新账户后重新操作");
+ }
+
+ for (int i = 0; i < request.RenewDuration; i++)
+ {
+ decimal recordNewPoints = user.Points - (decimal)shopPointsPerMonth * (i + 1);
+ UserPointsRecordEntity item = new UserPointsRecordEntity
+ {
+ OptionType = "续费",
+ OperatorType = "用户",
+ OperatorName = userName,
+ UserName = userName,
+ UserNick = user.NickName,
+ DeltaPoints = shopPointsPerMonth,
+ NewPoints = recordNewPoints,
+ ShopId = shop.ShopId,
+ ShopAccount = shop.ShopAccount,
+ Remark = remark,
+ AddTime = now
+ };
+
+ int affectedRecord = _freeSql.Insert(item).ExecuteAffrows();
+ if (affectedRecord != 1)
+ {
+ throw new InvalidOperationException("新增操作记录失败,请稍候重试");
+ }
+ }
+
+ if (currentExpire == null)
+ {
+ UserPointsShopExpireEntity expire = new UserPointsShopExpireEntity
+ {
+ ShopId = shopId,
+ ExpireTime = newExpireTime,
+ AddTime = now,
+ MdyTime = now
+ };
+
+ int affectedExpire = _freeSql.Insert(expire).ExecuteAffrows();
+ if (affectedExpire != 1)
+ {
+ throw new InvalidOperationException("新增商铺过期时间失败,请稍候重试");
+ }
+ }
+ else
+ {
+ int affectedExpire = _freeSql.Update()
+ .Set(a => a.ExpireTime, newExpireTime)
+ .Set(a => a.MdyTime, now)
+ .Where(a => a.Id == currentExpire.Id && a.ExpireTime == currentExpire.ExpireTime)
+ .ExecuteAffrows();
+ if (affectedExpire != 1)
+ {
+ throw new InvalidOperationException("店铺已发生改变,请刷新店铺后重新操作");
+ }
+ }
+
+ response.ShopId = request.ShopId;
+ response.UserPoints = newPoints;
+ response.ExpireTime = newExpireTime;
+ });
+
+ return response;
+ }
+
+ private static void ValidateRequest(ShopRenewRequest request)
+ {
+ if (request == null)
+ {
+ throw new ArgumentException("请求参数不能为空", nameof(request));
+ }
+ if (string.IsNullOrWhiteSpace(request.UserName))
+ {
+ throw new ArgumentException("用户名不能为空", nameof(request.UserName));
+ }
+ if (request.UserName.Trim().Length > UserNameMaxLength)
+ {
+ throw new ArgumentException("用户名长度不能超过20个字符", nameof(request.UserName));
+ }
+ if (string.IsNullOrWhiteSpace(request.ShopId))
+ {
+ throw new ArgumentException("店铺ID不能为空", nameof(request.ShopId));
+ }
+ if (request.ShopId.Trim().Length > ShopIdMaxLength)
+ {
+ throw new ArgumentException("店铺ID长度不能超过50个字符", nameof(request.ShopId));
+ }
+ if (request.RenewDuration <= 0)
+ {
+ throw new ArgumentException("续费时长必须大于0", nameof(request.RenewDuration));
+ }
+ if (request.RenewDuration > MaxRenewDuration)
+ {
+ throw new ArgumentException("续费时长不能超过120个月", nameof(request.RenewDuration));
+ }
+ if (!string.IsNullOrEmpty(request.Remark) && request.Remark.Trim().Length > RemarkMaxLength)
+ {
+ throw new ArgumentException("备注长度不能超过255个字符", nameof(request.Remark));
+ }
+ }
+
+ private static int CalculateNewExpireTime(DateTime expireBaseTime, int renewDuration)
+ {
+ DateTime newExpireDateTime;
+ try
+ {
+ newExpireDateTime = expireBaseTime.AddMonths(renewDuration);
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ throw new InvalidOperationException("续费时长超出允许范围");
+ }
+
+ if (newExpireDateTime > TimeUtils.StampToDateTime(int.MaxValue))
+ {
+ throw new InvalidOperationException("续费时间超出系统支持范围");
+ }
+
+ return TimeUtils.DateTimeToTimestampSeconds(newExpireDateTime);
+ }
+ }
+}
diff --git a/KJD_WebService/Web.config b/KJD_WebService/Web.config
index 4759219..5a5d82d 100644
--- a/KJD_WebService/Web.config
+++ b/KJD_WebService/Web.config
@@ -5,8 +5,8 @@
-->
-
-
+
+
diff --git a/KJD_WebService/packages.config b/KJD_WebService/packages.config
index 342f432..d741829 100644
--- a/KJD_WebService/packages.config
+++ b/KJD_WebService/packages.config
@@ -3,7 +3,9 @@
+
+
diff --git a/Store.Framework/Class1.cs b/Store.Framework/Class1.cs
new file mode 100644
index 0000000..769542d
--- /dev/null
+++ b/Store.Framework/Class1.cs
@@ -0,0 +1,6 @@
+namespace Store.Framework;
+
+public class Class1
+{
+
+}
diff --git a/Store.Framework/Store.Framework.csproj b/Store.Framework/Store.Framework.csproj
new file mode 100644
index 0000000..742e991
--- /dev/null
+++ b/Store.Framework/Store.Framework.csproj
@@ -0,0 +1,17 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Store.User/Class1.cs b/Store.User/Class1.cs
new file mode 100644
index 0000000..4f5b7c7
--- /dev/null
+++ b/Store.User/Class1.cs
@@ -0,0 +1,6 @@
+namespace Store.User;
+
+public class Class1
+{
+
+}
diff --git a/Store.User/Store.User.csproj b/Store.User/Store.User.csproj
new file mode 100644
index 0000000..69c64e0
--- /dev/null
+++ b/Store.User/Store.User.csproj
@@ -0,0 +1,5 @@
+
+
+
+
+