392 lines
15 KiB
C++
392 lines
15 KiB
C++
#include "uf2interface.h"
|
||
#include "utils/logmanager.h"
|
||
#include <QFile>
|
||
#include <QFileInfo>
|
||
#include <QDebug>
|
||
#include <QDir>
|
||
#include <QStack>
|
||
#include <functional>
|
||
#include <functional>
|
||
|
||
Uf2Interface::Uf2Interface(QObject *parent) : QObject(parent)
|
||
{
|
||
}
|
||
|
||
InterfaceInfo Uf2Interface::loadInterface(const QString& filePath)
|
||
{
|
||
InterfaceInfo result;
|
||
result.path = filePath;
|
||
|
||
QString sFilePath = filePath;
|
||
sFilePath.replace('\\', '/');
|
||
|
||
QFileInfo fileInfo(sFilePath);
|
||
if (!fileInfo.exists()) {
|
||
LogManager::instance()->logError(QString("%1 文件不存在!").arg(sFilePath));
|
||
return result;
|
||
}
|
||
|
||
QString sCodeName = fileInfo.baseName();
|
||
result.cname = sCodeName;
|
||
|
||
QFile file(sFilePath);
|
||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||
LogManager::instance()->logError(QString("%1 无法打开文件").arg(sFilePath));
|
||
return result;
|
||
}
|
||
|
||
QXmlStreamReader xml(&file);
|
||
QStack<QString> elementStack;
|
||
|
||
while (!xml.atEnd() && !xml.hasError()) {
|
||
QXmlStreamReader::TokenType token = xml.readNext();
|
||
|
||
if (token == QXmlStreamReader::StartElement) {
|
||
QString elementName = xml.name().toString();
|
||
elementStack.push(elementName);
|
||
|
||
if (elementName == "basic") {
|
||
QXmlStreamAttributes attrs = xml.attributes();
|
||
if (attrs.hasAttribute("englishName")) {
|
||
result.eName = attrs.value("englishName").toString();
|
||
}
|
||
if (attrs.hasAttribute("objectId")) {
|
||
result.functionNo = attrs.value("objectId").toString();
|
||
}
|
||
if (attrs.hasAttribute("flag")) {
|
||
result.flag = attrs.value("flag").toString();
|
||
}
|
||
if (attrs.hasAttribute("checkLicence")) {
|
||
result.checkLicence = (attrs.value("checkLicence").toString() == "true");
|
||
}
|
||
if (attrs.hasAttribute("returnResultSet")) {
|
||
result.returnResultSet = (attrs.value("returnResultSet").toString() == "true");
|
||
}
|
||
if (attrs.hasAttribute("needTransMonitor")) {
|
||
result.needTransMonitor = (attrs.value("needTransMonitor").toString() == "true");
|
||
}
|
||
}
|
||
else if (elementName == "stdFieldQuote") {
|
||
QXmlStreamAttributes attrs = xml.attributes();
|
||
|
||
QString parentName = elementStack.size() > 1 ? elementStack[elementStack.size() - 2] : QString();
|
||
|
||
FieldInfo field;
|
||
field.name = attrs.value("name").toString();
|
||
field.flag = attrs.value("flag").toString();
|
||
field.desc = attrs.value("comment").toString();
|
||
|
||
if (parentName == "import") {
|
||
result.inputFields[field.name] = field;
|
||
} else if (parentName == "export") {
|
||
result.outputFields[field.name] = field;
|
||
if (field.flag.contains("IO")) {
|
||
result.inputFields[field.name] = field;
|
||
}
|
||
}
|
||
}
|
||
else if (elementName == "variableField") {
|
||
QXmlStreamAttributes attrs = xml.attributes();
|
||
FieldInfo field;
|
||
field.name = attrs.value("name").toString();
|
||
field.cname = attrs.value("cname").toString();
|
||
field.hsType = attrs.value("type").toString();
|
||
field.desc = attrs.value("desc").toString();
|
||
result.variableFields[field.name] = field;
|
||
}
|
||
else if (elementName == "code") {
|
||
result.code = xml.readElementText();
|
||
// readElementText() 内部消费了 </code> EndElement,
|
||
// 但 elementStack 中 "code" 未被弹出,需要手动同步
|
||
if (!elementStack.isEmpty() && elementStack.top() == "code") {
|
||
elementStack.pop();
|
||
}
|
||
if (filePath.contains("不用编译")) {
|
||
result.code.clear();
|
||
}
|
||
}
|
||
}
|
||
else if (token == QXmlStreamReader::EndElement) {
|
||
if (!elementStack.isEmpty()) {
|
||
elementStack.pop();
|
||
}
|
||
}
|
||
}
|
||
|
||
file.close();
|
||
return result;
|
||
}
|
||
|
||
QString Uf2Interface::getModuleEName(const QString& moduleXmlPath)
|
||
{
|
||
QFile file(moduleXmlPath);
|
||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||
return QString();
|
||
}
|
||
|
||
QXmlStreamReader xml(&file);
|
||
while (!xml.atEnd() && !xml.hasError()) {
|
||
QXmlStreamReader::TokenType token = xml.readNext();
|
||
if (token == QXmlStreamReader::StartElement) {
|
||
QString elementName = xml.name().toString();
|
||
if (elementName == "info") {
|
||
QXmlStreamAttributes attrs = xml.attributes();
|
||
if (attrs.hasAttribute("ename")) {
|
||
return attrs.value("ename").toString();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
file.close();
|
||
return QString();
|
||
}
|
||
|
||
bool Uf2Interface::shouldFilter(const QString& filePath,
|
||
const QStringList& completeNameList,
|
||
const QStringList& containNameList)
|
||
{
|
||
QFileInfo fileInfo(filePath);
|
||
QString fileName = fileInfo.fileName();
|
||
|
||
if (completeNameList.contains(fileName)) {
|
||
return true;
|
||
}
|
||
|
||
for (const QString& name : containNameList) {
|
||
if (filePath.contains(name, Qt::CaseInsensitive)) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
QMap<QString, InterfaceInfo> Uf2Interface::scanDir(const QString& dirPath,
|
||
const QString& ufAcct20ProjectDir,
|
||
const QString& uftDbSett,
|
||
const QMap<QString, QStringList>& fileFilterDict,
|
||
const QJsonObject& custJson)
|
||
{
|
||
QMap<QString, InterfaceInfo> resultMap;
|
||
QStringList completeNameList = fileFilterDict.value("completeNameList");
|
||
QStringList containNameList = fileFilterDict.value("containNameList");
|
||
|
||
QMap<QString, QString> moduleCodeMap;
|
||
moduleCodeMap["转融通"] = "ref";
|
||
|
||
// 预提取cust.json中的模块英文名映射
|
||
QMap<QString, QString> custMoudleENameMap;
|
||
if (!custJson.isEmpty() && custJson.contains("moudleEName")) {
|
||
QJsonObject moudleENameObj = custJson["moudleEName"].toObject();
|
||
for (const QString& key : moudleENameObj.keys()) {
|
||
custMoudleENameMap[key] = moudleENameObj[key].toString();
|
||
}
|
||
}
|
||
|
||
std::function<void(const QString&)> scanModuleXmlRecursive;
|
||
scanModuleXmlRecursive = [&](const QString& path) {
|
||
if (path.isEmpty()) return;
|
||
QDir dir(path);
|
||
QStringList filters;
|
||
filters << "module.xml";
|
||
|
||
QFileInfoList fileList = dir.entryInfoList(filters, QDir::Files);
|
||
for (const QFileInfo& fileInfo : fileList) {
|
||
QString filePath = fileInfo.absoluteFilePath();
|
||
filePath.replace('\\', '/');
|
||
|
||
if (filePath.contains("/数据库/")) {
|
||
continue;
|
||
}
|
||
|
||
QString modName = fileInfo.dir().dirName();
|
||
QString modEName = getModuleEName(filePath);
|
||
|
||
// cust.json回退:当路径包含"外部接口(不用编译)"且英文名为空时
|
||
if (filePath.contains("外部接口(不用编译)") && modEName.trimmed().isEmpty()) {
|
||
if (custMoudleENameMap.contains(modName)) {
|
||
modEName = custMoudleENameMap[modName];
|
||
}
|
||
}
|
||
|
||
moduleCodeMap[modName] = modEName;
|
||
}
|
||
|
||
QFileInfoList dirList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||
for (const QFileInfo& dirInfo : dirList) {
|
||
scanModuleXmlRecursive(dirInfo.absoluteFilePath());
|
||
}
|
||
};
|
||
|
||
auto scanModuleXml = [&](const QString& path) {
|
||
scanModuleXmlRecursive(path);
|
||
};
|
||
|
||
scanModuleXml(dirPath);
|
||
scanModuleXml(ufAcct20ProjectDir);
|
||
scanModuleXml(uftDbSett);
|
||
|
||
std::function<void(const QString&, const QString&)> scanInterfaceFilesRecursive;
|
||
scanInterfaceFilesRecursive = [&](const QString& path, const QString& serverType) {
|
||
if (path.isEmpty()) {
|
||
LogManager::instance()->log("路径为空,跳过");
|
||
return;
|
||
}
|
||
QDir dir(path);
|
||
if (!dir.exists()) {
|
||
LogManager::instance()->log(QString("目录不存在: %1").arg(path));
|
||
return;
|
||
}
|
||
QStringList filters;
|
||
filters << "*.*";
|
||
|
||
QFileInfoList fileList = dir.entryInfoList(filters, QDir::Files);
|
||
for (const QFileInfo& fileInfo : fileList) {
|
||
QString filePath = fileInfo.absoluteFilePath();
|
||
filePath.replace('\\', '/');
|
||
if (shouldFilter(filePath, completeNameList, containNameList)) {
|
||
continue;
|
||
}
|
||
|
||
QString sFilePath = filePath;
|
||
int lastSlash = sFilePath.lastIndexOf('/');
|
||
QString pathWithoutFile = sFilePath.left(lastSlash);
|
||
int secondLastSlash = pathWithoutFile.lastIndexOf('/');
|
||
QString pathWithoutLastDir = pathWithoutFile.left(secondLastSlash);
|
||
int thirdLastSlash = pathWithoutLastDir.lastIndexOf('/');
|
||
QString modName = pathWithoutLastDir.mid(thirdLastSlash + 1);
|
||
|
||
InterfaceInfo interfaceInfo = loadInterface(filePath);
|
||
interfaceInfo.moudle = modName;
|
||
QString moudleEName = moduleCodeMap.value(modName);
|
||
interfaceInfo.moudleEName = moudleEName;
|
||
interfaceInfo.serverType = serverType;
|
||
|
||
QString key = interfaceInfo.cname;
|
||
if (resultMap.contains(key)) {
|
||
QString existingPath = resultMap[key].path;
|
||
if (existingPath.contains("不用编译")) {
|
||
resultMap[key] = interfaceInfo;
|
||
} else if (!filePath.contains("不用编译")) {
|
||
LogManager::instance()->logWarning(QString("存在重复接口名: %1").arg(key));
|
||
}
|
||
} else {
|
||
resultMap[key] = interfaceInfo;
|
||
}
|
||
}
|
||
|
||
QFileInfoList dirList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||
for (const QFileInfo& dirInfo : dirList) {
|
||
QString subDirPath = dirInfo.absoluteFilePath();
|
||
subDirPath.replace('\\', '/');
|
||
scanInterfaceFilesRecursive(subDirPath, serverType);
|
||
}
|
||
};
|
||
|
||
auto scanInterfaceFiles = [&](const QString& path, const QString& serverType) {
|
||
scanInterfaceFilesRecursive(path, serverType);
|
||
};
|
||
|
||
scanInterfaceFiles(dirPath, "trade");
|
||
scanInterfaceFiles(ufAcct20ProjectDir, "acct");
|
||
scanInterfaceFiles(uftDbSett, "dbsett");
|
||
|
||
LogManager::instance()->log(QString("扫描完成,共找到 %1 个接口").arg(resultMap.size()));
|
||
|
||
return resultMap;
|
||
}
|
||
|
||
QMap<QString, QString> Uf2Interface::buildModuleCodeMap(const QString& dirPath,
|
||
const QString& ufAcct20ProjectDir,
|
||
const QString& uftDbSett,
|
||
const QJsonObject& custJson)
|
||
{
|
||
QMap<QString, QString> moduleCodeMap;
|
||
moduleCodeMap["转融通"] = "ref";
|
||
|
||
// 预提取cust.json中的模块英文名映射
|
||
QMap<QString, QString> custMoudleENameMap;
|
||
if (!custJson.isEmpty() && custJson.contains("moudleEName")) {
|
||
QJsonObject moudleENameObj = custJson["moudleEName"].toObject();
|
||
for (const QString& key : moudleENameObj.keys()) {
|
||
custMoudleENameMap[key] = moudleENameObj[key].toString();
|
||
}
|
||
}
|
||
|
||
std::function<void(const QString&)> scanModuleXmlRecursive = [&](const QString& path) {
|
||
if (path.isEmpty()) return;
|
||
QDir dir(path);
|
||
QStringList filters;
|
||
filters << "module.xml";
|
||
QFileInfoList fileList = dir.entryInfoList(filters, QDir::Files);
|
||
for (const QFileInfo& fileInfo : fileList) {
|
||
QString filePath = fileInfo.absoluteFilePath();
|
||
filePath.replace('\\', '/');
|
||
if (filePath.contains("/数据库/")) continue;
|
||
QString modName = fileInfo.dir().dirName();
|
||
QString modEName = getModuleEName(filePath);
|
||
if (filePath.contains("外部接口(不用编译)") && modEName.trimmed().isEmpty()) {
|
||
if (custMoudleENameMap.contains(modName)) modEName = custMoudleENameMap[modName];
|
||
}
|
||
moduleCodeMap[modName] = modEName;
|
||
}
|
||
QFileInfoList dirList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||
for (const QFileInfo& dirInfo : dirList) scanModuleXmlRecursive(dirInfo.absoluteFilePath());
|
||
};
|
||
scanModuleXmlRecursive(dirPath);
|
||
scanModuleXmlRecursive(ufAcct20ProjectDir);
|
||
scanModuleXmlRecursive(uftDbSett);
|
||
return moduleCodeMap;
|
||
}
|
||
|
||
InterfaceInfo Uf2Interface::assemble(const QString& filePath,
|
||
const QString& serverType,
|
||
const QMap<QString, QString>& moduleCodeMap)
|
||
{
|
||
QString sFilePath = filePath;
|
||
sFilePath.replace('\\', '/');
|
||
int lastSlash = sFilePath.lastIndexOf('/');
|
||
QString pathWithoutFile = sFilePath.left(lastSlash);
|
||
int secondLastSlash = pathWithoutFile.lastIndexOf('/');
|
||
QString pathWithoutLastDir = pathWithoutFile.left(secondLastSlash);
|
||
int thirdLastSlash = pathWithoutLastDir.lastIndexOf('/');
|
||
QString modName = pathWithoutLastDir.mid(thirdLastSlash + 1);
|
||
|
||
InterfaceInfo interfaceInfo = loadInterface(filePath);
|
||
interfaceInfo.moudle = modName;
|
||
interfaceInfo.moudleEName = moduleCodeMap.value(modName);
|
||
interfaceInfo.serverType = serverType;
|
||
return interfaceInfo;
|
||
}
|
||
|
||
QList<Uf2FileEntry> Uf2Interface::collectFiles(const QString& dirPath,
|
||
const QString& serverType,
|
||
const QMap<QString, QStringList>& fileFilterDict)
|
||
{
|
||
QList<Uf2FileEntry> result;
|
||
QStringList completeNameList = fileFilterDict.value("completeNameList");
|
||
QStringList containNameList = fileFilterDict.value("containNameList");
|
||
|
||
std::function<void(const QString&, const QString&)> scanInterfaceFilesRecursive =
|
||
[&](const QString& path, const QString& st) {
|
||
if (path.isEmpty()) return;
|
||
QDir dir(path);
|
||
if (!dir.exists()) return;
|
||
QFileInfoList fileList = dir.entryInfoList(QStringList() << "*.*", QDir::Files);
|
||
for (const QFileInfo& fileInfo : fileList) {
|
||
QString filePath = fileInfo.absoluteFilePath();
|
||
filePath.replace('\\', '/');
|
||
if (shouldFilter(filePath, completeNameList, containNameList)) continue;
|
||
Uf2FileEntry entry;
|
||
entry.path = filePath;
|
||
entry.serverType = st;
|
||
entry.mtime = fileInfo.lastModified().toMSecsSinceEpoch();
|
||
result.append(entry);
|
||
}
|
||
QFileInfoList dirList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||
for (const QFileInfo& dirInfo : dirList) scanInterfaceFilesRecursive(dirInfo.absoluteFilePath(), st);
|
||
};
|
||
scanInterfaceFilesRecursive(dirPath, serverType);
|
||
return result;
|
||
}
|