Tidebuy.Platform 开发指引[内部使用]
本文档仅为开发内部开发使用,不做对外宣传。
1、工程结构
本系统以ABP为依托,根据公司实际情况,进行部分调整,最终结构如图所示:
工程命名前缀为:Tidebuy.Platform.**,各项目解释如下:
- Tidebuy.Platform.Test:测试工程
- Tidebuy.Platform.Application:应用层,常规业务逻辑层
- Tidebuy.Platform.EntityFramework:EF框架层,包括数据库存储,仓储实现,系统缓存等部分。
- Tidebuy.Platform.Model:Model层,包括Model和ViewModel(DTO)
- Tidebuy.Platform.Utility:基础设施层,常规的各种帮助方法的实现
- Tidebuy.Platform.Web:Web应用层,MVC中的Controller和View
- Tidebuy.Platform.WebApi:WebAPI应用层,能自动扫描注册接口,对外发布。
2、开发指引
本系统采用ABP基础框架,自动集成了Repository实现,所以对于部分基础功能开发,只需要创建Model和实现Application即可。
2.1、创建Model
在Model层创建所需Model,为了便于区分业务逻辑,人为给Model层分了多个物理文件夹,如图:
按照实际需要,创建了Amazon[亚马逊]
,Ebay[易贝]
,Common[通用]
,Systems[系统]
等文件夹,若后续需要,可继续扩充。
此实例以PlatformInfo**[站点信息]**为例,做开发说明:
在Systems中创建PlatformInfos的文件夹,为避免命名空间的冲突,将文件夹以复数形式命名。创建PlatformInfo的代码如下:
using Abp.Domain.Entities;
namespace Tidebuy.Platform.Common.PlatformInfos
{
/// <summary>
/// 平台信息
/// </summary>
public class PlatformInfo : Entity<int>
{
/// <summary>
/// 名称
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// 英文名称
/// </summary>
public virtual string EnName { get; set; }
/// <summary>
/// 备注信息
/// </summary>
public virtual string Remark { get; set; }
}
}
在上述代码中,PlatformInfo需要继承Entity的实例类,若需声明Id类型,可以进行标注,如本例中的Entity<int>
,Entity<long>
等。如此声明,该实体可自动继承Id熟悉。
若需其他集成类,ABP提供了如:CreationAuditedEntity,AuditedEntity,FullAuditedEntity等,具体可参考ABP的说明文档。
在DbContext中添加Model的声明,如下:
public class TBPFDbContext : AbpDbContext
{
/// <summary>
/// 站点信息
/// </summary>
public virtual IDbSet<Systems.PlatformInfos.PlatformInfo> PlatformInfo { get; set; }
//Other Code ...
}
此步骤很重要,否则Model将无法注册
2.2、创建DTO
在数据传输中,我们需要定义DTO(Data Transfer Objects)类,展现层传入数据传输对象(DTO)调用一个应用服务方法,接着应用服务通过领域对象执行一些特定的业务逻辑并且返回DTO给展现层。这样展现层和领域层被完全分离开了。在具有良好分层的应用程序中,展现层不会直接使用领域对象(仓库,实体)。
为了统一管理,我们直接在Model层中对应的文件夹内创建DTO,如在PlatformInfos中创建DTO,如图:
DTO的分为数据展示,数据列表,数据查询等,分为为XXXDto,XXXListDto,XXXQueryDto,在此实例中,分别如下:
PlatformInfoDto*数据展示,编辑,详情等使用*
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
namespace Tidebuy.Platform.Systems.PlatformInfos
{
/// <summary>
/// 站点信息显示Dto
/// </summary>
[AutoMap(typeof(PlatformInfo))]
public class PlatformInfoDto : EntityDto
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 英文名称
/// </summary>
public string EnName { get; set; }
/// <summary>
/// 备注信息
/// </summary>
public string Remark { get; set; }
}
}
该Dto继承自EntityDto,可自动继承Id熟悉,并通过AutoMapper,可自动将PlatformInfo转换为PlatformInfoDto,需要添加[AutoMap(typeof(PlatformInfo))]
属性。
PlatformInfoListDto*列表展示DTO,用于列表展示,可只包括Model的部分字段*
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
namespace Tidebuy.Platform.Systems.PlatformInfos
{
[AutoMapFrom(typeof(PlatformInfo))]
public class PlatformInfoListDto : EntityDto
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 英文名称
/// </summary>
public string EnName { get; set; }
/// <summary>
/// 备注信息
/// </summary>
public string Remark { get; set; }
}
}
其中的[AutoMapFrom(typeof(PlatformInfo))]
属性,可自动将PlatformInfo自动转换为PlatformInfoListDto。
PlatformInfoQueryDto 查询使用,包括一些通用查询熟悉,如分页信息等
namespace Tidebuy.Platform.Systems.PlatformInfos
{
/// <summary>
/// 系统菜单查询
/// </summary>
public class PlatformInfoQueryDto
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 英文名称
/// </summary>
public string EnName { get; set; }
}
}
此示例中没有集成查询信息。
2.3、实现仓储
若对象所需方法比较复杂,如需要通过Sql语句,存储过程等与数据库交互,或者有其他较为复杂的方法,则可通过添加仓储层进行实现,如下:
在该项目中,我们将仓储层存放在Tidebuy.Platform.EntityFrame层中,以便直接与数据库进行交互。
按照业务逻辑,我们也会添加部分物理文件夹,以便做区分,结构如下:
我们以SysUser为例,说明仓储层的实现,首先定义ISysUserRepository接口,以便定义方法,代码如下:
using Abp.Domain.Repositories;
using Tidebuy.Platform.Systems.SysUsers;
namespace Tidebuy.Platform.Repositories.Systems.SysUsers
{
/// <summary>
/// 系统用户
/// </summary>
public interface ISysUserRepository : IRepository<SysUser, int>
{
/// <summary>
/// 获取系统用户
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
SysUserDto GetUser(SysUserQueryDto dto);
}
}
为了能够使用系统自带的仓储方法,我们将ISysUserRepository继承自IRepository
using Abp.Dependency;
using Abp.EntityFramework;
using Abp.Extensions;
using Abp.Linq.Extensions;
using System.Linq;
using Tidebuy.Platform.Systems.SysUsers;
namespace Tidebuy.Platform.Repositories.Systems.SysUsers
{
public class SysUserRepository : TBPFRepositoryBase<SysUser, int>, ISysUserRepository, ITransientDependency
{
public SysUserRepository(IDbContextProvider<TBPFDbContext> dbContextProvider) : base(dbContextProvider)
{
}
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public SysUserDto GetUser(SysUserQueryDto dto)
{
using (var db = new TBPFDbContext())
{
var userdto = db.SysUser
.WhereIf(dto.UserId.HasValue, a => a.UserId == dto.UserId.Value)
.WhereIf(!dto.UserName.IsNullOrWhiteSpace(), a => a.UserName == dto.UserName)
.WhereIf(!dto.Email.IsNullOrWhiteSpace(), a => a.Email == dto.Email)
.Join(db.SysUserExt, user => user.UserId, ext => ext.UserId, (user, ext) => new SysUserDto()
{
User = user,
SysUserExt = ext
}).FirstOrDefault();
return userdto;
}
}
}
}
在这里,SysUserRepository继承了较多的接口和父类,注意不要漏写。
具体的方法实现,在这里可直接操作数据库,实现存储过程,Sql语句等。
2.3、实现Application
因PlatformInfo比较简单,无需单独去实现仓储功能,因此可直接实现Application。
Application层的主要功能是实现业务逻辑,分为服务接口和服务实现两类。
在Tidebuy.Platform.Application项目中,同Model一样,按照业务逻辑,添加了物理文件夹,如图:
定义IPlatformInfoAppService接口
在Application中,需要定义服务接口,如IPlatformInfoAppService,代码如下:
using Abp.Application.Services;
using System.Collections.Generic;
namespace Tidebuy.Platform.Systems.PlatformInfos
{
/// <summary>
/// 平台站点AppSvc
/// </summary>
public interface IPlatformInfoAppService : IApplicationService
{
/// <summary>
/// 获取列表
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
List<PlatformInfoListDto> GetList(PlatformInfoQueryDto dto);
/// <summary>
/// 获取编辑对象
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
PlatformInfoDto GetForEdit(int? id);
/// <summary>
/// 创建或更新
/// </summary>
/// <param name="dto"></param>
/// <returns>数据库新增ID</returns>
int CreateOrUpdate(PlatformInfoDto dto);
/// <summary>
/// 删除
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
bool Delete(int? id);
}
}
在该接口中,我们定义了基础的CRUD功能,若需其他接口,请自行添加。IPlatformInfoAppService继承了IApplicationService,系统自动实现了一些基础功能。
实现PlatformInfoAppService
PlatformInfoAppService继承IPlatformInfoAppService,TBPFAppServiceBase,其中TBPFAppServiceBase是系统自由的基础服务类,可在其中定义一些通用的方法。
TBPF是TidebuyPlatform的缩写,便于整体使用
- 构造函数
PlatformInfoAppService
public class PlatformInfoAppService : TBPFAppServiceBase, IPlatformInfoAppService
{
private readonly IRepository<PlatformInfo> _platformInfoRepository;
/// <summary>
/// 构造函数
/// 将Repository注入
/// </summary>
/// <param name="platformInfoRepository"></param>
public PlatformInfoAppService(IRepository<PlatformInfo> platformInfoRepository)
{
_platformInfoRepository = platformInfoRepository;
}
}
//Other Code...
在没有单独定义和实现IRepository的情况下,可以通过系统自带的IRepository<TEntity>
进行实现,在本例中,使用了IRepository<PlatformInfo>
,然后通过构造函数进行注入,即可在上下文中使用。
- CreateOrUpdate
/// <summary>
/// 创建或更新
///
/// 根据ID的存在情况,自动创建或者更新实体
/// </summary>
/// <param name="dto">
/// DTO信息,通过Controller或者JS调用进行传递
/// 转换为Model对象后,进行保存
/// </param>
/// <returns>新增或者更新后的ID</returns>
public int CreateOrUpdate(PlatformInfoDto dto)
{
try
{
//将DTO对象转换为Model对象
var model = dto.MapTo<PlatformInfo>();
//调用仓储的InsertOrUpdateAndGetId方法,保存后返回ID
return _platformInfoRepository.InsertOrUpdateAndGetId(model);
}
catch (Exception exp)
{
Logger.Error("PlatformInf新增出错", exp);
return -1;
}
}
- Delete
/// <summary>
/// 删除
/// </summary>
/// <param name="id">被删除ID</param>
/// <returns>是否产出成功</returns>
public bool Delete(int? id)
{
try
{
//调用删除方法,正常返回true
_platformInfoRepository.Delete(id.Value);
return true;
}
catch (Exception exp)
{
//删除出错,记录日志,返回false
Logger.Error("PlatformInfo删除出错", exp);
return false;
}
}
- GetForEdit
/// <summary>
/// 获取编辑对象
/// </summary>
/// <param name="id">ID</param>
/// <returns>编辑DTO</returns>
public PlatformInfoDto GetForEdit(int? id)
{
try
{
//获取对象
var dto = _platformInfoRepository.Get(id.Value);
//转换为DTO
return dto.MapTo<PlatformInfoDto>();
}
catch (Exception exp)
{
Logger.Error("获取编辑对象出错", exp);
return null;
}
}
- GetList
/// <summary>
/// 获取对象列表
/// </summary>
/// <param name="dto">查询DTO</param>
/// <returns>ListDTO列表</returns>
public List<PlatformInfoListDto> GetList(PlatformInfoQueryDto dto)
{
var lst = _platformInfoRepository.GetAll()
//过滤Name
.WhereIf(dto.Name.IsNullOrWhiteSpace(), a => a.Name.Contains(dto.Name))
//过滤EnName
.WhereIf(dto.EnName.IsNullOrWhiteSpace(), a => a.EnName.Contains(dto.EnName))
.ToList();
return lst.MapTo<List<PlatformInfoListDto>>();
}
2.4、添加Controller
在准备好Application接口及方法之后,即可添加对应的Controller信息,同理,Tidebuy.Platform.Web层也按照逻辑关系,划分了不同的Areas,如图所示:
按照之前的约定,我们将平台信息放入Systems区域中,添加Controller,并做适当修改,如下:
/// <summary>
/// 平台信息
/// </summary>
public class PlatformInfoController : TBPFControllerBase
{
private IPlatformInfoAppService _platformInfoAppService;
public PlatformInfoController(IPlatformInfoAppService platformInfoAppService)
{
_platformInfoAppService = platformInfoAppService;
}
//Other Code...
}
在此将IPlatformInfoAppService在构造函数里进行注入,以便上下文可以正常使用。注意Controller需要继承TBPFControllerBase。
其他对应方法如下:
// GET: Systems/PlatformInfo
public ActionResult Index()
{
return View();
}
/// <summary>
/// 获取平台数据
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public JsonResult GetPlatformInfo(PlatformInfoQueryDto dto)
{
var stores = _platformInfoAppService.GetList(dto);
return new JsonResult() { Data = stores, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
/// <summary>
/// 创建或新增
/// 此方法可进行批量创建
/// </summary>
/// <param name="dtos">DTO对象</param>
/// <returns>处理结果的JSON信息</returns>
[HttpPost]
public ActionResult CreateOrUpdate(List<PlatformInfoDto> dtos)
{
try
{
_platformInfoAppService.BatchCreateOrUpdate(dtos);
return Json(new { Success = true, Content = "平台更新成功" }, JsonRequestBehavior.AllowGet);
}
catch (Exception exp)
{
Logger.Error("平台批量更新失败", exp);
return Json(new { Success = false, Content = "平台批量更新失败" }, JsonRequestBehavior.AllowGet);
}
}
/// <summary>
/// 创建站点信息
/// </summary>
/// <param name="dto">DTO对象</param>
/// <returns>处理结果的JSON信息</returns>
[HttpPost]
public ActionResult Create(PlatformInfoDto dto)
{
try
{
if (!dto.Name.IsNullOrWhiteSpace())
{
var result = _platformInfoAppService.CreateOrUpdate(dto);
return Json(new { Success = result > 0, Content = result > 0 ? result.ToString() : "站点插入失败" }, JsonRequestBehavior.AllowGet);
}
else
{
return Json(new { Success = false, Content = "站点名称不能为空" }, JsonRequestBehavior.AllowGet);
}
}
catch (Exception exp)
{
Logger.Error("站点插入失败", exp);
return Json(new { Success = false, Content = "站点插入失败" }, JsonRequestBehavior.AllowGet);
}
}
/// <summary>
/// 删除站点
/// </summary>
/// <param name="id">ID</param>
/// <returns>处理结果的JSON信息</returns>
[HttpPost]
public ActionResult Delete(int id)
{
try
{
var result = _platformInfoAppService.Delete(id);
return Json(new { Success = result, Content = result ? "站点删除成功" : "站点删除失败" });
}
catch (Exception exp)
{
Logger.Error("站点删除失败", exp);
return Json(new { Success = false, Content = "站点删除失败" });
}
}
2.6、添加View展示层
在本项目中,使用EasyUI作为数据展示的控件,具体使用方法,请参考官网。
在对应的Views中,添加相应的Action方法,如Index,Create,Details等,具体使用同正常MVC开发一样,在此不再详述。
2.7、页面展示
最终页面效果如下: