Initial commit

This commit is contained in:
aixiaojun 2025-07-17 09:35:54 +08:00
commit 1dedb0586f
34 changed files with 2614 additions and 0 deletions

25
.dockerignore Normal file
View File

@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

186
.editorconfig Normal file
View File

@ -0,0 +1,186 @@
[*.cs]
# SA1200: Using directives should be placed correctly
dotnet_diagnostic.SA1200.severity = none
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
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
[*.{cs,vb}]
end_of_line = crlf
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_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity: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
tab_width = 4
indent_size = 4
dotnet_style_operator_placement_when_wrapping = beginning_of_line
[*.cs]
#### 命名样式 ####
# 命名规则
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
# 符号规范
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.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 =
# 命名样式
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_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
csharp_prefer_static_local_function = true:suggestion
csharp_style_prefer_readonly_struct = true:suggestion
csharp_style_prefer_readonly_struct_member = 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_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_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_prefer_utf8_string_literals = 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
csharp_space_around_binary_operators = before_and_after
csharp_indent_labels = one_less_than_current
# SA1202: Elements should be ordered by access
dotnet_diagnostic.SA1202.severity = none
[*.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

63
.gitattributes vendored Normal file
View File

@ -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

31
.gitignore vendored Normal file
View File

@ -0,0 +1,31 @@
################################################################################
# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
################################################################################
bin
obj
.vs
Debug
/BinaLiangHua/NPlatform.API/obj
/BinaLiangHua/NPlatform.API/bin
/BinaLiangHua/NPlatform.API/obj
dist
node_modules
/BinaLiangHua/NPlatform.API/wwwroot
/doc/db.pdb
/IdentityServer/vue-next-admin/node_modules.zip
/BZPT.IdentityServer/BZPT.IdentityServer.csproj.user
/BZPT.IdentityServer/appsettings.Development.json
/BZPT.Api/appsettings.Development.json
/BZPT.Api/BZPT.Api.csproj.user
*.user
/BZPT.Api/BZPT.API.csproj
/BZPT.Api/BZPT.API.csproj
/BZPT.Api/BZPT.API.csproj
/BZPT.Api/appsettings.Develop.json
/BZPT.Api/Temporary_Upload
/BZPT.Api/Upload
*_Upload
/BZPT.Api/logs
logs

View File

@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "7.0.10",
"commands": [
"dotnet-ef"
]
}
}
}

37
BZPT.Api/Dockerfile Normal file
View File

@ -0,0 +1,37 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM registry.cn-hangzhou.aliyuncs.com/newbe36524/aspnet:8.0 AS base
USER root
#定义时区参数
ENV TZ=Asia/Shanghai
#设置编码
ENV LANG C.UTF-8
WORKDIR /app
EXPOSE 19901
EXPOSE 443
FROM registry.cn-hangzhou.aliyuncs.com/newbe36524/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["BZPT.Api/BZPT.Api.csproj", "BZPT.Api/"]
COPY ["BZPT.Domains/BZPT.Domains.csproj", "BZPT.Domains/"]
COPY ["BZPT.DTO/BZPT.DTO.csproj", "BZPT.DTO/"]
COPY ["BZPT.SqlSugarRepository/BZPT.SqlSugarRepository.csproj", "BZPT.SqlSugarRepository/"]
RUN dotnet restore "BZPT.Api/BZPT.Api.csproj"
COPY . .
WORKDIR "/src/BZPT.Api"
RUN dotnet build "BZPT.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
WORKDIR "/src/BZPT.Api"
RUN dotnet publish "BZPT.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
#时区设置1
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
ENTRYPOINT ["dotnet", "BZPT.Api.dll"]

455
BZPT.Api/Program.cs Normal file
View File

@ -0,0 +1,455 @@
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using NPlatform.Infrastructure.Config;
using NPlatform.Middleware;
using NPlatform.API;
using NPlatform.DI;
using NPlatform.Repositories;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
using SqlSugar;
using IGeekFan.AspNetCore.Knife4jUI;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using IdentityServer4.Configuration;
using System.Security.Cryptography.X509Certificates;
using BZPT.Repositories;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NPlatform.Infrastructure.Config.Section;
using ServiceStack;
using Microsoft.IdentityModel.Tokens;
using Hangfire;
using Hangfire.Redis.StackExchange;
using Hangfire.Dashboard.BasicAuthorization;
using BZPT.Domains.Application;
using System.Security.Claims;
using NPlatform.Infrastructure.IdGenerators;
using NPOI.SS.Formula.Functions;
using Microsoft.AspNetCore.DataProtection;
using StackExchange.Redis;
using BZPT.Api.Middleware;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Newtonsoft.Json.Linq;
using Serilog.Context;
using Serilog;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.FileProviders;
using BZPT.Domains.IRepositories;
using BZPT.Domains.IService.Sys;
using BZPT.Api;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);
var serviceConfig = builder.Configuration.GetServiceConfig();
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
// 替换控制器激活器
builder.Services.Replace(ServiceDescriptor.Scoped<IControllerActivator, ServiceBasedControllerActivator>());
// 健康检查
builder.Services.AddHealthChecks().AddCheck<NHealthChecks>(serviceConfig.ServiceName);
// 内存缓存
builder.Services.AddMemoryCache();
// Autofac 配置
builder.Services.AddAutofac();
var redisConfig = builder.Configuration.GetRedisConfig();
//从配置获取 Redis 连接字符串
var redisConn = $"{redisConfig.Connections?.FirstOrDefault()},password={redisConfig.Password}";
Console.WriteLine("redis 连接:" + redisConn);
// 配置 Hangfire
builder.Services.AddHangfire(x =>
{
x.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseRedisStorage(redisConn, new RedisStorageOptions
{
Db = 11,
Prefix = "Hangfire:"
});
});
builder.Services.AddHangfireServer();
// 连接到 Redis
var connectionMultiplexer = ConnectionMultiplexer.Connect(redisConn);
// 注册 IConnectionMultiplexer
builder.Services.AddSingleton<IConnectionMultiplexer>(connectionMultiplexer);
// 注册 IDatabase
builder.Services.AddScoped<IDatabase>(provider =>
{
var connection = provider.GetRequiredService<IConnectionMultiplexer>();
return connection.GetDatabase(); // 返回 IDatabase 实例
});
// 主机配置
var repositoryOptions = new RepositoryOptions();
builder.Host.Configure(builder.Configuration, repositoryOptions);
// 数据库上下文配置
var dbHost = builder.Configuration["DB_HOST"];
var dbPort = builder.Configuration["DB_PORT"];
var dbName = builder.Configuration["DB_NAME"];
var logDbName = builder.Configuration["LOG_DB_NAME"];
var dbUser = builder.Configuration["DB_USER"];
var dbPass = builder.Configuration["DB_PASSWORD"];
builder.UseMySerilog($"Server={dbHost};Port={dbPort};DATABASE={logDbName};User Id={dbUser};PWD={dbPass};", serviceConfig.ServiceName);
//// 配置日志
//var log = builder.Logging.AddLog4Net();
//log.AddConsole();
//log.AddDebug();
//log.SetMinimumLevel(LogLevel.Debug);
builder.Services.AddScoped<DBContext>(serviceProvider =>
{
return new DBContext($"Server={dbHost};Port={dbPort};DATABASE={dbName};User Id={dbUser};PWD={dbPass};", DbType.Dm, ConfigId: "default");
});
builder.Services.AddScoped<IUnitOfWorkSugar, UnitOfWorkSugar>();
// 控制器配置
builder.Services.AddControllersWithViews(mvcOptions =>
{
mvcOptions.Filters.Remove(mvcOptions.Filters.OfType<UnsupportedContentTypeFilter>().FirstOrDefault());
}).AddJsonOptions(options =>
{
//options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
//options.JsonSerializerOptions.PropertyNameCaseInsensitive = false; // 确保区分大小写
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter("yyyy-MM-dd HH:mm:ss"));
});
// 从 appsettings.json 加载表单限制
builder.Services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit =
builder.Configuration.GetValue<long>("FormOptions:MultipartBodyLengthLimit");
});
// API 行为配置
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = actionContext =>
{
var errors = actionContext.ModelState?.Where(e => e.Value.Errors.Count > 0);
var strError = new StringBuilder();
foreach (var error in errors)
{
var msg = error.Value.Errors.FirstOrDefault()?.ErrorMessage;
if (!string.IsNullOrWhiteSpace(msg))
strError.Append(msg);
}
return new FailResult<object>("错误:" + strError.ToString());
};
});
// IdentityServer4 配置
var serviceProvider = builder.Services.BuildServiceProvider();
var dbContext = serviceProvider.GetService<DBContext>();
var keyPath = Path.Combine(Environment.CurrentDirectory, builder.Configuration["CERT_PATH"] ?? "");
Console.WriteLine($"Certificate path: {keyPath}, Exists: {File.Exists(keyPath)}");
// CORS 配置
builder.Services.AddCors(options => options.AddPolicy("CorsMy", policy =>
{
// 允许特定的源访问
policy.WithOrigins("*")
.AllowAnyHeader()
.AllowAnyMethod();
}));
if (!string.IsNullOrEmpty(keyPath) && File.Exists(keyPath))
{
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ConfigureHttpsDefaults(httpsOptions =>
{
httpsOptions.ServerCertificate = new X509Certificate2(keyPath, builder.Configuration["CERT_PASSWORD"]);
});
});
}
else
{
Console.WriteLine("⚠️ 证书未找到或环境变量未设置HTTPS 可能无法正常工作!");
}
Console.WriteLine($"AuthorityServer 配置: {builder.Configuration["AuthorityServer"]}");
// 配置 Cookie 认证
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Authority = builder.Configuration["AuthorityServer"];
options.Audience = serviceConfig.ServiceID; // 受众
#if DEBUG
options.RequireHttpsMetadata = false; // 开发环境允许 HTTP
#else
options.RequireHttpsMetadata = true; // 生产环境强制 HTTPS
#endif
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true, // 验证 Audience
ValidAudiences =new string[] { serviceConfig.ServiceID }, // 或者 "api.BZPT",取决于你的 API 需要验证哪个 Audience
ValidateIssuer = true, // 验证 Issuer
ValidIssuer = builder.Configuration["AuthorityServer"], // 确保与 IdentityServer4 的 Issuer 配置一致
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero // 生产环境建议设为小值或 TimeSpan.Zero防止 Token 刚签发就被认为过期
};
options.BackchannelHttpHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => true // 忽略 SSL 错误
};
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
Console.WriteLine($"{serviceConfig.ServiceID}|{builder.Configuration["AuthorityServer"]} ");
// 记录身份验证失败的详细信息
Console.WriteLine($"Authentication failed:{context.Exception.Message}");
return Task.CompletedTask;
},
OnTokenValidated = context =>
{
// 记录 token 验证成功的信息
Console.WriteLine("Token validated successfully.");
////打印 Claims 信息以便调试
if (context.Principal != null)
{
Console.WriteLine($"Authenticated User Name: {context.Principal.Identity?.Name}");
foreach (var claim in context.Principal.Claims)
{
Console.WriteLine($" Claim Type: {claim.Type}, Value: {claim.Value}");
}
}
return Task.CompletedTask;
},
OnMessageReceived = context =>
{
//Console.WriteLine("--- JwtBearer OnMessageReceived triggered ---");
if (context.Request.Headers.ContainsKey("Authorization"))
{
Console.WriteLine($"Raw Authorization Header: {context.Request.Headers["Authorization"]}");
}
// 这里的 context.Token 应该已经由 JwtBearerHandler 填充,除非有其他中间件干扰
Console.WriteLine($"Received token (JwtBearer): {context.Token ?? "NULL"}"); // 确保打印 NULL
Console.WriteLine("--- End JwtBearer OnMessageReceived ---");
return Task.CompletedTask;
},
OnChallenge = context =>
{
Console.WriteLine("JwtBearer Challenge triggered.");
Console.WriteLine($"Challenge Scheme: {context.Scheme.Name}");
Console.WriteLine($"Challenge Status Code: {context.Response.StatusCode}");
if (!string.IsNullOrEmpty(context.Error)) Console.WriteLine($"Challenge Error: {context.Error}");
if (!string.IsNullOrEmpty(context.ErrorDescription)) Console.WriteLine($"Challenge Error Description: {context.ErrorDescription}");
//全自定义 401 响应,可以在这里处理
// context.HandleResponse(); // 阻止默认的 401 行为
// context.Response.StatusCode = StatusCodes.Status401Unauthorized;
// context.Response.ContentType = "application/json";
// return context.Response.WriteAsync(JsonSerializer.Serialize(new { message = "Unauthorized", detail = context.ErrorDescription }));
return Task.CompletedTask;
}
};
});
// Swagger 配置
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc(serviceConfig.ServiceName, new OpenApiInfo { Title = $"{serviceConfig.ServiceName} 接口文档", Version = serviceConfig.ServiceVersion });
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "BZPT.Api.xml"), true);
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme {
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
} ,
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
}, new List<string>() }
});
// 定义 OAuth2 Client Credentials 安全方案
c.AddSecurityDefinition("OAuth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
ClientCredentials = new OpenApiOAuthFlow
{
TokenUrl = new Uri($"{builder.Configuration["AuthorityServer"]}/connect/token"), // IdentityServer4 令牌端点
Scopes = new Dictionary<string, string>
{
{ "api_scope", "访问 API 的权限" }
}
}
}
});
c.AddServer(new OpenApiServer { Url = "", Description = "vvv" });
c.CustomOperationIds(apiDesc => (apiDesc.ActionDescriptor as ControllerActionDescriptor)?.ControllerName + "-" + (apiDesc.ActionDescriptor as ControllerActionDescriptor)?.ActionName);
});
var redis = ConnectionMultiplexer.Connect(redisConn);
builder.Services.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys")
.SetApplicationName(serviceConfig.ServiceID);
builder.Services.AddAntiforgery(options =>
{
// 可以在这里配置防伪令牌的相关选项
options.HeaderName = "X-CSRF-TOKEN";
});
//将 Serilog 注册到默认日志工厂(非必须,但更兼容)
builder.Logging.ClearProviders(); // 可选:移除默认日志提供程序(如不需要控制台重复输出)
builder.Logging.AddSerilog(dispose: true); // 桥接 Microsoft.Extensions.Logging 到 Serilog
// 应用构建
var app = builder.Build();
// 并且在 UseAuthentication() 和 UseAuthorization() 之前。
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost
// 注意:通常不需要 ForwardedHeaders.XForwardedPort因为 X-Forwarded-Host 包含主机和端口
// 但如果您的反向代理只设置了 X-Forwarded-Port则可能需要添加
// !!!重要如果您的反向代理是内部的并且其IP是固定的建议指定 KnownProxies 或 KnownNetworks
// 以防止伪造的 X-Forwarded-* 头。如果这是在容器内部,且代理是 Ingress Controller 或 Docker 自己的网络,
// 通常可以安全地跳过 KnownProxies/Networks但在生产环境中要小心。
// KnownProxies = { IPAddress.Parse("YOUR_PROXY_IP_HERE") }
// KnownNetworks = { new IPNetwork(IPAddress.Parse("YOUR_PROXY_NETWORK_START_IP"), YOUR_PROXY_NETWORK_CIDR) }
});
// 2. 配置 ASP.NET Core 内置异常页(开发环境)
if (app.Environment.IsDevelopment())
{
}
else
{
app.UseHsts();
}
// 1. 中间件捕获请求管道中的异常
app.Use(async (context, next) =>
{
try
{
#if !DEBUG
if (DateTime.Now.Hour >= 23 || DateTime.Now.Hour < 7)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("系统维护中~");
return;
}
#endif
// 生成唯一IDGUID 或 Snowflake ID
var requestId = StringObjectIdGenerator.Instance.GenerateId().ToString();
// 注入请求头
context.Request.Headers["X-Request-ID"] = requestId;
// 存储到 HttpContext.Items 供后续使用
context.Items["X-Request-ID"] = requestId;
// 将 ID 添加到日志上下文
using (LogContext.PushProperty("RequestID", requestId))
{
await next();
}
// 处理响应状态码
if (context.Response.StatusCode == StatusCodes.Status401Unauthorized)
{
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
code = 401,
message = "未授权,请登录"
}));
}
}
catch (Exception ex)
{
LogContext.PushProperty("ErrorCode", 500);
Serilog.Log.Fatal(ex, "全局捕获的未处理异常(中间件)");
throw; // 重新抛出以触发内置错误页面
}
});
// 3. 捕获非 HTTP 管道的全局异常
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
{
var exception = e.ExceptionObject as Exception;
LogContext.PushProperty("ErrorCode", 500);
Serilog.Log.Fatal(exception, "全局捕获的非 HTTP 未处理异常");
};
TaskScheduler.UnobservedTaskException += (sender, e) =>
{
LogContext.PushProperty("ErrorCode", 500);
Serilog.Log.Fatal(e.Exception, "全局捕获的未观察到的任务异常");
e.SetObserved(); // 标记异常已处理
};
app.UseSwagger();
app.UseKnife4UI(c =>
{
c.RoutePrefix = "swagger";
c.SwaggerEndpoint($"/{serviceConfig.ServiceName}/swagger.json", serviceConfig.ServiceName);
});
app.UseHealthChecks("/healthChecks");
app.UseDefaultFiles(new DefaultFilesOptions { DefaultFileNames = new List<string> { "index.html" } });
app.UseAntiforgery();
app.UseRouting();
app.UseCors("CorsMy");
app.UseAuthentication();
app.UseAuthorization();
//app.UseMiddleware<OrganizationDataFilterMiddleware>();
// OPTIONS 请求处理
app.MapMethods("/{**path}", new[] { "OPTIONS" }, () => Results.NoContent()).RequireCors("CorsMy");
try
{
app.UseIdentityServer();
}
catch (Exception ex)
{
Console.WriteLine($"IdentityServer 中间件加载失败: {ex}");
}
app.MapControllers();
app.Run();

View File

@ -0,0 +1,38 @@
{
"profiles": {
"BZPT.API": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:19902;http://localhost:9079"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
"publishAllPorts": true,
"useSSL": true
}
},
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:18175",
"sslPort": 44326
}
}
}

View File

@ -0,0 +1,53 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Hangfire": "Warning",
"IdentityServer4": "Debug",
"IdentityServer4.EntityFramework": "Warning"
}
}
},
"Kestrel": { // Kestrel
"Limits": {
"MaxRequestBodySize": 104857600 // 100MB
}
},
"FormOptions": { //
"MultipartBodyLengthLimit": 104857600 // 100MB
},
"ServiceConfig": {
"DataCenterID": "dc1",
"ServiceID": "api.Sys",
"ServiceName": "系统管理API",
"ServiceVersion": "1.0",
"IOCAssemblys": "BZPT.Domains.dll,BZPT.SqlSugarRepository.dll,NPlatform.dll",
},
"DB_HOST": "LOCALHOST",
"DB_PORT": "5236",
"DB_NAME": "BZPT",
"DB_USER": "BZPT",
"DB_PASSWORD": "BZPT&123.lld",
"LOG_DB_NAME": "LOGS_DATA",
"CERT_PATH": "./Certificates/id4svr.pfx",
"CERT_PASSWORD": "ydl825913",
"ConsulServer": "http://localhost:8500",
"AllowedOrigins": "http://localhost:19901,http://192.168.0.100:40000",
"AuthorityServer": "https://192.168.1.100:19902",
"AllowedHosts": "*",
"Urls": "http://0.0.0.0:19901",
"RedisConfig": {
"RedisType": "Normal",
"Connections": [ "redismy:6379" ],
"dbNum": 10,
"Password": "redis&&!123"
}
}

54
BZPT.Api/appsettings.json Normal file
View File

@ -0,0 +1,54 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Hangfire": "Warning"
}
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Hangfire": "Warning",
"IdentityServer4": "Debug",
"IdentityServer4.EntityFramework": "Warning"
}
}
},
"FormOptions": {
"MultipartBodyLengthLimit": 104857600
},
"Kestrel": {
"Limits": {
"MaxRequestBodySize": 104857600
},
"Endpoints": {
"Http": {
"Url": "http://*:19901"
},
"Https": {
"Url": "https://*:8001"
}
}
},
"ServiceConfig": {
"DataCenterID": "dc1",
"ServiceID": "api.Sys",
"ServiceName": "系统管理API",
"ServiceVersion": "1.0",
"IOCAssemblys": "BZPT.Domains.dll,BZPT.SqlSugarRepository.dll,NPlatform.dll",
"AttachExtension": "gif,jpg,jpeg,png,bmp,rar,zip,doc,docx,xls,xlsx,ppt,pptx,txt,flv,apk,mp4,mpg,ts,mpeg,mp3,bak,pdf",
"AttachSize": 104857600
},
"LOG_DB_NAME": "LOGS_DATA",
"ConsulServer": "http://localhost:8500",
"AllowedOrigins": "http://localhost:19901,http://localhost:3000,http://localhost:5173",
"AllowedHosts": "*",
"RetryCount": 10,
"AuthorityServer": "https://106.52.199.114:8001/",
"SystemHome": "https://106.52.199.114:8000/",
"Hangfire": {
"AdminPassword": "HNjt123~"
}
}

33
BZPT.Api/log4net.config Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<!-- 线上禁止配置在程序目录,需修改为具体日志路径 -->
<file value="logs/logAll.log" />
<rollingStyle value="Date" />
<datePattern value="yyyyMMdd" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender">
<!-- 线上禁止配置在程序目录,需修改为具体日志路径 -->
<file value="logs/Error.log" />
<rollingStyle value="Date" />
<datePattern value="yyyyMMdd" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="ERROR" />
<param name="LevelMax" value="FATAL" />
</filter>
</appender>
<root>
<!-- -->
<level value="ALL" />
<appender-ref ref="RollingFileAppender" />
<appender-ref ref="ErrorAppender" />
</root>
</log4net>

BIN
BZPT.Api/msyh.ttc Normal file

Binary file not shown.

2
BZPT.Api/start.sh Normal file
View File

@ -0,0 +1,2 @@
systemctl start meirongsite1.service
systemctl status meirongsite1.service

14
BZPT.Api/stop.sh Normal file
View File

@ -0,0 +1,14 @@
PID=$(netstat -nlp | grep ":19901" | awk '{print $7}' | awk -F '[ / ]' '{print $1}')
if [ $? == 0 ]; then
echo "process id is:${PID}"
else
echo "process $1 no exit"
exit 0
fi
kill -9 ${PID}
if [ $? == 0 ]; then
echo "kill $1 success "
else
echo "kill $1 fail"
fi
systemctl stop meirongsite1.service

1
BZPT.Api/tempkey.jwk Normal file
View File

@ -0,0 +1 @@
{"alg":"RS256","d":"mhXFKhKsCaOocekPGwuxq85OiWDWp6K674yUWmZG3kYk9Dz9xvnUjvMVSIptu1UOrho3EFxCZQXgiHQ923Ywi78U2WRvjs2nRNe9uWL80D5TFWNKDN5WyVN_T9aK5uYkIPt7PE476-ECLTq1D4Yl0q793q1mKDCfEbfzbKfY13WYu2M3qWTrXXnC4uaYplP5TFtDvNvrQB0eYZ9qug6Cu5K9VVrsoDG5Bh8K1S-Oge2IzpSnjQGovKwRnSIdqhGyozX-jgNaCOfD6V3hzPylHaDSWvydL0xmckgpIrKh0ZfWLTJJ7nDQ1s816ku4ReXizBe6LosbozqwF9o7ocvFrQ","dp":"QAO484FPlDFcdmQc2Ab_Bdda_PCfxaP3a5PZiNYCTGGadD96BZUfkKIsW03cDDXIJCAerNedWfHM4eYkwHoBnYW5J9K1DTo2LctPIQ2dTUcTDpkXBmgfC7NCt0fz_hfG3Lp0syQwWTLCbHAJSpDHvFz_AgDDyvW4K40qITnK0dM","dq":"uuHvKDNMAblI1YiPVlV2jwBUKXJgsVdoKmRAXtdn4OHnnyfzpexp9guVNDJbSCaSMXKpO0gI6BOVxZPQcz-AIs6p3eZRnOUQoMpCSK0AK1jnXTt3RKZDYRFdze8_UhLpIi8zyU2uY9efXBRVvrCNECNNu5qaH-4zazTItwRqAHc","e":"AQAB","kid":"E48B1ABC25F3D2C6B152184A11F86432","kty":"RSA","n":"s4JFCJPMYNHHYyKH5MgEEz2Dchsq-XeJN_6Gbq0Rs0CcqhPuomUNBes49_UkrE1s_87y3V7fPHdq6UgnIn1x3z4IjGLT1PlaDA4Lyp1F-QPqQ7dZlymfrZ_YsnNkHhgRncjtp7bAu2b_E8kzo06_O8uiC2tAE535ctLSPH5oQOkkkG1qzBh8ydi9znbO5X37eNLQUgi2dX8hqD9PNH8ldAiW8G-WPgst7DarLdeiN4yYvcMAs5qjXisp6WwO2g84AkYLW2Gle3l4Sy8a3vMlQpZlFV3sdieecUxwXoN_tECNvzDVzhi2cFkZXIztiWhwBGWfMs0TiKQ13XbWpKQVaQ","p":"1KA4xDQykXTYDFv0ELWG6CUQ4jft6r6OsA35mXgylGaw6x-EwYRDmBIgEPKQHymdIg-8SRolaaqWKb1EgB7AMr9ooi33kNMqVf0HILFDJ5cRCAHWZa7gU7CGCJ-uSYK0YM6Sm1D-RDVYpPa-44WaN4OyMR5YP8AaLE_x3OPVOyc","q":"2CCbckoPaL5AOVLnY22sp6pBOyrpeFvyOIiMWClpCWr9Qz_gQn9PgT88jjnLRkS4nqOW4OA6w32nCdLX9R8pxjv8Y3mvRPicnqXwsvXjpBVjjUmPYVXq78NaPf5MTk6E2GyN8N-FyE8NTXWjbs0VI-uEalexFRWfRPpgGoN4xO8","qi":"YxmKUSoOybTEqOzSb9cfYb99wJS3SzXBHOhg4Q1tECNMHUsxe5pIoTMMhcqrUcd_qnQH5jmI0gGCnjHEkpDl7FhiuLSqKo7pgEOixYe3cMI_aMMaaiJ-BrKaMls1TFznWPp_wPV42Ym_YB1DhVKVX2vqupi55tvb7F5bT5qExFI"}

BIN
BZPT.Api/tips.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

40
BZPT.DTO/BZPT.DTO.csproj Normal file
View File

@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Input\DutyUserDto.cs" />
<Compile Remove="Input\RolePositionDto.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SqlSugarCore" Version="5.1.4.198" />
<PackageReference Include="SqlSugarCore.Dm" Version="8.8.0" />
<PackageReference Include="System.Security.Permissions" Version="8.0.0" />
<PackageReference Include="IdentityServer4" Version="4.1.2" />
<PackageReference Include="NPlatform" Version="3.0.1.8" />
<PackageReference Include="NPlatform.Infrastructure" Version="3.0.1.10" />
</ItemGroup>
<ItemGroup>
<Using Include="NPlatform" />
<Using Include="NPlatform.Infrastructure" />
<Using Include="NPlatform.Result" />
<Using Include="NPlatform.Query" />
<Using Include="NPlatform.Dto" />
<Using Include="NPlatform.Events" />
<Using Include="NPlatform.Consts" />
<Using Include="NPlatform.Exceptions" />
<Using Include="NPlatform.Enums" />
<Using Include="NPlatform.Bus" />
<Using Include="NPlatform.Domains.Entity" />
<Using Include="NPlatform.Repositories.IRepositories" />
<Using Include="System.Text.Json" />
<Using Include="System" />
<Using Include="System.Linq" />
<Using Include="System.Linq.Dynamic.Core" />
<Using Include="System.Linq.Expressions" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="System.Collections.Generic" />
<Using Include="System.Threading" />
<Using Include="System.Threading.Tasks" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BZPT.DTO.Captcha
{
public class CaptchaModel
{
public int X { get; set; }
public int Y { get; set; }
public Image Background { get; set; }
public Image Slide { get; set; }
}
public class Captcha64Model
{
public int X { get; set; }
public int Y { get; set; }
public string Background { get; set; }
public string Slide { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NPlatform.Const
{
public class ConstType
{
public const string BusinessType_CZ = "充值办卡";
public const string BusinessType_HK = "消费开单";
public const string OrderItemType_HK = "划卡";
public const string OrderItemType_Item = "单项";
public const string OrderItemType_Product = "产品";
}
}

6
BZPT.DTO/Readme.md Normal file
View File

@ -0,0 +1,6 @@
基于实际项目情况Dto对象使用 CodeSmith脚本初始化生成的文件名为 *.Designer.cs,减少开发了,便于落地。
DTO封装的是数据传输对象,有时候充当VO。
|----VO 用于UI呈现的 ViewModel。
|----Commands 是用于执行 CUD 操作的命令对象,他其实也算是数据传输对象的一种。
|----Querys 是用于封装查询对象。

20
BZPT.DTO/VO/TreeItem.cs Normal file
View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BZPT.Dto.VO {
public class TreeItem: IVO {
public string Name { get; set; }
public string CreateTime { get; set; }
public string CreateUser { get; set; }
public string Status { get; set; }
public string Sort { get; set; }
public string Description { get; set; }
public bool IsLeaf { get; set; }
public string LevelCode { get; set; }
public string Id { get; set; }
public TreeItem[] Children { get; set; }
}
}

View File

@ -0,0 +1,190 @@
//using AutoMapper;
//using NPlatform.AutoMap;
//using BZPT.Domains.Entity;
//using BZPT.Domains.Entity.Sys;
//using BZPT.Dto.VO;
//using NPlatform.Result;
//using System.Collections;
//using System.Collections.Generic;
//using System.Text;
//using BZPT.Dto.Sys;
//using System.Configuration;
//using System.Reflection;
//using BZPT.DTO.VO;
//using BZPT.Dto.Std;
//using System.Security.Cryptography;
//using BZPT.DTO.Const;
//using NPlatform.Infrastructure.Redis;
//using NPlatform.Infrastructure;
//using NPOI.SS.Formula.Functions;
//using ServiceStack.DataAnnotations;
//namespace BZPT.Domains
//{
// public class AutoMapperProfile : Profile, IProfile
// {
// [Autowired]
// private IRedisService _redisService { get; set; }
// private Dictionary<string, string> user;
// public AutoMapperProfile(IRedisService redisService)
// {
// _redisService = redisService;
// user = this._redisService.GlobalHashGetAllAsync<string>(CacheKeyBZPT.CACHE_USER_ALL).Result;
// //用户到VO对象的映射
// CreateMap<User, UserVO>()
// .ForMember(dst=>dst.Organization,src=>src.MapFrom(z=>z.Organization.Name))// 指定某个的属性从另一个对象的子属性获取(展平对象)
// .ForMember(dst => dst.RoleName,src => src.MapFrom(z => GetRoleName(z.Roles))) //来源字段
// .ForMember(dst => dst.DutyName, src => src.MapFrom(z => z.Dutys == null ? string.Empty : GetDutyName(z.Dutys)))
// .ForMember(dst => dst.DutyID,
// src => src.MapFrom(z => z.Dutys == null || !z.Dutys.Any()
// ? new string[] { } // 返回 []
// : GetDutyID(z.Dutys).ToArray())) // 如果有元素,连接两个空字符串并返回
// .ForMember(dst => dst.FilePath, src => src.MapFrom(z => z.OtherAttachments.SA04)) //来源字段
// .ForMember(dst => dst.Email, src => src.MapFrom(z => DCodeEmail(z.Email)))
// .ForMember(dst => dst.MobileNum,opt => opt.MapFrom(src => DCodedMobile(src.MobileNum)))
// .ForMember(dst => dst.CreateUserName, src => src.MapFrom(z => GetUserName(z.CreateUser)))
// .ReverseMap();
// CreateMap<User, UserDto>().ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser)))
// .ForMember(dst => dst.Password, src => ReplacePassword())
// .ForMember(dst => dst.Email, src => src.MapFrom(z => DCodeEmail(z.Email)))
// .ForMember(dst => dst.MobileNum, opt => opt.MapFrom(src => DCodedMobile(src.MobileNum)))
// .ReverseMap();
// CreateMap<Organization, OrganizationDto>()
// .ForMember(dst => dst.Parents, src => src.MapFrom(z => z.Parents.Split(",", StringSplitOptions.RemoveEmptyEntries)))
// .ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser))).ReverseMap();
// CreateMap<User, ResetUserVO>().ReverseMap();
// CreateMap<Dictionary, DictionaryDto>().ReverseMap();
// CreateMap<DictionaryItem, DictionaryItemDto>().ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser))).ReverseMap();
// CreateMap<Role, RoleDto>().ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser))).ReverseMap();
// CreateMap<Region, RegionDto>().ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser))).ReverseMap();
// CreateMap<SysClient, SysClientDto>().ReverseMap();
// CreateMap<Country, CountryDto>(MemberList.Destination).ReverseMap();
// CreateMap<Resources, ResourcesDto>().ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser))).ReverseMap();
// CreateMap<Resources, ResourceTreeNode>().ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser))).ReverseMap();
// CreateMap<Duty, DutyDto>().ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser))).ReverseMap();
// CreateMap<OwnerUnit, OwnerUnitDto>().ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser))).ReverseMap();
// CreateMap<Section, SectionDto>()
// .ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser)))
// .ForMember(dst => dst.CreateUserId, src => src.MapFrom(z => z.CreateUser))
// .ReverseMap();
// CreateMap<OtherAttachments, OtherAttachmentsDto>().ReverseMap();
// CreateMap<AttachmentConfig, AttachmentConfigDto>().ReverseMap();
// CreateMap<OperationLog, OperationLogDto>().ReverseMap();
// CreateMap<ErrorLog, ErrorLogDto>().ReverseMap();
// CreateMap<ProjectInfo, ProjectInfoDto>()
// .ForMember(dst => dst.CreateUserId, src => src.MapFrom(z => z.CreateUser))
// .ForMember(dst=>dst.CreateUser,src=>src.MapFrom(z=> GetUserName(z.CreateUser))).ReverseMap();
// CreateMap<LaborTeam, LaborTeamDto>()
// .ForMember(dst => dst.CreateUserId, src => src.MapFrom(z => z.CreateUser))
// .ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser))).ReverseMap();
// CreateMap<WorkerGroup, WorkerGroupDto>()
// .ForMember(dst => dst.CreateUserId, src => src.MapFrom(z => z.CreateUser))
// .ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser))).ReverseMap();
// CreateMap<MobileVersion, MobileVersionVO>().ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser)))
// .ForMember(dst => dst.FilePath, src => src.MapFrom(z => z.OtherAttachments.SA02))
// .ReverseMap();
// CreateMap<MobileMenu, MobileMenuVO>().ForMember(dst => dst.CreateUser, src => src.MapFrom(z => GetUserName(z.CreateUser)))
// .ForMember(dst => dst.FilePath, src => src.MapFrom(z => z.OtherAttachments.SA04))
// .ReverseMap();
// CreateMap<MobileVersion, MobileVersionDto>().ReverseMap();
// CreateMap<MobileMenu, MobileMenuDto>().ReverseMap();
// }
// public string ReplacePassword()
// {
// return string.Empty;
// }
// public string GetRoleName(List<Role> roles) {
// if(roles == null || roles.Count == 0)return string.Empty;
// StringBuilder strRole = new StringBuilder();
// foreach (Role role in roles) {
// strRole.Append(role.Name);
// strRole.Append(",");
// }
// return strRole.ToString();
// }
// public string GetDutyName(List<Duty> Dutys)
// {
// StringBuilder strDuty = new StringBuilder();
// foreach (Duty duty in Dutys)
// {
// strDuty.Append(duty.Name);
// strDuty.Append(",");
// }
// return strDuty.ToString();
// }
// public string GetUserName(string userid)
// {
// if (user.ContainsKey(userid))
// return user[userid];
// else return userid;
// }
// // GetDutyID 方法
// public string[] GetDutyID(List<Duty> Dutys)
// {
// if (Dutys == null || Dutys.Count == 0)
// {
// return new string[0]; // 如果传入的列表为空,返回空字符串数组
// }
// string[] dutyIds = new string[Dutys.Count];
// for (int i = 0; i < Dutys.Count; i++)
// {
// dutyIds[i] = Dutys[i].Id; // 将每个 Duty 的 Id 存入数组
// }
// return dutyIds;
// }
// public string DCodeEmail(string eml)
// {
// var deml=AES.Decode(eml);
// return MaskingHelper.MaskEmail(deml);
// }
// public string DCodedMobile(string phone)
// {
// phone = AES.Decode(phone);
// return MaskingHelper.MaskMobile(phone);
// }
// /// <summary>
// /// 字符串转时间类型
// /// </summary>
// public class DateTimeTypeConverter : ITypeConverter<string, DateTime>
// {
// public DateTime Convert(string source, DateTime destination, ResolutionContext context)
// {
// return System.Convert.ToDateTime(source);
// }
// }
// /// <summary>
// /// 字符串转Type类型
// /// </summary>
// public class TypeTypeConverter : ITypeConverter<string, Type>
// {
// public Type Convert(string source, Type destination, ResolutionContext context)
// {
// return Assembly.GetEntryAssembly().GetType(source);
// }
// }
// }
//}

View File

@ -0,0 +1,59 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<NoWarn>CS0619</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Remove="IRepositories\Sys\IRolePositionRepository.cs" />
<Compile Remove="IService\Sys\IClientService.cs" />
<Compile Remove="IService\Sys\IRolePositionService.cs" />
<Compile Remove="Services\Sys\ClientService.cs" />
<Compile Remove="Services\Sys\RolePositionService.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BZPT.DTO\BZPT.DTO.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SqlSugarCore" Version="5.1.4.198" />
<PackageReference Include="SqlSugarCore.Dm" Version="8.8.0" />
<PackageReference Include="System.Security.Permissions" Version="8.0.0" />
<PackageReference Include="DeveloperForce.Force" Version="2.1.0" />
<PackageReference Include="Hangfire.Core" Version="1.8.20" />
<PackageReference Include="NPlatform" Version="3.0.1.8" />
<PackageReference Include="NPlatform.Infrastructure" Version="3.0.1.10" />
<PackageReference Include="Serilog.Sinks.PeriodicBatching" Version="5.0.0" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.20" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<Using Include="NPlatform" />
<Using Include="NPlatform.Infrastructure" />
<Using Include="NPlatform.Result" />
<Using Include="NPlatform.Repositories" />
<Using Include="NPlatform.Dto" />
<Using Include="NPlatform.Query" />
<Using Include="NPlatform.Consts" />
<Using Include="NPlatform.Exceptions" />
<Using Include="NPlatform.Enums" />
<Using Include="NPlatform.AutoMap" />
<Using Include="NPlatform.Filters" />
<Using Include="BZPT.Domains.Entity.Sys" />
<Using Include="NPlatform.Infrastructure.IdGenerators" />
<Using Include="NPlatform.Domains.Entity" />
<Using Include="NPlatform.Repositories.IRepositories" />
<Using Include="NPlatform.Extends" />
<Using Include="NPlatform.Domains.Service" />
<Using Include="System.Text.Json" />
<Using Include="System" />
<Using Include="System.Linq" />
<Using Include="System.Linq.Dynamic.Core" />
<Using Include="System.Linq.Expressions" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="System.Collections.Generic" />
<Using Include="System.Threading" />
<Using Include="System.Threading.Tasks" />
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,101 @@
using NPlatform.Filters;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BZPT.Domains.Entity.Sys
{
/// <summary>
/// 租户
/// </summary>
[Serializable]
[Table(name: "Sys_Tenant")]
public class Tenant : EntityBase<string>
{
/// <summary>
/// 租户名称
/// </summary>
[Display(Name = "租户名称")]
[StringLength(200)]
[Required]
public string TenantName { get; set; }
/// <summary>
/// 租户编码
/// </summary>
[Display(Name = "租户编码")]
[StringLength(200)]
public string TenantCode { get; set; }
/// <summary>
/// 数据库连接
/// </summary>
[Display(Name = "数据库连接")]
[StringLength(600)]
[Required]
public string ConnectionString { get; set; }
/// <summary>
/// DbType
/// </summary>
[Display(Name = "DbType")]
[StringLength(200)]
public string DbType { get; set; }
/// <summary>
/// 缓存连接地址
/// </summary>
[Display(Name = "缓存连接地址")]
[StringLength(600)]
public string CacheConnection { get; set; }
/// <summary>
/// 附件存储路径
/// </summary>
[Display(Name = "附件存储路径")]
[StringLength(200)]
public string FilePath { get; set; }
/// <summary>
/// 扩展字段1
/// </summary>
[Display(Name = "扩展字段1")]
[StringLength(200)]
public string Col1 { get; set; }
/// <summary>
/// 扩展字段2
/// </summary>
[Display(Name = "扩展字段2")]
[StringLength(200)]
public string Col2 { get; set; }
/// <summary>
/// 扩展字段3
/// </summary>
[Display(Name = "扩展字段3")]
[StringLength(500)]
public string Col3 { get; set; }
/// <summary>
/// 扩展字段4
/// </summary>
[Display(Name = "扩展字段4")]
[StringLength(500)]
public string Col4 { get; set; }
/// <summary>
/// 扩展字段5
/// </summary>
[Display(Name = "扩展字段5")]
[StringLength(1000)]
public string Col5 { get; set; }
}
}

View File

@ -0,0 +1,24 @@
using BZPT.Domains.Entity.Sys;
using BZPT.Dto.VO;
using DevExtreme.AspNet.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BZPT.Domains.IService
{
public interface ICRUDService<TEntity,TDto>
{
Task<INPResult> Add(TDto addCommand);
Task<INPResult> Delete(string userId);
Task<INPResult> Edit(TDto editCommand);
Task<IListResult<TDto>> GetListAsync(QueryExp exp);
Task<IListResult<TDto>> GetListAsync(Expression<Func<TEntity, bool>> filter);
Task<IListResult<TDto>> GetPageAsync(QueryPageExp exp);
Task<IListResult<TDto>> GetPageAsync(DataSourceLoadOptions loadOptionsexp);
Task<IListResult<VO>> GetPageAsync<VO>(DataSourceLoadOptions loadOptionsexp);
Task<INPResult> GetAsync(string userId);
}
}

View File

@ -0,0 +1,182 @@
using BZPT.Domains.IRepositories.Sys;
using BZPT.Dto.Sys;
using BZPT.Repositories;
using Consul.Filtering;
using DevExtreme.AspNet.Data;
using NetTaste;
using NPlatform.Domains.Entity;
using NPOI.SS.Formula.Functions;
using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace BZPT.Domains.Services
{
public class SugarServiceBase<TEntity,TDto> :BaseService where TDto : BaseDto where TEntity:EntityBase<string>
{
#region CUD
[Autowired]
public ILogger<TDto> Loger { get; set; }
[Autowired]
public IRepositorySugarBase<TEntity, string> Repository { get; set; }
public virtual async Task<INPResult> Add(TDto add)
{
var vRst = add.Validates();
if (vRst.StatusCode != HttpStatusCode.OK.ToInt())
{
return vRst;
}
var entity = this.MapperService.Map<TEntity>(add);
entity.Id = StringObjectIdGenerator.Instance.GenerateId().ToString();
var rst = await Repository.AddsAsync(entity);
return Success(entity.Id);
}
public virtual async Task<INPResult> Delete(TDto dto)
{
var vRst = dto.Validates();
if (vRst.StatusCode != HttpStatusCode.OK.ToInt())
{
return vRst;
}
var rstCount = 0;
if (!string.IsNullOrWhiteSpace(dto.Id))
{
rstCount = await this.Repository.RemoveAsync(dto.Id);
}
return new SuccessResult<int>(rstCount);
}
public virtual async Task<INPResult> Delete(string id)
{
if(string.IsNullOrWhiteSpace(id))
{
return new FailResult<TEntity>($"id 参数不能为空");
}
var rstCount = await this.Repository.RemoveAsync(t=>t.Id== id);
return new SuccessResult<int>(rstCount);
}
public virtual async Task<INPResult> Delete(Expression< Func<TEntity,bool>> deleteWhere)
{
var rstCount = await this.Repository.RemoveAsync(deleteWhere);
return new SuccessResult<int>(rstCount);
}
public virtual async Task<INPResult> Edit(TDto edit)
{
var vRst = edit.Validates();
if (vRst.StatusCode != HttpStatusCode.OK.ToInt())
{
return vRst;
}
var entity = this.MapperService.Map<TEntity>(edit);
var rstCount = await this.Repository.AddOrUpdate(entity);
return new SuccessResult<int>(rstCount);
}
#endregion
#region Query
public override string GetDomainShortName()
{
return "请重写GetDomainShortName";
}
public virtual async Task<IListResult<TDto>> GetListAsync(QueryExp exp)
{
var vResult = exp.Validates();
if (vResult.StatusCode == 200)
{
var srcItems = await Repository.GetListByExpAsync(exp.GetExp<TEntity>(), exp.GetSelectSorts<TEntity>());
var dtos = this.MapperService.Map<IEnumerable<TEntity>, ListResult<TDto>>(srcItems);
return dtos;
}
return (IListResult<TDto>)vResult;
}
public virtual async Task<IListResult<TDto>> GetListAsync(Expression<Func<TEntity, bool>> filter)
{
var entitys = await Repository.GetListByExpAsync(filter);
var dtos = MapperService.Map<IEnumerable<TEntity>, IListResult<TDto>>(entitys);
return dtos;
}
public virtual async Task<IListResult<TDto>> GetPageAsync(QueryPageExp exp)
{
var vResult = exp.Validates();
if (vResult.StatusCode == 200)
{
var entitys = await Repository.GetPagedAsync(exp.PageNum, exp.PageSize, exp.GetExp<TEntity>(), exp.GetSelectSorts<TEntity>());
var dtos = MapperService.Map<IListResult<TEntity>, IListResult<TDto>>((IListResult<TEntity>)entitys);
return dtos;
}
return (IListResult<TDto>)vResult;
}
public virtual async Task<IListResult<TDto>> GetPageAsync(DataSourceLoadOptions loadOptionsexp)
{
if (loadOptionsexp.Sort == null || loadOptionsexp.Sort.Length == 0)
{
loadOptionsexp.Sort = new SortingInfo[] { new SortingInfo() { Selector = "CreateTime", Desc = true } };
}
Type type = typeof(TEntity);
// 检查是否存在指定名称的接口
Type interfaceType = type.GetInterface("ILogicDelete");
if (interfaceType != null && !loadOptionsexp.Filter.Contains("IsDeleted"))
{
loadOptionsexp.And(new List<object>(){
"IsDeleted","=",false });
}
var entitys = await Repository.GetPagedAsync(loadOptionsexp);
var dtos = MapperService.Map<IListResult<TEntity>, IListResult<TDto>>((IListResult<TEntity>)entitys);
return dtos;
}
public virtual async Task<IListResult<VO>> GetPageAsync<VO>(DataSourceLoadOptions loadOptionsexp)
{
if (loadOptionsexp.Sort == null || loadOptionsexp.Sort.Length == 0)
{
loadOptionsexp.Sort = new SortingInfo[] { new SortingInfo() { Selector = "CreateTime", Desc = true } };
}
Type type = typeof(TEntity);
// 检查是否存在指定名称的接口
Type interfaceType = type.GetInterface("ILogicDelete");
if (interfaceType != null&&!loadOptionsexp.Filter.Contains("IsDeleted"))
{
loadOptionsexp.And(new List<object>(){
"IsDeleted","=",false });
}
var entitys = await Repository.GetPagedAsync(loadOptionsexp);
var dtos = MapperService.Map<IListResult<TEntity>, IListResult<VO>>((IListResult<TEntity>)entitys);
return dtos;
}
/// <summary>
/// GetAsync
/// </summary>
/// <param name="entityId">ID</param>
/// <returns></returns>
public virtual async Task<INPResult> GetAsync(string entityId)
{
if (string.IsNullOrEmpty(entityId))
{
return base.FailParams<TDto>(nameof(entityId));
}
var entity = await Repository.FindByAsync(entityId);
var entityVo = MapperService.Map<TDto>(entity);
return Success(entityVo);
}
#endregion
}
}

View File

@ -0,0 +1,44 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.20" />
<PackageReference Include="Hangfire.Core" Version="1.8.20" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.198" />
<PackageReference Include="SqlSugarCore.Dm" Version="8.8.0" />
<PackageReference Include="System.Security.Permissions" Version="8.0.0" />
<PackageReference Include="NPlatform" Version="3.0.1.8" />
<PackageReference Include="NPlatform.Infrastructure" Version="3.0.1.10" />
</ItemGroup>
<ItemGroup>
<Using Include="NPlatform" />
<Using Include="NPlatform.Infrastructure" />
<Using Include="NPlatform.Result" />
<Using Include="NPlatform.Query" />
<Using Include="NPlatform.Dto" />
<Using Include="NPlatform.Events" />
<Using Include="NPlatform.Consts" />
<Using Include="NPlatform.Exceptions" />
<Using Include="NPlatform.Enums" />
<Using Include="NPlatform.Repositories" />
<Using Include="NPlatform.Domains.Entity" />
<Using Include="NPlatform.Repositories.IRepositories" />
<Using Include="NPlatform.Extends" />
<Using Include="System.Text.Json" />
<Using Include="System" />
<Using Include="System.Linq" />
<Using Include="System.Linq.Dynamic.Core" />
<Using Include="System.Linq.Expressions" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="System.Collections.Generic" />
<Using Include="System.Threading" />
<Using Include="System.Threading.Tasks" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BZPT.Domains\BZPT.Domains.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,218 @@
using BZPT.Domains.Application;
using BZPT.Domains.Entity.Sys;
using Castle.Core.Logging;
using Microsoft.Extensions.Logging;
using Mysqlx.Expr;
using Newtonsoft.Json;
using NPlatform.Filters;
using NPlatform.Infrastructure.IdGenerators;
using NPOI.HPSF;
using NPOI.SS.Formula.Functions;
using Serilog.Context;
using Serilog.Events;
using SqlSugar;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Configuration;
using System.Data.SqlTypes;
using System.Runtime.CompilerServices;
namespace BZPT.Repositories
{
public partial class DBContext : SqlSugarClient
{
private readonly string _connectionString;
private readonly DbType _dbProvider;
private readonly int _timeOut;
public DBContext(string connectString, DbType dbProvider, string ConfigId = "default", int timeOut = 180)
: base(new ConnectionConfig()
{
DbType = dbProvider,
ConnectionString = connectString,
IsAutoCloseConnection = true,
ConfigId = ConfigId,
ConfigureExternalServices = new ConfigureExternalServices()
{
EntityService = (property, column) =>
{
var attributes = property.GetCustomAttributes(true);
if (attributes.Any(it => it is KeyAttribute))
{
column.IsPrimarykey = true;
}
if (attributes.Any(it => it is NotMappedAttribute))
{
column.IsIgnore = true;
}
if (attributes.Any(t => t is ColumnAttribute))
{
var attr = attributes.First(it => it is ColumnAttribute) as ColumnAttribute;
if(!string.IsNullOrEmpty(attr?.Name))
column.DbColumnName = attr?.Name;
}
},
EntityNameService = (type, entity) =>
{
var attributes = type.GetCustomAttributes(true);
if (attributes.Any(it => it is TableAttribute))
{
var attr = attributes.First(it => it is TableAttribute) as TableAttribute;
if(!string.IsNullOrEmpty(attr?.Name))
entity.DbTableName = attr?.Name;
}
}
}
}, dbAction)
{
_connectionString = connectString;
_dbProvider = dbProvider;
_timeOut = timeOut;
}
public static Action<SqlSugarClient> dbAction = db =>
{
// SQL执行完
db.Aop.OnLogExecuted = (sql, pars) =>
{
try
{
var strSql = UtilMethods.GetNativeSql(sql, pars);
//Serilog.Log.Logger.Information($"time:{db.Ado.SqlExecutionTime.ToString()},sql:{strSql}");
}
catch (Exception ex)
{
// 处理异常,例如记录错误日志
Serilog.Log.Logger.Error(ex, "sql执行错误");
MessageApplication.SendErrorMessage("sql执行错误" + ex?.ToString());
}
};
db.Aop.OnLogExecuting = (sql, pars) =>
{
// 获取原生SQL推荐 5.1.4.63 性能OK
// UtilMethods.GetNativeSql(sql, pars)
// 获取无参数化SQL 影响性能只适合调试
// UtilMethods.GetSqlString(DbType.SqlServer, sql, pars)
};
db.Aop.OnError = (exp) =>
{
try
{
var sql = exp.Sql;
MessageApplication.SendErrorMessage("sql执行错误" + exp.ToString());
if (exp.Parametres != null)
{
foreach (var p in (exp.Parametres as SugarParameter[]))
{
switch (p.DbType)
{
case System.Data.DbType.String:
case System.Data.DbType.Date:
case System.Data.DbType.DateTime:
sql = sql.Replace(p.ParameterName, $"'{p.Value}'");
break;
default:
sql = sql.Replace(p.ParameterName, $"{p.Value}");
break;
}
}
}
Serilog.Log.Logger.Error($"time:{exp?.ToString()}{db.Ado.SqlExecutionTime.ToString()},sql:{sql}");
}
catch (Exception ex)
{
// 处理异常,例如记录错误日志
Serilog.Log.Logger.Error($"time:{exp?.Message}");
}
};
db.Aop.OnExecutingChangeSql = (sql, pars) =>
{
#if DEBUG
try
{
var strSql = UtilMethods.GetNativeSql(sql, pars);
Serilog.Log.Logger.Debug($"time:{db.Ado.SqlExecutionTime.ToString()},sql:{strSql}");
}
catch (Exception ex)
{
Serilog.Log.Logger.Error($"OnExecutingChangeSql:{ex.ToString()}");
// 处理异常,例如记录错误日志
}
#endif
return new KeyValuePair<string, SugarParameter[]>(sql, pars);
};
db.Aop.OnDiffLogEvent = it =>
{
// 操作前记录 包含: 字段描述 列名 值 表名 表描述
var editBeforeData = it.BeforeData;
// 操作后记录 包含: 字段描述 列名 值 表名 表描述
var editAfterData = it.AfterData;
var sql = it.Sql;
var parameter = it.Parameters;
var data = it.BusinessData; // 这边会显示你传进来的对象
var time = it.Time;
var diffType = it.DiffType; // enum insert 、update and delete
Serilog.Log.Logger.Information($"diffType:{diffType},editBeforeData:{editBeforeData},editAfterData:{editAfterData},sql:{sql},params:{JsonConvert.SerializeObject(parameter)}");
// Write logic
};
db.Aop.OnLogExecuted = (sql, p) =>
{
// 执行时间超过1秒
if (db.Ado.SqlExecutionTime.TotalSeconds > 2)
{
// 代码CS文件名
var fileName = db.Ado.SqlStackTrace.FirstFileName;
// 代码行数
var fileLine = db.Ado.SqlStackTrace.FirstLine;
// 方法名
var FirstMethodName = db.Ado.SqlStackTrace.FirstMethodName;
// db.Ado.SqlStackTrace.MyStackTraceList[1].xxx 获取上层方法的信息
LogContext.PushProperty("CostTime", db.Ado.SqlExecutionTime.TotalSeconds);
Serilog.Log.Logger.Warning($"SqlExecutionTime:{sql},params:{JsonConvert.SerializeObject(p)},fileName:{fileName},fileLine:{fileLine},MethodName:{FirstMethodName}");
}
// 相当于EF的 PrintToMiniProfiler
};
//db.Aop.DataExecuting = (oldValue, entityInfo) =>
//{
// /*** 列级别事件 :更新的每一列都会进事件 ***/
// if (entityInfo.PropertyName == "UpdateTime" && entityInfo.OperationType == DataFilterType.UpdateByObject)
// {
// entityInfo.SetValue(DateTime.Now); // 修改UpdateTime字段
// /*当前列获取特性*/
// // 5.1.3.23 +
// // entityInfo.IsAnyAttribute<特性>()
// // entityInfo.GetAttribute<特性>()
// }
// /*** 行级别事件 :更新一条记录只进一次 ***/
// if (entityInfo.EntityColumnInfo.IsPrimarykey)
// {
// // entityInfo.EntityValue 拿到单条实体对象
// }
// /*** 根据当前列修改另一列 ***/
// // if(当前列逻辑==XXX)
// // var properyDate = entityInfo.EntityValue.GetType().GetProperty("Date");
// // if(properyDate!=null)
// // properyDate.SetValue(entityInfo.EntityValue,1);
// // 可以写多个IF
// /*** 删除生效 (只有行级事件) ***/
// if (entityInfo.OperationType == DataFilterType.DeleteByObject)
// {
// // entityInfo.EntityValue 拿到所有实体对象
// }
//};
};
}
}

View File

@ -0,0 +1,59 @@
/***********************************************************
**:
**:
** :
** : 1.0
** 2015/12/7 16:06:56
**
************************************************************/
using BZPT.Domains.Entity;
using NPlatform.Extends;
using NPlatform.Result;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NPlatform.Filters;
using BZPT.Repositories;
using KubeClient.Models;
using SqlSugar;
using Consul.Filtering;
using BZPT.Domains.Entity.Sys;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using MathNet.Numerics.Distributions;
using ServiceStack;
using NPOI.SS.Formula.Functions;
using Mysqlx.Expr;
using System.Text;
using DevExtreme.AspNet.Data;
using ServiceStack.Script;
namespace BZPT.Repositories {
/// <summary>
/// 基础库、管理库共用库专用仓储。
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <typeparam name="string">主键类型</typeparam>
public abstract class DefaultRepository<TEntity> : RepositoryBase<TEntity>
where TEntity : EntityBase<string>, new()
{
[Autowired]
public new ILogger<DefaultRepository<TEntity>> loggerSvc { get; set; }
/// <summary>
/// 获基础信息库对象
/// </summary>
public new ISqlSugarClient Db;
public DefaultRepository(IRepositoryOptions option, DBContext db) :base(option, db)
{
Db= db;
}
}
}

View File

@ -0,0 +1,496 @@
/***********************************************************
**:
**:
** :
** : 1.0
** 2015/12/7 16:06:56
**
************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using SqlSugar;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using System.Text;
using NPlatform.Infrastructure.IdGenerators;
using NPlatform.Filters;
using System.DirectoryServices;
using DevExtreme.AspNet.Data;
namespace BZPT.Repositories
{
/// <summary>
/// 聚合仓储基类
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <typeparam name="string">主键类型</typeparam>
public abstract class RepositoryBase<TEntity> : ResultHelper, IRepository<TEntity, string>, IRepositorySugarBase<TEntity, string> where TEntity : EntityBase<string>, new()
{
[Autowired]
public ILogger<RepositoryBase<TEntity>> loggerSvc { get; set; }
[Autowired]
public IHttpContextAccessor httpContextAccessor { get; set; }
protected IRepositoryOptions Options;
/// <summary>
/// 数据库连接承载对象,默认注入基础库,管理所有的租户连接。
/// </summary>
protected DBContext ContextManage;
// 构造函数
public RepositoryBase(DBContext dbContext)
{
ContextManage = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
public string? GetUserId()
{
return httpContextAccessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
}
public string? GetUserName()
{
return httpContextAccessor.HttpContext?.User?.Identity?.Name;
}
public ISqlSugarClient DbDefault
{
get
{
return ContextManage;
}
}
/// <summary>
/// 获取业务库对象
/// </summary>
public ISqlSugarClient Db
{
get
{
var tenantId = httpContextAccessor.HttpContext?.User?.Claims?.Where(t => t.Type.ToUpper() =="TENANTID").FirstOrDefault()?.Value;
var connection = ContextManage.GetConnection("default");
if (!string.IsNullOrWhiteSpace(tenantId))
{
var tenant = ContextManage.GetConnection("default").Queryable<BZPT.Domains.Entity.Sys.Tenant>().Where(t => t.Id == tenantId).First();
var configId = tenantId;//集团ID也可以叫租户ID
if (!ContextManage.IsAnyConnection(configId))
{ //用非默认ConfigId进行测试
//添加业务库只在当前上下文有效原理SqlSugarScope模式入门文档去看
ContextManage.AddConnection(new ConnectionConfig()
{
ConfigId = configId,
ConnectionString = tenant.ConnectionString,
DbType = Enum.Parse<DbType>(tenant.DbType, true),
IsAutoCloseConnection = true
});
}
//原理说明
//IsAnyConnection、AddConnection和GetConnection 都是Scope周期不同请求不会有影响
connection = ContextManage.GetConnection(configId);
//可以给业务库result设置AOP和过滤滤器
}
// connection.QueryFilter.AddTableFilter<ILogicDelete>(it => it.IsDeleted == false);
//// 应用过滤器
//foreach (var ft in this.Options.QueryFilters)
//{
// var exp = ft.Value.GetFilter<TEntity>();
// if (exp != null)
// {
// connection.QueryFilter.AddTableFilter<TEntity>(exp);
// }
//}
return connection;
}
}
public TEntity this[string key]
{
get
{
if (EqualityComparer<string>.Default.Equals(key, default(string)))
{
return default(TEntity);
}
return this.Db.Queryable<TEntity>().Where(t => t.Id != null && t.Id.Equals(key)).First();
}
set
{
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}
//功能写法可以将插入和更新拆开,然后调用插入和更新独有特性
var x = Db.Storageable(value).ToStorage();
var rst = x.AsInsertable.ExecuteCommand();//不存在插入
var rst2 = x.AsUpdateable.ExecuteCommand();//存在更新
}
}
/// <summary>
/// Initializes a new instance of the <see cref="RepositoryBase{TEntity,string}"/> class.
/// 仓储基类
/// </summary>
/// <param name="option">
/// 仓储配置
/// </param>
public RepositoryBase(IRepositoryOptions option, DBContext dbContext)
{
Options = option;
ContextManage = dbContext;
}
#region
/// <summary>
/// 异步新增
/// </summary>
/// <param name="items">新增对象的集合</param>
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
public async virtual Task<int> AddsAsync(params TEntity[] items)
{
if (items is null)
{
throw new ArgumentNullException(nameof(items));
}
foreach(var item in items)
{
if(string.IsNullOrWhiteSpace(item.Id))
item.Id= StringObjectIdGenerator.Instance.GenerateId().ToString();
}
return await this.Db.Insertable<TEntity>(items).ExecuteCommandAsync();
}
/// <summary>
/// 异步新增
/// </summary>
/// <param name="items">新增对象的集合</param>
/// <returns><placeholder>A <see cref="Task"/> representing the asynchronous operation.</placeholder></returns>
public async virtual Task<int> AddOrUpdate(TEntity item)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item));
}
Db.Ado.CommandTimeOut = 30; // 设置30秒超时
//功能写法可以将插入和更新拆开,然后调用插入和更新独有特性
var x = Db.Storageable(item).ToStorage();
var rst = await x.AsInsertable.ExecuteCommandAsync();//不存在插入
var rst2 = await x.AsUpdateable.ExecuteCommandAsync();//存在更新
return rst + rst2;
}
public virtual async Task<int> RemoveAsync(Expression<Func<TEntity, bool>> filter)
{
if (filter == null) return -1;
if (typeof(TEntity).IsAssignableFrom(typeof(ILogicDelete)))
{
var rst = this.Db.Updateable<TEntity>()
.SetColumns(t => ((ILogicDelete)t).IsDeleted == true)
.Where(filter);
return await rst.ExecuteCommandAsync();
}
else
return await Db.Deleteable<TEntity>().Where(filter).ExecuteCommandAsync();
}
public virtual async Task<int> RemoveAsync(params string[] keys)
{
if (keys == null)
{
throw new ArgumentNullException(nameof(keys));
}
return await this.RemoveAsync(t => keys.Contains(t.Id));
}
public async Task<int> UpdateAsync(TEntity item)
{
return await Db.Updateable<TEntity>(item).ExecuteCommandAsync();
}
public async Task<int> UpdateAsync(Expression<Func<TEntity, TEntity>> columns, Expression<Func<TEntity, bool>> where)
{
return await Db.Updateable<TEntity>()
.SetColumns(columns)//类只能在表达示里面不能提取
.Where(where)
.ExecuteCommandAsync();
}
public async Task<int> UpdatesAsync(List<TEntity> entities, Expression<Func<TEntity, object>> columns)
{
return await Db.Updateable(entities)
.UpdateColumns(columns)
.ExecuteCommandAsync();
}
#endregion
#region
public async virtual Task<bool> ExistsAsync(string key)
{
if (EqualityComparer<string>.Default.Equals(key, default(string)))
{
return false;
}
return await this.Db.Queryable<TEntity>().AnyAsync(t => t.Id != null && t.Id.Equals(key));
}
public async virtual Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> filter)
{
if (filter == null)
{
return false;
}
//// 应用过滤器
//foreach (var ft in this.Options.QueryFilters)
//{
// var exp = ft.Value.GetFilter<TEntity>();
// if (exp != null)
// {
// filter = filter.AndAlso(exp);
// }
//}
return await this.Db.Queryable<TEntity>().AnyAsync(filter);
}
public async Task<IEnumerable<TEntity>> GetAllAsync(IEnumerable<SelectSort<TEntity>> sorts = null)
{
try
{
// 获取可查询对象
var query = ContextManage.Queryable<TEntity>();
// 如果有排序条件,则应用排序
if (sorts != null && sorts.Any())
{
query = query.OrderBy(sorts);
}
Type type = typeof(TEntity);
// 如果实现了ILogicDelete接口且filter不包含IsDeleted条件则添加IsDeleted=false条件
if (typeof(ILogicDelete).IsAssignableFrom(type))
{
query = query.Where(t => ((ILogicDelete)t).IsDeleted == false);
}
// 返回查询结果
return await query.ToListAsync();
}
catch (Exception ex)
{
throw new Exception("无法从数据库中检索数据.", ex);
}
}
public async virtual Task<TEntity> FindByAsync(string key)
{
if (EqualityComparer<string>.Default.Equals(key, default(string)))
{
return default(TEntity);
}
return await this.Db.Queryable<TEntity>().Where(t => t.Id != null && t.Id.Equals(key)).FirstAsync();
}
public async virtual Task<TEntity> GetFirstOrDefaultAsync(Expression<Func<TEntity, bool>> filter)
{
if (filter is null)
{
throw new ArgumentNullException(nameof(filter));
}
return await this.Db.Queryable<TEntity>().Where(filter).FirstAsync();
}
public async virtual Task<IEnumerable<TEntity>> GetListByExpAsync(Expression<Func<TEntity, bool>> filter, IEnumerable<SelectSort<TEntity>> sorts = null)
{
if (filter is null)
{
throw new ArgumentNullException(nameof(filter));
}
Type type = typeof(TEntity);
// 如果实现了ILogicDelete接口且filter不包含IsDeleted条件则添加IsDeleted=false条件
if (typeof(ILogicDelete).IsAssignableFrom(type))
{
if (!filter.ToString().Contains("IsDeleted"))
{
// 动态构建 t.IsDeleted == false 条件
filter = filter.AndAlso(t => ((ILogicDelete)t).IsDeleted == false);
}
}
var setAll = this.Db.Queryable<TEntity>().Where(filter);
if (sorts != null)
setAll = setAll.OrderBy(sorts);
return await setAll.ToListAsync();
}
/// <summary>
/// EF 版本EF实现指定字段查询比较困难所以先查出所有字段
/// </summary>
/// <param name="columnNames"></param>
/// <param name="filter"></param>
/// <param name="sorts"></param>
/// <returns></returns>
public async virtual Task<IEnumerable<TEntity>> GetListWithColumnsAsync(IEnumerable<string> columnNames, Expression<Func<TEntity, bool>> filter, IEnumerable<SelectSort<TEntity>> sorts = null)
{
if (filter is null)
{
throw new ArgumentNullException(nameof(filter));
}
Type type = typeof(TEntity);
// 如果实现了ILogicDelete接口且filter不包含IsDeleted条件则添加IsDeleted=false条件
if (typeof(ILogicDelete).IsAssignableFrom(type))
{
if (!filter.ToString().Contains("IsDeleted"))
{
// 动态构建 t.IsDeleted == false 条件
filter = filter.AndAlso(t => ((ILogicDelete)t).IsDeleted == false);
}
}
var rst = await GetListByExpAsync(filter, sorts);
return rst;
}
public async virtual Task<IListResult<TEntity>> GetPagedAsync(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> filter, IEnumerable<SelectSort<TEntity>> sorts)
{
if (filter is null)
{
throw new ArgumentNullException(nameof(filter));
}
RefAsync<int> total = 0;
Type type = typeof(TEntity);
// 如果实现了ILogicDelete接口且filter不包含IsDeleted条件则添加IsDeleted=false条件
if (typeof(ILogicDelete).IsAssignableFrom(type))
{
if (!filter.ToString().Contains("IsDeleted"))
{
// 动态构建 t.IsDeleted == false 条件
filter = filter.AndAlso(t => ((ILogicDelete)t).IsDeleted == false);
}
}
var resultSet = this.Db.Queryable<TEntity>().Where(filter);
if (sorts != null)
resultSet = resultSet.OrderBy(sorts);
var pageData = await resultSet.ToPageListAsync(pageIndex, pageSize, total);
return base.ListData(pageData, total);
}
public async virtual Task<IListResult<TEntity>> GetPagedAsync(DataSourceLoadOptions loadOptions)
{
var query = this.Db.Queryable<TEntity>();
var datas=await query.LoadAsync(loadOptions);
return datas;
}
#endregion
#region
public async Task<int> CountAsync(Expression<Func<TEntity, bool>> filter)
{
if (filter is null)
{
throw new ArgumentNullException(nameof(filter));
}
return await this.Db.Queryable<TEntity>().CountAsync(filter);
}
public async Task<TValue> MaxAsync<TValue>(Expression<Func<TEntity, TValue>> selector, Expression<Func<TEntity, bool>> filter = null)
{
if (selector is null)
{
throw new ArgumentNullException(nameof(selector));
}
if (filter is null)
{
throw new ArgumentNullException(nameof(filter));
}
return await this.Db.Queryable<TEntity>().Where(filter).MaxAsync(selector);
}
public async Task<TValue> MinAsync<TValue>(Expression<Func<TEntity, TValue>> selector, Expression<Func<TEntity, bool>> filter = null)
{
if (selector is null)
{
throw new ArgumentNullException(nameof(selector));
}
if (filter is null)
{
throw new ArgumentNullException(nameof(filter));
}
return await this.Db.Queryable<TEntity>().Where(filter).MinAsync(selector);
}
public async Task<decimal> SumAsync(Expression<Func<TEntity, decimal>> selector, Expression<Func<TEntity, bool>> filter = null)
{
if (selector is null)
{
throw new ArgumentNullException(nameof(selector));
}
if (filter is null)
{
throw new ArgumentNullException(nameof(filter));
}
return await this.Db.Queryable<TEntity>().Where(filter).SumAsync(selector);
}
public async Task<decimal> AVGAsync(Expression<Func<TEntity, decimal>> selector, Expression<Func<TEntity, bool>> filter = null)
{
if (selector is null)
{
throw new ArgumentNullException(nameof(selector));
}
if (filter is null)
{
throw new ArgumentNullException(nameof(filter));
}
return await this.Db.Queryable<TEntity>().Where(filter).AvgAsync(selector);
}
public Task<int> SaveChangesAsync()
{
throw new NotImplementedException("sqlsugar 不支持");
}
#endregion
}
}

View File

@ -0,0 +1,65 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity.Data;
using Microsoft.Extensions.DependencyInjection;
using BZPT.Domains.Entity;
using SqlSugar;
using System.Text;
namespace BZPT.Repositories
{
public static class SqlSugarExt
{
public static SqlSugar.DbType ToSugarDbType(this DBProvider dBProvider)
{
switch (dBProvider)
{
case DBProvider.MySqlClient:
return DbType.MySql;
case DBProvider.SqlClient:
return DbType.SqlServer;
case DBProvider.OracleClient:
return DbType.Oracle;
case DBProvider.PostgreSQL:
return DbType.PostgreSQL;
case DBProvider.SQLite:
return DbType.Sqlite;
default:
return DbType.Odbc;
}
}
/// <summary>
/// 根据指定属性名称对序列进行排序
/// </summary>
/// <typeparam name="TSource">source中的元素的类型</typeparam>
/// <param name="source">一个要排序的值序列</param>
/// <param name="property">属性名称</param>
/// <param name="descending">是否降序</param>
/// <returns></returns>
public static ISugarQueryable<TEntity> OrderBy<TEntity>(this ISugarQueryable<TEntity> source, IEnumerable<SelectSort<TEntity>> sorts = null) where TEntity : IEntity, new()
{
if (sorts != null)
{
StringBuilder strOrder = new StringBuilder();
foreach (var sort in sorts)
{
if(sort.FieldExp==null)
{
throw new Exception("未指定排序表达式FieldExpSqlSugar 仓储禁用 FieldName 指定排序字段");
}
source = source.OrderBy(sort.FieldExp, sort.IsAsc ? OrderByType.Asc : OrderByType.Desc);
}
}
return source;
}
/// <summary>
/// sqlsugar
/// </summary>
/// <param name="service"></param>
public static void RegisterSqlSugar(IServiceCollection service)
{
}
}
}

61
BZPT.sln Normal file
View File

@ -0,0 +1,61 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32319.34
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ServiceUnit", "ServiceUnit", "{E98E6E96-D64F-44CF-9EDA-3AA1D3DA825C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{93A61945-03CE-45E4-8530-A04D066FF795}"
ProjectSection(SolutionItems) = preProject
.dockerignore = .dockerignore
.gitignore = .gitignore
Dockerfile = Dockerfile
LICENSE = LICENSE
msyh.ttc = msyh.ttc
doc\README.md = doc\README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BZPT.Domains", "BZPT.Domains\BZPT.Domains.csproj", "{8F1EB32F-2DAC-4831-804B-A8567138AD3C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BZPT.DTO", "BZPT.DTO\BZPT.DTO.csproj", "{9E704A72-2F51-4B28-9CE6-16E4A3DC3042}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BZPT.SqlSugarRepository", "BZPT.SqlSugarRepository\BZPT.SqlSugarRepository.csproj", "{EF01CE3B-4E1C-4562-91D6-2056FB0B0A24}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BZPT.Api", "BZPT.Api\BZPT.Api.csproj", "{52D85EB7-84F3-40CF-B7B8-FBED8C2921B2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8F1EB32F-2DAC-4831-804B-A8567138AD3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8F1EB32F-2DAC-4831-804B-A8567138AD3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F1EB32F-2DAC-4831-804B-A8567138AD3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F1EB32F-2DAC-4831-804B-A8567138AD3C}.Release|Any CPU.Build.0 = Release|Any CPU
{9E704A72-2F51-4B28-9CE6-16E4A3DC3042}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E704A72-2F51-4B28-9CE6-16E4A3DC3042}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E704A72-2F51-4B28-9CE6-16E4A3DC3042}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E704A72-2F51-4B28-9CE6-16E4A3DC3042}.Release|Any CPU.Build.0 = Release|Any CPU
{EF01CE3B-4E1C-4562-91D6-2056FB0B0A24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF01CE3B-4E1C-4562-91D6-2056FB0B0A24}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF01CE3B-4E1C-4562-91D6-2056FB0B0A24}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF01CE3B-4E1C-4562-91D6-2056FB0B0A24}.Release|Any CPU.Build.0 = Release|Any CPU
{52D85EB7-84F3-40CF-B7B8-FBED8C2921B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{52D85EB7-84F3-40CF-B7B8-FBED8C2921B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52D85EB7-84F3-40CF-B7B8-FBED8C2921B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52D85EB7-84F3-40CF-B7B8-FBED8C2921B2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8F1EB32F-2DAC-4831-804B-A8567138AD3C} = {E98E6E96-D64F-44CF-9EDA-3AA1D3DA825C}
{9E704A72-2F51-4B28-9CE6-16E4A3DC3042} = {E98E6E96-D64F-44CF-9EDA-3AA1D3DA825C}
{EF01CE3B-4E1C-4562-91D6-2056FB0B0A24} = {E98E6E96-D64F-44CF-9EDA-3AA1D3DA825C}
{52D85EB7-84F3-40CF-B7B8-FBED8C2921B2} = {E98E6E96-D64F-44CF-9EDA-3AA1D3DA825C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A684986A-43C9-48FE-B685-1BCD19ED349F}
EndGlobalSection
EndGlobal

BIN
msyh.ttc Normal file

Binary file not shown.

2
readme.md Normal file
View File

@ -0,0 +1,2 @@
增加简单的数据库加密,进行数据脱敏
测试12