--#preproc ---读取xxx数据时,会在原来数据的基础上动态创建一个字典KVTable,Key值为数据主键,Value值为数据。如果为多主键结构则如下 ---KVTab = { --- [Key1] = { --- [Key2] = { --- [Key3] = Info, --- }, --- }, ---} ---同时为该数据表生成一个控制接口DataEntry,为其设置元表DataSheetMT,其中包含了所有通用的控制模块,如获取数据长度GetDataLength,获取迭代器GetEnumerator等,后续有新增需求可再扩展 ---DataSheetManager本身存储各个数据表的控制接口DataEntry,与数据名绑定,即DataSheetManager[xxx] = DataEntry ---DataSheetManager.RawDataTable则存储各个数据表,与控制接口绑定,即RawDataTable[DataEntry] = xxxData ---通过表名读取数据的过程,即先取出对应表名的DataEntry,由RawDataTable再取到数据,再进行相应的读取操作 local DataSheetManager = {} local private = {} local DataSheetUtil = require("Common/DataSheetUtil") local rawget = rawget local rawset = rawset local setmetatable = setmetatable local select = select local assert = assert local pcall = pcall local pairs = pairs local ipairs = ipairs local type = type local next = next local require = require local PATH = --[[`USE_DATA_SHEET_CHECKER` '/' or]] 'Data/' local VariantCoverDiffSheetInfo = {} --覆盖部分行的表 local VariantCoverWholeSheetInfo = {} --覆盖整张表的表 --存放所有数据 Key-Entry Value-Data private.RawDataTable = {} local function IsTabType(value) return type(value) == 'table' end local function IpairsValue(tab,i) i = i+1 local data = tab[i] if data then return i,tab[i] else return nil end end local function getMethodName(primaryTab, type) if type == 1 then return string.format("GetDataBy%s", table.concat(primaryTab, "_")) elseif type == 2 then return string.format("GetDataLengthBy%s", table.concat(primaryTab, "_")) elseif type == 3 then return string.format("TryGetDataBy%s", table.concat(primaryTab, "_")) end end --创建KVTab,结构如下 --[[ KVTab = { [Key1] = { [Key2] = { [Key3] = Info, EnumRange = ? }, EnumRange = ? }, } 除最后一级为确定的某行数据外,每一级键值表中的EnumRange存储数据对应的索引(如果只有一行则为number,多行则为table,[1],[2]分别为起始与终止),用来遍历 ]] local function InsertData(info,tab,index,infoIndex,keyNum,primaryTab) if(index>keyNum)then return end --先拿到第index个主键对应的值 local val = info[primaryTab[index]] --tab为最终创建的KVTable --tb为KVTab中第index级的键值表,最终指向info,即数据表中对应的某行数据 local tb = tab[val] if tb == nil then --该主键下为空,判断当前index是否达到对应的键值数 if (index>=keyNum) then --已经达到,直接指向数据,返回 tab[val] = info return else --未达到主键数,继续往表下级创建新表 tb = {} --创建EnumRange,新表EnumRange默认为number型 tb.EnumRange = infoIndex tab[val] = tb end else --主键值为已经创建过的表,需要更新Index if IsTabType(tb.EnumRange) then --可能不是连续的行,改成存储每行的行数 table.insert(tb.EnumRange,infoIndex) else local Index = tb.EnumRange tb.EnumRange = {Index,infoIndex} end end index = index+1 InsertData(info,tb,index,infoIndex,keyNum,primaryTab) end local function GetDataFromDir(dir,...) local count = select('#',...) local data = dir for i = 1, count do local key = select(i,...) data = data[key] end return data end --传参少于主键数量,返回一个可遍历的接口 local function RealGetDataByKey(self, isCheckExist, ...) local rawData = rawget(private.RawDataTable, self) local count = select('#', ...) local keyNum = #rawData.primaryTable assert(count<=keyNum,"PrimaryKey Num is Over!") local ret,tempTab = pcall(GetDataFromDir,rawData.KVTable, ...) if ret and tempTab then if keyNum == count then return tempTab else self.CurEnumRange = tempTab.EnumRange if IsTabType(tempTab.EnumRange) then return self.IpairsEnumRangeTab,self,0 else return self.IpairsEnumRangeNum,self,0 end end end if isCheckExist then logging.error(LTAG.ERROR, "DataSheet key not found", self.DataSheetName, ...) end return nil end local function GetDataByKey(self, ...) return RealGetDataByKey(self, true, ...) end -- 尝试获取数据值,返回两个值(bool, data),类似C#的Dictionary的TryGet local function TryGetDataByKey(self, ...) local returnData = RealGetDataByKey(self, false, ...) return returnData ~= nil, returnData end local function GetDataLengthByKey(self,...) local rawData = rawget(private.RawDataTable,self) local count = select('#',...) local keyNum = #rawData.primaryTable assert(count<=keyNum,"PrimaryKey Num is Over!") local ret,tempTab = pcall(GetDataFromDir,rawData.KVTable,...) if ret and tempTab then if IsTabType(tempTab.EnumRange) then return #tempTab.EnumRange else return 1 end end return 0 end --region -- **************** DataSheetMT ******************* -- local DataSheetEntryMT = {} DataSheetEntryMT.__index = DataSheetEntryMT function DataSheetEntryMT:GetDataCount() local rawData = rawget(private.RawDataTable,self) return #rawData.dataTable end function DataSheetEntryMT:GetEnumerator() local rawData = rawget(private.RawDataTable,self) return IpairsValue,rawData.dataTable,0 end function DataSheetEntryMT:GetPropertyEnumerator() local rawData = rawget(private.RawDataTable,self) return next,rawData.propertyTable,nil end function DataSheetEntryMT:GetPropertyIndex(propertyName) local rawData = rawget(private.RawDataTable,self) return rawData.propertyTable[propertyName] end --多主键表调用:根据主键1和索引值返回数据,该索引值为1-主键1的数据长度(不是源数据的索引) function DataSheetEntryMT:GetDataByFirstKeyAndIndex(key,index) local rawData = rawget(private.RawDataTable,self) local kvData = rawData.KVTable if kvData[key] == nil then logging.error(LTAG.ERROR, "GetDataByFirstKeyAndIndex,Error:key not exits,Key:", key) return nil end if kvData[key].EnumRange then if IsTabType(kvData[key].EnumRange) then local rawDataIndex = kvData[key].EnumRange[index] return rawData.dataTable[rawDataIndex] else local rawDataIndex = kvData[key].EnumRange return rawData.dataTable[rawDataIndex] end else return kvData[key] end end function DataSheetEntryMT.IpairsEnumRangeTab(self,i) local rawData = rawget(private.RawDataTable,self) local enumRange = self.CurEnumRange i = i + 1 local index = enumRange[i] if index then return i,rawData.dataTable[index] else return nil end end function DataSheetEntryMT.IpairsEnumRangeNum(self,i) local rawData = rawget(private.RawDataTable,self) local enumRange = self.CurEnumRange i = i + 1 if i == 1 then return 1,rawData.dataTable[enumRange] else return nil end end --endregion DataSheetMT --region -- **************** private ******************* -- --对每个Data创建K-V表 KVTable function private.CreatKVTab(data) local primaryTab = data.primaryTable if primaryTab then local tempTab = {} local keyNum = #primaryTab if keyNum == 1 then --单主键 for _,info in ipairs(data.dataTable) do if tempTab[info[primaryTab[1]]] then logging.error(LTAG.ERROR, "Error:Same KEY Exits!Key:", info[primaryTab[1]]) end tempTab[info[primaryTab[1]]] = info end else --多主键 for _,info in ipairs(data.dataTable) do InsertData(info,tempTab,1,_,keyNum,primaryTab) end end rawset(data,"KVTable",tempTab) else logging.error(LTAG.ERROR, "Error:No PrimaryKEY Exits!") end end --修改primaryTable中的主键,然后把dataTable分组,根据不同的sheet名执行方法。要返回一个形如{dataTable, propertyTable, primaryTable}这样的table数组 function private.SplitSheetFunc(splitData, rawData) local sheetName = splitData.sheetName local splitKey = splitData.splitKey local splitIndex = rawData.propertyTable[splitKey] table.remove(rawData.primaryTable, splitIndex) local groupData = {} for i, v in ipairs(rawData.dataTable) do local splitValue = v[splitIndex] if not groupData[splitValue] then groupData[splitValue] = {} end table.insert(groupData[splitValue], v) end local propertyTable = rawData.propertyTable local primaryTable = rawData.primaryTable local funcTable = {} for k, v in pairs(groupData) do local rawGroupData = { dataTable = v, propertyTable = propertyTable, primaryTable = primaryTable, } private.CreatKVTab(rawGroupData) local dataEntry = setmetatable({ [getMethodName(rawGroupData.primaryTable, 1)] = GetDataByKey, [getMethodName(rawGroupData.primaryTable, 2)] = GetDataLengthByKey, [getMethodName(rawGroupData.primaryTable, 3)] = TryGetDataByKey, }, DataSheetEntryMT) rawset(private.RawDataTable, dataEntry, rawGroupData) funcTable[k] = dataEntry end --下次取数据就不用通过元方法取了 rawset(DataSheetManager, sheetName, funcTable) end --变体表覆盖原始表的部分行数据 function private.VariantOverrideSheetPartData(rawData, sheetName, variantSheetSuffix) local path = PATH .. sheetName .. variantSheetSuffix local variantData = require(path) for i, rawDataValue in ipairs(rawData.dataTable) do for _, variantDataValue in ipairs(variantData.dataTable) do if private.IsPrimaryKeyMatch(rawDataValue, rawData.primaryTable, variantDataValue, variantData.primaryTable) then rawData.dataTable[i] = variantDataValue end end end --覆盖完毕,卸载变体表 local packagePath = string.gsub(path, "/", ".") package.loaded[packagePath] = nil return rawData end --变体表覆盖原始表的全部数据 function private.VariantOverrideSheetAllData(rawData, sheetName, variantSheetSuffix) local path = PATH .. sheetName .. variantSheetSuffix local variantData = require(path) table.clear(rawData.dataTable) for _, variantDataValue in ipairs(variantData.dataTable) do table.insert(rawData.dataTable, variantDataValue) end --覆盖完毕,卸载变体表 local packagePath = string.gsub(path, "/", ".") package.loaded[packagePath] = nil return rawData end --对比原数据表和变体表的主键是否相同 function private.IsPrimaryKeyMatch(rawDataValue, rawDataPrimaryTable, variantDataValue, variantDataPrimaryTable) for i, v in ipairs(rawDataPrimaryTable) do if rawDataValue[v] ~= variantDataValue[variantDataPrimaryTable[i]] then return false end end return true end --通过表名读取数据 function private.ReadDataFromName(dataName) local dataEntry = rawget(DataSheetManager, dataName) if dataEntry == nil then local rawData = require(PATH .. dataName) if rawData then dataEntry = private.DataSheetPreProcess(dataName, rawData) end end return dataEntry end function private.DataSheetPreProcess(dataName, rawData) --变体预处理——覆盖部分行数据 if VariantCoverDiffSheetInfo[dataName] ~= nil then rawData = private.VariantOverrideSheetPartData(rawData, dataName, VariantCoverDiffSheetInfo[dataName]) end --变体预处理——覆盖整张表 if VariantCoverWholeSheetInfo[dataName] ~= nil then rawData = private.VariantOverrideSheetAllData(rawData, dataName, VariantCoverWholeSheetInfo[dataName]) end --拆分表处理 local splitData = DataSheetUtil.DataSheetPreProcess(dataName) if splitData then private.SplitSheetFunc(splitData, rawData) --内部函数进行了rawset,直接跳出 return rawget(DataSheetManager, dataName) end --设置表rawset private.CreatKVTab(rawData) local dataEntry = setmetatable({ ["DataSheetName"] = dataName, [getMethodName(rawData.primaryTable, 1)] = GetDataByKey, [getMethodName(rawData.primaryTable, 2)] = GetDataLengthByKey, [getMethodName(rawData.primaryTable, 3)] = TryGetDataByKey, }, DataSheetEntryMT) rawset(private.RawDataTable, dataEntry, rawData) --下次取数据就不用通过元方法取了 rawset(DataSheetManager, dataName, dataEntry) return dataEntry end --endregion private --region -- **************** DataSheetManager ******************* -- function DataSheetManager.ClearData() private.RawDataTable = {} end function DataSheetManager.GetRawDataList(dataName) local result = {} local rawData = require(PATH .. dataName) for i, info in ipairs(rawData.dataTable) do result[i] = info end return result end function DataSheetManager.SetVariantDiffSheetInfo(sheetData, variantSheetSuffix) if variantSheetSuffix then table.clear(VariantCoverDiffSheetInfo) for i, v in pairs(sheetData) do VariantCoverDiffSheetInfo[v] = variantSheetSuffix end end end function DataSheetManager.SetVariantCoverWholeSheetInfo(sheetData, variantSheetSuffix) if variantSheetSuffix then table.clear(VariantCoverWholeSheetInfo) for i, v in pairs(sheetData) do VariantCoverWholeSheetInfo[v] = variantSheetSuffix end end end --endregion DataSheetManager local commonDataMT = { __index = function(t,k) return private.ReadDataFromName(k) end, __newindex = function() logging.error(LTAG.ERROR, "Attempt to modify read-only table") end } return setmetatable(DataSheetManager, commonDataMT)