Browse Source

feat(cli): add flutter rest api generator commands

pull/844/head
colin 2 years ago
parent
commit
6a66e85aec
  1. 12
      aspnet-core/LINGYUN.MicroService.All.sln
  2. 2
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN.Abp.Cli.csproj
  3. 9
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs
  4. 5
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateProxyCommand.cs
  5. 3
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateViewCommand.cs
  6. 340
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/FlutterModelScriptGenerator.cs
  7. 128
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/FlutterServiceProxyGenerator.cs
  8. 11
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/FlutterServiceProxyOptions.cs
  9. 10
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/IFlutterHttpScriptGenerator.cs
  10. 10
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/IFlutterModelScriptGenerator.cs
  11. 194
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/RestServiceScriptGenerator.cs
  12. 15
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/FlutterGetXViewScriptGenerator.cs
  13. 47
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/FlutterViewGenerator.cs
  14. 11
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/IFlutterGetXViewScriptGenerator.cs
  15. 10
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXBindingScript.tpl
  16. 5
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXControllerScript.tpl
  17. 5
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXIndexScript.tpl
  18. 3
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXRouteNamesScript.tpl
  19. 14
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXRouteScript.tpl
  20. 15
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXViewScript.tpl
  21. 4
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/Properties/launchSettings.json
  22. 66
      aspnet-core/modules/cli/LINGYUN.Abp.Cli/System/StringExtensions.cs

12
aspnet-core/LINGYUN.MicroService.All.sln

@ -639,7 +639,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.EntityChange.Ap
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.EntityChange.Application", "modules\entity-change\LINGYUN.Abp.EntityChange.Application\LINGYUN.Abp.EntityChange.Application.csproj", "{AC41F335-E240-47E0-B409-AFAD1400E626}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.EntityChange.HttpApi", "modules\entity-change\LINGYUN.Abp.EntityChange.HttpApi\LINGYUN.Abp.EntityChange.HttpApi.csproj", "{1D420BA6-2155-4E0D-AAAF-EECC0330A38C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.EntityChange.HttpApi", "modules\entity-change\LINGYUN.Abp.EntityChange.HttpApi\LINGYUN.Abp.EntityChange.HttpApi.csproj", "{1D420BA6-2155-4E0D-AAAF-EECC0330A38C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cli", "cli", "{59627844-A66A-46AC-B882-E8F302D0EC24}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Cli", "modules\cli\LINGYUN.Abp.Cli\LINGYUN.Abp.Cli.csproj", "{2F49E870-DAE2-4D89-98CA-46BBD91C68E2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -1643,6 +1647,10 @@ Global
{1D420BA6-2155-4E0D-AAAF-EECC0330A38C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D420BA6-2155-4E0D-AAAF-EECC0330A38C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D420BA6-2155-4E0D-AAAF-EECC0330A38C}.Release|Any CPU.Build.0 = Release|Any CPU
{2F49E870-DAE2-4D89-98CA-46BBD91C68E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F49E870-DAE2-4D89-98CA-46BBD91C68E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F49E870-DAE2-4D89-98CA-46BBD91C68E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F49E870-DAE2-4D89-98CA-46BBD91C68E2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1953,6 +1961,8 @@ Global
{7779D9BD-5928-49A2-965F-537967004238} = {DD1B10ED-73E2-41BE-928A-46501050FE2A}
{AC41F335-E240-47E0-B409-AFAD1400E626} = {DD1B10ED-73E2-41BE-928A-46501050FE2A}
{1D420BA6-2155-4E0D-AAAF-EECC0330A38C} = {DD1B10ED-73E2-41BE-928A-46501050FE2A}
{59627844-A66A-46AC-B882-E8F302D0EC24} = {C5CAD011-DF84-4914-939C-0C029DCEF26F}
{2F49E870-DAE2-4D89-98CA-46BBD91C68E2} = {59627844-A66A-46AC-B882-E8F302D0EC24}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718}

2
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN.Abp.Cli.csproj

@ -21,6 +21,8 @@
<None Include="README.md" Pack="true" PackagePath="\" />
<None Remove="LINGYUN\Abp\Cli\UI\Vben\Templates\*.tpl" />
<EmbeddedResource Include="LINGYUN\Abp\Cli\UI\Vben\Templates\*.tpl" />
<None Remove="LINGYUN\Abp\Cli\UI\Flutter\GetX\Templates\*.tpl" />
<EmbeddedResource Include="LINGYUN\Abp\Cli\UI\Flutter\GetX\Templates\*.tpl" />
<EmbeddedResource Include="README.md" />
</ItemGroup>

9
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/AbpCliModule.cs

@ -1,7 +1,9 @@
using LINGYUN.Abp.Cli.Commands;
using LINGYUN.Abp.Cli.ServiceProxying.CSharp;
using LINGYUN.Abp.Cli.ServiceProxying.Flutter;
using LINGYUN.Abp.Cli.ServiceProxying.TypeScript;
using LINGYUN.Abp.Cli.UI;
using LINGYUN.Abp.Cli.UI.Flutter.GetX;
using LINGYUN.Abp.Cli.UI.Vben;
using Volo.Abp.Autofac;
using Volo.Abp.Cli;
@ -39,6 +41,7 @@ namespace LINGYUN.Abp.Cli
{
options.Generators[TypeScriptServiceProxyGenerator.Name] = typeof(TypeScriptServiceProxyGenerator);
options.Generators[CSharpServiceProxyGenerator.Name] = typeof(CSharpServiceProxyGenerator);
options.Generators[FlutterServiceProxyGenerator.Name] = typeof(FlutterServiceProxyGenerator);
});
Configure<TypeScriptServiceProxyOptions>(options =>
@ -49,9 +52,15 @@ namespace LINGYUN.Abp.Cli
options.ScriptGenerators[UniAppAxiosHttpApiScriptGenerator.Name] = new UniAppAxiosHttpApiScriptGenerator();
});
Configure<FlutterServiceProxyOptions>(options =>
{
options.ScriptGenerators[RestServiceScriptGenerator.Name] = new RestServiceScriptGenerator();
});
Configure<AbpCliViewGeneratorOptions>(options =>
{
options.Generators[VbenViewGenerator.Name] = typeof(VbenViewGenerator);
options.Generators[FlutterViewGenerator.Name] = typeof(FlutterViewGenerator);
});
}
}

5
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateProxyCommand.cs

@ -121,6 +121,10 @@ public class GenerateProxyCommand : IConsoleCommand, ITransientDependency
sb.AppendLine(" ts");
sb.AppendLine(" -asp|--api-script-proxy <api-script-proxy> The generated api proxy type(axios, vben-axios, vben-dynamic). default: vben-dynamic.");
sb.AppendLine(" -o|--output <output-name> TypeScript file path or folder to place generated code in.");
sb.AppendLine(" flutter");
sb.AppendLine(" -asp|--api-script-proxy <api-script-proxy> The generated api proxy type(dio, rest-service). default: rest-service.");
sb.AppendLine(" -o|--output <output-name> Flutter script file path or folder to place generated code in.");
sb.AppendLine("-p|--provider <client-proxy-provider> The client proxy provider(http, dapr).");
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI");
@ -133,6 +137,7 @@ public class GenerateProxyCommand : IConsoleCommand, ITransientDependency
sb.AppendLine(" labp generate-proxy --folder MyProxies/InnerFolder -url https://localhost:44302/");
sb.AppendLine(" labp generate-proxy -t ts -m identity -o api/identity -url https://localhost:44302/");
sb.AppendLine(" labp generate-proxy -t ts -asp vben-dynamic -m identity -o api/identity -url https://localhost:44302/");
sb.AppendLine(" labp generate-proxy -t flutter -asp rest-service -m identity -o api/identity -url https://localhost:44302/");
return sb.ToString();
}

3
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/Commands/GenerateViewCommand.cs

@ -90,6 +90,8 @@ public class GenerateViewCommand : IConsoleCommand, ITransientDependency
sb.AppendLine("-t|--type <type> The name of generate type (vben-view).");
sb.AppendLine(" vben-view");
sb.AppendLine(" -o|--output <output-name> js/vue file path or folder to place generated code in.");
sb.AppendLine(" flutter-view");
sb.AppendLine(" -o|--output <output-name> flutter file path or folder to place generated code in.");
sb.AppendLine("-u|--url <url> API definition URL from.");
sb.AppendLine("-m|--module <module-name> (default: 'app') The name of the backend module you wish to generate proxies for.");
sb.AppendLine("");
@ -97,6 +99,7 @@ public class GenerateViewCommand : IConsoleCommand, ITransientDependency
sb.AppendLine("");
sb.AppendLine(" labp generate-proxy -t vben-view -m identity -o api/identity -url https://localhost:44302/");
sb.AppendLine(" labp generate-proxy -t flutter-view -m identity -o api/identity -url https://localhost:44302/");
return sb.ToString();
}

340
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/FlutterModelScriptGenerator.cs

@ -0,0 +1,340 @@
using LINGYUN.Abp.Cli.ServiceProxying.TypeScript;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Modeling;
namespace LINGYUN.Abp.Cli.ServiceProxying.Flutter;
public class FlutterModelScriptGenerator : IFlutterModelScriptGenerator, ITransientDependency
{
public ILogger<FlutterModelScriptGenerator> Logger { protected get; set; }
public FlutterModelScriptGenerator()
{
Logger = NullLogger<FlutterModelScriptGenerator>.Instance;
}
public string CreateScript(
ApplicationApiDescriptionModel appModel,
ControllerApiDescriptionModel actionModel)
{
var modelScriptBuilder = new StringBuilder();
// dto基类
modelScriptBuilder.AppendLine("import 'package:core/models/abp.dto.dart';");
// json库
// flutter pub add json_annotation
// flutter pub add build_runner --dev
// flutter pub add json_serializable --dev
// flutter pub run build_runner build --delete-conflicting-outputs
modelScriptBuilder.AppendLine("import 'package:json_annotation/json_annotation.dart';");
// json库实现,
modelScriptBuilder.AppendLine("part 'models.g.dart';");
// modelScriptBuilder.AppendLine($"part '{actionModel.ControllerName.ToSnakeCase()}.g.dart';");
modelScriptBuilder.AppendLine();
var modelBaseTypes = new List<string>();
var modelTypes = new List<string>();
foreach (var action in actionModel.Actions)
{
foreach (var paramter in action.Value.Parameters)
{
if (!TypeScriptModelGenerator.AbpBaseTypes.Contains(paramter.TypeSimple) &&
appModel.Types.TryGetValue(paramter.Type, out var modelType))
{
var modelTypeName = paramter.Type[(paramter.Type.LastIndexOf('.') + 1)..];
if (!modelTypes.Contains(modelTypeName))
{
Logger.LogInformation($" Generating flutter model: {modelTypeName} script.");
modelScriptBuilder.AppendLine(CreateModel(modelTypeName, appModel, modelType));
Logger.LogInformation($" Flutter model: {modelTypeName} generated successful.");
modelTypes.AddIfNotContains(modelTypeName);
}
// 字段类型
foreach (var propertity in modelType.Properties)
{
modelBaseTypes.AddIfNotContains(FindBaseTypes(appModel, propertity));
}
// 类型基类
modelBaseTypes.AddIfNotContains(FindBaseTypes(appModel, modelType));
}
}
foreach (var paramter in action.Value.ParametersOnMethod)
{
if (appModel.Types.TryGetValue(paramter.Type, out var modelType))
{
var modelTypeName = paramter.Type[(paramter.Type.LastIndexOf('.') + 1)..];
if (!modelTypes.Contains(modelTypeName))
{
Logger.LogInformation($" Generating flutter model: {modelTypeName} script.");
modelScriptBuilder.AppendLine(CreateModel(modelTypeName, appModel, modelType));
Logger.LogInformation($" Flutter model: {modelTypeName} generated successful.");
modelTypes.AddIfNotContains(modelTypeName);
}
// 字段类型
foreach (var propertity in modelType.Properties)
{
modelBaseTypes.AddIfNotContains(FindBaseTypes(appModel, propertity));
}
// 类型基类
modelBaseTypes.AddIfNotContains(FindBaseTypes(appModel, modelType));
}
}
// 返回类型
var returnType = action.Value.ReturnValue.TypeSimple;
var abpBaseType = TypeScriptModelGenerator.AbpBaseTypes.FirstOrDefault(basType => returnType.StartsWith(basType));
if (!abpBaseType.IsNullOrWhiteSpace())
{
returnType = returnType
.Replace(abpBaseType, "")
.Replace("<", "")
.Replace(">", "");
}
returnType = returnType.ReplaceFlutterTypeSimple();
if (appModel.Types.TryGetValue(returnType, out var returnBaseType))
{
foreach (var propertity in returnBaseType.Properties)
{
var propType = propertity.TypeSimple;
if (propertity.TypeSimple.StartsWith("[") && propertity.TypeSimple.EndsWith("]"))
{
propType = propType.ReplaceFirst("[", "").RemovePostFix("]", "");
}
if (appModel.Types.TryGetValue(propType, out var propBaseType))
{
modelBaseTypes.AddIfNotContains(propType);
modelBaseTypes.AddIfNotContains(FindBaseTypes(appModel, propBaseType));
}
}
}
modelBaseTypes.AddIfNotContains(returnType);
}
// 基类导出
foreach (var baseType in modelBaseTypes)
{
if (appModel.Types.TryGetValue(baseType, out var modelType))
{
var modelTypeName = baseType[(baseType.LastIndexOf('.') + 1)..];
Logger.LogInformation($" Generating base flutter model: {modelTypeName} script.");
modelScriptBuilder.AppendLine(CreateModel(modelTypeName, appModel, modelType));
Logger.LogInformation($" The base flutter model: {modelTypeName} generate successful.");
}
}
return modelScriptBuilder.ToString();
}
protected virtual string CreateModel(
string modelName,
ApplicationApiDescriptionModel appModel,
TypeApiDescriptionModel model)
{
var modelBuilder = new StringBuilder();
if (model.IsEnum)
{
modelBuilder.AppendLine($"enum {modelName} {{");
for (var index = 0; index < model.EnumNames.Length; index++)
{
var enumName = model.EnumNames[index].Replace("\"", "'");
modelBuilder.AppendFormat(" {0}('{1}', {2})", enumName.ToCamelCase().Replace("'", ""), enumName, model.EnumValues[index]);
if (index == model.EnumNames.Length - 1)
{
modelBuilder.Append(';');
modelBuilder.AppendLine();
}
else
{
modelBuilder.Append(',');
modelBuilder.AppendLine();
}
}
modelBuilder.AppendLine(" final String name;");
modelBuilder.AppendLine(" final int value;");
modelBuilder.AppendLine($" const {modelName}(this.name, this.value);");
modelBuilder.AppendLine("}");
}
else
{
// example:
/*
@JsonSerializable()
class RemoteServiceErrorInfo {
RemoteServiceErrorInfo({
required this.code,
required this.message,
this.details,
this.data,
this.validationErrors,
});
String code;
String message;
String? details;
Map<String, String>? data;
List<RemoteServiceValidationErrorInfo>? validationErrors;
factory RemoteServiceErrorInfo.fromJson(Map<String, dynamic> json) => _$RemoteServiceErrorInfoFromJson(json);
Map<String, dynamic> toJson() => _$RemoteServiceErrorInfoToJson(this);
}
*/
modelBuilder.AppendLine("@JsonSerializable()");
modelBuilder.AppendFormat("class {0} ", modelName);
if (!model.BaseType.IsNullOrWhiteSpace())
{
var baseType = ReplaceAbpBaseType(model.BaseType);
baseType = baseType.ReplaceFlutterTypeSimple();
modelBuilder.AppendFormat("extends {0} ", baseType[(baseType.LastIndexOf('.') + 1)..]);
}
modelBuilder.AppendLine("{");
modelBuilder.AppendLine($" {modelName}({{");
CreateCtorProperties(modelBuilder, appModel, model);
modelBuilder.AppendLine(" });");
CreateProperties(modelBuilder, model.Properties);
modelBuilder.AppendLine($" factory {modelName}.fromJson(Map<String, dynamic> json) => _${modelName}FromJson(json);");
modelBuilder.AppendLine($" Map<String, dynamic> toJson() => _${modelName}ToJson(this);");
modelBuilder.AppendLine("}");
modelBuilder.AppendLine("");
}
return modelBuilder.ToString();
}
protected virtual void CreateCtorProperties(
StringBuilder modelScript,
ApplicationApiDescriptionModel appModel,
TypeApiDescriptionModel model,
bool abstractMember = false)
{
for (var index = 0; index < model.Properties.Length; index++)
{
var isRequired = model.Properties[index].IsRequired;
if (!isRequired)
{
isRequired = !model.Properties[index].Type.Equals("System.String") && model.Properties[index].Type.EndsWith("?");
}
var propCharacter = isRequired ? " required" : " ";
modelScript.AppendFormat("{0} {1}.{2},", propCharacter, abstractMember ? "super" : "this", model.Properties[index].Name.ToCamelCase());
modelScript.AppendLine("");
}
if (!model.BaseType.IsNullOrWhiteSpace())
{
var replaceKey = model.BaseType.MiddleString("<", ">");
if (replaceKey.IsNullOrWhiteSpace())
{
replaceKey = "<TPrimaryKey>";
}
if (appModel.Types.TryGetValue(model.BaseType.Replace(replaceKey, "<T0>"), out var abpBaseModel))
{
CreateCtorProperties(modelScript, appModel, abpBaseModel, true);
}
else
{
if (appModel.Types.TryGetValue(model.BaseType, out var baseModel))
{
CreateCtorProperties(modelScript, appModel, baseModel, true);
}
}
}
}
protected virtual void CreateProperties(StringBuilder modelScript, PropertyApiDescriptionModel[] properties)
{
for (var index = 0; index < properties.Length; index++)
{
var propTypeName = properties[index].Type.ReplaceFlutterType();
if (!properties[index].IsRequired && properties[index].Type.Equals("System.String"))
{
propTypeName += "?";
}
modelScript.AppendFormat(" {0}", propTypeName);
//var propCharacter = properties[index].IsRequired ? " " : "? ";
modelScript.AppendFormat("{0}{1};", " ", properties[index].Name.ToCamelCase());
modelScript.AppendLine("");
}
}
protected virtual bool IsAbpBaseType(string typeSimple) => TypeScriptModelGenerator.AbpBaseTypes.Any(typeSimple.StartsWith);
protected virtual List<string> FindBaseTypes(ApplicationApiDescriptionModel apiModel, TypeApiDescriptionModel model)
{
var types = new List<string>();
if (!model.BaseType.IsNullOrWhiteSpace() &&
!TypeScriptModelGenerator.AbpBaseTypes.Contains(model.BaseType) &&
apiModel.Types.TryGetValue(model.BaseType, out var baseType))
{
types.Add(model.BaseType);
types.AddRange(FindBaseTypes(apiModel, baseType));
}
return types;
}
protected virtual List<string> FindBaseTypes(ApplicationApiDescriptionModel apiModel, PropertyApiDescriptionModel model)
{
var types = new List<string>();
var propertityType = model.Type.ReplaceFlutterType();
if (!TypeScriptModelGenerator.AbpBaseTypes.Contains(propertityType) &&
apiModel.Types.TryGetValue(propertityType, out var baseType))
{
types.Add(propertityType);
types.AddRange(FindBaseTypes(apiModel, baseType));
}
return types;
}
protected virtual string ReplaceAbpBaseType(string typeSimple)
{
var abpBaseType = TypeScriptModelGenerator.AbpBaseTypes.FirstOrDefault(t => t.StartsWith(typeSimple));
if (abpBaseType.IsNullOrWhiteSpace())
{
return typeSimple;
}
return abpBaseType[(abpBaseType.LastIndexOf('.') + 1)..];
}
}

128
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/FlutterServiceProxyGenerator.cs

@ -0,0 +1,128 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Cli;
using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.ServiceProxying;
using Volo.Abp.DependencyInjection;
using Volo.Abp.IO;
using Volo.Abp.Json;
namespace LINGYUN.Abp.Cli.ServiceProxying.Flutter;
public class FlutterServiceProxyGenerator : ServiceProxyGeneratorBase<FlutterServiceProxyGenerator>, ITransientDependency
{
public const string Name = "FLUTTER";
private readonly IFlutterModelScriptGenerator _flutterModelScriptGenerator;
private readonly FlutterServiceProxyOptions _flutterServiceProxyOptions;
public FlutterServiceProxyGenerator(
CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer,
IFlutterModelScriptGenerator flutterModelScriptGenerator,
IOptions<FlutterServiceProxyOptions> flutterServiceProxyOptions)
: base(cliHttpClientFactory, jsonSerializer)
{
_flutterModelScriptGenerator = flutterModelScriptGenerator;
_flutterServiceProxyOptions = flutterServiceProxyOptions.Value;
}
public async override Task GenerateProxyAsync(Volo.Abp.Cli.ServiceProxying.GenerateProxyArgs args)
{
var applicationApiDescriptionModel = await GetApplicationApiDescriptionModelAsync(
args,
new Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto
{
IncludeTypes = true
});
var outputFolderRoot = args.Output;
foreach (var module in applicationApiDescriptionModel.Modules)
{
Logger.LogInformation($"Generating flutter model script with remote service: {module.Value.RemoteServiceName}.");
foreach (var controller in module.Value.Controllers)
{
Logger.LogInformation($" [{module.Value.RemoteServiceName}], Generating flutter model script with controller: {controller.Value.ControllerName}.");
var modelScript = _flutterModelScriptGenerator
.CreateScript(applicationApiDescriptionModel, controller.Value);
Logger.LogInformation($" [{module.Value.RemoteServiceName}], {controller.Value.ControllerName} model script generated.");
var scriptPath = Path.Combine(
outputFolderRoot,
module.Value.RemoteServiceName.ToKebabCase(),
controller.Value.ControllerGroupName.ToKebabCase());
DirectoryHelper.CreateIfNotExists(scriptPath);
var modelScriptFile = Path.Combine(scriptPath, "models.dart");
Logger.LogInformation($"The flutter model script output file: {modelScriptFile}.");
Logger.LogInformation($"Saving flutter model script: {modelScriptFile}.");
FileHelper.DeleteIfExists(modelScriptFile);
await File.AppendAllTextAsync(modelScriptFile, modelScript);
Logger.LogInformation($"Saved flutter model script: {modelScriptFile} has successful.");
// api script
var apiScriptType = (args as GenerateProxyArgs).ApiScriptProxy;
if (!_flutterServiceProxyOptions.ScriptGenerators.ContainsKey(apiScriptType))
{
throw new CliUsageException($"Option Api Script Type {apiScriptType} value is invalid.");
}
var httpApiScriptProxy = _flutterServiceProxyOptions.ScriptGenerators[apiScriptType];
Logger.LogInformation($" [{module.Value.RemoteServiceName}], Generating flutter api script with {apiScriptType}.");
Logger.LogInformation($" [{module.Value.RemoteServiceName}], Generating flutter api script with controller: {controller.Value.ControllerName}.");
var apiScript = httpApiScriptProxy.CreateScript(
applicationApiDescriptionModel,
module.Value,
controller.Value);
Logger.LogInformation($" [{module.Value.RemoteServiceName}], {controller.Value.ControllerName} api script generated.");
DirectoryHelper.CreateIfNotExists(scriptPath);
var apiScriptFile = Path.Combine(scriptPath, "service.dart");
Logger.LogInformation($"The flutter api script output file: {apiScriptFile}.");
Logger.LogInformation($"Saving flutter api script: {apiScriptFile}.");
FileHelper.DeleteIfExists(apiScriptFile);
await File.AppendAllTextAsync(apiScriptFile, apiScript);
var scriptExportFile = Path.Combine(scriptPath, "index.dart");
Logger.LogInformation($"The flutter export script output file: {scriptExportFile}.");
Logger.LogInformation($"Saving flutter export script: {scriptExportFile}.");
FileHelper.DeleteIfExists(scriptExportFile);
var scriptExportScript = new StringBuilder();
scriptExportScript.AppendLine("export 'models.dart';");
scriptExportScript.AppendLine("export 'service.dart';");
await File.AppendAllTextAsync(scriptExportFile, scriptExportScript.ToString());
Logger.LogInformation($"Saved api script: {apiScriptFile} has successful.");
}
}
Logger.LogInformation($"Generate type script proxy has completed.");
}
protected override ServiceType? GetDefaultServiceType(Volo.Abp.Cli.ServiceProxying.GenerateProxyArgs args)
{
return ServiceType.Application;
}
}

11
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/FlutterServiceProxyOptions.cs

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.Cli.ServiceProxying.Flutter;
public class FlutterServiceProxyOptions
{
public IDictionary<string, IFlutterHttpScriptGenerator> ScriptGenerators { get; }
public FlutterServiceProxyOptions()
{
ScriptGenerators = new Dictionary<string, IFlutterHttpScriptGenerator>();
}
}

10
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/IFlutterHttpScriptGenerator.cs

@ -0,0 +1,10 @@
using Volo.Abp.Http.Modeling;
namespace LINGYUN.Abp.Cli.ServiceProxying.Flutter;
public interface IFlutterHttpScriptGenerator
{
string CreateScript(
ApplicationApiDescriptionModel appModel,
ModuleApiDescriptionModel apiModel,
ControllerApiDescriptionModel actionModel);
}

10
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/IFlutterModelScriptGenerator.cs

@ -0,0 +1,10 @@
using Volo.Abp.Http.Modeling;
namespace LINGYUN.Abp.Cli.ServiceProxying.Flutter;
public interface IFlutterModelScriptGenerator
{
string CreateScript(
ApplicationApiDescriptionModel appModel,
ControllerApiDescriptionModel actionModel);
}

194
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/ServiceProxying/Flutter/RestServiceScriptGenerator.cs

@ -0,0 +1,194 @@
using LINGYUN.Abp.Cli.ServiceProxying.TypeScript;
using System;
using System.Linq;
using System.Text;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Modeling;
namespace LINGYUN.Abp.Cli.ServiceProxying.Flutter;
public class RestServiceScriptGenerator : IFlutterHttpScriptGenerator, ITransientDependency
{
public const string Name = "rest-service";
public string CreateScript(
ApplicationApiDescriptionModel appModel,
ModuleApiDescriptionModel apiModel,
ControllerApiDescriptionModel actionModel)
{
var apiScriptBuilder = new StringBuilder();
apiScriptBuilder.AppendLine($"import 'models.dart';");
apiScriptBuilder.AppendLine("import 'package:core/modles/abp.dto.dart';");
apiScriptBuilder.AppendLine("import 'package:core/services/rest.service.dart';");
apiScriptBuilder.AppendLine("import 'package:dio/dio.dart';");
apiScriptBuilder.AppendLine("import 'package:get/get.dart';");
apiScriptBuilder.AppendLine("");
apiScriptBuilder.AppendLine($"class {actionModel.ControllerName.ToPascalCase()}Service {{");
apiScriptBuilder.AppendLine(" RestService get _restService => Get.find();");
apiScriptBuilder.AppendLine("");
foreach (var action in actionModel.Actions)
{
var url = action.Value.Url.EnsureStartsWith('/');
var isFormatUrl = false;
var formatUrlIndex = 0;
var apiRetuanName = action.Value.ReturnValue.TypeSimple;
if (apiRetuanName.Contains("ListResultDto"))
{
apiRetuanName = apiRetuanName[(apiRetuanName.IndexOf("<") + 1)..];
apiRetuanName = apiRetuanName[..^1];
apiRetuanName = apiRetuanName[(apiRetuanName.LastIndexOf('.') + 1)..];
apiRetuanName = $"ListResultDto<{apiRetuanName}>";
}
else if (apiRetuanName.Contains("PagedResultDto"))
{
apiRetuanName = apiRetuanName[(apiRetuanName.IndexOf("<") + 1)..];
apiRetuanName = apiRetuanName[..^1];
apiRetuanName = apiRetuanName[(apiRetuanName.LastIndexOf('.') + 1)..];
apiRetuanName = $"PagedResultDto<{apiRetuanName}>";
}
else
{
apiRetuanName = apiRetuanName[(apiRetuanName.LastIndexOf('.') + 1)..];
}
apiRetuanName = apiRetuanName.Replace("Void", "void");
apiScriptBuilder.AppendFormat(" Future<{0}> {1}(", apiRetuanName, action.Value.UniqueName.ToCamelCase());
var optionalParams = action.Value.ParametersOnMethod.Where(p => p.IsOptional).ToList();
var notOptionalParams = action.Value.ParametersOnMethod.Where(p => !p.IsOptional).ToList();
for (var index = 0; index < notOptionalParams.Count; index++)
{
var paramter = notOptionalParams[index];
var apiParamName = paramter.Type.ReplaceFlutterType();
apiParamName = apiParamName[(apiParamName.LastIndexOf('.') + 1)..];
apiScriptBuilder.AppendFormat("{0} {1}", apiParamName, paramter.Name);
if (index < notOptionalParams.Count - 1)
{
apiScriptBuilder.Append(", ");
}
// 需要格式化url
var formatUrlPath = paramter.Name;
if (url.Contains(formatUrlPath))
{
formatUrlIndex = url.IndexOf(formatUrlPath) + formatUrlPath.Length;
// 'api/platform/packages/{id}/blob/{Name}' => `api/platform/packages/$id/blob/{input.name}`
url = url.Replace(formatUrlPath, $"${formatUrlPath}").Replace($"{{${formatUrlPath}}}", $"${formatUrlPath}");
isFormatUrl = true;
}
if (formatUrlIndex >= 0 && formatUrlIndex + formatUrlPath.Length <= url.Length)
{
var formatUrl = url[(formatUrlIndex + formatUrlPath.Length)..].MiddleString("{", "}");
if (!formatUrl.IsNullOrWhiteSpace())
{
if (appModel.Types.TryGetValue(paramter.Type, out var paramType))
{
var formatParamInUrl = paramType.Properties
.FirstOrDefault(p => formatUrl.Contains(p.Name));
if (formatParamInUrl != null)
{
// 'api/platform/packages/xxx/blob/{Name}' => `api/platform/packages/xxx/blob/${input.name}`
url = url.Replace(
formatUrl,
string.Concat("${", paramter.Name, ".", formatParamInUrl.Name.ToCamelCase(), "}"));
isFormatUrl = true;
}
}
}
}
}
if (optionalParams.Count > 0)
{
apiScriptBuilder.AppendLine(",{");
for (var index = 0; index < optionalParams.Count; index++)
{
var paramter = optionalParams[index];
var apiParamName = paramter.Type.ReplaceFlutterType();
apiParamName = apiParamName[(apiParamName.LastIndexOf('.') + 1)..];
apiScriptBuilder.AppendFormat("{0} {1}", apiParamName, paramter.Name);
apiScriptBuilder.AppendLine("");
}
apiScriptBuilder.AppendLine("}");
}
apiScriptBuilder.AppendLine(") {");
apiScriptBuilder.AppendFormat(" return _restService.{0}('{1}',", action.Value.HttpMethod.ToLower(), url);
apiScriptBuilder.AppendLine("");
var inPathParams = action.Value.Parameters
.Where(p => TypeScriptModelGenerator.DataInParamSources.Contains(p.BindingSourceId))
.DistinctBy(p => p.NameOnMethod);
var inBodyParams = action.Value.Parameters.Where(p => p.BindingSourceId == "Body");
var inFormParams = action.Value.Parameters
.Where(p => TypeScriptModelGenerator.DataInFormSources.Contains(p.BindingSourceId))
.DistinctBy(p => p.NameOnMethod);
if (!isFormatUrl && inPathParams.Any())
{
apiScriptBuilder.AppendLine(" queryParameters: {");
foreach (var paramter in inPathParams)
{
apiScriptBuilder.AppendFormat(" {0}: {1}.{2},", paramter.Name, paramter.NameOnMethod, paramter.Name);
apiScriptBuilder.AppendLine("");
}
apiScriptBuilder.AppendLine(" },");
}
if (inBodyParams.Any())
{
apiScriptBuilder.AppendFormat(" data: {0},", inBodyParams.First().NameOnMethod);
apiScriptBuilder.AppendLine("");
}
if (inFormParams.Any())
{
apiScriptBuilder.AppendFormat(" data: {0},", inFormParams.First().NameOnMethod);
apiScriptBuilder.AppendLine("");
}
if (action.Value.AllowAnonymous == true || inFormParams.Any())
{
apiScriptBuilder.AppendLine(" options: Options(");
if (action.Value.AllowAnonymous == true)
{
apiScriptBuilder.AppendLine(" extra: {");
apiScriptBuilder.AppendLine(" 'ignore_token': true");
apiScriptBuilder.AppendLine(" },");
}
apiScriptBuilder.AppendLine(" headers: {");
if (inFormParams.Any())
{
apiScriptBuilder.AppendLine(" 'Content-type': 'multipart/form-data'");
}
apiScriptBuilder.AppendLine(" },");
apiScriptBuilder.AppendLine($" ),");
}
if (apiRetuanName.Equals("void"))
{
apiScriptBuilder.AppendLine($" );");
}
else
{
apiScriptBuilder.AppendLine($" ).then((json) => {apiRetuanName}.fromJson(json));");
}
apiScriptBuilder.AppendLine(" }");
apiScriptBuilder.AppendLine();
}
apiScriptBuilder.AppendLine("}");
apiScriptBuilder.AppendLine();
return apiScriptBuilder.ToString();
}
}

15
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/FlutterGetXViewScriptGenerator.cs

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Http.Modeling;
namespace LINGYUN.Abp.Cli.UI.Flutter.GetX;
public class FlutterGetXViewScriptGenerator : IFlutterGetXViewScriptGenerator
{
public Task<string> CreateView(GenerateViewArgs args, ApplicationApiDescriptionModel appModel, ControllerApiDescriptionModel controllerModel)
{
throw new NotImplementedException();
}
}

47
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/FlutterViewGenerator.cs

@ -0,0 +1,47 @@
using Microsoft.Extensions.Logging;
using System.IO;
using System.Threading.Tasks;
using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.ServiceProxying;
using Volo.Abp.DependencyInjection;
using Volo.Abp.IO;
using Volo.Abp.Json;
namespace LINGYUN.Abp.Cli.UI.Flutter.GetX;
public class FlutterViewGenerator : ViewGeneratorBase<FlutterViewGenerator>, ISingletonDependency
{
public const string Name = "Flutter-View";
protected IFlutterGetXViewScriptGenerator ViewScriptGenerator { get; }
public FlutterViewGenerator(
CliHttpClientFactory cliHttpClientFactory,
IJsonSerializer jsonSerializer,
IFlutterGetXViewScriptGenerator viewScriptGenerator)
: base(cliHttpClientFactory, jsonSerializer)
{
ViewScriptGenerator = viewScriptGenerator;
}
public override Task GenerateAsync(GenerateViewArgs args)
{
throw new System.NotImplementedException();
}
protected override ServiceType? GetDefaultServiceType(GenerateViewArgs args)
{
return ServiceType.Application;
}
private async Task CreateAndSaveSciptToDisk(string scriptFile, string script)
{
Logger.LogInformation($"The script output file: {scriptFile}.");
Logger.LogInformation($"Saving script: {scriptFile}.");
FileHelper.DeleteIfExists(scriptFile);
await File.AppendAllTextAsync(scriptFile, script);
Logger.LogInformation($"Saved script: {scriptFile} has successful.");
}
}

11
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/IFlutterGetXViewScriptGenerator.cs

@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Volo.Abp.Http.Modeling;
namespace LINGYUN.Abp.Cli.UI.Flutter.GetX;
public interface IFlutterGetXViewScriptGenerator
{
Task<string> CreateView(
GenerateViewArgs args,
ApplicationApiDescriptionModel appModel,
ControllerApiDescriptionModel controllerModel);
}

10
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXBindingScript.tpl

@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'controller.dart';
class {{ model.application }}Binding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => {{ model.application }}Controller());
}
}

5
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXControllerScript.tpl

@ -0,0 +1,5 @@
import 'package:get/get.dart';
class {{ model.application }}Controller extends GetxController {
}

5
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXIndexScript.tpl

@ -0,0 +1,5 @@
export 'binding.dart';
export 'controller.dart';
export 'route.dart';
export 'route.names.dart';
export 'view.dart';

3
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXRouteNamesScript.tpl

@ -0,0 +1,3 @@
class {{ model.application }}Routes {
static String index = '/{{ model.application }}/index';
}

14
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXRouteScript.tpl

@ -0,0 +1,14 @@
import 'package:get/get.dart';
import 'view.dart';
import 'route.names.dart';
class {{ model.application }}Route {
static List<GetPage> routes = [
GetPage(
name: {{ model.application }}Routes.index,
page: () => const {{ model.application }}Page(),
binding: {{ model.application }}Binding(),
),
];
}

15
aspnet-core/modules/cli/LINGYUN.Abp.Cli/LINGYUN/Abp/Cli/UI/Flutter/GetX/Templates/FlutterGetXViewScript.tpl

@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import 'controller.dart';
import 'package:get/get.dart';
class {{ model.application }}Page extends GetView<{{ model.application }}Controller> {
const {{ model.application }}Page({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('{{ model.application }}'),
);
}
}

4
aspnet-core/modules/cli/LINGYUN.Abp.Cli/Properties/launchSettings.json

@ -2,8 +2,8 @@
"profiles": {
"LINGYUN.Abp.Cli": {
"commandName": "Project",
"commandLineArgs": "generate-view -t vben-view -m auditing -o D:\\Projects\\Development\\view-script -url http://127.0.0.1:30000/"
//"commandLineArgs": "generate-proxy -t ts -asp vben-dynamic -u http://127.0.0.1:30000 -m platform -o D:\\Projects\\Development\\type-script"
//"commandLineArgs": "generate-view -t vben-view -m auditing -o D:\\Projects\\Development\\view-script -url http://127.0.0.1:30000/"
"commandLineArgs": "generate-proxy -t flutter -asp rest-service -u http://99.11.10.2:30000 -m platform -o D:\\Projects\\Development\\flutter-script -skip-cli-version-check"
}
}
}

66
aspnet-core/modules/cli/LINGYUN.Abp.Cli/System/StringExtensions.cs

@ -5,11 +5,11 @@ internal static class StringExtensions
public static string ReplaceTypeSimple(this string typeSimple)
{
typeSimple = typeSimple
.Replace("?", "")
.Replace("<System.String>", "<string>")
.Replace("<System.Guid>", "<string>")
.Replace("<System.Int32>", "<number>")
.Replace("<System.Int64>", "<number>")
.Replace("<System.DateTime>", "<Date>")
.Replace("IRemoteStreamContent", "Blob")
.Replace("{string:string}", "Dictionary<string, string>")
.Replace("{number:string}", "Dictionary<number, string>")
@ -25,6 +25,70 @@ internal static class StringExtensions
return typeSimple;
}
public static string ReplaceFlutterType(this string type)
{
type = type
.Replace("{System.String:System.String}", "Map<String, String>")
.Replace("{System.Int32:System.String}", "Map<num, String>")
.Replace("{System.Int64:System.String}", "Map<num, String>")
.Replace("{System.String:System.Int32}", "Map<String, num>")
.Replace("{System.String:System.Int64}", "Map<String, num>")
.Replace("{System.String:System.Object}", "Map<String, dynamic>")
.Replace("System.String", "String")
.Replace("System.Guid", "String")
.Replace("System.Int32", "num")
.Replace("System.Int64", "num")
.Replace("System.DateTime", "DateTime")
.Replace("System.Boolean", "bool")
.Replace("System.Object", "dynamic")
.Replace("IRemoteStreamContent", "Blob");
if (type.StartsWith("[") && type.EndsWith("]"))
{
if (type.LastIndexOf('.') >= 0)
{
type = type[(type.LastIndexOf('.') + 1)..];
}
type = type.RemovePreFix("[", "<").RemovePostFix("]", ">");
type = "List<" + type + ">";
}
else
{
if (type.LastIndexOf('.') >= 0)
{
type = type[(type.LastIndexOf('.') + 1)..];
}
}
return type;
}
public static string ReplaceFlutterTypeSimple(this string typeSimple)
{
typeSimple = typeSimple
.Replace("?", "")
.Replace("<System.String>", "<String>")
.Replace("<System.Guid>", "<String>")
.Replace("<System.Int32>", "<num>")
.Replace("<System.Int64>", "<num>")
.Replace("<System.DateTime>", "<DateTime>")
.Replace("IRemoteStreamContent", "Blob")
.Replace("{string:string}", "Map<String, String>")
.Replace("{number:string}", "Map<int, String>")
.Replace("{string:number}", "Map<String, num>")
.Replace("{string:object}", "Map<String, dynamic>");
if (typeSimple.StartsWith("[") && typeSimple.EndsWith("]"))
{
typeSimple = typeSimple.RemovePreFix("[", "<").RemovePostFix("]", ">");
typeSimple = "List<" + typeSimple + ">";
}
return typeSimple;
}
public static string MiddleString(this string sourse, string startstr, string endstr)
{
var result = string.Empty;

Loading…
Cancel
Save