diff --git a/.idea/.idea.Bunny.WebApi/.idea/dataSources.xml b/.idea/.idea.Bunny.WebApi/.idea/dataSources.xml
index a485d43..9b7261b 100644
--- a/.idea/.idea.Bunny.WebApi/.idea/dataSources.xml
+++ b/.idea/.idea.Bunny.WebApi/.idea/dataSources.xml
@@ -1,15 +1,23 @@
-
+
sqlite.xerial
true
org.sqlite.JDBC
- jdbc:sqlite:D:\MyFolder\Bunny\Bunny-cli\CSharp\CSharp-Single-EFCore\Bunny.WebApi\bunny.db
+ jdbc:sqlite:D:\Project\web\Bunny-Cli\template\CSharp\CSharp-Single-EFCore\Bunny.WebApi\Database\bunny.db
$ProjectFileDir$
+
+
+ file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar
+
+
+ file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar
+
+
\ No newline at end of file
diff --git a/Bunny.Common/Connect/EFCoreContext.cs b/Bunny.Common/Connect/EFCoreContext.cs
index 645970b..57652cb 100644
--- a/Bunny.Common/Connect/EFCoreContext.cs
+++ b/Bunny.Common/Connect/EFCoreContext.cs
@@ -28,14 +28,9 @@ public class EfCoreContext : DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
- // 添加过滤器
- modelBuilder.Entity()
- .HasQueryFilter(e => !e.IsDeleted);
-
- // 默认值为false,表示未删除
- modelBuilder.Entity()
- .Property(e => e.IsDeleted)
- .HasDefaultValue(false);
+ foreach (var entityType in modelBuilder.Model.GetEntityTypes())
+ if (typeof(BaseEntity).IsAssignableFrom(entityType.ClrType))
+ entityType.AddSoftDeleteQueryFilter();
base.OnModelCreating(modelBuilder);
}
@@ -74,7 +69,7 @@ public class EfCoreContext : DbContext
if (item.Entity is BaseEntity entity)
switch (item.State)
{
- //添加操作
+ // 添加操作
case EntityState.Added:
{
if (entity.Id == string.Empty) entity.Id = Guid.NewGuid().ToString();
@@ -84,10 +79,10 @@ public class EfCoreContext : DbContext
entity.UpdateUserId = BaseContext.GetUserId()!.Value;
break;
}
- //修改操作
+ // 修改操作
case EntityState.Modified:
entity.UpdateTime = DateTime.Now;
- entity.UpdateUserId = BaseContext.GetUserId()!.Value;
+ if (BaseContext.GetUserId() != null) entity.UpdateUserId = BaseContext.GetUserId()!.Value;
break;
case EntityState.Detached:
break;
@@ -97,8 +92,6 @@ public class EfCoreContext : DbContext
entity.UpdateTime = DateTime.Now;
entity.UpdateUserId = BaseContext.GetUserId()!.Value;
break;
- default:
- throw new ArgumentOutOfRangeException();
}
}
}
\ No newline at end of file
diff --git a/Bunny.Common/Connect/RedisContext.cs b/Bunny.Common/Connect/RedisContext.cs
index c8bd9d3..8949c64 100644
--- a/Bunny.Common/Connect/RedisContext.cs
+++ b/Bunny.Common/Connect/RedisContext.cs
@@ -5,7 +5,7 @@ namespace Bunny.Common.Connect;
public static class RedisContext
{
- public static IDatabase RedisDatabase;
+ public static IDatabase? RedisDatabase;
private static readonly EndPointCollection EndPointCollection = new();
public static void AddRedisContext(this WebApplicationBuilder builder)
diff --git a/Bunny.Common/Connect/SoftDeleteQueryExtension.cs b/Bunny.Common/Connect/SoftDeleteQueryExtension.cs
new file mode 100644
index 0000000..b583b5c
--- /dev/null
+++ b/Bunny.Common/Connect/SoftDeleteQueryExtension.cs
@@ -0,0 +1,36 @@
+using System.Linq.Expressions;
+using System.Reflection;
+using Bunny.Dao.Entity.Base;
+using Microsoft.EntityFrameworkCore.Metadata;
+
+namespace Bunny.Common.Connect;
+
+///
+/// 软删除过滤器
+///
+public static class SoftDeleteQueryExtension
+{
+ ///
+ /// 软删除过滤器
+ ///
+ ///
+ public static void AddSoftDeleteQueryFilter(this IMutableEntityType entityData)
+ {
+ var methodToCall = typeof(SoftDeleteQueryExtension)
+ .GetMethod(nameof(GetSoftDeleteFilter), BindingFlags.NonPublic | BindingFlags.Static)
+ ?.MakeGenericMethod(entityData.ClrType);
+ var filter = methodToCall?.Invoke(null, []);
+ entityData.SetQueryFilter((LambdaExpression)filter!);
+ }
+
+ ///
+ /// 软删除
+ ///
+ ///
+ ///
+ private static LambdaExpression GetSoftDeleteFilter() where TEntity : BaseEntity
+ {
+ Expression> filter = x => !x.IsDeleted;
+ return filter;
+ }
+}
\ No newline at end of file
diff --git a/Bunny.Common/Context/BaseContext.cs b/Bunny.Common/Context/BaseContext.cs
index 297d59e..2eab517 100644
--- a/Bunny.Common/Context/BaseContext.cs
+++ b/Bunny.Common/Context/BaseContext.cs
@@ -3,33 +3,33 @@
public class BaseContext
{
// 使用Lazy初始化确保线程安全
- public static readonly ThreadLocal UserId = new();
- public static readonly ThreadLocal Token = new();
+ public static readonly ThreadLocal? UserId = null;
+ public static readonly ThreadLocal? Token = null;
// 用户id相关
public static long? GetUserId()
{
- return UserId.Value;
+ return UserId?.Value;
}
public static void SetUserId(long? userId)
{
- UserId.Value = userId;
+ UserId!.Value = userId;
}
public static string? GetToken()
{
- return Token.Value;
+ return Token?.Value;
}
public static void SetToken(string token)
{
- Token.Value = token;
+ Token!.Value = token;
}
public static void RemoveUser()
{
- Token.Value = null;
- UserId.Value = null;
+ if (Token != null) Token.Value = null;
+ if (UserId != null) UserId.Value = null;
}
}
\ No newline at end of file
diff --git a/Bunny.Dao/Entity/Base/BaseEntity.cs b/Bunny.Dao/Entity/Base/BaseEntity.cs
index 5875efc..d3ece92 100644
--- a/Bunny.Dao/Entity/Base/BaseEntity.cs
+++ b/Bunny.Dao/Entity/Base/BaseEntity.cs
@@ -12,7 +12,5 @@ public class BaseEntity
public long UpdateUserId { get; set; }
- public string? OperationMessage { get; set; }
-
public bool IsDeleted { get; set; }
}
\ No newline at end of file
diff --git a/Bunny.Dao/Entity/System/Blog.cs b/Bunny.Dao/Entity/System/Blog.cs
index 516e119..986ec52 100644
--- a/Bunny.Dao/Entity/System/Blog.cs
+++ b/Bunny.Dao/Entity/System/Blog.cs
@@ -5,5 +5,5 @@ namespace Bunny.Dao.Entity.System;
public class Blog : BaseEntity
{
- [Required(ErrorMessage = "邮箱是必填项")] public string Url { get; set; }
+ [Required(ErrorMessage = "Url是必填项")] public string Url { get; set; }
}
\ No newline at end of file
diff --git a/Bunny.Service/IService/Service/JobService.cs b/Bunny.Service/IService/Service/JobService.cs
index 0924f09..3a60d3b 100644
--- a/Bunny.Service/IService/Service/JobService.cs
+++ b/Bunny.Service/IService/Service/JobService.cs
@@ -13,7 +13,6 @@ public class JobService : IJobService
public void StartSimpleJob()
{
var schedulerFactory = new StdSchedulerFactory();
- // TODO 如果使用异步,必须使用await 和 async
// var scheduler = schedulerFactory.GetScheduler();
var scheduler = schedulerFactory.GetScheduler().GetAwaiter().GetResult();
@@ -32,10 +31,6 @@ public class JobService : IJobService
// 使用同步方法
scheduler.ScheduleJob(jobDetail, trigger).GetAwaiter().GetResult();
scheduler.Start().GetAwaiter().GetResult();
-
- // TODO 使用异步
- // await _scheduler.ScheduleJob(jobDetail, trigger);
- // await _scheduler.Start();
}
///
@@ -49,8 +44,8 @@ public class JobService : IJobService
var jobDetail = JobBuilder.Create()
.UsingJobData("username", "用户名")
.UsingJobData("password", "密码")
- // TODO 如果不设置持久的会报错;
- // TODO Jobs added with no trigger must be durable.Quartz.SchedulerException: Jobs added with no trigger must be durable.
+ // 如果不设置持久的会报错;
+ // Jobs added with no trigger must be durable.Quartz.SchedulerException: Jobs added with no trigger must be durable.
.StoreDurably() // 设置作业为持久的
.WithIdentity("simpleJob", "简单的JOB")
.Build();
@@ -63,9 +58,9 @@ public class JobService : IJobService
.UsingJobData("trigger", "trigger值")
.WithIdentity("testTrigger", "测试发出器")
.StartNow()
- // TODO 设置调度时间单位
+ // 设置调度时间单位
// .WithSimpleSchedule(x => x.WithIntervalInSeconds(1).WithRepeatCount(10))
- // TODO 自定义调度时间单位
+ // 自定义调度时间单位
.WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromMinutes(1)).WithRepeatCount(10))
.Build();
@@ -95,9 +90,9 @@ public class JobService : IJobService
.UsingJobData("trigger", "trigger值")
.WithIdentity("testTrigger", "测试发出器")
.StartNow()
- // TODO StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(20, 0)):触发器从每天的20:00(晚上8点)开始。
- // TODO EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(22, 0)):触发器在每天的22:00(晚上10点)结束。
- // TODO WithIntervalInSeconds(2):在开始和结束时间之间,触发器将每2秒触发一次作业。
+ // StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(20, 0)):触发器从每天的20:00(晚上8点)开始。
+ // EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(22, 0)):触发器在每天的22:00(晚上10点)结束。
+ // WithIntervalInSeconds(2):在开始和结束时间之间,触发器将每2秒触发一次作业。
.WithDailyTimeIntervalSchedule(x => x.StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(16, 0))
.EndingDailyAt(TimeOfDay.HourAndMinuteOfDay(22, 0))
// 设置工作时间在每周的星期几
diff --git a/Bunny.WebApi/Config/BaseConfig.cs b/Bunny.WebApi/Config/BaseConfig.cs
index f7de169..c05ccbb 100644
--- a/Bunny.WebApi/Config/BaseConfig.cs
+++ b/Bunny.WebApi/Config/BaseConfig.cs
@@ -11,7 +11,7 @@ public class BaseConfig(WebApplicationBuilder builder)
///
public void Initialize()
{
- // 配置跨域
+ // 配置跨域
UseCors();
// 配置日志相关
// builder.Logging.AddLog4Net("Config/log4net.config");
@@ -24,8 +24,11 @@ public class BaseConfig(WebApplicationBuilder builder)
// 设置文件最大上传大小
options.Limits.MaxRequestBodySize = 1024 * 1024 * 100;
});
- // 添加Service服务
+ // 注入后台服务
builder.AddApplicationServices();
+ // 设置控制器返回时间,统一格式
+ builder.Services.AddControllers().AddJsonOptions(options =>
+ options.JsonSerializerOptions.Converters.Add(new JsonDateTimeConverter()));
// 添加后台服务
builder.AddApplicationBackendServices();
// 添加验证码
diff --git a/Bunny.WebApi/Database/bunny.db b/Bunny.WebApi/Database/bunny.db
index ba69319..8381f69 100644
Binary files a/Bunny.WebApi/Database/bunny.db and b/Bunny.WebApi/Database/bunny.db differ
diff --git a/Bunny.WebApi/Database/bunny.db-shm b/Bunny.WebApi/Database/bunny.db-shm
index 7600918..83f77db 100644
Binary files a/Bunny.WebApi/Database/bunny.db-shm and b/Bunny.WebApi/Database/bunny.db-shm differ
diff --git a/Bunny.WebApi/Database/bunny.db-wal b/Bunny.WebApi/Database/bunny.db-wal
index 07ff647..45880b8 100644
Binary files a/Bunny.WebApi/Database/bunny.db-wal and b/Bunny.WebApi/Database/bunny.db-wal differ
diff --git a/Bunny.WebApi/ReadMe.md b/Bunny.WebApi/ReadMe.md
index 297b093..4e6e28a 100644
--- a/Bunny.WebApi/ReadMe.md
+++ b/Bunny.WebApi/ReadMe.md
@@ -62,3 +62,77 @@ service.AddCaptcha(options =>
});
}
```
+
+## AutoFac配置
+
+### 自动注入按照名称注入
+
+**AutoFac配置详情**
+
+```csharp
+using Autofac;
+using Bunny.Common.Attribute;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Bunny.WebApi.Config;
+
+public static class AddAutofacConfig
+{
+ ///
+ /// AutoFac自动注入约定名称
+ /// 接口以Service结尾注入到IOC中
+ ///
+ ///
+ public static void BuildContainer(ContainerBuilder builder)
+ {
+ // 扫描所以前缀Bunny的命名空间
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(t => t.FullName!.StartsWith("Bunny"));
+
+ // 遍历注入服务
+ foreach (var assembly in assemblies)
+ {
+ var types = assembly.GetTypes();
+
+ // 注入Service
+ var serviceTypes = types.Where(t => t.GetCustomAttributes(typeof(ServiceAttribute), false).Length != 0);
+ foreach (var serviceType in serviceTypes)
+ {
+ var interfaces = serviceType.GetInterfaces();
+ builder.RegisterType(serviceType).As(interfaces).InstancePerDependency();
+ }
+
+ // 注入数据库相关
+ var mapperTypes = types.Where(t => t.GetCustomAttributes(typeof(MapperAttribute), false).Length != 0);
+ foreach (var mapperType in mapperTypes)
+ {
+ var interfaces = mapperType.GetInterfaces();
+ builder.RegisterType(mapperType).As(interfaces).InstancePerDependency();
+ }
+
+ // 注入后台服务
+ var componentTypes =
+ types.Where(t => t.GetCustomAttributes(typeof(ComponentAttribute), false).Length != 0);
+ foreach (var componentType in componentTypes)
+ builder.RegisterType(componentType).AsSelf().As().SingleInstance();
+ }
+
+ // 如果不在在 Controller 层写构造函数可以打开这个,自动完成注入
+ var controllerBaseType = typeof(ControllerBase);
+ builder.RegisterAssemblyTypes(typeof(Program).Assembly)
+ .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
+ .PropertiesAutowired(); //支持属性注入
+ }
+}
+```
+
+**主程序入口配置**
+
+```csharp
+// 让控制器实例由容器创建
+builder.Services.Replace(ServiceDescriptor.Transient());
+
+// 如果将Service放在 WebApi同级目录下,可以完成Controller自动注入,不需要写构造函数
+builder.Services.Replace(ServiceDescriptor.Transient());
+builder.Services.AddControllers().AddJsonOptions(options =>
+ options.JsonSerializerOptions.Converters.Add(new JsonDateTimeConverter()));
+```
\ No newline at end of file