后端项目
概述
- 项目后端,包含业务逻辑、数据访问,根据服务接口动态生成
WebApi
。
- 项目
SDK
为Microsoft.NET.Sdk.Web
。
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<!--引用包,如果不采用Auto模式,可以删除前1个包-->
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.8" />
<PackageReference Include="Coravel" Version="5.0.4" /> <!--后台定时任务包,不用可删除-->
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.8" />
<!--其他数据库提供者
<PackageReference Include="System.Data.OleDb" Version="8.0.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
<PackageReference Include="MySqlConnector" Version="2.3.7" />
<PackageReference Include="Npgsql" Version="8.0.3" />
-->
<PackageReference Include="Known.Cells" Version="1.0.2" /> <!--Excel操作包,不用可删除-->
<PackageReference Include="Known.Core" Version="2.0.10" />
<ProjectReference Include="..\Sample.Client\Sample.Client.csproj" />
</ItemGroup>
<!--资源文件-->
<ItemGroup>
<EmbeddedResource Include="Resources\SQLite.sql" />
</ItemGroup>
</Project>
业务逻辑
- 业务逻辑服务类继承
ServiceBase
类和实现对应的服务接口,放在Services
文件夹中。
- 通常一个模块的增删改查导只需要设计3个接口,分别是查询、删除和保存。
- 分页查询和导出共用一个接口,由查询条件参数来区分是查询和导出。
- 删除单条和多条共用一个删除接口。
- 新增和编辑共用一个保存接口。
- 导入分同步和异步两种模式,对于数据量大的,建议使用异步导入模式。
// 服务实现类
class TestService(Context context) : ServiceBase(context), ITestService
{
// 分页查询和导出数据
public Task<PagingResult<TbTest>> QueryTestsAsync(PagingCriteria criteria)
{
return Database.QueryPageAsync<TbTest>(criteria);
}
// 删除数据
public async Task<Result> DeleteTestsAsync(List<TbTest> models)
{
if (models == null || models.Count == 0)
return Result.Error(Language.SelectOneAtLeast);
// 无附件删除逻辑
return await Database.TransactionAsync(Language.Delete, async db =>
{
foreach (var item in models)
{
// 如果有申请流程,则删除流程
//await Platform.DeleteFlowAsync(db, item.Id);
await db.DeleteAsync(item);
}
});
// 带附件删除逻辑
/*var oldFiles = new List<string>();
var result = await Database.TransactionAsync(Language.Delete, async db =>
{
foreach (var item in models)
{
await Platform.DeleteFilesAsync(db, item.Id, oldFiles); // 删除附件
await db.DeleteAsync(item);
}
});
if (result.IsValid)
Platform.DeleteFiles(oldFiles); // 删除服务器磁盘附件
return result;*/
}
// 保存表单数据
public async Task<Result> SaveTestAsync(TbTest model)
{
var vr = model.Validate(Context);
if (vr.IsValid)
{
// 此处验证数据库数据,例如:商品编码已存在
}
if (!vr.IsValid)
return vr;
return await Database.TransactionAsync(Language.Save, async db =>
{
if (entity.IsNew)
{
// 如果有申请流程,则创建流程
//await Platform.CreateFlowAsync(db, ApplyFlow.GetBizInfo(entity));
}
await db.SaveAsync(model);
}, model);
}
// 保存带附件的表单
public async Task<Result> SaveTestAsync(UploadInfo<TbTest> info)
{
var model = info.Model;
var vr = model.Validate(Context);
if (vr.IsValid)
{
// 此处验证数据库数据,例如:商品编码已存在
}
if (!vr.IsValid)
return vr;
var user = CurrentUser;
var bizType = "TestFiles";
var bizFiles = info.Files.GetAttachFiles(user, nameof(TbTest.Files), bizType);
return await Database.TransactionAsync(Language.Save, async db =>
{
await Platform.AddFilesAsync(db, bizFiles, model.Id, bizType);
model.Files = $"{model.Id}_{bizType}";
await db.SaveAsync(model);
}, model);
}
}
数据导入
- 数据导入分同步和异步,导入处理逻辑通过导入类来实现。
- 导入类继承
ImportBase<T>
类,放在Imports
文件夹中。
- 框架自动注入导入实现类,同步和异步导入时自动调用导入处理逻辑。
注意:
导入类的名称命名规则,实体类名+Import,如:SysDictionaryImport。
class TbTestImport(ImportContext context) : ImportBase<TbTest>(context)
{
// 初始化导入规范栏位
public override void InitColumns()
{
AddColumn(c => c.Field1);
AddColumn(c => c.Field2);
}
// 处理导入文件,实现导入逻辑
public override async Task<Result> ExecuteAsync(SysFile file)
{
var models = new List<TbTest>();
var result = ImportHelper.ReadFile<TbTest>(Context, file, item =>
{
var model = new TbTest
{
Field1 = item.GetValue(c => c.Field1),
Field2 = item.GetValueT(c => c.Field2)
};
var vr = model.Validate(Context);
if (!vr.IsValid)
item.ErrorMessage = vr.Message;
else
models.Add(model);
});
if (!result.IsValid)
return result;
return await Database.TransactionAsync(Language.Import, async db =>
{
foreach (var item in models)
{
await db.SaveAsync(item);
}
});
}
}
数据访问
- 数据访问类为静态类,用于查询复杂
SQL
语句的数据源,放在Repositories
文件夹中。
class TestRepository
{
// 存在编码字段
internal static async Task<bool> ExistsTestCodeAsync(Database db, TbTest model)
{
var sql = "select count(*) from TbTest where Id<>@Id and Code=@Code";
return await db.ScalarAsync<int>(sql, new { model.Id, model.Code }) > 0;
}
}
工作流
- 内置工作流默认支持创建、提交、撤回、指派、审核(通过或退回)、重新申请。
- 工作流继承
BaseFlow
类,可实现框架流程各环节操作前后调用业务系统数据。
- 工作流在业务表单新增保存方法中创建。
- 工作流提供日志插入和查询。
// 申请流程类
class ApplyFlow(Context context) : BaseFlow(context)
{
private const string FlowCode = "ApplyFlow"; // 流程代码
private const string FlowName = "申请流程"; // 流程名称
// 获取流程信息
internal static FlowBizInfo GetBizInfo(TbTest entity)
{
return new FlowBizInfo
{
FlowCode = FlowCode,
FlowName = FlowName,
BizId = entity.Id,
BizName = entity.BizNo,
BizUrl = "",
BizStatus = FlowStatus.Save
};
}
// 表单提交前
public override async Task<Result> OnCommitingAsync(Database db, FlowFormInfo info)
{
// 表单提交前的校验
var model = await db.QueryByIdAsync<TbTest>(info.BizId);
var vr = model.Validate(Context);
if (!vr.IsValid)
return vr;
return await base.OnCommitingAsync(db, info);
}
// 表单提交后
public override Task OnCommitedAsync(Database db, FlowFormInfo info) => base.OnCommitedAsync(db, info);
// 表单撤回前
public override Task<Result> OnRevokingAsync(Database db, FlowFormInfo info) => base.OnRevokingAsync(db, info);
// 表单撤回后
public override Task OnRevokedAsync(Database db, FlowFormInfo info) => base.OnRevokedAsync(db, info);
// 表单审核前
public override Task<Result> OnVerifingAsync(Database db, FlowFormInfo info) => base.OnVerifingAsync(db, info);
// 表单审核后
public override Task OnVerifiedAsync(Database db, FlowFormInfo info)
{
// 表单审核通过后操作其他业务
if (info.FlowStatus == FlowStatus.Over)
{
}
return base.OnVerifiedAsync(db, info);
}
// 表单重启前
public override Task<Result> OnRepeatingAsync(Database db, FlowFormInfo info) => base.OnRepeatingAsync(db, info);
// 表单重启后
public override Task OnRepeatedAsync(Database db, FlowFormInfo info) => base.OnRepeatedAsync(db, info);
// 流程停止前
public override Task<Result> OnStoppingAsync(Database db, FlowFormInfo info) => base.OnStoppingAsync(db, info);
// 流程停止后
public override Task OnStoppedAsync(Database db, FlowFormInfo info) => base.OnStoppedAsync(db, info);
}