22 changed files with 915 additions and 4 deletions
@ -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)..]; |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
@ -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>(); |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using Volo.Abp.Http.Modeling; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Cli.ServiceProxying.Flutter; |
||||
|
|
||||
|
public interface IFlutterModelScriptGenerator |
||||
|
{ |
||||
|
string CreateScript( |
||||
|
ApplicationApiDescriptionModel appModel, |
||||
|
ControllerApiDescriptionModel actionModel); |
||||
|
} |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
@ -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."); |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
@ -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()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
import 'package:get/get.dart'; |
||||
|
|
||||
|
class {{ model.application }}Controller extends GetxController { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
export 'binding.dart'; |
||||
|
export 'controller.dart'; |
||||
|
export 'route.dart'; |
||||
|
export 'route.names.dart'; |
||||
|
export 'view.dart'; |
||||
@ -0,0 +1,3 @@ |
|||||
|
class {{ model.application }}Routes { |
||||
|
static String index = '/{{ model.application }}/index'; |
||||
|
} |
||||
@ -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(), |
||||
|
), |
||||
|
]; |
||||
|
} |
||||
@ -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 }}'), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue