feat(cli): add ms microservice template support

1. 新增`ms`微服务模板,包含共享项目、微服务、网关、应用等完整架构
2. 为`hua new`命令添加`ms`模板选项并更新文档
3. 将模板文件嵌入CLI项目输出目录
4. 实现微服务模板的项目构建逻辑,自动安装并使用dotnet自定义模板创建项目
This commit is contained in:
ShaoHua
2026-07-15 11:30:43 +08:00
parent e639ade569
commit 2a6f5f81b7
60 changed files with 1259 additions and 2 deletions
+111 -1
View File
@@ -1 +1,111 @@
# Hua.Cli # Hua.Cli
面向 .NET 生态的全局命令行工具(Global .NET CLI Tool),为开发者提供项目脚手架生成和依赖版本管理等基础开发工作流支持。
## 安装
```bash
dotnet tool install -g Hua.Cli
```
## 命令列表
| 命令 | 描述 |
|---|---|
| `hua help` | 显示帮助信息 |
| `hua new` | 基于模板创建新项目/解决方案 |
| `hua update` | 更新项目 NuGet 包版本 |
## 使用示例
### 创建新项目
```bash
# 创建标准分层应用
hua new MyProject
# 创建微服务解决方案
hua new MyMicroservice -t ms
# 指定模板和选项
hua new MyProject -t app -db ef -u mvc
# 指定输出目录
hua new MyProject -o D:\Projects
```
### 更新依赖
```bash
# 更新到最新版本
hua update
# 更新到指定版本
hua update -v 9.0.0
# 试运行
hua update --dry-run
```
### 查看帮助
```bash
# 查看所有命令
hua help
# 查看指定命令的详细帮助
hua help new
hua help update
```
## 项目结构
```
Hua.Cli/
├── src/
│ ├── Hua.Cli/ # 启动入口
│ └── Hua.Cli.Core/ # 核心逻辑
│ └── Hua/Cli/
│ ├── Args/ # 命令行参数解析
│ ├── Commands/ # 命令实现 (help/new/update)
│ ├── Configuration/ # 配置管理
│ ├── Http/ # HTTP 客户端
│ ├── ProjectBuilding/ # 项目构建流水线引擎
│ ├── ProjectModification/ # 项目修改工具
│ ├── Utils/ # 工具类
│ └── Version/ # 版本管理
└── test/
└── Hua.Cli.Tests/ # 单元测试
```
## 技术栈
- **目标框架**: .NET 9.0
- **依赖注入**: Autofac (Volo.Abp.Autofac)
- **日志**: Serilog
- **模板引擎**: 自研文件替换引擎
- **NuGet**: NuGet.Versioning + NuGet.Protocol
## 架构设计
本项目借鉴 [Volo.Abp.Cli](https://github.com/abpframework/abp) 的分层插件化架构设计:
- **命令模式**: 所有操作封装为 `IConsoleCommand` 实现,通过 `CommandSelector` 动态路由
- **流水线模式**: 项目构建采用 `ProjectBuildPipeline` 串联多个 `ProjectBuildPipelineStep`
- **策略模式**: 不同模板通过继承 `TemplateInfo` 基类实现各自构建步骤
## 开发
```bash
# 还原依赖
dotnet restore
# 编译
dotnet build
# 运行测试
dotnet test
# 本地运行
dotnet run --project src/Hua.Cli -- help
```
+3
View File
@@ -70,6 +70,9 @@ hua new <模板名> [选项]
| 模板名 | 描述 | 优先级 | | 模板名 | 描述 | 优先级 |
|---|---|---| |---|---|---|
| `app` | 标准分层应用(Application / Application.Contracts / Domain / Domain.Shared / EntityFrameworkCore / Web / HttpApi | P0 | | `app` | 标准分层应用(Application / Application.Contracts / Domain / Domain.Shared / EntityFrameworkCore / Web / HttpApi | P0 |
| `ms` | 微服务解决方案(shared + microservices + gateways + applications + modules | P0 |
模板使用 .NET 自定义模板机制(`.template.config/template.json`),通过 `dotnet new` 引擎创建项目,`sourceName` 自动替换命名空间和项目名。
#### 3.2.2 `hua update` — 更新依赖 #### 3.2.2 `hua update` — 更新依赖
+4
View File
@@ -20,4 +20,8 @@
<PackageReference Include="Volo.Abp.Json" Version="9.0.0" /> <PackageReference Include="Volo.Abp.Json" Version="9.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="..\..\templates\ms\**\*" CopyToOutputDirectory="PreserveNewest" LinkBase="templates\ms\" />
</ItemGroup>
</Project> </Project>
@@ -114,6 +114,7 @@ public class NewCommand : IConsoleCommand, ITransientDependency
Options: Options:
-t|--template <template-name> (default: app) -t|--template <template-name> (default: app)
Available: app, ms
-v|--version <version> Semantic version number -v|--version <version> Semantic version number
-o|--output <output-directory> Output directory -o|--output <output-directory> Output directory
-db|--database-provider <provider> Database provider (ef) -db|--database-provider <provider> Database provider (ef)
@@ -127,6 +128,7 @@ Examples:
hua new MyProject hua new MyProject
hua new MyProject -t app -db ef -u mvc hua new MyProject -t app -db ef -u mvc
hua new MyProject -t ms
hua new MyProject -o D:\Projects"; hua new MyProject -o D:\Projects";
} }
@@ -1,5 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Cli.ProjectBuilding.Templates.App; using Volo.Abp.Cli.ProjectBuilding.Templates.App;
using Volo.Abp.Cli.ProjectBuilding.Templates.Ms;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.ProjectBuilding; namespace Volo.Abp.Cli.ProjectBuilding;
@@ -11,6 +12,7 @@ public class TemplateInfoProvider : ITemplateInfoProvider, ITransientDependency
TemplateInfo? template = name switch TemplateInfo? template = name switch
{ {
"app" => new AppTemplate(), "app" => new AppTemplate(),
"ms" => new MicroserviceTemplate(),
_ => null _ => null
}; };
@@ -1,6 +1,9 @@
using System;
using System.IO; using System.IO;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Cli.ProjectBuilding.Building; using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.ProjectBuilding; namespace Volo.Abp.Cli.ProjectBuilding;
@@ -9,13 +12,16 @@ public class TemplateProjectBuilder : ITransientDependency
{ {
protected ISourceCodeStore SourceCodeStore { get; } protected ISourceCodeStore SourceCodeStore { get; }
protected TemplateProjectBuildPipelineBuilder PipelineBuilder { get; } protected TemplateProjectBuildPipelineBuilder PipelineBuilder { get; }
protected ICmdHelper CmdHelper { get; }
public TemplateProjectBuilder( public TemplateProjectBuilder(
ISourceCodeStore sourceCodeStore, ISourceCodeStore sourceCodeStore,
TemplateProjectBuildPipelineBuilder pipelineBuilder) TemplateProjectBuildPipelineBuilder pipelineBuilder,
ICmdHelper cmdHelper)
{ {
SourceCodeStore = sourceCodeStore; SourceCodeStore = sourceCodeStore;
PipelineBuilder = pipelineBuilder; PipelineBuilder = pipelineBuilder;
CmdHelper = cmdHelper;
} }
public async Task BuildAsync(ProjectBuildArgs args) public async Task BuildAsync(ProjectBuildArgs args)
@@ -25,6 +31,12 @@ public class TemplateProjectBuilder : ITransientDependency
return; return;
} }
if (args.Template.Name == "ms")
{
await BuildMicroserviceAsync(args);
return;
}
var templatePath = await SourceCodeStore.GetAsync( var templatePath = await SourceCodeStore.GetAsync(
args.Template.Name, args.Version); args.Template.Name, args.Version);
@@ -43,4 +55,75 @@ public class TemplateProjectBuilder : ITransientDependency
var pipeline = PipelineBuilder.Build(context); var pipeline = PipelineBuilder.Build(context);
await pipeline.ExecuteAsync(); await pipeline.ExecuteAsync();
} }
private async Task BuildMicroserviceAsync(ProjectBuildArgs args)
{
var templateDir = ResolveTemplateDirectory();
if (!Directory.Exists(templateDir))
{
throw new FileNotFoundException(
$"Microservice template not found at: {templateDir}. " +
"Ensure the 'templates/ms' directory exists alongside the tool.");
}
// Install the template
var installResult = await CmdHelper.RunAsync($"dotnet new install \"{templateDir}\"");
if (!installResult.IsSuccess)
{
throw new InvalidOperationException(
$"Failed to install microservice template: {installResult.Error}");
}
try
{
// Run dotnet new with the template
var outputDir = args.OutputDirectory;
var projectName = args.ProjectName;
var newResult = await CmdHelper.RunAsync(
$"dotnet new hua-ms -n \"{projectName}\" -o \"{outputDir}\"");
if (!newResult.IsSuccess)
{
throw new InvalidOperationException(
$"Failed to create project: {newResult.Error}");
}
}
finally
{
// Uninstall the template to clean up
await CmdHelper.RunAsync($"dotnet new uninstall \"{templateDir}\"");
}
}
private static string ResolveTemplateDirectory()
{
var assemblyDir = Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location) ?? ".";
// First, check relative to the assembly directory (works for published output
// where templates/ms is copied alongside the assembly via CopyToOutputDirectory)
var assemblyRelativePath = Path.Combine(assemblyDir, "templates", "ms");
if (Directory.Exists(assemblyRelativePath))
{
return assemblyRelativePath;
}
// Fall back to upward traversal (works in development when templates/ms
// lives in the repository root above bin/Debug/net9.0)
var directory = new DirectoryInfo(assemblyDir);
while (directory != null)
{
var templatePath = Path.Combine(directory.FullName, "templates", "ms");
if (Directory.Exists(templatePath))
{
return templatePath;
}
directory = directory.Parent;
}
return assemblyRelativePath;
}
} }
@@ -0,0 +1,11 @@
namespace Volo.Abp.Cli.ProjectBuilding.Templates.Ms;
public class MicroserviceTemplate : TemplateInfo
{
public MicroserviceTemplate()
: base("ms",
"Microservice solution (gateways + microservices + applications + shared)",
null)
{
}
}
@@ -0,0 +1,35 @@
{
"$schema": "http://json.schemastore.org/template",
"author": "Hua",
"classifications": ["Hua", "Microservice", "Web", "Solution"],
"identity": "Hua.Microservice.Template",
"name": "Hua Microservice Solution",
"shortName": "hua-ms",
"sourceName": "Company.Project",
"preferNameDirectory": true,
"tags": {
"language": "C#",
"type": "project"
},
"symbols": {
"databaseProvider": {
"type": "parameter",
"datatype": "choice",
"choices": [
{ "choice": "ef", "description": "Entity Framework Core" }
],
"defaultValue": "ef",
"description": "Database provider"
},
"uiFramework": {
"type": "parameter",
"datatype": "choice",
"choices": [
{ "choice": "mvc", "description": "ASP.NET Core MVC" },
{ "choice": "none", "description": "No UI" }
],
"defaultValue": "mvc",
"description": "UI framework"
}
}
}
@@ -0,0 +1,48 @@
using Microsoft.Extensions.DependencyInjection;
using Company.Project.Shared;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Mvc.UI.BasicTheme;
using Volo.Abp.Autofac;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.Identity;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.IdentityServer.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
namespace Company.Project.AuthServer;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule),
typeof(AbpIdentityEntityFrameworkCoreModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpIdentityServerEntityFrameworkCoreModule),
typeof(AbpAccountWebIdentityServerModule),
typeof(AbpTenantManagementEntityFrameworkCoreModule)
)]
public class AuthServerHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = ProjectConsts.IsMultiTenancyEnabled;
});
Configure<AbpDbContextOptions>(options => options.UseSqlServer());
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseConfiguredEndpoints();
}
}
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>Company.Project.AuthServer</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Autofac" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" Version="9.0.0" />
<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Volo.Abp.IdentityServer.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Volo.Abp.Account.Web.IdentityServer" Version="9.0.0" />
<PackageReference Include="Volo.Abp.TenantManagement.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<ProjectReference Include="..\..\shared\Company.Project.Shared\Company.Project.Shared.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,34 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Volo.Abp;
namespace Company.Project.AuthServer;
public class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
try { CreateHostBuilder(args).Build().Run(); }
catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly"); }
finally { Log.CloseAndFlush(); }
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac().UseSerilog()
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Company.Project.AuthServer;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<AuthServerHostModule>();
}
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
app.InitializeApplication();
}
}
@@ -0,0 +1,7 @@
{
"ConnectionStrings": {
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=Company.Project_AuthServer;Trusted_Connection=True"
},
"Serilog": { "MinimumLevel": { "Default": "Information" } },
"AllowedHosts": "*"
}
@@ -0,0 +1,43 @@
using Company.Project.Shared;
using ProductManagement;
using ProductManagement.Web;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc.Client;
using Volo.Abp.AspNetCore.Mvc.UI.BasicTheme;
using Volo.Abp.Autofac;
using Volo.Abp.Http.Client.IdentityModel.Web;
using Volo.Abp.Identity.Web;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TenantManagement.Web;
namespace Company.Project.BackendAdminApp;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpAspNetCoreMvcClientModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule),
typeof(AbpHttpClientIdentityModelWebModule),
typeof(AbpIdentityWebModule),
typeof(AbpTenantManagementWebModule),
typeof(ProductManagementHttpApiClientModule),
typeof(ProductManagementWebModule)
)]
public class BackendAdminAppHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = ProjectConsts.IsMultiTenancyEnabled;
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
app.UseStaticFiles();
app.UseRouting();
app.UseConfiguredEndpoints();
}
}
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>Company.Project.BackendAdminApp</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Autofac" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.Client" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" Version="9.0.0" />
<PackageReference Include="Volo.Abp.Http.Client.IdentityModel.Web" Version="9.0.0" />
<PackageReference Include="Volo.Abp.Identity.Web" Version="9.0.0" />
<PackageReference Include="Volo.Abp.TenantManagement.Web" Version="9.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<ProjectReference Include="..\..\shared\Company.Project.Shared\Company.Project.Shared.csproj" />
<ProjectReference Include="..\..\modules\product\src\ProductManagement.HttpApi.Client\ProductManagement.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\modules\product\src\ProductManagement.Web\ProductManagement.Web.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,34 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Volo.Abp;
namespace Company.Project.BackendAdminApp;
public class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
try { CreateHostBuilder(args).Build().Run(); }
catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly"); }
finally { Log.CloseAndFlush(); }
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac().UseSerilog()
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Company.Project.BackendAdminApp;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<BackendAdminAppHostModule>();
}
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
app.InitializeApplication();
}
}
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>Company.Project.InternalGateway</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Autofac" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="9.0.0" />
<PackageReference Include="Volo.Abp.Identity.HttpApi" Version="9.0.0" />
<PackageReference Include="Ocelot" Version="18.0.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<ProjectReference Include="..\..\shared\Company.Project.Shared\Company.Project.Shared.csproj" />
<ProjectReference Include="..\..\modules\product\src\ProductManagement.HttpApi\ProductManagement.HttpApi.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,47 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Company.Project.Shared;
using Volo.Abp;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
namespace Company.Project.InternalGateway;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAspNetCoreMultiTenancyModule)
)]
public class InternalGatewayHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = ProjectConsts.IsMultiTenancyEnabled;
});
context.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Internal Gateway API", Version = "v1" });
});
context.Services.AddOcelot(configuration);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
app.UseRouting();
app.UseSwagger();
app.UseSwaggerUI(options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "Internal Gateway"));
app.UseOcelot().Wait();
}
}
@@ -0,0 +1,44 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Volo.Abp;
namespace Company.Project.InternalGateway;
public class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
try
{
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac()
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Company.Project.InternalGateway;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<InternalGatewayHostModule>();
}
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
app.InitializeApplication();
}
}
@@ -0,0 +1,9 @@
{
"Redis": { "Configuration": "localhost" },
"Serilog": { "MinimumLevel": { "Default": "Information" } },
"Routes": [
{ "DownstreamPathTemplate": "/api/identity/{everything}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [{ "Host": "localhost", "Port": 44368 }], "UpstreamPathTemplate": "/api/identity/{everything}", "UpstreamHttpMethod": ["Get", "Post", "Put", "Delete"] },
{ "DownstreamPathTemplate": "/api/product-management/{everything}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [{ "Host": "localhost", "Port": 44344 }], "UpstreamPathTemplate": "/api/product-management/{everything}", "UpstreamHttpMethod": ["Get", "Post", "Put", "Delete"] }
],
"AllowedHosts": "*"
}
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>Company.Project.PublicWebGateway</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Autofac" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="9.0.0" />
<PackageReference Include="Ocelot" Version="18.0.4" />
<ProjectReference Include="..\..\shared\Company.Project.Shared\Company.Project.Shared.csproj" />
<ProjectReference Include="..\..\modules\product\src\ProductManagement.HttpApi\ProductManagement.HttpApi.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,34 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Volo.Abp;
namespace Company.Project.PublicWebGateway;
public class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
try { CreateHostBuilder(args).Build().Run(); }
catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly"); }
finally { Log.CloseAndFlush(); }
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac().UseSerilog()
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}
@@ -0,0 +1,39 @@
using Microsoft.Extensions.DependencyInjection;
using Company.Project.Shared;
using Volo.Abp;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
namespace Company.Project.PublicWebGateway;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAspNetCoreMultiTenancyModule)
)]
public class PublicWebGatewayHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = ProjectConsts.IsMultiTenancyEnabled;
});
context.Services.AddOcelot(configuration);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
app.UseRouting();
app.UseOcelot().Wait();
}
}
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Company.Project.PublicWebGateway;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<PublicWebGatewayHostModule>();
}
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
app.InitializeApplication();
}
}
@@ -0,0 +1,7 @@
{
"Redis": { "Configuration": "localhost" },
"Routes": [
{ "DownstreamPathTemplate": "/api/product-management/{everything}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [{ "Host": "localhost", "Port": 44344 }], "UpstreamPathTemplate": "/api/product-management/{everything}", "UpstreamHttpMethod": ["Get"] }
],
"AllowedHosts": "*"
}
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>Company.Project.IdentityService</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Autofac" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="9.0.0" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.SqlServer" Version="9.0.0" />
<PackageReference Include="Volo.Abp.EventBus.RabbitMQ" Version="9.0.0" />
<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Volo.Abp.Identity.Application" Version="9.0.0" />
<PackageReference Include="Volo.Abp.Identity.HttpApi" Version="9.0.0" />
<PackageReference Include="Volo.Abp.TenantManagement.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<ProjectReference Include="..\..\shared\Company.Project.Shared\Company.Project.Shared.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,61 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Company.Project.Shared;
using Volo.Abp;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Autofac;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Identity;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
namespace Company.Project.IdentityService;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAspNetCoreMultiTenancyModule),
typeof(AbpEntityFrameworkCoreSqlServerModule),
typeof(AbpEventBusRabbitMqModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpIdentityHttpApiModule),
typeof(AbpIdentityEntityFrameworkCoreModule),
typeof(AbpTenantManagementEntityFrameworkCoreModule),
typeof(AbpPermissionManagementEntityFrameworkCoreModule),
typeof(AbpSettingManagementEntityFrameworkCoreModule)
)]
public class IdentityServiceHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = ProjectConsts.IsMultiTenancyEnabled;
});
context.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Identity Service API", Version = "v1" });
});
Configure<AbpDbContextOptions>(options => options.UseSqlServer());
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
app.UseRouting();
app.UseSwagger();
app.UseSwaggerUI(options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "Identity Service API"));
app.UseConfiguredEndpoints();
}
}
@@ -0,0 +1,44 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Volo.Abp;
namespace Company.Project.IdentityService;
public class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
try
{
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac()
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Company.Project.IdentityService;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<IdentityServiceHostModule>();
}
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
app.InitializeApplication();
}
}
@@ -0,0 +1,21 @@
{
"ConnectionStrings": {
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=Company.Project_Identity;Trusted_Connection=True"
},
"Redis": {
"Configuration": "localhost"
},
"RabbitMQ": {
"Connections": {
"Default": { "HostName": "localhost" }
},
"EventBus": {
"ClientName": "Company.Project_Identity",
"ExchangeName": "Company.Project"
}
},
"Serilog": {
"MinimumLevel": { "Default": "Information" }
},
"AllowedHosts": "*"
}
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>Company.Project.ProductService</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Autofac" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="9.0.0" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.SqlServer" Version="9.0.0" />
<PackageReference Include="Volo.Abp.EventBus.RabbitMQ" Version="9.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<ProjectReference Include="..\..\shared\Company.Project.Shared\Company.Project.Shared.csproj" />
<ProjectReference Include="..\..\modules\product\src\ProductManagement.Application\ProductManagement.Application.csproj" />
<ProjectReference Include="..\..\modules\product\src\ProductManagement.EntityFrameworkCore\ProductManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\modules\product\src\ProductManagement.HttpApi\ProductManagement.HttpApi.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,55 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Company.Project.Shared;
using ProductManagement;
using ProductManagement.EntityFrameworkCore;
using Volo.Abp;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Autofac;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
namespace Company.Project.ProductService;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAspNetCoreMultiTenancyModule),
typeof(AbpEntityFrameworkCoreSqlServerModule),
typeof(AbpEventBusRabbitMqModule),
typeof(ProductManagementApplicationModule),
typeof(ProductManagementHttpApiModule),
typeof(ProductManagementEntityFrameworkCoreModule)
)]
public class ProductServiceHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = ProjectConsts.IsMultiTenancyEnabled;
});
context.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Product Service API", Version = "v1" });
});
Configure<AbpDbContextOptions>(options => options.UseSqlServer());
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
app.UseRouting();
app.UseSwagger();
app.UseSwaggerUI(options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "Product Service API"));
app.UseConfiguredEndpoints();
}
}
@@ -0,0 +1,44 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Volo.Abp;
namespace Company.Project.ProductService;
public class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
try
{
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac()
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Company.Project.ProductService;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<ProductServiceHostModule>();
}
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
app.InitializeApplication();
}
}
@@ -0,0 +1,21 @@
{
"ConnectionStrings": {
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=Company.Project_ProductManagement;Trusted_Connection=True"
},
"Redis": {
"Configuration": "localhost"
},
"RabbitMQ": {
"Connections": {
"Default": { "HostName": "localhost" }
},
"EventBus": {
"ClientName": "Company.Project_Product",
"ExchangeName": "Company.Project"
}
},
"Serilog": {
"MinimumLevel": { "Default": "Information" }
},
"AllowedHosts": "*"
}
@@ -0,0 +1,17 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace ProductManagement;
public interface IProductAppService :
ICrudAppService<ProductDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateProductDto>
{
}
public class CreateUpdateProductDto
{
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public int StockCount { get; set; }
}
@@ -0,0 +1,11 @@
using System;
using Volo.Abp.Application.Dtos;
namespace ProductManagement;
public class ProductDto : FullAuditedEntityDto<Guid>
{
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public int StockCount { get; set; }
}
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><TargetFramework>netstandard2.0</TargetFramework></PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Application" Version="9.0.0" />
<ProjectReference Include="..\ProductManagement.Domain.Shared\ProductManagement.Domain.Shared.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,6 @@
using Volo.Abp.Modularity;
namespace ProductManagement;
[DependsOn(typeof(ProductManagementDomainSharedModule))]
public class ProductManagementApplicationContractsModule : AbpModule { }
@@ -0,0 +1,14 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace ProductManagement;
public class ProductAppService :
CrudAppService<Product, ProductDto, Guid, PagedAndSortedResultRequestDto, CreateUpdateProductDto>,
IProductAppService
{
public ProductAppService(IRepository<Product, Guid> repository)
: base(repository) { }
}
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><TargetFramework>net8.0</TargetFramework></PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AutoMapper" Version="9.0.0" />
<ProjectReference Include="..\ProductManagement.Application.Contracts\ProductManagement.Application.Contracts.csproj" />
<ProjectReference Include="..\ProductManagement.Domain\ProductManagement.Domain.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,9 @@
using Volo.Abp.Modularity;
namespace ProductManagement;
[DependsOn(
typeof(ProductManagementDomainModule),
typeof(ProductManagementApplicationContractsModule)
)]
public class ProductManagementApplicationModule : AbpModule { }
@@ -0,0 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><TargetFramework>netstandard2.0</TargetFramework></PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Localization" Version="9.0.0" />
</ItemGroup>
</Project>
@@ -0,0 +1,7 @@
using Volo.Abp.Modularity;
using Volo.Abp.Localization;
namespace ProductManagement;
[DependsOn(typeof(AbpLocalizationModule))]
public class ProductManagementDomainSharedModule : AbpModule { }
@@ -0,0 +1,11 @@
using System;
using Volo.Abp.Domain.Entities.Auditing;
namespace ProductManagement;
public class Product : FullAuditedAggregateRoot<Guid>
{
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public int StockCount { get; set; }
}
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><TargetFramework>net8.0</TargetFramework></PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="9.0.0" />
<ProjectReference Include="..\ProductManagement.Domain.Shared\ProductManagement.Domain.Shared.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,6 @@
using Volo.Abp.Modularity;
namespace ProductManagement;
[DependsOn(typeof(ProductManagementDomainSharedModule))]
public class ProductManagementDomainModule : AbpModule { }
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><TargetFramework>net8.0</TargetFramework></PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.EntityFrameworkCore.SqlServer" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<ProjectReference Include="..\ProductManagement.Domain\ProductManagement.Domain.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace ProductManagement.EntityFrameworkCore;
public class ProductManagementDbContext : AbpDbContext<ProductManagementDbContext>
{
public DbSet<Product> Products { get; set; }
public ProductManagementDbContext(DbContextOptions<ProductManagementDbContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Product>(b =>
{
b.ToTable("PmProducts");
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
}
}
@@ -0,0 +1,6 @@
using Volo.Abp.Modularity;
namespace ProductManagement.EntityFrameworkCore;
[DependsOn(typeof(ProductManagementDomainModule))]
public class ProductManagementEntityFrameworkCoreModule : AbpModule { }
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><TargetFramework>net8.0</TargetFramework></PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Http.Client" Version="9.0.0" />
<ProjectReference Include="..\ProductManagement.Application.Contracts\ProductManagement.Application.Contracts.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,6 @@
using Volo.Abp.Modularity;
namespace ProductManagement;
[DependsOn(typeof(ProductManagementApplicationContractsModule))]
public class ProductManagementHttpApiClientModule : AbpModule { }
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><TargetFramework>net8.0</TargetFramework></PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="9.0.0" />
<ProjectReference Include="..\ProductManagement.Application.Contracts\ProductManagement.Application.Contracts.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,6 @@
using Volo.Abp.Modularity;
namespace ProductManagement;
[DependsOn(typeof(ProductManagementApplicationContractsModule))]
public class ProductManagementHttpApiModule : AbpModule { }
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AutoMapper" Version="9.0.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared" Version="9.0.0" />
<ProjectReference Include="..\ProductManagement.HttpApi\ProductManagement.HttpApi.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,6 @@
using Volo.Abp.Modularity;
namespace ProductManagement;
[DependsOn(typeof(ProductManagementHttpApiModule))]
public class ProductManagementWebModule : AbpModule { }
@@ -0,0 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>Company.Project.Shared</RootNamespace>
</PropertyGroup>
</Project>
@@ -0,0 +1,6 @@
namespace Company.Project.Shared;
public static class ProjectConsts
{
public const bool IsMultiTenancyEnabled = true;
}