【特性】支持从excel文件的标题头里直接读取定义,定义和数据一体了。简化写记录定义的麻烦

main
walon 2021-07-30 18:22:08 +08:00
parent 94197d9968
commit 2ca0cebe99
8 changed files with 199 additions and 26 deletions

View File

@ -52,10 +52,46 @@ namespace Luban.Job.Cfg.DataSources.Excel
} }
} }
public Sheet LoadFirstSheet(string rawUrl, string sheetName, Stream stream)
{
s_logger.Trace("{filename} {sheet}", rawUrl, sheetName);
RawUrl = rawUrl;
string ext = Path.GetExtension(rawUrl);
using (var reader = ext != ".csv" ? ExcelReaderFactory.CreateReader(stream) : ExcelReaderFactory.CreateCsvReader(stream))
{
do
{
if (sheetName == null || reader.Name == sheetName)
{
try
{
var sheet = ReadSheetHeader(rawUrl, reader);
if (sheet != null)
{
return sheet;
}
}
catch (Exception e)
{
throw new Exception($"excel:{rawUrl} sheet:{reader.Name} 读取失败. ==> {e.Message}", e);
}
}
} while (reader.NextResult());
}
throw new Exception($"excel:{rawUrl} 不包含有效的单元薄(有效单元薄的A0单元格必须是##).");
}
private Sheet ReadSheet(string url, IExcelDataReader reader) private Sheet ReadSheet(string url, IExcelDataReader reader)
{ {
var sheet = new Sheet(url, reader.Name ?? ""); var sheet = new Sheet(url, reader.Name ?? "");
return sheet.Load(reader) ? sheet : null; return sheet.Load(reader, false) ? sheet : null;
}
private Sheet ReadSheetHeader(string url, IExcelDataReader reader)
{
var sheet = new Sheet(url, reader.Name ?? "");
return sheet.Load(reader, true) ? sheet : null;
} }
public override List<Record> ReadMulti(TBean type) public override List<Record> ReadMulti(TBean type)

View File

@ -25,6 +25,10 @@ namespace Luban.Job.Cfg.DataSources.Excel
private Title _rootTitle; private Title _rootTitle;
public List<Title> RootFields => _rootTitle.SubTitleList;
public List<List<Cell>> RowColumns => _rowColumns;
public class Title public class Title
{ {
public int FromIndex { get; set; } public int FromIndex { get; set; }
@ -213,7 +217,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
this.Name = name; this.Name = name;
} }
public bool Load(IExcelDataReader reader) public bool Load(IExcelDataReader reader, bool headerOnly)
{ {
//s_logger.Info("read sheet:{sheet}", reader.Name); //s_logger.Info("read sheet:{sheet}", reader.Name);
if (!ParseMeta(reader)) if (!ParseMeta(reader))
@ -221,7 +225,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
return false; return false;
} }
LoadRemainRows(reader); LoadRemainRows(reader, headerOnly);
return true; return true;
} }
@ -368,7 +372,7 @@ namespace Luban.Job.Cfg.DataSources.Excel
} }
private void LoadRemainRows(IExcelDataReader reader) private void LoadRemainRows(IExcelDataReader reader, bool headerOnly)
{ {
// TODO 优化性能 // TODO 优化性能
// 几个思路 // 几个思路
@ -508,7 +512,14 @@ namespace Luban.Job.Cfg.DataSources.Excel
//} //}
// 删除标题行 // 删除标题行
if (headerOnly)
{
this._rowColumns.RemoveRange(0, Math.Min(titleRowNum, this._rowColumns.Count));
}
else
{
this._rowColumns.RemoveRange(0, Math.Min(TitleRows + titleRowNum - 1, this._rowColumns.Count)); this._rowColumns.RemoveRange(0, Math.Min(TitleRows + titleRowNum - 1, this._rowColumns.Count));
}
// 删除忽略的记录行 // 删除忽略的记录行
this._rowColumns.RemoveAll(row => AbstractDataSource.IsIgnoreTag(GetRowTag(row))); this._rowColumns.RemoveAll(row => AbstractDataSource.IsIgnoreTag(GetRowTag(row)));

View File

@ -1,11 +1,15 @@
using Luban.Common.Utils; using Luban.Common.Utils;
using Luban.Job.Cfg.DataSources.Excel;
using Luban.Job.Cfg.RawDefs; using Luban.Job.Cfg.RawDefs;
using Luban.Job.Cfg.Utils;
using Luban.Job.Common.Defs; using Luban.Job.Common.Defs;
using Luban.Job.Common.RawDefs; using Luban.Job.Common.RawDefs;
using Luban.Server.Common; using Luban.Server.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq; using System.Xml.Linq;
namespace Luban.Job.Cfg.Defs namespace Luban.Job.Cfg.Defs
@ -152,9 +156,6 @@ namespace Luban.Job.Cfg.Defs
_cfgServices.Add(new Service() { Name = name, Manager = manager, Groups = groups, Refs = refs }); _cfgServices.Add(new Service() { Name = name, Manager = manager, Groups = groups, Refs = refs });
} }
private readonly List<string> _tableOptionalAttrs = new List<string> { "index", "mode", "group", "branch_input", "comment" };
private readonly List<string> _tableRequireAttrs = new List<string> { "name", "value", "input" };
private readonly Dictionary<string, Table> _name2CfgTable = new Dictionary<string, Table>(); private readonly Dictionary<string, Table> _name2CfgTable = new Dictionary<string, Table>();
@ -213,6 +214,9 @@ namespace Luban.Job.Cfg.Defs
return mode; return mode;
} }
private readonly List<string> _tableOptionalAttrs = new List<string> { "index", "mode", "group", "branch_input", "comment", "define_from_file" };
private readonly List<string> _tableRequireAttrs = new List<string> { "name", "value", "input" };
private void AddTable(XElement e) private void AddTable(XElement e)
{ {
ValidAttrKeys(e, _tableOptionalAttrs, _tableRequireAttrs); ValidAttrKeys(e, _tableOptionalAttrs, _tableRequireAttrs);
@ -222,6 +226,7 @@ namespace Luban.Job.Cfg.Defs
Name = XmlUtil.GetRequiredAttribute(e, "name"), Name = XmlUtil.GetRequiredAttribute(e, "name"),
Namespace = CurNamespace, Namespace = CurNamespace,
ValueType = XmlUtil.GetRequiredAttribute(e, "value"), ValueType = XmlUtil.GetRequiredAttribute(e, "value"),
LoadDefineFromFile = XmlUtil.GetOptionBoolAttribute(e, "define_from_file"),
Index = XmlUtil.GetOptionalAttribute(e, "index"), Index = XmlUtil.GetOptionalAttribute(e, "index"),
Groups = CreateGroups(XmlUtil.GetOptionalAttribute(e, "group")), Groups = CreateGroups(XmlUtil.GetOptionalAttribute(e, "group")),
Comment = XmlUtil.GetOptionalAttribute(e, "comment"), Comment = XmlUtil.GetOptionalAttribute(e, "comment"),
@ -266,6 +271,124 @@ namespace Luban.Job.Cfg.Defs
private async Task<CfgBean> LoadDefineFromFileAsync(Table table, string dataDir)
{
var inputFileInfos = await DataLoaderUtil.CollectInputFilesAsync(this.Agent, table.InputFiles, dataDir);
var file = inputFileInfos[0];
var source = new ExcelDataSource();
var stream = new MemoryStream(await this.Agent.GetFromCacheOrReadAllBytesAsync(file.ActualFile, file.MD5));
var sheet = source.LoadFirstSheet(file.OriginFile, file.SheetName, stream);
var cb = new CfgBean() { Namespace = table.Namespace, Name = table.ValueType, };
var rc = sheet.RowColumns;
var attrRow = sheet.RowColumns[0];
var titleRow = sheet.RowColumns[1];
var descRow = sheet.RowColumns[2];
foreach (var f in sheet.RootFields)
{
var cf = new CfgField() { Name = f.Name, Id = 0 };
string[] attrs = attrRow[f.FromIndex].Value?.ToString().Split('&');
if (attrs.Length == 0 || string.IsNullOrWhiteSpace(attrs[0]))
{
throw new Exception($"table:{table.Name} file:{file.OriginFile} title:{f.Name} type missing!");
}
// 优先取desc行如果为空,则取title行
cf.Comment = descRow[f.FromIndex].Value?.ToString();
if (string.IsNullOrWhiteSpace(cf.Comment))
{
cf.Comment = titleRow[f.FromIndex].Value?.ToString();
}
if (string.IsNullOrWhiteSpace(cf.Comment))
{
cf.Comment = "";
}
cf.Type = attrs[0];
for (int i = 1; i < attrs.Length; i++)
{
var pair = attrs[i].Split('=');
if (pair.Length != 2)
{
throw new Exception($"table:{table.Name} file:{file.OriginFile} title:{f.Name} attr: '{attrs[i]}' is invalid!");
}
var attrName = pair[0];
var attrValue = pair[1];
switch (attrName)
{
case "index":
{
cf.Index = attrValue;
break;
}
case "sep":
{
cf.Sep = attrValue;
break;
}
case "ref":
case "path":
case "range":
{
var validator = new Validator() { Type = attrName, Rule = attrValue };
cf.Validators.Add(validator);
cf.ValueValidators.Add(validator);
break;
}
case "multi_lines":
{
cf.IsMultiRow = attrValue == "1" || attrValue.Equals("true", StringComparison.OrdinalIgnoreCase);
break;
}
case "group":
{
cf.Groups = attrValue.Split(',').Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s)).ToList();
break;
}
case "comment":
{
cf.Comment = attrValue;
break;
}
case "convert":
{
cf.Converter = attrValue;
break;
}
default:
{
throw new Exception($"table:{table.Name} file:{file.OriginFile} title:{f.Name} attr: '{attrs[i]}' is invalid!");
}
}
}
cb.Fields.Add(cf);
}
return cb;
}
public async Task LoadDefinesFromFileAsync(string dataDir)
{
var loadTasks = new List<Task<CfgBean>>();
foreach (var table in this._cfgTables.Where(t => t.LoadDefineFromFile))
{
loadTasks.Add(Task.Run(async () => await this.LoadDefineFromFileAsync(table, dataDir)));
}
foreach (var task in loadTasks)
{
this._beans.Add(await task);
}
}
private static readonly List<string> _fieldOptionalAttrs = new List<string> { private static readonly List<string> _fieldOptionalAttrs = new List<string> {
"index", "sep", "validator", "key_validator", "value_validator", "index", "sep", "validator", "key_validator", "value_validator",
"ref", "path", "range", "multi_rows", "group", "res", "convert", "comment" }; "ref", "path", "range", "multi_rows", "group", "res", "convert", "comment" };

View File

@ -261,6 +261,7 @@ namespace Luban.Job.Cfg
timer.StartPhase("build defines"); timer.StartPhase("build defines");
var loader = new CfgDefLoader(agent); var loader = new CfgDefLoader(agent);
await loader.LoadAsync(args.DefineFile); await loader.LoadAsync(args.DefineFile);
await loader.LoadDefinesFromFileAsync(inputDataDir);
timer.EndPhaseAndLog(); timer.EndPhaseAndLog();
var rawDefines = loader.BuildDefines(); var rawDefines = loader.BuildDefines();

View File

@ -29,6 +29,8 @@ namespace Luban.Job.Cfg.RawDefs
public string ValueType { get; set; } public string ValueType { get; set; }
public bool LoadDefineFromFile { get; set; }
public ETableMode Mode { get; set; } public ETableMode Mode { get; set; }
public string Comment { get; set; } public string Comment { get; set; }

View File

@ -18,7 +18,7 @@ import ByteBuf = Bright.Serialization.ByteBuf";
} }
public const string BrightByteBufPathImportsFormat = "import ByteBuf from '{0}/serialization/ByteBuf'"; public const string BrightByteBufPathImportsFormat = "import ByteBuf from '{0}/serialization/ByteBuf'";
public const string BrightByteBufPackageImportsFormat = "import ByteBuf from '{0}'"; public const string BrightByteBufPackageImportsFormat = "import {{ByteBuf}} from '{0}'";
public static string GetVectorImports(string path, string package) public static string GetVectorImports(string path, string package)
{ {
@ -31,9 +31,9 @@ import Vector3 from '{0}/math/Vector3'
import Vector4 from '{0}/math/Vector4' import Vector4 from '{0}/math/Vector4'
"; ";
public const string VectorPackageImportsFormat = @" public const string VectorPackageImportsFormat = @"
import Vector2 from '{0}' import {{Vector2}} from '{0}'
import Vector3 from '{0}' import {{Vector3}} from '{0}'
import Vector4 from '{0}' import {{Vector4}} from '{0}'
"; ";
public static string GetSerializeImports(string path, string package) public static string GetSerializeImports(string path, string package)
{ {
@ -41,14 +41,14 @@ import Vector4 from '{0}'
} }
public const string SerializePathImportsFormat = @"import BeanBase from '{0}/serialization/BeanBase'"; public const string SerializePathImportsFormat = @"import BeanBase from '{0}/serialization/BeanBase'";
public const string SerializePackageImportsFormat = @"import BeanBase from '{0}'"; public const string SerializePackageImportsFormat = @"import {{BeanBase}} from '{0}'";
public static string GetProtocolImports(string path, string package) public static string GetProtocolImports(string path, string package)
{ {
return string.IsNullOrEmpty(package) ? string.Format(ProtocolPathImportsFormat, path) : string.Format(ProtocolPackageImportsFormat, package); return string.IsNullOrEmpty(package) ? string.Format(ProtocolPathImportsFormat, path) : string.Format(ProtocolPackageImportsFormat, package);
} }
public const string ProtocolPathImportsFormat = "import Protocol from '{0}/net/Protocol'"; public const string ProtocolPathImportsFormat = "import Protocol from '{0}/net/Protocol'";
public const string ProtocolPackageImportsFormat = "import Protocol from '{0}'"; public const string ProtocolPackageImportsFormat = "import {{Protocol}} from '{0}'";
public const string SerializeTypes = @" public const string SerializeTypes = @"
export interface ISerializable { export interface ISerializable {

View File

@ -106,7 +106,7 @@ export {{x.ts_class_modifier}} class {{name}} extends {{if parent_def_type}} {{x
if (value == null) throw new Error() if (value == null) throw new Error()
{{~end~}} {{~end~}}
if (this.isManaged) { if (this.isManaged) {
let txn = TransactionContext.current let txn = TransactionContext.current!
txn.putFieldLong(this.getObjectId() + {{field.id}}, new {{name}}.{{field.log_type}}(this, value)) txn.putFieldLong(this.getObjectId() + {{field.id}}, new {{name}}.{{field.log_type}}(this, value))
{{~if ctype.need_set_children_root~}} {{~if ctype.need_set_children_root~}}
value?.initRoot(this.getRoot()) value?.initRoot(this.getRoot())

View File

@ -162,19 +162,19 @@ namespace Luban.Job.Db
else else
{ {
fileContent.Add($"import {{FieldLogger, FieldLoggerGeneric1, FieldLoggerGeneric2}} from '{brightPackageName}'"); fileContent.Add($"import {{FieldLogger, FieldLoggerGeneric1, FieldLoggerGeneric2}} from '{brightPackageName}'");
fileContent.Add($"import TxnBeanBase from '{brightPackageName}'"); fileContent.Add($"import {{TxnBeanBase}} from '{brightPackageName}'");
fileContent.Add($"import {{TxnTable, TxnTableGeneric}} from '{brightPackageName}'"); fileContent.Add($"import {{TxnTable, TxnTableGeneric}} from '{brightPackageName}'");
fileContent.Add($"import TransactionContext from '{brightPackageName}'"); fileContent.Add($"import {{TransactionContext}} from '{brightPackageName}'");
fileContent.Add($"import {{FieldTag}} from '{brightPackageName}'"); fileContent.Add($"import {{FieldTag}} from '{brightPackageName}'");
fileContent.Add($"import TKey from '{brightPackageName}'"); fileContent.Add($"import {{TKey}} from '{brightPackageName}'");
fileContent.Add($"import PList from '{brightPackageName}'"); fileContent.Add($"import {{PList}} from '{brightPackageName}'");
fileContent.Add($"import PList1 from '{brightPackageName}'"); fileContent.Add($"import {{PList1}} from '{brightPackageName}'");
fileContent.Add($"import PList2 from '{brightPackageName}'"); fileContent.Add($"import {{PList2}} from '{brightPackageName}'");
fileContent.Add($"import PSet from '{brightPackageName}'"); fileContent.Add($"import {{PSet}} from '{brightPackageName}'");
fileContent.Add($"import PMap from '{brightPackageName}'"); fileContent.Add($"import {{PMap}} from '{brightPackageName}'");
fileContent.Add($"import PMap1 from '{brightPackageName}'"); fileContent.Add($"import {{PMap1}} from '{brightPackageName}'");
fileContent.Add($"import PMap2 from '{brightPackageName}'"); fileContent.Add($"import {{PMap2}} from '{brightPackageName}'");
fileContent.Add($"import SerializeFactory from '{brightPackageName}'"); fileContent.Add($"import {{SerializeFactory}} from '{brightPackageName}'");
} }
fileContent.Add($"export namespace {ass.TopModule} {{"); fileContent.Add($"export namespace {ass.TopModule} {{");