smartadmin.core.urf 这个项目是基于asp.net core 3.1(最新)基础上参照领域驱动设计(DDD)的理念,并参考目前最为了流行的abp架构开发的一套轻量级的快速开发web application 技术架构,专注业务核心需求,减少重复代码,开始构建和发布,让初级程序员也能开发出专业并且漂亮的Web应用程序

域驱动设计(DDD)是一种通过将实现与不断发展的模型相连接来满足复杂需求的软件开发方法。域驱动设计的前提如下:

  • 将项目的主要重点放在核心领域和领域逻辑上;
  • 将复杂的设计基于领域模型;
  • 启动技术专家和领域专家之间的创造性合作,以迭代方式完善解决特定领域问题的概念模型。

最终的核心思想还是SOLID,只是实现的方式有所不同,ABP可能目前对DDD设计理念最好的实现方式。但对于小项目我还是更喜欢 URF.Core https://github.com/urfnet/URF.Core 这个超轻量级的实现。

同时这个项目也就是我2年前的一个开源项目 ASP.NET MVC 5 SmartCode Scaffolding for Visual Studio.Net 的升级版,支持.net core.目前没有把所有功能都迁移到.net core,其中最重要的就是代码生成这块。再接下来的时间里主要就是完善代码生成的插件。当然也要看是否受欢迎,如果反应一般,我可能不会继续更新。

Demo 网站

 演示站点 
账号:demo 密码:123456

GitHub 源代码 https://github.com/neozhu/smartadmin.core.urf

喜欢请给个 Star 每一颗Star都是鼓励我继续更新的动力 谢谢
如果你用于自己公司及盈利性的项目,希望给与金钱上的赞助,并且保留原作者的版权

分层

smartadmin.core.urf遵行DDD设计模式来实现应用程序的四层模型

  • 表示层(Presentation Layer):用户操作展示界面,使用SmartAdmin - Responsive WebApp模板+Jquery EasyUI
  • 应用层(Application Layer):在表示层与域层之间,实现具体应用程序逻辑,业务用例,Project:StartAdmin.Service.csproj
  • 域层(Domain Layer):包括业务对象(Entity)和核心(域)业务规则,应用程序的核心,使用EntityFrmework Core Code-first + Repository实现
  • 基础结构层(Infrastructure Layer):提供通用技术功能,这些功能主要有第三方库来支持,比如日志:Nlog,服务发现:Swagger UI,事件总线(EventBus):dotnetcore/CAP,认证与授权:Microsoft.AspNetCore.Identity,后面会具体介绍

内容

域层(Domain Layer)

  • 实体(Entity,BaseEntity) 通常实体就是映射到关系数据库中的表,这里说名一下最佳做法和惯例:
  1. 在域层定义:本项目就是(SmartAdmin.Entity.csproj)
  2. 继承一个基类 Entity,添加必要审计类比如:创建时间,最后修改时间等
  3. 必须要有一个主键最好是GRUID(不推荐复合主键),但本项目使用递增的int类型
  4. 字段不要过多的冗余,可以通过定义关联关系
  5. 字段属性和方法尽量使用virtual关键字。有些ORM和动态代理工具需要
  • 存储库(Repositories) 封装基本数据操作方法(CRUD),本项目应用 URF.Core实现
  • 域服务
  • 技术指标
  • 应用层

    • 应用服务:用于实现应用程序的用例。它们用于将域逻辑公开给表示层,从表示层(可选)使用DTO(数据传输对象)作为参数调用应用程序服务。它使用域对象执行某些特定的业务逻辑,并(可选)将DTO返回到表示层。因此,表示层与域层完全隔离。对应本项目:(SmartAdmin.Service.csproj)
    • 数据传输对象(DTO):用于在应用程序层和表示层或其他类型的客户端之间传输数据,通常,使用DTO作为参数从表示层(可选)调用应用程序服务。它使用域对象执行某些特定的业务逻辑,并(可选)将DTO返回到表示层。因此,表示层与域层完全隔离.对应本项目:(SmartAdmin.Dto.csproj)
    • Unit of work:管理和控制应用程序中操作数据库连接和事务 ,本项目使用 URF.Core实现
  • 基础服务层

    • UI样式定义:根据用户喜好选择多种页面显示模式
    • 租户管理:使用EntityFrmework Core提供的Global Filter实现简单多租户应用
    • 账号管理: 对登录系统账号维护,注册,注销,锁定,解锁,重置密码,导入、导出等功能
    • 角色管理:使用Microsoft身份库管理角色,用户及其权限管理
    • 导航菜单:系统主导航栏配置
    • 角色授权:配置角色显示的菜单
    • 键值对配置:常用的数据字典维护,如何正确使用和想法后面会介绍
    • 导入&导出配置:使用Excel导入导出做一个可配置的功能
    • 系统日志:asp.net core 自带的日志+Nlog把所有日志保存到数据库方便查询和分析
    • 消息订阅:集中订阅CAP分布式事件总线的消息
    • WebApi: Swagger UI Api服务发现和在线调试工具
    • CAP: CAP看板查看发布和订阅的消息

快速上手开发

  • 开发环境

    • Visual Studio .Net 2019
    • .Net Core 3.1
    • Sql Server(LocalDb)
  • 附加数据库

    使用SQL Server Management Studio 附加.\src\SmartAdmin.Data\db\smartadmindb.mdf 数据库(如果是localdb,那么不需要修改数据库连接配置)

  • 打开解决方案

第一个简单的需求开始 
新增 Company 企业信息 完成CRUD 导入导出功能

  • 新建实体对象(Entity)

在SmartAdmin.Entity.csproj项目的Models目录下新增一个Company.cs类

 //记住:定义实体对象最佳做法,继承基类,使用virtual关键字,尽可能的定义每个属性,名称,类型,长度,校验规则,索引,默认值等
namespace SmartAdmin.Data.Models
{
public partial class Company : URF.Core.EF.Trackable.Entity
{
[Display(Name = "企业名称", Description = "归属企业名称")]
[MaxLength()]
[Required]
//[Index(IsUnique = true)]
public virtual string Name { get; set; }
[Display(Name = "组织代码", Description = "组织代码")]
[MaxLength()]
//[Index(IsUnique = true)]
[Required]
public virtual string Code { get; set; }
[Display(Name = "地址", Description = "地址")]
[MaxLength()]
[DefaultValue("-")]
public virtual string Address { get; set; }
[Display(Name = "联系人", Description = "联系人")]
[MaxLength()]
public virtual string Contect { get; set; }
[Display(Name = "联系电话", Description = "联系电话")]
[MaxLength()]
public virtual string PhoneNumber { get; set; }
[Display(Name = "注册日期", Description = "注册日期")]
[DefaultValue("now")]
public virtual DateTime RegisterDate { get; set; }
}
}
//在 SmartAdmin.Data.csproj 项目 SmartDbContext.cs 添加
public virtual DbSet<Company> Companies { get; set; }
  • 添加服务对象 Service

在项目 SmartAdmin.Service.csproj 中添加ICompanyService.cs,CompanyService.cs 就是用来实现业务需求 用例的地方

 //ICompany.cs
//根据实际业务用例来创建方法,默认的CRUD,增删改查不需要再定义
namespace SmartAdmin.Service
{
// Example: extending IService<TEntity> and/or ITrackableRepository<TEntity>, scope: ICustomerService
public interface ICompanyService : IService<Company>
{
// Example: adding synchronous Single method, scope: ICustomerService
Company Single(Expression<Func<Company, bool>> predicate);
Task ImportDataTableAsync(DataTable datatable);
Task<Stream> ExportExcelAsync(string filterRules = "", string sort = "Id", string order = "asc");
}
}
// 具体实现接口的方法
namespace SmartAdmin.Service
{
public class CompanyService : Service<Company>, ICompanyService
{
private readonly IDataTableImportMappingService mappingservice;
private readonly ILogger<CompanyService> logger;
public CompanyService(
IDataTableImportMappingService mappingservice,
ILogger<CompanyService> logger,
ITrackableRepository<Company> repository) : base(repository)
{
this.mappingservice = mappingservice;
this.logger = logger;
} public async Task<Stream> ExportExcelAsync(string filterRules = "", string sort = "Id", string order = "asc")
{
var filters = PredicateBuilder.FromFilter<Company>(filterRules);
var expcolopts = await this.mappingservice.Queryable()
.Where(x => x.EntitySetName == "Company")
.Select(x => new ExpColumnOpts()
{
EntitySetName = x.EntitySetName,
FieldName = x.FieldName,
IgnoredColumn = x.IgnoredColumn,
SourceFieldName = x.SourceFieldName
}).ToArrayAsync(); var works = (await this.Query(filters).OrderBy(n => n.OrderBy(sort, order)).SelectAsync()).ToList();
var datarows = works.Select(n => new
{
Id = n.Id,
Name = n.Name,
Code = n.Code,
Address = n.Address,
Contect = n.Contect,
PhoneNumber = n.PhoneNumber,
RegisterDate = n.RegisterDate.ToString("yyyy-MM-dd HH:mm:ss")
}).ToList();
return await NPOIHelper.ExportExcelAsync("Company", datarows, expcolopts);
} public async Task ImportDataTableAsync(DataTable datatable)
{
var mapping = await this.mappingservice.Queryable()
.Where(x => x.EntitySetName == "Company" &&
(x.IsEnabled == true || (x.IsEnabled == false && x.DefaultValue != null))
).ToListAsync();
if (mapping.Count == )
{
throw new NullReferenceException("没有找到Work对象的Excel导入配置信息,请执行[系统管理/Excel导入配置]");
}
foreach (DataRow row in datatable.Rows)
{ var requiredfield = mapping.Where(x => x.IsRequired == true && x.IsEnabled == true && x.DefaultValue == null).FirstOrDefault()?.SourceFieldName;
if (requiredfield != null || !row.IsNull(requiredfield))
{
var item = new Company();
foreach (var field in mapping)
{
var defval = field.DefaultValue;
var contain = datatable.Columns.Contains(field.SourceFieldName ?? "");
if (contain && !row.IsNull(field.SourceFieldName))
{
var worktype = item.GetType();
var propertyInfo = worktype.GetProperty(field.FieldName);
var safetype = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
var safeValue = (row[field.SourceFieldName] == null) ? null : Convert.ChangeType(row[field.SourceFieldName], safetype);
propertyInfo.SetValue(item, safeValue, null);
}
else if (!string.IsNullOrEmpty(defval))
{
var worktype = item.GetType();
var propertyInfo = worktype.GetProperty(field.FieldName);
if (string.Equals(defval, "now", StringComparison.OrdinalIgnoreCase) && (propertyInfo.PropertyType == typeof(DateTime) || propertyInfo.PropertyType == typeof(Nullable<DateTime>)))
{
var safetype = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
var safeValue = Convert.ChangeType(DateTime.Now, safetype);
propertyInfo.SetValue(item, safeValue, null);
}
else if (string.Equals(defval, "guid", StringComparison.OrdinalIgnoreCase))
{
propertyInfo.SetValue(item, Guid.NewGuid().ToString(), null);
}
else if (string.Equals(defval, "user", StringComparison.OrdinalIgnoreCase))
{
propertyInfo.SetValue(item, "", null);
}
else
{
var safetype = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
var safeValue = Convert.ChangeType(defval, safetype);
propertyInfo.SetValue(item, safeValue, null);
}
}
}
this.Insert(item);
}
}
} // Example, adding synchronous Single method
public Company Single(Expression<Func<Company, bool>> predicate)
{ return this.Repository.Queryable().Single(predicate); }
}
}
  • 添加Controller

MVC Controller

 namespace SmartAdmin.WebUI.Controllers
{
public class CompaniesController : Controller
{
private readonly ICompanyService companyService;
private readonly IUnitOfWork unitOfWork;
private readonly ILogger<CompaniesController> _logger;
private readonly IWebHostEnvironment _webHostEnvironment;
public CompaniesController(ICompanyService companyService,
IUnitOfWork unitOfWork,
IWebHostEnvironment webHostEnvironment,
ILogger<CompaniesController> logger)
{
this.companyService = companyService;
this.unitOfWork = unitOfWork;
this._logger = logger;
this._webHostEnvironment = webHostEnvironment;
} // GET: Companies
public IActionResult Index()=> View();
//datagrid 数据源
public async Task<JsonResult> GetData(int page = , int rows = , string sort = "Id", string order = "asc", string filterRules = "")
{
try
{
var filters = PredicateBuilder.FromFilter<Company>(filterRules);
var total = await this.companyService
.Query(filters)
.AsNoTracking()
.CountAsync()
;
var pagerows = (await this.companyService
.Query(filters)
.AsNoTracking()
.OrderBy(n => n.OrderBy(sort, order))
.Skip(page - ).Take(rows)
.SelectAsync())
.Select(n => new
{
Id = n.Id,
Name = n.Name,
Code = n.Code,
Address = n.Address,
Contect = n.Contect,
PhoneNumber = n.PhoneNumber,
RegisterDate = n.RegisterDate.ToString("yyyy-MM-dd HH:mm:ss")
}).ToList();
var pagelist = new { total = total, rows = pagerows };
return Json(pagelist);
}
catch(Exception e) {
throw e;
} }
//编辑
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<JsonResult> Edit(Company company)
{
if (ModelState.IsValid)
{
try
{
this.companyService.Update(company); var result = await this.unitOfWork.SaveChangesAsync();
return Json(new { success = true, result = result });
}
catch (Exception e)
{
return Json(new { success = false, err = e.GetBaseException().Message });
}
}
else
{
var modelStateErrors = string.Join(",", this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors.Select(n => n.ErrorMessage)));
return Json(new { success = false, err = modelStateErrors });
//DisplayErrorMessage(modelStateErrors);
}
//return View(work);
}
//新建
[HttpPost]
[ValidateAntiForgeryToken] public async Task<JsonResult> Create([Bind("Name,Code,Address,Contect,PhoneNumber,RegisterDate")] Company company)
{
if (ModelState.IsValid)
{
try
{
this.companyService.Insert(company);
await this.unitOfWork.SaveChangesAsync();
return Json(new { success = true});
}
catch (Exception e)
{
return Json(new { success = false, err = e.GetBaseException().Message });
} //DisplaySuccessMessage("Has update a Work record");
//return RedirectToAction("Index");
}
else
{
var modelStateErrors = string.Join(",", this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors.Select(n => n.ErrorMessage)));
return Json(new { success = false, err = modelStateErrors });
//DisplayErrorMessage(modelStateErrors);
}
//return View(work);
}
//删除当前记录
//GET: Companies/Delete/:id
[HttpGet]
public async Task<JsonResult> Delete(int id)
{
try
{
await this.companyService.DeleteAsync(id);
await this.unitOfWork.SaveChangesAsync();
return Json(new { success = true });
} catch (Exception e)
{
return Json(new { success = false, err = e.GetBaseException().Message });
}
}
//删除选中的记录
[HttpPost]
public async Task<JsonResult> DeleteChecked(int[] id)
{
try
{
foreach (var key in id)
{
await this.companyService.DeleteAsync(key);
}
await this.unitOfWork.SaveChangesAsync();
return Json(new { success = true });
}
catch (Exception e)
{
return Json(new { success = false, err = e.GetBaseException().Message });
}
}
//保存datagrid编辑的数据
[HttpPost]
public async Task<JsonResult> AcceptChanges(Company[] companies)
{
if (ModelState.IsValid)
{
try
{
foreach (var item in companies)
{
this.companyService.ApplyChanges(item);
}
var result = await this.unitOfWork.SaveChangesAsync();
return Json(new { success = true, result });
}
catch (Exception e)
{
return Json(new { success = false, err = e.GetBaseException().Message });
}
}
else
{
var modelStateErrors = string.Join(",", ModelState.Keys.SelectMany(key => ModelState[key].Errors.Select(n => n.ErrorMessage)));
return Json(new { success = false, err = modelStateErrors });
} }
//导出Excel
[HttpPost]
public async Task<IActionResult> ExportExcel(string filterRules = "", string sort = "Id", string order = "asc")
{
var fileName = "compnay" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx";
var stream = await this.companyService.ExportExcelAsync(filterRules, sort, order);
return File(stream, "application/vnd.ms-excel", fileName);
}
//导入excel
[HttpPost]
public async Task<IActionResult> ImportExcel()
{
try
{
var watch = new Stopwatch();
watch.Start();
var total = ;
if (Request.Form.Files.Count > )
{
for (var i = ; i < this.Request.Form.Files.Count; i++)
{
var model = Request.Form["model"].FirstOrDefault() ?? "company";
var folder = Request.Form["folder"].FirstOrDefault() ?? "company";
var autosave = Convert.ToBoolean(Request.Form["autosave"].FirstOrDefault());
var properties = (Request.Form["properties"].FirstOrDefault()?.Split(','));
var file = Request.Form.Files[i];
var filename = file.FileName;
var contenttype = file.ContentType;
var size = file.Length;
var ext = Path.GetExtension(filename);
var path = Path.Combine(this._webHostEnvironment.ContentRootPath, "UploadFiles", folder);
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
var datatable = await NPOIHelper.GetDataTableFromExcelAsync(file.OpenReadStream(), ext);
await this.companyService.ImportDataTableAsync(datatable);
await this.unitOfWork.SaveChangesAsync();
total = datatable.Rows.Count;
if (autosave)
{
var filepath = Path.Combine(path, filename);
file.OpenReadStream().Position = ; using (var stream = System.IO.File.Create(filepath))
{
await file.CopyToAsync(stream);
}
} }
}
watch.Stop();
return Json(new { success = true, total = total, elapsedTime = watch.ElapsedMilliseconds });
}
catch (Exception e) {
this._logger.LogError(e, "Excel导入失败");
return this.Json(new { success = false, err = e.GetBaseException().Message });
}
}
//下载模板
public async Task<IActionResult> Download(string file) { this.Response.Cookies.Append("fileDownload", "true");
var path = Path.Combine(this._webHostEnvironment.ContentRootPath, file);
var downloadFile = new FileInfo(path);
if (downloadFile.Exists)
{
var fileName = downloadFile.Name;
var mimeType = MimeTypeConvert.FromExtension(downloadFile.Extension);
var fileContent = new byte[Convert.ToInt32(downloadFile.Length)];
using (var fs = downloadFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
{
await fs.ReadAsync(fileContent, , Convert.ToInt32(downloadFile.Length));
}
return this.File(fileContent, mimeType, fileName);
}
else
{
throw new FileNotFoundException($"文件 {file} 不存在!");
}
} }
}
  • 新建 View

MVC Views\Companies\Index

 @model SmartAdmin.Data.Models.Company
@{
ViewData["Title"] = "企业信息";
ViewData["PageName"] = "Companies_Index";
ViewData["Heading"] = "<i class='fal fa-window text-primary'></i> 企业信息";
ViewData["Category1"] = "组织架构";
ViewData["PageDescription"] = "";
}
<div class="row">
<div class="col-lg-12 col-xl-12">
<div id="panel-1" class="panel">
<div class="panel-hdr">
<h2>
公司信息
</h2>
<div class="panel-toolbar">
<button class="btn btn-panel bg-transparent fs-xl w-auto h-auto rounded-0" data-action="panel-collapse" data-toggle="tooltip" data-offset="0,10" data-original-title="Collapse"><i class="fal fa-window-minimize"></i></button>
<button class="btn btn-panel bg-transparent fs-xl w-auto h-auto rounded-0" data-action="panel-fullscreen" data-toggle="tooltip" data-offset="0,10" data-original-title="Fullscreen"><i class="fal fa-expand"></i></button>
</div> </div>
<div class="panel-container show">
<div class="panel-content py-2 rounded-bottom border-faded border-left-0 border-right-0 text-muted bg-subtlelight-fade ">
<div class="row no-gutters align-items-center">
<div class="col">
<!-- 开启授权控制请参考 @@if (Html.IsAuthorize("Create") -->
<div class="btn-group btn-group-sm">
<button onclick="append()" class="btn btn-default">
<span class="fal fa-plus mr-1"></span> 新增
</button>
</div>
<div class="btn-group btn-group-sm">
<button name="deletebutton" disabled onclick="removeit()" class="btn btn-default">
<span class="fal fa-times mr-1"></span> 删除
</button>
</div>
<div class="btn-group btn-group-sm">
<button name="savebutton" disabled onclick="acceptChanges()" class="btn btn-default">
<span class="fal fa-save mr-1"></span> 保存
</button>
</div>
<div class="btn-group btn-group-sm">
<button name="cancelbutton" disabled onclick="rejectChanges()" class="btn btn-default">
<span class="fal fa-ban mr-1"></span> 取消
</button>
</div>
<div class="btn-group btn-group-sm">
<button onclick="reload()" class="btn btn-default"> <span class="fal fa-search mr-1"></span> 查询 </button>
<button type="button" class="btn btn-default dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu dropdown-menu-animated">
<a class="dropdown-item js-waves-on" href="javascript:void()"> 我的记录 </a>
<div class="dropdown-divider"></div>
<a class="dropdown-item js-waves-on" href="javascript:void()"> 自定义查询 </a>
</div>
</div>
<div class="btn-group btn-group-sm hidden-xs">
<button type="button" onclick="importExcel.upload()" class="btn btn-default"><span class="fal fa-cloud-upload mr-1"></span> 导入 </button>
<button type="button" class="btn btn-default dropdown-toggle dropdown-toggle-split waves-effect waves-themed" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu dropdown-menu-animated">
<a class="dropdown-item js-waves-on" href="javascript:importExcel.downloadtemplate()">
<span class="fal fa-download"></span> 下载模板
</a>
</div>
</div>
<div class="btn-group btn-group-sm hidden-xs">
<button onclick="exportexcel()" class="btn btn-default">
<span class="fal fa-file-export mr-1"></span> 导出
</button>
</div> </div> </div> </div>
<div class="panel-content">
<div class="table-responsive">
<table id="companies_datagrid">
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 弹出窗体form表单 -->
<div id="companydetailwindow" class="easyui-window"
title="明细数据"
data-options="modal:true,
closed:true,
minimizable:false,
collapsible:false,
maximized:false,
iconCls:'fal fa-window',
onBeforeClose:function(){
var that = $(this);
if(companyhasmodified()){
$.messager.confirm('确认','你确定要放弃保存修改的记录?',function(r){
if (r){
var opts = that.panel('options');
var onBeforeClose = opts.onBeforeClose;
opts.onBeforeClose = function(){};
that.panel('close');
opts.onBeforeClose = onBeforeClose;
hook = false;
}
});
return false;
}
},
onOpen:function(){
$(this).window('vcenter');
$(this).window('hcenter');
},
onRestore:function(){
},
onMaximize:function(){
}
" style="width:820px;height:420px;display:none">
<!-- toolbar -->
<div class="panel-content py-2 rounded-bottom border-faded border-left-0 border-right-0 text-muted bg-subtlelight-fade sticky-top">
<div class="d-flex flex-row-reverse pr-4">
<div class="btn-group btn-group-sm mr-1">
<button name="saveitembutton" onclick="savecompanyitem()" class="btn btn-default">
<i class="fal fa-save"></i> 保存
</button>
</div>
<div class="btn-group btn-group-sm mr-1" id="deleteitem-btn-group">
<button onclick="deletecompanyitem()" class="btn btn-danger">
<i class="fal fa-trash-alt"></i> 删除
</button>
</div>
</div>
</div>
<div class="panel-container show">
<div class="container">
<div class="panel-content">
<form id="company_form"
class="easyui-form form-horizontal p-1"
method="post"
data-options="novalidate:true,
onChange: function(target){
hook = true;
$('button[name*=\'saveitembutton\']').prop('disabled', false);
},
onLoadSuccess:function(data){
hook = false;
$('button[name*=\'saveitembutton\']').prop('disabled', true);
}">
@Html.AntiForgeryToken()
<!--Primary Key-->
@Html.HiddenFor(model => model.Id)
<fieldset class="form-group">
<!-- begin row -->
<!--名称-->
<div class="row h-100 justify-content-center align-items-center">
<label class="col-md-2 pr-1 form-label text-right text-danger">@Html.DisplayNameFor(model => model.Name)</label>
<div class="col-md-4 mb-1 pl-1">
<input id="@Html.IdFor(model => model.Name)"
name="@Html.NameFor(model => model.Name)"
value="@Html.ValueFor(model => model.Name)"
tabindex="" required
class="easyui-textbox"
style="width:100%"
type="text"
data-options="prompt:'@Html.DescriptionFor(model => model.Name)',
required:true,
validType: 'length[0,50]'
" />
</div>
<label class="col-md-2 pr-1 form-label text-right text-danger">@Html.DisplayNameFor(model => model.Code)</label>
<div class="col-md-4 mb-1 pl-1">
<input id="@Html.IdFor(model => model.Code)"
name="@Html.NameFor(model => model.Code)"
value="@Html.ValueFor(model => model.Code)"
tabindex="" required
class="easyui-textbox"
style="width:100%"
type="text"
data-options="prompt:'@Html.DescriptionFor(model => model.Code)',
required:true,
validType: 'length[0,12]'
" />
</div>
<label class="col-md-2 pr-1 form-label text-right">@Html.DisplayNameFor(model => model.Address)</label>
<div class="col-md-4 mb-1 pl-1">
<input id="@Html.IdFor(model => model.Address)"
name="@Html.NameFor(model => model.Address)"
value="@Html.ValueFor(model => model.Address)"
tabindex=""
class="easyui-textbox"
style="width:100%"
type="text"
data-options="prompt:'@Html.DescriptionFor(model => model.Address)',
required:false,
validType: 'length[0,50]'
" />
</div>
<label class="col-md-2 pr-1 form-label text-right">@Html.DisplayNameFor(model => model.Contect)</label>
<div class="col-md-4 mb-1 pl-1">
<input id="@Html.IdFor(model => model.Contect)"
name="@Html.NameFor(model => model.Contect)"
value="@Html.ValueFor(model => model.Contect)"
tabindex=""
class="easyui-textbox"
style="width:100%"
type="text"
data-options="prompt:'@Html.DescriptionFor(model => model.Contect)',
required:false,
validType: 'length[0,12]'
" />
</div>
<label class="col-md-2 pr-1 form-label text-right">@Html.DisplayNameFor(model => model.PhoneNumber)</label>
<div class="col-md-4 mb-1 pl-1">
<input id="@Html.IdFor(model => model.PhoneNumber)"
name="@Html.NameFor(model => model.PhoneNumber)"
value="@Html.ValueFor(model => model.PhoneNumber)"
tabindex=""
class="easyui-textbox"
style="width:100%"
type="text"
data-options="prompt:'@Html.DescriptionFor(model => model.PhoneNumber)',
required:false,
validType: 'length[0,20]'
" />
</div>
<label class="col-md-2 pr-1 form-label text-right text-danger">@Html.DisplayNameFor(model => model.RegisterDate)</label>
<div class="col-md-4 mb-1 pl-1">
<input id="@Html.IdFor(model => model.RegisterDate)"
name="@Html.NameFor(model => model.RegisterDate)"
value="@Html.ValueFor(model => model.RegisterDate)"
tabindex="" required
class="easyui-datebox"
style="width:100%"
type="text"
data-options="prompt:'@Html.DescriptionFor(model => model.RegisterDate)',
required:true,
formatter:dateformatter" />
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
</div> @await Component.InvokeAsync("ImportExcel", new ImportExcelOptions { entity="Company",
folder="Companies",
url="/Companies/ImportExcel",
tpl="/Companies/Download" }) @section HeadBlock {
<link href="~/css/notifications/toastr/toastr.css" rel="stylesheet" asp-append-version="true" />
<link href="~/css/formplugins/bootstrap-daterangepicker/bootstrap-daterangepicker.css" rel="stylesheet" asp-append-version="true" />
<link href="~/js/easyui/themes/insdep/easyui.css" rel="stylesheet" asp-append-version="true" />
}
@section ScriptsBlock {
<script src="~/js/dependency/moment/moment.js" asp-append-version="true"></script>
<script src="~/js/notifications/toastr/toastr.js"></script>
<script src="~/js/formplugins/bootstrap-daterangepicker/bootstrap-daterangepicker.js" asp-append-version="true"></script>
<script src="~/js/easyui/jquery.easyui.min.js" asp-append-version="true"></script>
<script src="~/js/easyui/plugins/datagrid-filter.js" asp-append-version="true"></script>
<script src="~/js/easyui/plugins/columns-ext.js" asp-append-version="true"></script>
<script src="~/js/easyui/plugins/columns-reset.js" asp-append-version="true"></script>
<script src="~/js/easyui/locale/easyui-lang-zh_CN.js" asp-append-version="true"></script>
<script src="~/js/easyui/jquery.easyui.component.js" asp-append-version="true"></script>
<script src="~/js/plugin/filesaver/FileSaver.js" asp-append-version="true"></script>
<script src="~/js/plugin/jquery.serializejson/jquery.serializejson.js" asp-append-version="true"></script>
<script src="~/js/jquery.custom.extend.js" asp-append-version="true"></script>
<script src="~/js/jquery.extend.formatter.js" asp-append-version="true"></script>
<script>
var $dg = $('#companies_datagrid');
var EDITINLINE = true;
var company = null;
var editIndex = undefined;
//下载Excel导入模板 //执行导出下载Excel
function exportexcel() {
const filterRules = JSON.stringify($dg.datagrid('options').filterRules);
console.log(filterRules);
$.messager.progress({ title: '请等待',msg:'正在执行导出...' });
let formData = new FormData();
formData.append('filterRules', filterRules);
formData.append('sort', 'Id');
formData.append('order', 'asc');
$.postDownload('/Companies/ExportExcel', formData).then(res => {
$.messager.progress('close');
toastr.success('导出成功!');
}).catch(err => {
//console.log(err);
$.messager.progress('close');
$.messager.alert('导出失败', err.statusText, 'error');
}); }
//弹出明细信息
function showdetailswindow(id, index) {
const company = $dg.datagrid('getRows')[index];
opencompanydetailwindow(company, 'Modified');
}
function reload() {
$dg.datagrid('uncheckAll');
$dg.datagrid('reload');
}
//新增记录
function append() {
company = {
Address: '-',
RegisterDate: moment().format('YYYY-MM-DD HH:mm:ss'),
};
if (!EDITINLINE) {
//弹出新增窗口
opencompanydetailwindow(company, 'Added');
} else {
if (endEditing()) {
//对必填字段进行默认值初始化
$dg.datagrid('insertRow',
{
index: ,
row: company
});
editIndex = ;
$dg.datagrid('selectRow', editIndex)
.datagrid('beginEdit', editIndex);
hook = true;
}
}
}
//删除编辑的行
function removeit() {
if (this.$dg.datagrid('getChecked').length <= && EDITINLINE) {
if (editIndex !== undefined) {
const delindex = editIndex;
$dg.datagrid('cancelEdit', delindex)
.datagrid('deleteRow', delindex);
hook = true;
} else {
const rows =$dg.datagrid('getChecked');
rows.slice().reverse().forEach(row => {
const rowindex =$dg.datagrid('getRowIndex', row);
$dg.datagrid('deleteRow', rowindex);
hook = true;
});
}
} else {
deletechecked();
}
}
//删除该行
function deleteRow(id) {
$.messager.confirm('确认', '你确定要删除该记录?', result => {
if (result) {
dodeletechecked([id]);
}
})
}
//删除选中的行
function deletechecked() {
const id =$dg.datagrid('getChecked').filter(item => item.Id != null && item.Id > ).map(item => {
return item.Id;
});
if (id.length > ) {
$.messager.confirm('确认', `你确定要删除这 <span class='badge badge-icon position-relative'>${id.length} </span> 行记录?`, result => {
if (result) {
dodeletechecked(id);
}
});
} else {
$.messager.alert('提示', '请先选择要删除的记录!', 'question');
}
}
//执行删除
function dodeletechecked(id) {
$.post('/Companies/DeleteChecked', { id: id })
.done(response => {
if (response.success) {
toastr.error(`成功删除[${id.length}]行记录`);
reload();
} else {
$.messager.alert('错误', response.err, 'error');
}
})
.fail((jqXHR, textStatus, errorThrown) => {
$.messager.alert('异常', `${jqXHR.status}: ${jqXHR.statusText} `, 'error');
});
}
//开启编辑状态
function onClickCell(index, field) { company = $dg.datagrid('getRows')[index];
const _actions = ['action', 'ck'];
if (!EDITINLINE || $.inArray(field, _actions) >= ) {
return;
} if (editIndex !== index) {
if (endEditing()) {
$dg.datagrid('selectRow', index)
.datagrid('beginEdit', index);
hook = true;
editIndex = index;
const ed = $dg.datagrid('getEditor', { index: index, field: field });
if (ed) {
($(ed.target).data('textbox') ? $(ed.target).textbox('textbox') : $(ed.target)).focus();
}
} else {
$dg.datagrid('selectRow', editIndex);
}
}
}
//关闭编辑状态
function endEditing() { if (editIndex === undefined) {
return true;
}
if (this.$dg.datagrid('validateRow', editIndex)) {
$dg.datagrid('endEdit', editIndex);
return true;
} else {
const invalidinput = $('input.validatebox-invalid', $dg.datagrid('getPanel'));
const fieldnames = invalidinput.map((index, item) => {
return $(item).attr('placeholder') || $(item).attr('id');
});
$.messager.alert('提示', `${Array.from(fieldnames)} 输入有误.`, 'error');
return false;
}
}
//提交保存后台数据库
function acceptChanges() {
if (endEditing()) {
if ($dg.datagrid('getChanges').length > ) {
const inserted = $dg.datagrid('getChanges', 'inserted').map(item => {
item.TrackingState = ;
return item;
});
const updated = $dg.datagrid('getChanges', 'updated').map(item => {
item.TrackingState =
return item;
});
const deleted = $dg.datagrid('getChanges', 'deleted').map(item => {
item.TrackingState =
return item;
});
//过滤已删除的重复项
const changed = inserted.concat(updated.filter(item => {
return !deleted.includes(item);
})).concat(deleted);
//$.messager.progress({ title: '请等待', msg: '正在保存数据...', interval: 200 });
$.post('/Companies/AcceptChanges', { companies: changed })
.done(response => {
//$.messager.progress('close');
//console.log(response);
if (response.success) {
toastr.success('保存成功');
$dg.datagrid('acceptChanges');
reload();
hook = false;
} else {
$.messager.alert('错误', response.err, 'error');
}
})
.fail((jqXHR, textStatus, errorThrown) => {
//$.messager.progress('close');
$.messager.alert('异常', `${jqXHR.status}: ${jqXHR.statusText} `, 'error');
});
}
}
}
function rejectChanges() {
$dg.datagrid('rejectChanges');
editIndex = undefined;
hook = false;
}
$(document).ready(function () {
//定义datagrid结构
$dg.datagrid({
rownumbers: true,
checkOnSelect: false,
selectOnCheck: false,
idField: 'Id',
sortName: 'Id',
sortOrder: 'desc',
remoteFilter: true,
singleSelect: true,
method: 'get',
onClickCell: onClickCell,
clientPaging: false,
pagination: true,
striped: true,
filterRules: [],
onHeaderContextMenu: function (e, field) {
e.preventDefault();
$(this).datagrid('columnMenu').menu('show', {
left: e.pageX,
top: e.pageY
});
},
onBeforeLoad: function () {
const that = $(this);
document.addEventListener('panel.onfullscreen', () => {
setTimeout(() => {
that.datagrid('resize');
}, )
})
},
onLoadSuccess: function (data) {
editIndex = undefined;
$("button[name*='deletebutton']").prop('disabled', true);
$("button[name*='savebutton']").prop('disabled', true);
$("button[name*='cancelbutton']").prop('disabled', true);
},
onCheck: function () {
$("button[name*='deletebutton']").prop('disabled', false);
},
onUncheck: function () {
const checked = $(this).datagrid('getChecked').length > ;
$("button[name*='deletebutton']").prop('disabled', !checked);
},
onSelect: function (index, row) {
company = row;
},
onBeginEdit: function (index, row) {
//const editors = $(this).datagrid('getEditors', index); },
onEndEdit: function (index, row) {
editIndex = undefined;
},
onBeforeEdit: function (index, row) {
editIndex = index;
row.editing = true;
$("button[name*='deletebutton']").prop('disabled', false);
$("button[name*='cancelbutton']").prop('disabled', false);
$("button[name*='savebutton']").prop('disabled', false);
$(this).datagrid('refreshRow', index);
},
onAfterEdit: function (index, row) {
row.editing = false;
editIndex = undefined;
$(this).datagrid('refreshRow', index);
},
onCancelEdit: function (index, row) {
row.editing = false;
editIndex = undefined;
$("button[name*='deletebutton']").prop('disabled', true);
$("button[name*='savebutton']").prop('disabled', true);
$("button[name*='cancelbutton']").prop('disabled', true);
$(this).datagrid('refreshRow', index);
},
frozenColumns: [[
/*开启CheckBox选择功能*/
{ field: 'ck', checkbox: true },
{
field: 'action',
title: '操作',
width: ,
sortable: false,
resizable: true,
formatter: function showdetailsformatter(value, row, index) {
if (!row.editing) {
return `<div class="btn-group">\
<button onclick="showdetailswindow('${row.Id}', ${index})" class="btn btn-primary btn-sm btn-icon waves-effect waves-themed" title="查看明细" ><i class="fal fa-edit"></i> </button>\
<button onclick="deleteRow('${row.Id}',${index})" class="btn btn-primary btn-sm btn-icon waves-effect waves-themed" title="删除记录" ><i class="fal fa-times"></i> </button>\
</div>`;
} else {
return `<button class="btn btn-primary btn-sm btn-icon waves-effect waves-themed" disabled title="查看明细" ><i class="fal fa-edit"></i> </button>`;
}
}
}
]],
columns: [[ { /*名称*/
field: 'Name',
title: '<span class="required">@Html.DisplayNameFor(model => model.Name)</span>',
width: ,
hidden: false,
editor: {
type: 'textbox',
options: { prompt: '@Html.DescriptionFor(model => model.Name)', required: true, validType: 'length[0,50]' }
},
sortable: true,
resizable: true
},
{ /*组织代码*/
field: 'Code',
title: '<span class="required">@Html.DisplayNameFor(model => model.Code)</span>',
width: ,
hidden: false,
editor: {
type: 'textbox',
options: { prompt: '@Html.DescriptionFor(model => model.Code)', required: true, validType: 'length[0,12]' }
},
sortable: true,
resizable: true
},
{ /*地址*/
field: 'Address',
title: '@Html.DisplayNameFor(model => model.Address)',
width: ,
hidden: false,
editor: {
type: 'textbox',
options: { prompt: '@Html.DescriptionFor(model => model.Address)', required: false, validType: 'length[0,50]' }
},
sortable: true,
resizable: true
},
{ /*联系人*/
field: 'Contect',
title: '@Html.DisplayNameFor(model => model.Contect)',
width: ,
hidden: false,
editor: {
type: 'textbox',
options: { prompt: '@Html.DescriptionFor(model => model.Contect)', required: false, validType: 'length[0,12]' }
},
sortable: true,
resizable: true
},
{ /*联系电话*/
field: 'PhoneNumber',
title: '@Html.DisplayNameFor(model => model.PhoneNumber)',
width: ,
hidden: false,
editor: {
type: 'textbox',
options: { prompt: '@Html.DescriptionFor(model => model.PhoneNumber)', required: false, validType: 'length[0,20]' }
},
sortable: true,
resizable: true
},
{ /*注册日期*/
field: 'RegisterDate',
title: '<span class="required">@Html.DisplayNameFor(model => model.RegisterDate)</span>',
width: ,
align: 'right',
hidden: false,
editor: {
type: 'datebox',
options: { prompt: '@Html.DescriptionFor(model => model.RegisterDate)', required: true }
},
formatter: dateformatter,
sortable: true,
resizable: true
},
]]
}).datagrid('columnMoving')
.datagrid('resetColumns')
.datagrid('enableFilter', [
{ /*Id*/
field: 'Id',
type: 'numberbox',
op: ['equal', 'notequal', 'less', 'lessorequal', 'greater', 'greaterorequal']
},
{ /*注册日期*/
field: 'RegisterDate',
type: 'dateRange',
options: {
onChange: value => {
$dg.datagrid('addFilterRule', {
field: 'RegisterDate',
op: 'between',
value: value
}); $dg.datagrid('doFilter');
}
}
},
])
.datagrid('load', '/Companies/GetData');
}
); </script>
<script type="text/javascript">
//判断新增编辑状态
var MODELSTATE = 'Added';
var companyid = null;
function opencompanydetailwindow(data, state) {
MODELSTATE = state;
initcompanydetailview();
companyid = (data.Id || );
$("#companydetailwindow").window("open");
$('#company_form').form('reset');
$('#company_form').form('load', data);
}
//删除当前记录
function deletecompanyitem() {
$.messager.confirm('确认', '你确定要删除该记录?', result => {
if (result) {
const url = `/Companies/Delete/${companyid}`;
$.get(url).done(res => {
if (res.success) {
toastr.success("删除成功");
$("#companydetailwindow").window("close");
reload();
} else {
$.messager.alert("错误", res.err, "error");
}
});
}
});
}
//async 保存数据
async function savecompanyitem() {
const $companyform = $('#company_form');
if ($companyform.form('enableValidation').form('validate')) {
let company = $companyform.serializeJSON();
let url = '/Companies/Edit';
//判断是新增或是修改方法
if (MODELSTATE === 'Added') {
url = '/Companies/Create';
}
var token = $('input[name="__RequestVerificationToken"]', $companyform).val();
//$.messager.progress({ title: '请等待', msg: '正在保存数据...', interval: 200 });
$.ajax({
type: "POST",
url: url,
data: {
__RequestVerificationToken: token,
company: company
},
dataType: 'json',
contentType: 'application/x-www-form-urlencoded; charset=utf-8'
})
.done(response => {
//$.messager.progress('close');
if (response.success) {
hook = false;
$companyform.form('disableValidation');
$dg.datagrid('reload');
$('#companydetailwindow').window("close");
toastr.success("保存成功");
} else {
$.messager.alert("错误", response.err, "error");
}
})
.fail((jqXHR, textStatus, errorThrown) => {
//$.messager.progress('close');
$.messager.alert('异常', `${jqXHR.status}: ${jqXHR.statusText} `, 'error');
});
}
}
//关闭窗口
function closecompanydetailwindow() {
$('#companydetailwindow').window('close');
} //判断是否有没有保存的记录
function companyhasmodified() {
return hook;
} function initcompanydetailview() {
//判断是否显示功能按钮
if (MODELSTATE === 'Added') {
$('#deleteitem-btn-group').hide();
} else {
$('#deleteitem-btn-group').show();
} //回车光标移动到下个输入控件
//日期类型 注册日期
$('#RegisterDate').datebox('textbox').bind('keydown', function (e) {
if (e.keyCode == ) {
$(e.target).emulateTab();
}
});
}
</script>
}

上面View层的代码非常的复杂,但都是固定格式,可以用scaffold快速生成

  • 配置依赖注入(DI),注册服务

打开 startup.cs 在 public void ConfigureServices(IServiceCollection services) 注册服务 services.AddScoped<IRepositoryX, RepositoryX>(); 
services.AddScoped<ICustomerService, CustomerService>();

  • 更新数据库

EF Core Code-First 同步更新数据库 
在 Visual Studio.Net 
Package Manager Controle 运行 
PM>:add-migration create_Company 
PM>:update-database 
PM>:更新完成

  • Debug 运行项目 

高级应用

CAP 分布式事务的解决方案及应用场景 
nuget 安装组件 
PM> Install-Package DotNetCore.CAP 
PM> Install-Package DotNetCore.CAP.RabbitMQ 
PM> Install-Package DotNetCore.CAP.SqlServer \

  • 配置Startup.cs

 public void ConfigureServices(IServiceCollection services)
{
services.AddCap(x =>
{
x.UseEntityFramework<SmartDbContext>();
x.UseRabbitMQ("127.0.0.1");
x.UseDashboard();
x.FailedRetryCount = ;
x.FailedThresholdCallback = failed =>
{
var logger = failed.ServiceProvider.GetService<ILogger<Startup>>();
logger.LogError($@"A message of type {failed.MessageType} failed after executing {x.FailedRetryCount} several times,
requiring manual troubleshooting. Message name: {failed.Message.GetName()}");
};
});
}
  • 发布消息
  • 订阅消息

roadmap

  • 完善主要的开发文档
  • 支持My SQL数据库
  • 还会继续重构和完善代码
  • 开发Scaffold MVC模板,生成定制化的Controller 和 View 减少开发人员重复工作
  • 完善授权访问策略(policy-based authorization)
  • 开发Visual Sutdio.net代码生成插件(类似国内做比较好的52abp)

我的联系方式,qq群,赞助二维码

基于领域驱动设计(DDD)超轻量级快速开发架构的更多相关文章

  1. 基于领域驱动设计(DDD)超轻量级快速开发架构(二)动态linq查询的实现方式

    -之动态查询,查询逻辑封装复用 基于领域驱动设计(DDD)超轻量级快速开发架构详细介绍请看 https://www.cnblogs.com/neozhu/p/13174234.html 需求 配合Ea ...

  2. 领域驱动设计(DDD)

    领域驱动设计(DDD)实现之路 2004年,当Eric Evans的那本<领域驱动设计——软件核心复杂性应对之道>(后文简称<领域驱动设计>)出版时,我还在念高中,接触到领域驱 ...

  3. 领域驱动设计(DDD:Domain-Driven Design)

    领域驱动设计(DDD:Domain-Driven Design) Eric Evans的"Domain-Driven Design领域驱动设计"简称DDD,Evans DDD是一套 ...

  4. python 全栈开发,Day116(可迭代对象,type创建动态类,偏函数,面向对象的封装,获取外键数据,组合搜索,领域驱动设计(DDD))

    昨日内容回顾 1. 三个类 ChangeList,封装列表页面需要的所有数据. StarkConfig,生成URL和视图对应关系 + 默认配置 AdminSite,用于保存 数据库类 和 处理该类的对 ...

  5. 关于领域驱动设计 DDD(Domain-Driven Design)

    以下旨在 理解DDD. 1.     什么是领域? 妈妈好是做母婴新零售的产品,应该属于电商平台,那么电商平台就是一个领域. 同一个领域的系统都有相同的核心业务. eg: 电商领域都有:商品浏览.购物 ...

  6. 分享我对领域驱动设计(DDD)的学习成果

    本文内容提要: 1. 领域驱动设计之领域模型 2. 为什么建立一个领域模型是重要的 3. 领域通用语言(Ubiquitous Language) 4.将领域模型转换为代码实现的最佳实践 5. 领域建模 ...

  7. 我对领域驱动设计(DDD)的学习成果

    领域驱动设计之领域模型 2004年Eric Evans发表Domain-Driven Design – Tackling Complexity in the Heart of Software (领域 ...

  8. 领域驱动设计(DDD)实践之路(一)

    本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/gk-Hb84Dt7JqBRVkMqM7Eg  作者:张文博 领域驱动设计(Domain Dr ...

  9. 领域驱动设计(DDD)实现之路

    2004年,当Eric Evans的那本<领域驱动设计——软件核心复杂性应对之道>(后文简称<领域驱动设计>)出版时,我还在念高中,接触到领域驱动设计(DDD)已经是8年后的事 ...

随机推荐

  1. 问答题:你下班后是选择关电脑?Or,只关闭显示器?

    首百问答的答案:jingmentudou 因为你永远不知道什么时间会被叫醒.开个远程就能避免半夜去公司了. 月尧jade 干这一行的,电脑自按下开机键开始,除了驱动会自动启动外,你需要重新检查各大运行 ...

  2. 【Win10】BeyondCompare时提示"许可证密钥已被撤销"的解决办法

    删除...AppData\Roaming\Scooter Software\Beyond Compare 3目录下所有文件. 应该是对应了bcompare的配置文件以及记录文件.删除了之后,就等于新安 ...

  3. Java实现 LeetCode 715 Range 模块(选范围)

    715. Range 模块 Range 模块是跟踪数字范围的模块.你的任务是以一种有效的方式设计和实现以下接口. addRange(int left, int right) 添加半开区间 [left, ...

  4. IDEA突然无法运行

    可能是你类的main方法被idea的智能提示改了 PS: 小编经常用智能提示,它给我把main方法的static关键字删掉了好几次,当时怎么也没想到是把main方法改了 ~难受

  5. Java实现最小费用最大流问题

    1 问题描述 在最大流有多组解时,给每条边在附上一个单位费用的量,问在满足最大流时的最小费用是多少? 2 解决方案 下面代码所使用的测试数据如下图: package com.liuzhen.pract ...

  6. java实现第四届蓝桥杯快速排序

    快速排序 题目描述 快速排序算法是典型的分治思想的运用.它使用某个key把全部元素分成两组,其中一组的元素不大于另一组.然后对这两组再次进行递归排序. 以下代码实现了快速排序.请仔细阅读代码,填写缺少 ...

  7. java实现第四届蓝桥杯猜年龄

    猜年龄 题目描述 美国数学家维纳(N.Wiener)智力早熟,11岁就上了大学.他曾在1935~1936年应邀来中国清华大学讲学. 一次,他参加某个重要会议,年轻的脸孔引人注目.于是有人询问他的年龄, ...

  8. IOS App如何调用python后端服务

    本篇文章旨在通过一个小的Demo形式来了解ios app是如何调用python后端服务的,以便我们在今后的工作中可以清晰的明白ios app与后端服务之间是如何实现交互的,今天的示例是拿登录功能做一个 ...

  9. TCP/IP三次握手协议

    一.简介         三次握手协议指的是在发送数据的准备阶段,服务器端和客户端之间需要进行三次交互,OSI参考模型中的网络层,在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一 ...

  10. GitHub 热点速览 Vol.23:前后端最佳实践

    作者:HelloGitHub-小鱼干 摘要:最佳实践,又名 best-practices,是 GitHub 常见的项目名,也是本周 Trending 关键词.25 年 Python 开发经验的 Dav ...