Browse Source

添加物料脚手架

master
stevelee 2 years ago
parent
commit
d71a95bd65
32 changed files with 915 additions and 130 deletions
  1. +1
    -7
      BPA.SaaS.Material.Api.sln
  2. +22
    -20
      src/BPA.SaaS.Material.Api.Bootstrap/BPA.SaaS.Material.Api.Bootstrap.csproj
  3. +163
    -0
      src/BPA.SaaS.Material.Api.Bootstrap/SetupBootstrap.cs
  4. +67
    -0
      src/BPA.SaaS.Material.Api.Bootstrap/SetupRabbitMqConfig.cs
  5. +47
    -0
      src/BPA.SaaS.Material.Api.Bootstrap/SetupRedisConfig.cs
  6. +47
    -0
      src/BPA.SaaS.Material.Api.Bootstrap/SetupServiceSDK.cs
  7. +5
    -2
      src/BPA.SaaS.Material.Api.DTO/BPA.SaaS.Material.Api.DTO.csproj
  8. +26
    -0
      src/BPA.SaaS.Material.Api.DTO/UnitDto.cs
  9. +35
    -0
      src/BPA.SaaS.Material.Api.DTO/UnitDtoQuery.cs
  10. +10
    -9
      src/BPA.SaaS.Material.Api.Entity/BPA.SaaS.Material.Api.Entity.csproj
  11. +24
    -0
      src/BPA.SaaS.Material.Api.Entity/BPA_Unit.cs
  12. +3
    -2
      src/BPA.SaaS.Material.Api.IRepository/BPA.SaaS.Material.Api.IRepository.csproj
  13. +48
    -0
      src/BPA.SaaS.Material.Api.IRepository/IUnitRepository.cs
  14. +1
    -1
      src/BPA.SaaS.Material.Api.IService/BPA.SaaS.Material.Api.IService.csproj
  15. +49
    -0
      src/BPA.SaaS.Material.Api.IService/IUnitService.cs
  16. +22
    -15
      src/BPA.SaaS.Material.Api.Model/BPA.SaaS.Material.Api.Model.csproj
  17. +42
    -0
      src/BPA.SaaS.Material.Api.Model/MaterialApiConfig.cs
  18. +14
    -0
      src/BPA.SaaS.Material.Api.Model/MaterialDbSugarClient.cs
  19. +19
    -15
      src/BPA.SaaS.Material.Api.Repository/BPA.SaaS.Material.Api.Repository.csproj
  20. +58
    -0
      src/BPA.SaaS.Material.Api.Repository/UnitRepository.cs
  21. +12
    -11
      src/BPA.SaaS.Material.Api.SDK/BPA.SaaS.Material.Api.SDK.csproj
  22. +14
    -0
      src/BPA.SaaS.Material.Api.SDK/MaterialWebApiSDK.cs
  23. +5
    -2
      src/BPA.SaaS.Material.Api.Service/BPA.SaaS.Material.Api.Service.csproj
  24. +45
    -0
      src/BPA.SaaS.Material.Api.Service/UnitService.cs
  25. +19
    -6
      src/BPA.SaaS.Material.Api.UnitTest/BPA.SaaS.Material.Api.UnitTest.csproj
  26. +52
    -0
      src/BPA.SaaS.Material.Api.UnitTest/BaseUnitTest.cs
  27. +1
    -1
      src/BPA.SaaS.Material.Api.WebApi/BPA.SaaS.Material.Api.WebApi.csproj
  28. +32
    -2
      src/BPA.SaaS.Material.Api.WebApi/Program.cs
  29. +7
    -20
      src/BPA.SaaS.Material.Api.WebApi/Properties/launchSettings.json
  30. +25
    -0
      src/BPA.SaaS.Material.Api.WebApi/UnitController.cs
  31. +0
    -8
      src/BPA.SaaS.Material.Api.WebApi/appsettings.Development.json
  32. +0
    -9
      src/BPA.SaaS.Material.Api.WebApi/appsettings.json

+ 1
- 7
BPA.SaaS.Material.Api.sln View File

@@ -43,11 +43,6 @@ ProjectSection(SolutionItems) = preProject
src\nuget.config = src\nuget.config
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".pack", ".pack", "{5CE18647-CEFB-483A-B384-99D4FB47DA29}"
ProjectSection(SolutionItems) = preProject
src\.pack\pack.sh = src\.pack\pack.sh
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -114,9 +109,8 @@ Global
{4F48E97A-2DC3-4720-98F7-25935258D79C} = {0265A6CF-0D40-4A59-B6DF-2F25099DA95B}
{CFDFD099-D03F-441A-A3CD-28C7320742C5} = {71207855-D2BE-48D3-86AD-D1E5BC389E64}
{FBE0E584-BF1F-498D-BBFC-66B790EAFC7C} = {FEDACFF6-C6AF-4C34-9575-9456856AEACC}
{5CE18647-CEFB-483A-B384-99D4FB47DA29} = {0265A6CF-0D40-4A59-B6DF-2F25099DA95B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7A177DC6-A13E-4628-97F6-40DC7573252A}
EndGlobalSection
EndGlobal
EndGlobal

+ 22
- 20
src/BPA.SaaS.Material.Api.Bootstrap/BPA.SaaS.Material.Api.Bootstrap.csproj View File

@@ -1,22 +1,24 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.2.3" />
<PackageReference Include="BPA.Component.CAP.RabbitMQ" Version="1.0.2" />
<PackageReference Include="BPA.Component.DTOCommon" Version="1.0.6" />
<PackageReference Include="BPA.Component.Extensions" Version="1.0.3" />
<PackageReference Include="BPA.Component.WebApiExtensions" Version="1.0.8" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.DTO\BPA.SaaS.Material.Api.DTO.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Model\BPA.SaaS.Material.Api.Model.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Service\BPA.SaaS.Material.Api.Service.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Repository\BPA.SaaS.Material.Api.Repository.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.2.3" />
<PackageReference Include="BPA.Component.CAP.RabbitMQ" Version="1.0.2" />
<PackageReference Include="BPA.Component.DTOCommon" Version="1.0.8" />
<PackageReference Include="BPA.Component.Extensions" Version="1.0.4" />
<PackageReference Include="BPA.Component.WebApiExtensions" Version="1.0.17" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BPA.SaaS.Material.Api.DTO\BPA.SaaS.Material.Api.DTO.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Model\BPA.SaaS.Material.Api.Model.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Service\BPA.SaaS.Material.Api.Service.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Repository\BPA.SaaS.Material.Api.Repository.csproj" />
</ItemGroup>
</Project>

+ 163
- 0
src/BPA.SaaS.Material.Api.Bootstrap/SetupBootstrap.cs View File

@@ -0,0 +1,163 @@
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using Autofac;
using BPA.Component.ApolloClient;
using BPA.Component.CAP;
using BPA.Component.Extensions;
using BPA.Component.LogClient.Extensions;
using BPA.Component.SDKCommon;
using BPA.Component.WebApiExtensions.Extensions;
using BPA.SaaS.Material.Api.Model;
using BPA.SaaS.Material.Api.Repository;
using BPA.SaaS.Material.Api.Service;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using IPAddress = System.Net.IPAddress;

namespace BPA.SaaS.Material.Api.Bootstrap;

/// <summary>
/// 启动配置
/// </summary>
public static class SetupBootstrap
{
private static IConfiguration _configuration;

/// <summary>
/// 添加配置中心
/// </summary>
/// <param name="configuration"></param>
public static IConfiguration SetupConfiguration(this IConfiguration configuration)
{
_configuration = configuration;

// 初始化化 apollo配置
return configuration.AddApolloConfiguration();
}

/// <summary>
/// DI Inject
/// </summary>
public static IServiceCollection AddBPAMiddlewareServices(this IServiceCollection services, string[] args)
{
if (services == null) throw new ArgumentNullException(nameof(services));

services.AddApollo<MaterialApiConfig>();
services.AddBPALog();
services.AddRedis();
services.AddRabbitMQ();
services.AddHttpContextAccessor();
services.AddControllers(options => { options.AddDefaultMvcOptions(); });
services.AddEndpointsApiExplorer();
services.AddServiceSDK(_configuration);
services.AddHealthChecks();

// services.AddCap(x =>
// {
// var config = services.GetPreConfig(args);
// x.UseRabbitMQ(mqOptions =>
// {
// mqOptions.UserName = config.RabbitMqConfig.UserName;
// mqOptions.Password = config.RabbitMqConfig.Password;
// mqOptions.Port = config.RabbitMqConfig.Port;
// mqOptions.HostName = config.RabbitMqConfig.HostName;
// mqOptions.VirtualHost = config.RabbitMqConfig.VirtualHost;
// });
// x.UseMySql(config.BasicMySqlDb.MasterDbConnectionString);
// });

services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = AppDomain.CurrentDomain.FriendlyName,
Description = AppDomain.CurrentDomain.FriendlyName,
Version = "v1"
});
options.CustomSchemaIds(type => type.GetDescribe(true, "BPA.", "BPA.SaaS."));
});

return services;
}

/// <summary>
/// DI Register
/// </summary>
public static ContainerBuilder SetupConfigureContainer(this ContainerBuilder builder)
{
// 注入DB
builder.RegisterType<MaterialDbSugarClient>().InstancePerLifetimeScope();

// 服务层
builder.RegisterAssemblyTypes(typeof(UnitService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces();

// 仓储层
builder.RegisterAssemblyTypes(typeof(UnitRepository).Assembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces();

return builder;
}

/// <summary>
/// pre run
/// </summary>
/// <param name="serviceProvider"></param>
public static void PreHeatRun(this IServiceProvider serviceProvider)
{
serviceProvider.GetService<MaterialApiConfig>();
serviceProvider.GetService<MaterialDbSugarClient>()?.DbMaintenance.GetTableInfoList();
serviceProvider.PreHeatRunRedis();
serviceProvider.PreHeatRunRabbitMQ();
serviceProvider.SetDebug(true);
serviceProvider.GetService<ICapPublisher>();
}

/// <summary>
/// APP健康检查
/// </summary>
/// <param name="app"></param>
public static void UseBpaHealthChecks(this IApplicationBuilder app)
{
var ips = new List<dynamic>();
foreach (var network in NetworkInterface.GetAllNetworkInterfaces())
{
var ipAddress = network.GetIPProperties()
.UnicastAddresses
.FirstOrDefault(p => p.Address.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(p.Address))
?.Address.ToString();
if (ipAddress == null)
continue;
ips.Add(new {name = network.Name, ip = ipAddress});
}

var ipsJson = ips.ToJson();

app.UseHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = (httpContext, _) => httpContext.Response.WriteAsync(ipsJson, Encoding.UTF8)
});
}

private static MaterialApiConfig GetPreConfig(this IServiceCollection services, string[] args)
{
var tmpBuilder = WebApplication.CreateBuilder(args);
tmpBuilder.Configuration.SetupConfiguration();
tmpBuilder.Services.AddApollo<MaterialApiConfig>();
tmpBuilder.Services.AddBPALog();
var tmpApp = tmpBuilder.Build();
var config = tmpApp.Services.GetService<MaterialApiConfig>();
tmpApp.StopAsync().ConfigureAwait(false).GetAwaiter();
tmpApp.DisposeAsync().ConfigureAwait(false).GetAwaiter();
tmpBuilder = null;
tmpApp = null;
return config;
}
}

+ 67
- 0
src/BPA.SaaS.Material.Api.Bootstrap/SetupRabbitMqConfig.cs View File

@@ -0,0 +1,67 @@
using BPA.Component.RabbitMQClient.Extensions;
using BPA.Component.RabbitMQClient.Provider;
using BPA.SaaS.Material.Api.Model;
using Microsoft.Extensions.DependencyInjection;

namespace BPA.SaaS.Material.Api.Bootstrap
{
/// <summary>
/// RabbitMQ配置
/// </summary>
public static class SetupRabbitMqConfig
{
/// <summary>
/// 使用消息队列
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddRabbitMQ(this IServiceCollection services)
{
//注入 RabbitMQProvider
#pragma warning disable CS8603
services.AddRabbitMQProvider(sp => sp.GetService<MaterialApiConfig>()?.RabbitMqConfig);
#pragma warning restore CS8603
// //注入 业务日志 生产者 单例
// services.UseRabbitMQProducer<CreateBizLogProducer, CreateBizLogMsgBody>();
// //注入 消息 生产者 单例
// services.UseRabbitMQProducer<BatchAddMessageProduct, BatchAddMessageMsgBody>();

return services;
}

// public static IServiceCollection UseCap(this IServiceCollection services, Func<IServiceProvider, CapOptions> configFunc)
// {
// if (configFunc == null) throw new ArgumentNullException(nameof(configFunc));
// services.AddSingleton(sp => { return configFunc(sp); });
// return services;
// }


/// <summary>
/// 预热消息队列
/// </summary>
/// <param name="serviceProvider"></param>
/// <returns></returns>
public static IServiceProvider PreHeatRunRabbitMQ(this IServiceProvider serviceProvider)
{
//获取rabbitMqProvider
var rabbitMqProvider = serviceProvider.UseRabbitMQProvider<RabbitMQProvider>();

// //创建交换机配置对象
// var createBizLogExchangeConfig = rabbitMqProvider.BuildExchangeConfig(MqNameConfig.CreateBizLogExchange);
// //启用交换机
// createBizLogExchangeConfig.UseExchange();
// //启用生产者
// createBizLogExchangeConfig.UseProducer<CreateBizLogMsgBody, CreateBizLogProducer>();

// //创建交换机配置对象
// var addMessageExchangeConfig = rabbitMqProvider.BuildExchangeConfig(MqNameConfig.BatchAddMessageExchange, ExchangeType.Fanout);
// //启用交换机
// addMessageExchangeConfig.UseExchange();
// //启用生产者
// addMessageExchangeConfig.UseProducer<BatchAddMessageMsgBody, BatchAddMessageProduct>();

return serviceProvider;
}
}
}

+ 47
- 0
src/BPA.SaaS.Material.Api.Bootstrap/SetupRedisConfig.cs View File

@@ -0,0 +1,47 @@
using BPA.Component.RedisClient;
using BPA.SaaS.Material.Api.Model;
using Microsoft.Extensions.DependencyInjection;

namespace BPA.SaaS.Material.Api.Bootstrap;

/// <summary>
/// redis DI配置
/// </summary>
public static class SetupRedisConfig
{
/// <summary>
/// 注入redis
/// </summary>
/// <param name="services"></param>
public static void AddRedis(this IServiceCollection services)
{
services.UseRedisSingleton(sp => GetRedisSingletonConnectionString(sp.GetService<MaterialApiConfig>()!));
}

/// <summary>
/// 预热redis
/// </summary>
/// <param name="serviceProvider"></param>
public static void PreHeatRunRedis(this IServiceProvider serviceProvider)
{
var config = serviceProvider.GetService<MaterialApiConfig>();

// 添加配置改变时的事件
if (config != null)
config.OnConfigChange += (stockWebApiConfig, key, _, _, _) =>
{
if (nameof(stockWebApiConfig.RedisConfig) != key) return;
var conStr = GetRedisSingletonConnectionString(stockWebApiConfig);

//单个redis时 配置变化 触发生成新的实例
RedisConfigExtensions.OnRedisSingletonConnectionStringChange(conStr);
};
}

/// <summary>
/// 获取单例 Redis 连接字符串
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
private static string GetRedisSingletonConnectionString(MaterialApiConfig config) => config.RedisConfig[new Random().Next(0, config.RedisConfig.Count)];
}

+ 47
- 0
src/BPA.SaaS.Material.Api.Bootstrap/SetupServiceSDK.cs View File

@@ -0,0 +1,47 @@
using BPA.Component.DTOCommon.Langs;
using BPA.Component.SDKCommon;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace BPA.SaaS.Material.Api.Bootstrap;

/// <summary>
/// 其他SDK服务
/// </summary>
public static class SetupServiceSDK
{
/// <summary>
/// 注入其他SDK
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
public static void AddServiceSDK(this IServiceCollection services, IConfiguration configuration)
{
// 初始化sdk配置和语言获取
services.AddApiSDK(_ => /*sp.GetService<IHttpContextAccessor>()?.HttpContext?.Request.GetLanguage() ??*/ LangType.Zn);

// 获取当前API启动时定义的环境
var environment = configuration.GetValue<string>("ASPNETCORE_ENVIRONMENT");

// 环境选择
switch (environment)
{
case "Development":
// 开发本地连集群
// services.AddApiSDK<WebApi2SDK>(_ => "http://[IP]:[port]");
// 开发集群环境内
// services.UseApiSDK<WebApi2SDK>(_ => "http://serviceName");
break;


case "Production":
// services.AddApiSDK<WebApi2SDK>(_ => "http://[domainName].com:[80]/[serviceName]");
break;


case "Testing":
// services.AddApiSDK<WebApi2SDK>(_ => "http://test.[domainName].com:[80]/[serviceName]/");
break;
}
}
}

+ 5
- 2
src/BPA.SaaS.Material.Api.DTO/BPA.SaaS.Material.Api.DTO.csproj View File

@@ -1,11 +1,14 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<VersionPrefix>1.0.1</VersionPrefix>
<PackageReleaseNotes />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BPA.Component.DTOCommon" Version="1.0.5" />
<PackageReference Include="BPA.Component.DTOCommon" Version="1.0.8" />
<PackageReference Include="Nuget.Tools.V2" Version="1.1.7" />
</ItemGroup>
</Project>

+ 26
- 0
src/BPA.SaaS.Material.Api.DTO/UnitDto.cs View File

@@ -0,0 +1,26 @@
namespace BPA.SaaS.Material.Api.DTO
{
public class UnitDto
{
/// <summary>
/// 设备类型Id
/// </summary>
public string Id { get; set; }

/// <summary>
/// 设备类型名称
/// </summary>
public string Name { get; set; }

/// <summary>
/// 设备类型备注
/// </summary>
public string Remark { get; set; }

// TODO:状态需要用枚举
/// <summary>
/// 状态
/// </summary>
public int Status { get; set; }
}
}

+ 35
- 0
src/BPA.SaaS.Material.Api.DTO/UnitDtoQuery.cs View File

@@ -0,0 +1,35 @@
namespace BPA.SaaS.Material.Api.DTO
{
public class UnitDtoQuery
{
/// <summary>
/// TypeID
/// </summary>
public string TypeID { get; set; }

/// <summary>
/// 类型编号
/// </summary>
public string Id { get; set; }

/// <summary>
/// 类型名称
/// </summary>
public string Name { get; set; }

/// <summary>
/// 状态
/// </summary>
public string Status { get; set; }

/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }

/// <summary>
/// 创建时间
/// </summary>
public DateTime? CreateAt { get; set; }
}
}

+ 10
- 9
src/BPA.SaaS.Material.Api.Entity/BPA.SaaS.Material.Api.Entity.csproj View File

@@ -1,11 +1,12 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BPA.Common.Entity" Version="1.0.2" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BPA.Common.Entity" Version="1.0.3" />
<PackageReference Include="BPA.Common.Enums" Version="1.0.6" />
</ItemGroup>
</Project>

+ 24
- 0
src/BPA.SaaS.Material.Api.Entity/BPA_Unit.cs View File

@@ -0,0 +1,24 @@
using BPA.Common.Entity;
using SqlSugar;

namespace BPA.SaaS.Material.Api.Entity
{
///<summary>
/// 原料单位表
///</summary>
[SugarTable("BPA_Uint")]
public class BPA_Unit : BaseGroupIdEntity
{
/// <summary>
/// 单位名称
/// </summary>
[SugarColumn(ColumnDataType = "Nvarchar(200)", IsNullable = false)]
public string Name { get; set; }

/// <summary>
/// 备注
/// </summary>
[SugarColumn(ColumnDataType = "Nvarchar(500)", IsNullable = true)]
public string Remark { get; set; }
}
}

+ 3
- 2
src/BPA.SaaS.Material.Api.IRepository/BPA.SaaS.Material.Api.IRepository.csproj View File

@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
@@ -6,7 +6,8 @@
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BPA.Component.DbClient" Version="1.0.6" />
<PackageReference Include="BPA.Component.DbClient" Version="1.0.7" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.DTO\BPA.SaaS.Material.Api.DTO.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Model\BPA.SaaS.Material.Api.Model.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Entity\BPA.SaaS.Material.Api.Entity.csproj" />


+ 48
- 0
src/BPA.SaaS.Material.Api.IRepository/IUnitRepository.cs View File

@@ -0,0 +1,48 @@
using BPA.Component.DTOCommon.BaseDTOs;
using BPA.SaaS.Material.Api.DTO;

namespace BPA.SaaS.Material.Api.IRepository;

public interface IUnitRepository
{
/// <summary>
/// 获取类型列表
/// </summary>
/// <returns></returns>
Task<BaseResult<List<UnitDto>>> GetUnitList();

/// <summary>
/// 类型查询
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<BaseResultPage<UnitDtoQuery>> PostType(UnitDtoQuery dto);

/// <summary>
/// 添加商品类型
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<BaseResult<bool>> AddType(UnitDto dto);

/// <summary>
/// 修改类型
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<BaseResult<bool>> UpdateType(UnitDto dto);

/// <summary>
/// 删除类型
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<BaseResult<bool>> DeleteType(string id);

/// <summary>
/// 批量删除类型
/// </summary>
/// <param name="Ids"></param>
/// <returns></returns>
Task<BaseResult<bool>> BatchDeleteType(string[] Ids);
}

+ 1
- 1
src/BPA.SaaS.Material.Api.IService/BPA.SaaS.Material.Api.IService.csproj View File

@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>


+ 49
- 0
src/BPA.SaaS.Material.Api.IService/IUnitService.cs View File

@@ -0,0 +1,49 @@
using BPA.Component.DTOCommon.BaseDTOs;
using BPA.SaaS.Material.Api.DTO;

namespace BPA.SaaS.Material.Api.IService
{
public interface IUnitService
{
/// <summary>
/// 获取类型列表
/// </summary>
/// <returns></returns>
Task<BaseResult<List<UnitDto>>> GetUnitList();

/// <summary>
/// 类型查询
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<BaseResultPage<UnitDtoQuery>> PostType(UnitDtoQuery dto);

/// <summary>
/// 添加商品类型
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<BaseResult<bool>> AddType(UnitDto dto);

/// <summary>
/// 修改类型
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
Task<BaseResult<bool>> UpdateType(UnitDto dto);

/// <summary>
/// 删除类型
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<BaseResult<bool>> DeleteType(string id);

/// <summary>
/// 批量删除类型
/// </summary>
/// <param name="Ids"></param>
/// <returns></returns>
Task<BaseResult<bool>> BatchDeleteType(string[] Ids);
}
}

+ 22
- 15
src/BPA.SaaS.Material.Api.Model/BPA.SaaS.Material.Api.Model.csproj View File

@@ -1,17 +1,24 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BPA.Component.ApolloClient" Version="1.0.3" />
<PackageReference Include="BPA.Component.LogClient" Version="1.0.3" />
<PackageReference Include="BPA.Component.MongoClient" Version="1.0.3" />
<PackageReference Include="BPA.Component.RabbitMQClient" Version="1.0.7" />
<PackageReference Include="BPA.Component.RedisClient" Version="1.0.3" />
<PackageReference Include="BPA.Component.Extensions" Version="1.0.3" />
<PackageReference Include="BPA.Component.DbClient" Version="1.0.6" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<NoWarn>1701;1702;0618;8618</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<NoWarn>1701;1702;0618</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BPA.Component.ApolloClient" Version="1.0.9" />
<PackageReference Include="BPA.Component.DTOCommon" Version="1.0.8" />
<PackageReference Include="BPA.Component.LogClient" Version="1.0.3" />
<PackageReference Include="BPA.Component.MongoClient" Version="1.0.3" />
<PackageReference Include="BPA.Component.RabbitMQClient" Version="1.0.7" />
<PackageReference Include="BPA.Component.RedisClient" Version="1.0.3" />
<PackageReference Include="BPA.Component.Extensions" Version="1.0.4" />
<PackageReference Include="BPA.Component.DbClient" Version="1.0.7" />
</ItemGroup>
</Project>

+ 42
- 0
src/BPA.SaaS.Material.Api.Model/MaterialApiConfig.cs View File

@@ -0,0 +1,42 @@
using BPA.Component.ApolloClient;
using BPA.Component.DbClient;
using BPA.Component.RabbitMQClient.Connection;
using Com.Ctrip.Framework.Apollo;
using Microsoft.Extensions.Configuration;

namespace BPA.SaaS.Material.Api.Model;

/// <summary>
/// BasicWebApiConfig
/// </summary>
// ReSharper disable once ClassNeverInstantiated.Global
public class MaterialApiConfig : ApolloBPAConfig<MaterialApiConfig>
{
/// <summary>
/// BasicMySqlDb
/// </summary>
[AutoWrite]
public new DbConfig MaterialMySqlDb { get; set; }

/// <summary>
/// RedisConfig
/// </summary>
[AutoWrite]
public new List<string> RedisConfig { set; get; }

/// <summary>
/// 是否打印SQL
/// </summary>
[AutoWrite]
public bool IsLogSql { get; set; } = true;

/// <summary>
/// RabbitMQ配置
/// </summary>
[AutoWrite]
public new RabbitMqConnectionConfig RabbitMqConfig { get; set; }

public MaterialApiConfig(ApolloConfigurationManager apolloConfigurationManager, IConfiguration configuration) : base(apolloConfigurationManager, configuration)
{
}
}

+ 14
- 0
src/BPA.SaaS.Material.Api.Model/MaterialDbSugarClient.cs View File

@@ -0,0 +1,14 @@
using BPA.Component.DbClient;
using Microsoft.Extensions.Logging;

namespace BPA.SaaS.Material.Api.Model;

public class MaterialDbSugarClient : BaseDbClient<MaterialDbSugarClient>
{
public MaterialDbSugarClient(ILogger<MaterialDbSugarClient> logger, MaterialApiConfig config)
: base(BuildConnectionConfig(config.MaterialMySqlDb),
config.IsLogSql,
logger)
{
}
}

+ 19
- 15
src/BPA.SaaS.Material.Api.Repository/BPA.SaaS.Material.Api.Repository.csproj View File

@@ -1,17 +1,21 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BPA.Component.Extensions" Version="1.0.3" />
<PackageReference Include="BPA.Component.SDKCommon" Version="1.0.3" />
<PackageReference Include="BPA.Common.Entity" Version="1.0.2" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.DTO\BPA.SaaS.Material.Api.DTO.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Model\BPA.SaaS.Material.Api.Model.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Entity\BPA.SaaS.Material.Api.Entity.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.IRepository\BPA.SaaS.Material.Api.IRepository.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BPA.Common.Eums" Version="1.0.7" />
<PackageReference Include="BPA.Component.DTOCommon" Version="1.0.8" />
<PackageReference Include="BPA.Component.Extensions" Version="1.0.4" />
<PackageReference Include="BPA.Component.SDKCommon" Version="1.0.3" />
<PackageReference Include="BPA.Common.Entity" Version="1.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BPA.SaaS.Material.Api.DTO\BPA.SaaS.Material.Api.DTO.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Model\BPA.SaaS.Material.Api.Model.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Entity\BPA.SaaS.Material.Api.Entity.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.IRepository\BPA.SaaS.Material.Api.IRepository.csproj" />
</ItemGroup>
</Project>

+ 58
- 0
src/BPA.SaaS.Material.Api.Repository/UnitRepository.cs View File

@@ -0,0 +1,58 @@
using BPA.Component.DbClient;
using BPA.Component.DbClient.RepositoryModel;
using BPA.Component.DTOCommon.BaseDTOs;
using BPA.Component.Extensions;
using BPA.SaaS.Material.Api.DTO;
using BPA.SaaS.Material.Api.Entity;
using BPA.SaaS.Material.Api.IRepository;
using BPA.SaaS.Material.Api.Model;

namespace BPA.SaaS.Material.Api.Repository;

public class UnitRepository : BaseDbRepository<MaterialDbSugarClient, BPA_Unit>, IUnitRepository
{
public UnitRepository(MaterialDbSugarClient dbClient) : base(dbClient)
{
}

/// <summary>
/// 获取产品列表
/// </summary>
/// <returns></returns>
public async Task<BaseResult<List<UnitDto>>> GetUnitList() =>
BaseResult<List<UnitDto>>
.BuildOK((await SimpleClient.AsQueryable().Where(a => a.IsDeleted).ToListAsync())
.MapToList<UnitDto, BPA_Unit>()
.ToList());

/// <summary>
///
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public Task<BaseResultPage<UnitDtoQuery>> PostType(UnitDtoQuery dto)
{
throw new NotImplementedException();
}

public Task<BaseResult<bool>> AddType(UnitDto dto)
{
throw new NotImplementedException();
}

public Task<BaseResult<bool>> UpdateType(UnitDto dto)
{
throw new NotImplementedException();
}

public Task<BaseResult<bool>> DeleteType(string id)
{
throw new NotImplementedException();
}

public Task<BaseResult<bool>> BatchDeleteType(string[] Ids)
{
throw new NotImplementedException();
}
}

+ 12
- 11
src/BPA.SaaS.Material.Api.SDK/BPA.SaaS.Material.Api.SDK.csproj View File

@@ -1,13 +1,14 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\BPA.SaaS.Material.Api.DTO\BPA.SaaS.Material.Api.DTO.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.IService\BPA.SaaS.Material.Api.IService.csproj" />
<PackageReference Include="BPA.Component.SDKCommon" Version="1.0.3" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BPA.SaaS.Material.Api.DTO" Version="1.0.1" />
<PackageReference Include="BPA.Component.DTOCommon" Version="1.0.8" />
<PackageReference Include="BPA.Component.SDKCommon" Version="1.0.3" />
<PackageReference Include="Nuget.Tools.V2" Version="1.1.7" />
</ItemGroup>
</Project>

+ 14
- 0
src/BPA.SaaS.Material.Api.SDK/MaterialWebApiSDK.cs View File

@@ -0,0 +1,14 @@
using BPA.Component.SDKCommon;

namespace BPA.SaaS.Material.Api.SDK;

public class MaterialWebApiSDK : BaseApiSDK<MaterialWebApiSDK>
{
/// <summary>
/// BasicWebApiSDK
/// </summary>
/// <param name="serviceProvider"></param>
public MaterialWebApiSDK(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
}

+ 5
- 2
src/BPA.SaaS.Material.Api.Service/BPA.SaaS.Material.Api.Service.csproj View File

@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
@@ -6,8 +6,11 @@
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BPA.Component.Extensions" Version="1.0.3" />
<PackageReference Include="BPA.Component.DTOCommon" Version="1.0.8" />
<PackageReference Include="BPA.Component.Extensions" Version="1.0.4" />
<PackageReference Include="BPA.Component.SDKCommon" Version="1.0.3" />
<PackageReference Include="BPA.Component.WebApiExtensions" Version="1.0.17" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.DTO\BPA.SaaS.Material.Api.DTO.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Model\BPA.SaaS.Material.Api.Model.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.IRepository\BPA.SaaS.Material.Api.IRepository.csproj" />


+ 45
- 0
src/BPA.SaaS.Material.Api.Service/UnitService.cs View File

@@ -0,0 +1,45 @@
using BPA.Component.DTOCommon.BaseDTOs;
using BPA.SaaS.Material.Api.DTO;
using BPA.SaaS.Material.Api.IService;

namespace BPA.SaaS.Material.Api.Service
{
public class UnitService : IUnitService
{
public UnitService()
{
}

public Task<BaseResult<List<UnitDto>>> GetUnitList()
{
throw new NotImplementedException();
}

public Task<BaseResultPage<UnitDtoQuery>> PostType(UnitDtoQuery dto)
{
throw new NotImplementedException();
}

public Task<BaseResult<bool>> AddType(UnitDto dto)
{
throw new NotImplementedException();
}

public Task<BaseResult<bool>> UpdateType(UnitDto dto)
{
throw new NotImplementedException();
}

public Task<BaseResult<bool>> DeleteType(string id)
{
throw new NotImplementedException();
}

public Task<BaseResult<bool>> BatchDeleteType(string[] Ids)
{
throw new NotImplementedException();
}
}
}

+ 19
- 6
src/BPA.SaaS.Material.Api.UnitTest/BPA.SaaS.Material.Api.UnitTest.csproj View File

@@ -1,7 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.9" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.9" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BPA.SaaS.Material.Api.Bootstrap\BPA.SaaS.Material.Api.Bootstrap.csproj" />
<ProjectReference Include="..\BPA.SaaS.Material.Api.Service\BPA.SaaS.Material.Api.Service.csproj" />
</ItemGroup>
</Project>

+ 52
- 0
src/BPA.SaaS.Material.Api.UnitTest/BaseUnitTest.cs View File

@@ -0,0 +1,52 @@
using Autofac;
using Autofac.Extensions.DependencyInjection;
using BPA.Component.ApolloClient;
using BPA.SaaS.Material.Api.Bootstrap;
using BPA.SaaS.Material.Api.Model;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace BPA.SaaS.Material.Api.UnitTest
{
/// <summary>
/// 测试基类
/// </summary>
public class BaseUnitTest
{
#pragma warning disable CS8618
private static ILifetimeScope lifetimeScope;
#pragma warning restore CS8618

public BaseUnitTest()
{
var builder = new ConfigurationBuilder();
builder.AddEnvironmentVariables();
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
Environment.SetEnvironmentVariable("APOLLO_META_SERVER_URL", "http://10.2.1.21:28080");
Environment.SetEnvironmentVariable("APOLLO_COMMON_NAMESPACE", "DEV.Common");
Environment.SetEnvironmentVariable("APP_NAME", "BPA.SaaS.Basic.Api");
var configuration = builder.Build();
configuration.SetupConfiguration();

var services = new ServiceCollection();
services.AddApollo<MaterialApiConfig>();
services.AddBPAMiddlewareServices(new[] {"null arg"});

var factory = new AutofacServiceProviderFactory();
var containerBuild = factory.CreateBuilder(services);
containerBuild.SetupConfigureContainer();
containerBuild.RegisterBuildCallback(scope => lifetimeScope = scope);
var serviceProvider = factory.CreateServiceProvider(containerBuild);
serviceProvider.PreHeatRun();

// var db = serviceProvider.CreateScope().ServiceProvider.GetService<BasicDbSugarClient>();
}

/// <summary>
/// 从容器中获取对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
protected static T GetService<T>() where T : notnull => lifetimeScope.Resolve<T>();
}
}

+ 1
- 1
src/BPA.SaaS.Material.Api.WebApi/BPA.SaaS.Material.Api.WebApi.csproj View File

@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>


+ 32
- 2
src/BPA.SaaS.Material.Api.WebApi/Program.cs View File

@@ -1,8 +1,38 @@
using Autofac;
using Autofac.Extensions.DependencyInjection;
using BPA.Component.WebApiExtensions.Extensions;
using BPA.SaaS.Material.Api.Bootstrap;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Configuration.SetupConfiguration();

// DI
builder.Services.AddBPAMiddlewareServices(args);

// DI config
builder.Services.Configure<ApiBehaviorOptions>(options => { options.AddDefaultBehaviorOptions(); });

// Use Autofac
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => { containerBuilder.SetupConfigureContainer(); });

var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

// Use module
app.UseRouting();
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseBpaHealthChecks();
app.MapControllers();
app.Run();
app.UseGlobalErrorHandler();

// PreRun
((IApplicationBuilder) app).ApplicationServices.PreHeatRun();

app.Run();

+ 7
- 20
src/BPA.SaaS.Material.Api.WebApi/Properties/launchSettings.json View File

@@ -1,30 +1,17 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:11305",
"sslPort": 44315
}
},
"profiles": {
"BPA.SaaS.Stock.Api": {
"BPA.SaaS.Basic.Api": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "https://localhost:7047;http://localhost:5047",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5051",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
"ASPNETCORE_ENVIRONMENT": "Development",
"APOLLO_META_SERVER_URL": "http://10.2.1.21:28080",
"APOLLO_COMMON_NAMESPACE": "DEV.Common",
"APP_NAME": "BPA.SaaS.Material.Api"
}
}
}


+ 25
- 0
src/BPA.SaaS.Material.Api.WebApi/UnitController.cs View File

@@ -0,0 +1,25 @@
using BPA.Component.DTOCommon.BaseDTOs;
using BPA.SaaS.Material.Api.DTO;
using BPA.SaaS.Material.Api.IService;
using Microsoft.AspNetCore.Mvc;

namespace BPA.SaaS.Material.Api.WebApi;

[ApiController]
[Route("api/[controller]/[action]")]
public class UnitController : ControllerBase
{
private readonly IUnitService _unitService;

public UnitController(IUnitService unitService)
{
_unitService = unitService;
}

/// <summary>
/// 获取单位列表
/// </summary>
/// <returns></returns>
[HttpGet]
public Task<BaseResult<List<UnitDto>>> GetUnitList() => _unitService.GetUnitList();
}

+ 0
- 8
src/BPA.SaaS.Material.Api.WebApi/appsettings.Development.json View File

@@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

+ 0
- 9
src/BPA.SaaS.Material.Api.WebApi/appsettings.json View File

@@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

Loading…
Cancel
Save