diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN.Abp.ProjectManagement.Application.Contracts.csproj b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN.Abp.ProjectManagement.Application.Contracts.csproj
new file mode 100644
index 000000000..67b20c248
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN.Abp.ProjectManagement.Application.Contracts.csproj
@@ -0,0 +1,24 @@
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN/Abp/ProjectManagement/AbpProjectManagementApplicationContractsModule.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN/Abp/ProjectManagement/AbpProjectManagementApplicationContractsModule.cs
new file mode 100644
index 000000000..8262849f0
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN/Abp/ProjectManagement/AbpProjectManagementApplicationContractsModule.cs
@@ -0,0 +1,29 @@
+using LINGYUN.Abp.ProjectManagement.Localization;
+using Volo.Abp.Application;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.ProjectManagement
+{
+ [DependsOn(
+ typeof(AbpDddApplicationContractsModule),
+ typeof(AbpProjectManagementDomainSharedModule))]
+ public class AbpProjectManagementApplicationContractsModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddVirtualJson("/LINGYUN/Abp/ProjectManagement/Localization/ApplicationContracts");
+ });
+ }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN/Abp/ProjectManagement/Localization/ApplicationContracts/en.json b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN/Abp/ProjectManagement/Localization/ApplicationContracts/en.json
new file mode 100644
index 000000000..189662ee7
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN/Abp/ProjectManagement/Localization/ApplicationContracts/en.json
@@ -0,0 +1,5 @@
+{
+ "culture": "en",
+ "texts": {
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN/Abp/ProjectManagement/Localization/ApplicationContracts/zh-Hans.json b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN/Abp/ProjectManagement/Localization/ApplicationContracts/zh-Hans.json
new file mode 100644
index 000000000..2d29a8125
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application.Contracts/LINGYUN/Abp/ProjectManagement/Localization/ApplicationContracts/zh-Hans.json
@@ -0,0 +1,5 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application/LINGYUN.Abp.ProjectManagement.Application.csproj b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application/LINGYUN.Abp.ProjectManagement.Application.csproj
new file mode 100644
index 000000000..5f6e3d728
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application/LINGYUN.Abp.ProjectManagement.Application.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application/LINGYUN/Abp/ProjectManagement/AbpProjectManagementApplicationModule.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application/LINGYUN/Abp/ProjectManagement/AbpProjectManagementApplicationModule.cs
new file mode 100644
index 000000000..0ed86ec96
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Application/LINGYUN/Abp/ProjectManagement/AbpProjectManagementApplicationModule.cs
@@ -0,0 +1,12 @@
+using Volo.Abp.Application;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.ProjectManagement
+{
+ [DependsOn(
+ typeof(AbpDddApplicationModule),
+ typeof(AbpProjectManagementApplicationContractsModule))]
+ public class AbpProjectManagementApplicationModule : AbpModule
+ {
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN.Abp.ProjectManagement.Domain.Shared.csproj b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN.Abp.ProjectManagement.Domain.Shared.csproj
new file mode 100644
index 000000000..bec3a4099
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN.Abp.ProjectManagement.Domain.Shared.csproj
@@ -0,0 +1,19 @@
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/AbpProjectManagementDomainSharedModule.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/AbpProjectManagementDomainSharedModule.cs
new file mode 100644
index 000000000..e7da5f6ed
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/AbpProjectManagementDomainSharedModule.cs
@@ -0,0 +1,30 @@
+using LINGYUN.Abp.ProjectManagement.Localization;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.Validation;
+using Volo.Abp.Validation.Localization;
+using Volo.Abp.VirtualFileSystem;
+
+namespace LINGYUN.Abp.ProjectManagement
+{
+ [DependsOn(
+ typeof(AbpValidationModule))]
+ public class AbpProjectManagementDomainSharedModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Add()
+ .AddBaseTypes(typeof(AbpValidationResource))
+ .AddVirtualJson("/LINGYUN/Abp/ProjectManagement/Localization/Domain");
+ });
+ }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Localization/AbpProjectManagementResource.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Localization/AbpProjectManagementResource.cs
new file mode 100644
index 000000000..c021a3d08
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Localization/AbpProjectManagementResource.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Localization;
+
+namespace LINGYUN.Abp.ProjectManagement.Localization
+{
+ [LocalizationResourceName("AbpProjectManagement")]
+ public class AbpProjectManagementResource
+ {
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Localization/Domain/en.json b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Localization/Domain/en.json
new file mode 100644
index 000000000..189662ee7
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Localization/Domain/en.json
@@ -0,0 +1,5 @@
+{
+ "culture": "en",
+ "texts": {
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Localization/Domain/zh-Hans.json b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Localization/Domain/zh-Hans.json
new file mode 100644
index 000000000..2d29a8125
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Localization/Domain/zh-Hans.json
@@ -0,0 +1,5 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Projects/BuildStatus.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Projects/BuildStatus.cs
new file mode 100644
index 000000000..94087b8b8
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Projects/BuildStatus.cs
@@ -0,0 +1,9 @@
+namespace LINGYUN.Abp.ProjectManagement.Projects
+{
+ public enum BuildStatus
+ {
+ Created = 0,
+ Successed = 1,
+ Failed = 10
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Projects/RepositoryType.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Projects/RepositoryType.cs
new file mode 100644
index 000000000..d16fa0102
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Projects/RepositoryType.cs
@@ -0,0 +1,8 @@
+namespace LINGYUN.Abp.ProjectManagement.Projects
+{
+ public enum RepositoryType
+ {
+ Git = 0,
+ Subversion = 1
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Templates/OptionsType.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Templates/OptionsType.cs
new file mode 100644
index 000000000..42d2c64a5
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain.Shared/LINGYUN/Abp/ProjectManagement/Templates/OptionsType.cs
@@ -0,0 +1,9 @@
+namespace LINGYUN.Abp.ProjectManagement.Templates
+{
+ public enum OptionsType
+ {
+ Empty = 0,
+ Input = 1,
+ Options = 2
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN.Abp.ProjectManagement.Domain.csproj b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN.Abp.ProjectManagement.Domain.csproj
new file mode 100644
index 000000000..f9b3dde0b
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN.Abp.ProjectManagement.Domain.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/AbpProjectManagementDomainModule.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/AbpProjectManagementDomainModule.cs
new file mode 100644
index 000000000..d7ff622a6
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/AbpProjectManagementDomainModule.cs
@@ -0,0 +1,14 @@
+using Volo.Abp.Cli;
+using Volo.Abp.Domain;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.ProjectManagement
+{
+ [DependsOn(
+ typeof(AbpDddDomainModule),
+ typeof(AbpCliCoreModule),
+ typeof(AbpProjectManagementDomainSharedModule))]
+ public class AbpProjectManagementDomainModule : AbpModule
+ {
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Packages/Package.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Packages/Package.cs
new file mode 100644
index 000000000..86594a76b
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Packages/Package.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace LINGYUN.Abp.ProjectManagement.Packages
+{
+ public class Package
+ {
+
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/IProjectLogRepository.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/IProjectLogRepository.cs
new file mode 100644
index 000000000..2ca32bf63
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/IProjectLogRepository.cs
@@ -0,0 +1,16 @@
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Repositories;
+
+namespace LINGYUN.Abp.ProjectManagement.Projects
+{
+ public interface IProjectLogRepository : IRepository
+ {
+ Task> GetListAsync(
+ Guid projectId,
+ LogLevel? level = null);
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/IProjectRepository.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/IProjectRepository.cs
new file mode 100644
index 000000000..92dc2a179
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/IProjectRepository.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Repositories;
+
+namespace LINGYUN.Abp.ProjectManagement.Projects
+{
+ public interface IProjectRepository : IRepository
+ {
+ Task CheckNameAsync(
+ string name,
+ CancellationToken cancellationToken = default);
+ Task FindByNameAsync(
+ string name,
+ CancellationToken cancellationToken = default);
+
+ Task FindTemplateAsync(
+ Guid id,
+ CancellationToken cancellationToken = default);
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/IProjectRepositoryExtensions.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/IProjectRepositoryExtensions.cs
new file mode 100644
index 000000000..0f6312451
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/IProjectRepositoryExtensions.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Entities;
+
+namespace LINGYUN.Abp.ProjectManagement.Projects
+{
+ public static class IProjectRepositoryExtensions
+ {
+ public static async Task GetTemplateAsync(
+ this IProjectRepository repository,
+ Guid id,
+ CancellationToken cancellationToken = default)
+ {
+ var template = await repository.FindTemplateAsync(id, cancellationToken);
+
+ return template ??
+ throw new EntityNotFoundException(typeof(ProjectTemplate), id);
+ }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProductBuildLog.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProductBuildLog.cs
new file mode 100644
index 000000000..3d8d760ca
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProductBuildLog.cs
@@ -0,0 +1,23 @@
+using Microsoft.Extensions.Logging;
+using System;
+using Volo.Abp.Domain.Entities;
+
+namespace LINGYUN.Abp.ProjectManagement.Projects
+{
+ public class ProductBuildLog : Entity
+ {
+ public virtual Guid ProjectId { get; set; }
+ public virtual string Message { get; set; }
+ public virtual LogLevel Level { get; set; }
+ protected ProductBuildLog() { }
+ public ProductBuildLog(
+ Guid projectId,
+ string message,
+ LogLevel level = LogLevel.Information)
+ {
+ ProjectId = projectId;
+ Message = message;
+ Level = level;
+ }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/Project.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/Project.cs
new file mode 100644
index 000000000..fe1163305
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/Project.cs
@@ -0,0 +1,68 @@
+using System;
+using Volo.Abp.Domain.Entities.Auditing;
+using Volo.Abp.Timing;
+
+namespace LINGYUN.Abp.ProjectManagement.Projects
+{
+ public class Project : AuditedAggregateRoot
+ {
+ public virtual string Name { get; protected set; }
+ public virtual string Version { get; protected set; }
+ public virtual BuildStatus Status { get; protected set; }
+ public virtual DateTime? BuildTime { get; protected set; }
+ public virtual string BuildError { get; protected set; }
+ public virtual string PackageIconUrl { get; protected set; }
+ public virtual string PackageProjectUrl { get; protected set; }
+ public virtual string PackageLicenseExpression { get; protected set; }
+ public virtual RepositoryType? RepositoryType { get; protected set; }
+ public virtual string RepositoryUrl { get; protected set; }
+ public virtual string Template { get; protected set; }
+ protected Project() { }
+ public Project(
+ Guid id,
+ string template,
+ string name,
+ string version = "1.0.0.0",
+ string packageIconUrl = "",
+ string packageProjectUrl = "",
+ string packageLicenseExpression = "MIT",
+ RepositoryType? repositoryType = null,
+ string repositoryUrl = "")
+ : base(id)
+ {
+ Name = name;
+ Template = template;
+ Version = version;
+ PackageIconUrl = packageIconUrl;
+ PackageProjectUrl = packageProjectUrl;
+ PackageLicenseExpression = packageLicenseExpression;
+ RepositoryType = repositoryType;
+ RepositoryUrl = repositoryUrl;
+
+ Status = BuildStatus.Created;
+ }
+
+ public void BuildSuccess(IClock clock)
+ {
+ BuildError = "";
+ BuildTime = clock.Now;
+ Status = BuildStatus.Successed;
+ }
+
+ public void BuildFailed(IClock clock, Exception ex)
+ {
+ BuildTime = clock.Now;
+ Status = BuildStatus.Failed;
+ BuildError = GetBaseExceptionError(ex);
+ }
+
+ private static string GetBaseExceptionError(Exception ex)
+ {
+ if (ex == null)
+ {
+ return "";
+ }
+ return string.Concat(ex?.Message ?? "", GetBaseExceptionError(ex?.InnerException));
+ }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectItem.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectItem.cs
new file mode 100644
index 000000000..6746e56c1
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectItem.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Volo.Abp.Domain.Entities;
+
+namespace LINGYUN.Abp.ProjectManagement.Projects
+{
+ public class ProjectItem : AggregateRoot
+ {
+ public virtual string Path { get; protected set; }
+ public virtual string Name { get; protected set; }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectManager.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectManager.cs
new file mode 100644
index 000000000..7dd0c5a24
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectManager.cs
@@ -0,0 +1,154 @@
+using ICSharpCode.SharpZipLib.Zip;
+using LINGYUN.Abp.ProjectManagement.Templates;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Cli;
+using Volo.Abp.Domain.Services;
+using Volo.Abp.IO;
+using Volo.Abp.Uow;
+
+namespace LINGYUN.Abp.ProjectManagement.Projects
+{
+ public class ProjectManager : DomainService
+ {
+ protected IProjectRepository ProjectRepository { get; }
+ protected ITemplateRepository TemplateRepository { get; }
+
+ protected CliService Service => LazyServiceProvider.LazyGetRequiredService();
+ protected IUnitOfWorkManager UnitOfWorkManager => LazyServiceProvider.LazyGetRequiredService();
+
+ public ProjectManager(
+ IProjectRepository projectRepository,
+ ITemplateRepository templateRepository)
+ {
+ ProjectRepository = projectRepository;
+ TemplateRepository = templateRepository;
+ }
+
+ public virtual async Task CreateAsync(Project project)
+ {
+ if (await ProjectRepository.CheckNameAsync(project.Name))
+ {
+ throw new BusinessException("");
+ }
+ project = await ProjectRepository.InsertAsync(project);
+
+ return project;
+ }
+
+ public virtual async Task UpdateAsync(Project project)
+ {
+ project = await ProjectRepository.UpdateAsync(project);
+
+ return project;
+ }
+
+ public virtual async Task DeleteAsync(Project project)
+ {
+ await ProjectRepository.DeleteAsync(project);
+ }
+
+ public virtual async Task BuildAsync(Project project)
+ {
+ using var unitOfWork = UnitOfWorkManager.Begin();
+ try
+ {
+ var projectTemplate = await ProjectRepository.FindTemplateAsync(project.Id);
+
+ var template = await TemplateRepository.GetAsync(projectTemplate.TemplateId);
+
+ // 组成参数列表
+ var projectBuildArgs = projectTemplate.Options
+ .Select(x => new { x.Key, x.Value });
+ if (projectTemplate.ExtraProperties.Any())
+ {
+ projectBuildArgs = projectBuildArgs.Union(
+ projectTemplate.ExtraProperties
+ .Select(x => new { x.Key, Value = x.Value?.ToString() ?? "" }));
+ }
+ // 检查必须参数
+ var ignoredArgs = template.GetMustOptions().Where(x => !projectBuildArgs.Any(y => x.Key.Equals(y.Key)));
+ if (ignoredArgs.Any())
+ {
+ project.BuildFailed(Clock, new Exception($"构建项目失败,缺失必要的参数: {ignoredArgs.Select(x => x.Key).JoinAsString(";")}"));
+ }
+ else
+ {
+ // 取项目ID作为存储路径
+ var path = Path.Combine(
+ Directory.GetCurrentDirectory(),
+ project.Id.ToString());
+ var argsList = new List
+ {
+ "new", // new命令
+ "--skip-cli-version-check", // 不检查cli版本
+ project.Name,
+ "-o", // 输出目录
+ path
+ };
+
+ foreach (var option in projectBuildArgs)
+ {
+ argsList.Add(option.Key);
+ if (!option.Value.IsNullOrWhiteSpace())
+ {
+ var value = option.Value;
+ if (value.Contains(" "))
+ {
+ if (!value.StartsWith("\""))
+ {
+ value = "\"" + value;
+ }
+ if (!value.EndsWith("\""))
+ {
+ value += "\"";
+ }
+ }
+ argsList.Add(value);
+ }
+ }
+
+ await Service.RunAsync(argsList.ToArray());
+ project.BuildSuccess(Clock);
+ }
+ }
+ catch (Exception ex)
+ {
+ project.BuildFailed(Clock, ex);
+ }
+
+ await ProjectRepository.UpdateAsync(project);
+
+ await unitOfWork.CompleteAsync();
+
+ return project.Status == BuildStatus.Successed;
+ }
+
+ public virtual async Task AddPackagesAsync()
+ {
+
+ }
+
+ public virtual async Task DownloadAsync(Project project)
+ {
+ var projectDir = Path.Combine(
+ Directory.GetCurrentDirectory(),
+ project.Id.ToString());
+ var projectZipFile = Path.Combine(projectDir, project.Name + ".zip");
+ FileHelper.DeleteIfExists(projectZipFile);
+
+ if (!Directory.Exists(projectDir))
+ {
+ throw new BusinessException("");
+ }
+ var zip = new FastZip();
+ zip.CreateZip(projectZipFile, projectDir, true, null);
+
+ return await FileHelper.ReadAllBytesAsync(projectZipFile);
+ }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectOptions.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectOptions.cs
new file mode 100644
index 000000000..e380eeea8
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectOptions.cs
@@ -0,0 +1,22 @@
+using System;
+using Volo.Abp.Domain.Entities;
+
+namespace LINGYUN.Abp.ProjectManagement.Projects
+{
+ public class ProjectOptions : Entity
+ {
+ public virtual Guid ProjectId { get; protected set; }
+ public virtual string Key { get; protected set; }
+ public virtual string Value { get; protected set; }
+ protected ProjectOptions() { }
+ public ProjectOptions(
+ Guid projectId,
+ string key,
+ string value = "")
+ {
+ ProjectId = projectId;
+ Key = key;
+ Value = value;
+ }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectTemplate.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectTemplate.cs
new file mode 100644
index 000000000..cfe51bfee
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Projects/ProjectTemplate.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using Volo.Abp.Domain.Entities;
+
+namespace LINGYUN.Abp.ProjectManagement.Projects
+{
+ public class ProjectTemplate : AggregateRoot
+ {
+ public virtual Guid ProjectId { get; protected set; }
+ public virtual Guid TemplateId { get; protected set; }
+ public virtual ICollection Options { get; protected set; }
+ protected ProjectTemplate() { }
+ public ProjectTemplate(
+ Guid id,
+ Guid projectId,
+ Guid templateId)
+ : base(id)
+ {
+ ProjectId = projectId;
+ TemplateId = templateId;
+
+ Options = new Collection();
+ }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/ITemplateRepository.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/ITemplateRepository.cs
new file mode 100644
index 000000000..acd0ed498
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/ITemplateRepository.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Repositories;
+
+namespace LINGYUN.Abp.ProjectManagement.Templates
+{
+ public interface ITemplateRepository : IRepository
+ {
+ Task FindByNameAsync(
+ string name,
+ CancellationToken cancellationToken = default);
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/ITemplateRepositoryExtensions.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/ITemplateRepositoryExtensions.cs
new file mode 100644
index 000000000..f30c6d83b
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/ITemplateRepositoryExtensions.cs
@@ -0,0 +1,20 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Entities;
+
+namespace LINGYUN.Abp.ProjectManagement.Templates
+{
+ public static class ITemplateRepositoryExtensions
+ {
+ public static async Task GetByNameAsync(
+ this ITemplateRepository repository,
+ string name,
+ CancellationToken cancellationToken = default)
+ {
+ var template = await repository.FindByNameAsync(name, cancellationToken);
+
+ return template ??
+ throw new EntityNotFoundException(typeof(Template), name);
+ }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/Template.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/Template.cs
new file mode 100644
index 000000000..37237b84b
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/Template.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using Volo.Abp.Domain.Entities;
+
+namespace LINGYUN.Abp.ProjectManagement.Templates
+{
+ public class Template : AggregateRoot
+ {
+ public virtual string Name { get; protected set; }
+ public virtual ICollection Options { get; protected set; }
+ protected Template() { }
+ public Template(
+ Guid id,
+ string name)
+ : base(id)
+ {
+ Name = name;
+ Options = new Collection();
+ }
+
+ public IEnumerable GetMustOptions()
+ {
+ return Options.Where(x => !x.Optional);
+ }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/TemplateOptions.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/TemplateOptions.cs
new file mode 100644
index 000000000..ec15fff44
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.Domain/LINGYUN/Abp/ProjectManagement/Templates/TemplateOptions.cs
@@ -0,0 +1,30 @@
+using System;
+using Volo.Abp.Domain.Entities;
+
+namespace LINGYUN.Abp.ProjectManagement.Templates
+{
+ public class TemplateOptions : AggregateRoot
+ {
+ public virtual bool Optional { get; protected set; }
+ public virtual string Key { get; protected set; }
+ public virtual string FullKey { get; protected set; }
+ public virtual OptionsType Type { get; protected set; }
+ public virtual string Description { get; protected set; }
+ protected TemplateOptions() { }
+ public TemplateOptions(
+ Guid id,
+ string key,
+ string fullKey,
+ bool optional = true,
+ OptionsType type = OptionsType.Empty,
+ string description = "")
+ : base(id)
+ {
+ Key = key;
+ FullKey = fullKey;
+ Optional = optional;
+ Type = type;
+ Description = description;
+ }
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.EntityFrameworkCore/LINGYUN.Abp.ProjectManagement.EntityFrameworkCore.csproj b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.EntityFrameworkCore/LINGYUN.Abp.ProjectManagement.EntityFrameworkCore.csproj
new file mode 100644
index 000000000..16a7ab54b
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.EntityFrameworkCore/LINGYUN.Abp.ProjectManagement.EntityFrameworkCore.csproj
@@ -0,0 +1,14 @@
+
+
+
+
+
+ netstandard2.1
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.EntityFrameworkCore/LINGYUN/Abp/ProjectManagement/AbpProjectManagementEntityFrameworkCoreModule.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.EntityFrameworkCore/LINGYUN/Abp/ProjectManagement/AbpProjectManagementEntityFrameworkCoreModule.cs
new file mode 100644
index 000000000..860178ab2
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.EntityFrameworkCore/LINGYUN/Abp/ProjectManagement/AbpProjectManagementEntityFrameworkCoreModule.cs
@@ -0,0 +1,12 @@
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.ProjectManagement.EntityFrameworkCore
+{
+ [DependsOn(
+ typeof(AbpProjectManagementDomainModule),
+ typeof(AbpEntityFrameworkCoreModule))]
+ public class AbpProjectManagementEntityFrameworkCoreModule : AbpModule
+ {
+ }
+}
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.HttpApi/LINGYUN.Abp.ProjectManagement.HttpApi.csproj b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.HttpApi/LINGYUN.Abp.ProjectManagement.HttpApi.csproj
new file mode 100644
index 000000000..d892f1e73
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.HttpApi/LINGYUN.Abp.ProjectManagement.HttpApi.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+
+ net5.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.HttpApi/LINGYUN/Abp/ProjectManagement/AbpProjectManagementHttpApiModule.cs b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.HttpApi/LINGYUN/Abp/ProjectManagement/AbpProjectManagementHttpApiModule.cs
new file mode 100644
index 000000000..e9fc1f0c7
--- /dev/null
+++ b/aspnet-core/modules/project/LINGYUN.Abp.ProjectManagement.HttpApi/LINGYUN/Abp/ProjectManagement/AbpProjectManagementHttpApiModule.cs
@@ -0,0 +1,35 @@
+using LINGYUN.Abp.ProjectManagement.Localization;
+using Localization.Resources.AbpUi;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.ProjectManagement
+{
+ [DependsOn(
+ typeof(AbpAspNetCoreMvcModule),
+ typeof(AbpProjectManagementApplicationContractsModule))]
+ public class AbpProjectManagementHttpApiModule : AbpModule
+ {
+ public override void PreConfigureServices(ServiceConfigurationContext context)
+ {
+ PreConfigure(mvcBuilder =>
+ {
+ mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpProjectManagementHttpApiModule).Assembly);
+ });
+ }
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddBaseTypes(
+ typeof(AbpUiResource)
+ );
+ });
+ }
+ }
+}