优化转码添加LS的限制,优化缓存读取的问题

This commit is contained in:
taocong
2026-08-20 09:52:55 +08:00
parent 0fddcbea81
commit cb738cef09
8 changed files with 218 additions and 38 deletions
+44 -2
View File
@@ -11,6 +11,7 @@
#include "uft3table.h" #include "uft3table.h"
#include "utils/logmanager.h" #include "utils/logmanager.h"
#include "utils/configmanager.h" #include "utils/configmanager.h"
#include "utils/datacache.h"
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QSet> #include <QSet>
@@ -256,6 +257,35 @@ bool MetadataProcessor::writeCache(const QJsonObject& cache)
return writeJson(cachePath, cache); return writeJson(cachePath, cache);
} }
void MetadataProcessor::syncJsonToCache(const QString& jsonPath, bool isUft3)
{
if (!DataCache::instance()->initDatabase()) {
LogManager::instance()->logError("元数据同步缓存失败 - 数据库初始化失败");
return;
}
QJsonObject json = readJson(jsonPath);
if (json.isEmpty()) {
LogManager::instance()->logWarning(
QString("元数据同步缓存跳过 - 读取为空: %1").arg(jsonPath));
return;
}
bool ok = isUft3 ? DataCache::instance()->saveUFT3Config(json)
: DataCache::instance()->saveUF20Config(json);
if (!ok) {
LogManager::instance()->logError(
QString("元数据同步缓存失败 - 写入%1失败").arg(isUft3 ? "UFT3" : "UF20"));
return;
}
// 记录当前工程路径指纹,供启动比对(详见 configreaderbase.cpp)
QStringList parts = {m_uf20Path, m_ufAcct20Path, m_uftDbSettPath,
m_uft30PubPath, m_uft30CbptransPath, m_uft30Path};
QStringList norm;
for (const QString& p : parts) norm.append(PathUtils::normPath(p));
DataCache::instance()->saveProjectPaths({{"fingerprint", norm.join("|")}});
LogManager::instance()->log(
QString("元数据同步缓存成功 - %1 已写入 config_cache.db").arg(isUft3 ? "UFT3" : "UF20"));
}
bool MetadataProcessor::processUf2Interfaces(const QString& basePath) bool MetadataProcessor::processUf2Interfaces(const QString& basePath)
{ {
LogManager::instance()->log("========== 开始处理 UF2.0 接口 =========="); LogManager::instance()->log("========== 开始处理 UF2.0 接口 ==========");
@@ -274,7 +304,13 @@ bool MetadataProcessor::processUf2Interfaces(const QString& basePath)
Uf2Interface uf2; Uf2Interface uf2;
QString outputPath = QDir(m_uf2touft3Path).filePath("uf2.json"); QString outputPath = QDir(m_uf2touft3Path).filePath("uf2.json");
return processInterfaces(&uf2, ctx, m_uf20Path, outputPath, QString(), "uf2", false); bool ok = processInterfaces(&uf2, ctx, m_uf20Path, outputPath, QString(), "uf2", false);
// 生成成功后立即把新 json 同步进 config_cache.db(功能查询的数据源),
// 避免“换机器/换路径重新生成元数据后,db 仍停留在旧工程数据”导致功能查询查不到
if (ok) {
syncJsonToCache(outputPath, false);
}
return ok;
} }
bool MetadataProcessor::processUft3Interfaces(const QString& basePath) bool MetadataProcessor::processUft3Interfaces(const QString& basePath)
@@ -293,7 +329,13 @@ bool MetadataProcessor::processUft3Interfaces(const QString& basePath)
Uft3Interface uft3; Uft3Interface uft3;
QString outputPath = QDir(m_uf2touft3Path).filePath("uft3.json"); QString outputPath = QDir(m_uf2touft3Path).filePath("uft3.json");
QString transOutputPath = QDir(m_uf2touft3Path).filePath("uft3Trans.json"); QString transOutputPath = QDir(m_uf2touft3Path).filePath("uft3Trans.json");
return processInterfaces(&uft3, ctx, m_uft30Path, outputPath, transOutputPath, "uft3", true); bool ok = processInterfaces(&uft3, ctx, m_uft30Path, outputPath, transOutputPath, "uft3", true);
// 生成成功后立即把新 json 同步进 config_cache.db(功能查询的数据源),
// 避免“换机器/换路径重新生成元数据后,db 仍停留在旧工程数据”导致功能查询查不到
if (ok) {
syncJsonToCache(outputPath, true);
}
return ok;
} }
bool MetadataProcessor::processInterfaces(IInterfaceScanner* scanner, bool MetadataProcessor::processInterfaces(IInterfaceScanner* scanner,
+4
View File
@@ -55,6 +55,10 @@ private:
const QString& cacheKey, const QString& cacheKey,
bool isUft3); bool isUft3);
// 生成元数据后,把 json 同步进 config_cache.db(功能查询数据源)并刷新工程路径指纹;
// isUft3 区分 UF2/UFT3 两套 cname 表
void syncJsonToCache(const QString& jsonPath, bool isUft3);
QString m_basePath; QString m_basePath;
QString m_uf2touft3Path; QString m_uf2touft3Path;
+36 -1
View File
@@ -26,6 +26,31 @@
#include "ElaProgressBar.h" #include "ElaProgressBar.h"
#include "ElaText.h" #include "ElaText.h"
namespace {
bool validateLsFunctionNames(QWidget *parent, const QStringList &functionNames)
{
QStringList nonLsNames;
for (const QString &name : functionNames) {
const QString trimmedName = name.trimmed();
if (!trimmedName.isEmpty()
&& !trimmedName.startsWith("LS_")
&& !nonLsNames.contains(trimmedName)) {
nonLsNames.append(trimmedName);
}
}
if (nonLsNames.isEmpty()) {
return true;
}
QMessageBox::warning(parent, "提示",
QString("不支持非LS业务转码:\n%1").arg(nonLsNames.join('\n')));
return false;
}
}
class StatusDelegate : public QStyledItemDelegate class StatusDelegate : public QStyledItemDelegate
{ {
public: public:
@@ -264,6 +289,10 @@ void BatchConvertPage::onEditRow(int row)
continue; continue;
} }
if (!validateLsFunctionNames(this, QStringList{newName})) {
continue;
}
if (newName == oldName) { if (newName == oldName) {
return; return;
} }
@@ -303,7 +332,13 @@ void BatchConvertPage::onAddFunction()
btnLayout->addWidget(cancelBtn); btnLayout->addWidget(cancelBtn);
layout->addLayout(btnLayout); layout->addLayout(btnLayout);
connect(okBtn, &QPushButton::clicked, &dialog, &QDialog::accept); connect(okBtn, &QPushButton::clicked, &dialog, [this, &dialog, textEdit]() {
const QStringList funcNames =
textEdit->toPlainText().split('\n', Qt::SkipEmptyParts);
if (validateLsFunctionNames(this, funcNames)) {
dialog.accept();
}
});
connect(cancelBtn, &QPushButton::clicked, &dialog, &QDialog::reject); connect(cancelBtn, &QPushButton::clicked, &dialog, &QDialog::reject);
if (dialog.exec() != QDialog::Accepted) { if (dialog.exec() != QDialog::Accepted) {
+28
View File
@@ -2,9 +2,23 @@
#include "Constants.h" #include "Constants.h"
#include "datacache.h" #include "datacache.h"
#include "logmanager.h" #include "logmanager.h"
#include "configmanager.h"
#include <QFile> #include <QFile>
#include <QJsonDocument> #include <QJsonDocument>
// 计算当前工程路径指纹(含盘符/路径变化即视为不同工程),用于识别
// “换机器/换路径后旧缓存 db 仍非空”导致功能查询读到旧数据的情况
static QString currentProjectPathFingerprint()
{
QStringList parts;
const QStringList keys = {"uf20", "ufAcct20", "UftdbSett", "uft30pub", "uft30cbptrans", "uft30"};
ConfigManager* cfg = ConfigManager::instance();
for (const QString& k : keys) {
parts.append(PathUtils::normPath(cfg->getValue("projectPath", k)));
}
return parts.join("|");
}
bool ConfigReaderBase::doLoadAllConfig(const ConfigReaderTraits& traits) bool ConfigReaderBase::doLoadAllConfig(const ConfigReaderTraits& traits)
{ {
m_failedFiles.clear(); m_failedFiles.clear();
@@ -19,10 +33,21 @@ bool ConfigReaderBase::doLoadAllConfig(const ConfigReaderTraits& traits)
QString filePath = Constants::toolDir() + "/" + traits.mainFile; QString filePath = Constants::toolDir() + "/" + traits.mainFile;
if (traits.hasConfig(traits.mainFile)) { if (traits.hasConfig(traits.mainFile)) {
// 缓存表非空:检测工程路径是否与上次写入时一致,
// 不一致(如 A 电脑的缓存 db 打包给 B 电脑)则强制重新从 json 同步,
// 避免功能查询读到旧工程的 cname 数据
QMap<QString, QString> meta = DataCache::instance()->loadProjectPaths();
const QString stored = meta.value("fingerprint");
const QString current = currentProjectPathFingerprint();
if (stored != current) {
LogManager::instance()->log(QString("%1配置加载 - 工程路径已变化(旧:%2 新:%3),强制重新同步缓存")
.arg(traits.logTag).arg(stored).arg(current));
} else {
m_loaded = true; m_loaded = true;
DataCache::instance()->setDataLoaded(true); DataCache::instance()->setDataLoaded(true);
return true; return true;
} }
}
QFile file(filePath); QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
@@ -51,6 +76,9 @@ bool ConfigReaderBase::doLoadAllConfig(const ConfigReaderTraits& traits)
return false; return false;
} }
// 记录当前工程路径指纹,供下次启动比对(详见 doLoadAllConfig 开头)
DataCache::instance()->saveProjectPaths({{"fingerprint", currentProjectPathFingerprint()}});
m_loaded = true; m_loaded = true;
DataCache::instance()->setDataLoaded(true); DataCache::instance()->setDataLoaded(true);
return true; return true;
+51 -4
View File
@@ -79,23 +79,70 @@ bool DataCache::createTables()
return false; return false;
} }
// 工程路径指纹表:记录上次写入 cname 表时使用的工程路径,
// 用于在换机器/换路径场景下识别旧缓存
QString createMetaTable = R"(
CREATE TABLE IF NOT EXISTS cache_meta (
key TEXT PRIMARY KEY,
value TEXT
)
)";
if (!query.exec(createMetaTable)) {
LogManager::instance()->logError(QString("数据缓存 - 创建cache_meta失败: %1").arg(query.lastError().text()));
return false;
}
LogManager::instance()->log("数据缓存 - 表创建成功"); LogManager::instance()->log("数据缓存 - 表创建成功");
return true; return true;
} }
bool DataCache::saveProjectPaths(const QMap<QString, QString>& paths)
{
if (!m_db.isOpen()) {
return false;
}
QSqlQuery query(m_db);
query.exec("DELETE FROM cache_meta");
for (auto it = paths.begin(); it != paths.end(); ++it) {
query.prepare("INSERT OR REPLACE INTO cache_meta (key, value) VALUES (?, ?)");
query.addBindValue(it.key());
query.addBindValue(it.value());
if (!query.exec()) {
LogManager::instance()->logError(QString("数据缓存 - 写入cache_meta失败: %1").arg(query.lastError().text()));
return false;
}
}
return true;
}
QMap<QString, QString> DataCache::loadProjectPaths()
{
QMap<QString, QString> result;
if (!m_db.isOpen()) {
return result;
}
QSqlQuery query(m_db);
if (query.exec("SELECT key, value FROM cache_meta")) {
while (query.next()) {
result[query.value(0).toString()] = query.value(1).toString();
}
}
return result;
}
bool DataCache::initDatabase() bool DataCache::initDatabase()
{ {
QString dbPath = getDatabasePath(); // 已打开则直接返回,避免重复打印“数据库路径”(该函数启动期会被多次调用)
LogManager::instance()->log(QString("数据缓存 - 数据库路径: %1").arg(dbPath));
if (QSqlDatabase::contains("qt_sql_default_connection")) { if (QSqlDatabase::contains("qt_sql_default_connection")) {
m_db = QSqlDatabase::database("qt_sql_default_connection"); m_db = QSqlDatabase::database("qt_sql_default_connection");
if (m_db.isOpen()) { if (m_db.isOpen()) {
LogManager::instance()->log("数据缓存 - 数据库已打开");
return true; return true;
} }
} }
QString dbPath = getDatabasePath();
LogManager::instance()->log(QString("数据缓存 - 数据库路径: %1").arg(dbPath));
m_db = QSqlDatabase::addDatabase("QSQLITE"); m_db = QSqlDatabase::addDatabase("QSQLITE");
m_db.setDatabaseName(dbPath); m_db.setDatabaseName(dbPath);
+5
View File
@@ -29,6 +29,11 @@ public:
bool hasUF20Config(const QString& fileName); bool hasUF20Config(const QString& fileName);
bool hasUFT3Config(const QString& fileName); bool hasUFT3Config(const QString& fileName);
// 工程路径指纹:记录上次成功写入 cname 表时所用工程路径,
// 用于检测“换机器/换路径后旧缓存 db 仍非空”导致功能查询读到旧数据的问题
bool saveProjectPaths(const QMap<QString, QString>& paths);
QMap<QString, QString> loadProjectPaths();
bool checkUF20CnameExists(const QString& cname); bool checkUF20CnameExists(const QString& cname);
QSet<QString> getAllUF20Cnames(); QSet<QString> getAllUF20Cnames();
QList<FunctionInfo> searchUF20Functions(const QString& keyword, const QString& field = "", bool exactMatch = false); QList<FunctionInfo> searchUF20Functions(const QString& keyword, const QString& field = "", bool exactMatch = false);
+40 -22
View File
@@ -3,6 +3,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QDateTime> #include <QDateTime>
#include <QDate>
#include <QDebug> #include <QDebug>
#include <QSettings> #include <QSettings>
#include <QFileInfo> #include <QFileInfo>
@@ -18,6 +19,10 @@ LogManager* LogManager::m_instance = nullptr;
LogManager::LogManager(QObject *parent) : QObject(parent) LogManager::LogManager(QObject *parent) : QObject(parent)
{ {
// 日志目录固定为 exe 所在目录下的 logs,尽早确定,
// 避免 ConfigManager::loadConfig 早于 initLogConfig 写日志时 m_logDir 仍为空,
// 导致日志落到当前工作目录(exe 根目录)
m_logDir = QCoreApplication::applicationDirPath() + "/logs";
} }
LogManager::~LogManager() LogManager::~LogManager()
@@ -37,21 +42,28 @@ LogManager* LogManager::instance()
void LogManager::initLogFile() void LogManager::initLogFile()
{ {
QString dir = m_logDir.isEmpty() ? QDir::currentPath() : m_logDir; QDir logDir(resolveLogDir());
QDir logDir(dir);
if (!logDir.exists()) { if (!logDir.exists()) {
logDir.mkpath("."); logDir.mkpath(".");
} }
m_logFilePath = logDir.absoluteFilePath(m_fileName);
if (m_toFile) { if (m_toFile) {
cleanupByAge(); // 启动即清理过期日志 cleanupByAge(); // 启动即清理过期日志
ensureFileOpen(); ensureFileOpen(); // 打开“当天日期-基础名”日志文件并设置 m_logFilePath
} }
writeLog(LogLevel::Info, "日志系统初始化完成"); writeLog(LogLevel::Info, "日志系统初始化完成");
} }
QString LogManager::resolveLogDir() const
{
// 日志目录固定为 exe 目录/logs;即便 m_logDir 因故为空,也兜底到 exeDir/logs,
// 绝不落到不可控的当前工作目录
if (!m_logDir.isEmpty()) {
return m_logDir;
}
return QCoreApplication::applicationDirPath() + "/logs";
}
QString LogManager::getLogFilePath() const QString LogManager::getLogFilePath() const
{ {
return m_logFilePath; return m_logFilePath;
@@ -59,12 +71,22 @@ QString LogManager::getLogFilePath() const
void LogManager::ensureFileOpen() void LogManager::ensureFileOpen()
{ {
if (m_logFile.isOpen()) { // 实际文件名带当天日期前缀:YYYYMMDD-<基础名>,如 20260812-change_code.log
return; const QString tag = QDate::currentDate().toString("yyyyMMdd");
const QString filePath = QDir(resolveLogDir())
.absoluteFilePath(QString("%1-%2").arg(tag, m_baseName));
// 跨天运行时,当前打开的文件不属于今天 -> 关闭旧文件,切换到今天的文件
if (m_logFile.isOpen() && m_logFilePath != filePath) {
m_logFile.close();
} }
m_logFile.setFileName(m_logFilePath);
if (!m_logFile.isOpen()) {
m_logFilePath = filePath;
m_logFile.setFileName(filePath);
if (!m_logFile.open(QIODevice::Append | QIODevice::Text)) { if (!m_logFile.open(QIODevice::Append | QIODevice::Text)) {
qDebug() << "无法打开日志文件:" << m_logFilePath; qDebug() << "无法打开日志文件:" << filePath;
}
} }
} }
@@ -103,10 +125,10 @@ void LogManager::cleanupByAge()
if (m_maxAgeDays <= 0) { if (m_maxAgeDays <= 0) {
return; return;
} }
QFileInfo fi(m_logFilePath); QDir dir(resolveLogDir());
QDir dir(fi.absolutePath()); // 匹配所有日期前缀的日志文件(含轮转 .N):*<基础名>*
QString base = fi.fileName(); QString base = "*" + m_baseName;
QFileInfoList list = dir.entryInfoList(QStringList() << (base + ".*"), QDir::Files); QFileInfoList list = dir.entryInfoList(QStringList() << base, QDir::Files);
QDateTime now = QDateTime::currentDateTime(); QDateTime now = QDateTime::currentDateTime();
for (const QFileInfo& f : list) { for (const QFileInfo& f : list) {
if (f.lastModified().daysTo(now) > m_maxAgeDays) { if (f.lastModified().daysTo(now) > m_maxAgeDays) {
@@ -129,15 +151,11 @@ void LogManager::initLogConfig()
else if (levelStr == "ERROR" || levelStr == "ERR") level = LogLevel::Error; else if (levelStr == "ERROR" || levelStr == "ERR") level = LogLevel::Error;
m_logLevel = level; m_logLevel = level;
// 2) 输出路径与文件名 // 2) 输出路径固定为:可执行文件所在目录 / logs(不再从 ini 读取,避免配置不一致)
QString path = cc.value("log/path", QString()).toString().trimmed(); m_logDir = QCoreApplication::applicationDirPath() + "/logs";
if (!path.isEmpty() && !QDir::isAbsolutePath(path)) { m_baseName = cc.value("log/file", "change_code.log").toString().trimmed();
path = QCoreApplication::applicationDirPath() + "/" + QDir::fromNativeSeparators(path); if (m_baseName.isEmpty()) {
} m_baseName = "change_code.log";
m_logDir = path;
m_fileName = cc.value("log/file", "change_code.log").toString().trimmed();
if (m_fileName.isEmpty()) {
m_fileName = "change_code.log";
} }
// 3) 轮转与清理 // 3) 轮转与清理
+3 -2
View File
@@ -44,12 +44,13 @@ private:
~LogManager(); ~LogManager();
void writeLog(LogLevel level, const QString& message); void writeLog(LogLevel level, const QString& message);
void ensureFileOpen(); // 确保日志文件已打开(未打开则打开) void ensureFileOpen(); // 确保日志文件已打开(未打开则打开,跨天自动切换)
void rotateFile(); // 超过 maxSize 时按 maxFiles 轮转 void rotateFile(); // 超过 maxSize 时按 maxFiles 轮转
void cleanupByAge(); // 删除超过 maxAge 天的历史日志 void cleanupByAge(); // 删除超过 maxAge 天的历史日志
QString captureStackTrace(); // 调用堆栈(caller=true 时) QString captureStackTrace(); // 调用堆栈(caller=true 时)
static qint64 parseSizeToBytes(const QString& s); // "10MB" -> 字节 static qint64 parseSizeToBytes(const QString& s); // "10MB" -> 字节
static QString translateTimeFormat(const QString& fmt); // Go 布局 -> Qt 格式 static QString translateTimeFormat(const QString& fmt); // Go 布局 -> Qt 格式
QString resolveLogDir() const; // 解析日志目录(空 -> 当前目录)
static LogManager* m_instance; static LogManager* m_instance;
QFile m_logFile; QFile m_logFile;
@@ -59,7 +60,7 @@ private:
bool m_toConsole = true; // 是否输出到控制台 bool m_toConsole = true; // 是否输出到控制台
bool m_toFile = true; // 是否写入日志文件 bool m_toFile = true; // 是否写入日志文件
QString m_logDir; // 日志目录(空 -> 当前目录) QString m_logDir; // 日志目录(空 -> 当前目录)
QString m_fileName = "change_code.log"; // 日志文件名 QString m_baseName = "change_code.log"; // 日志基础文件名(实际文件名会带日期前缀 YYYYMMDD-)
qint64 m_maxSizeBytes = 10LL * 1024 * 1024; // 单文件大小上限(字节),0=不限制 qint64 m_maxSizeBytes = 10LL * 1024 * 1024; // 单文件大小上限(字节),0=不限制
int m_maxFiles = 100; // 轮转保留文件数 int m_maxFiles = 100; // 轮转保留文件数
int m_maxAgeDays = 30; // 保留天数,0=不限制 int m_maxAgeDays = 30; // 保留天数,0=不限制