processthread.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. #include "processthread.h"
  2. #include <QProcess>
  3. #include <QTemporaryFile>
  4. #include "api/configapi.h"
  5. #include "appevent.h"
  6. #include "processmodel.h"
  7. #include "qjsonarray.h"
  8. #include "qjsonobject.h"
  9. #include <cwf/sqldatabasestorage.h>
  10. #include <optional>
  11. #include "api/processapi.h"
  12. #include "qjsonvalue.h"
  13. #include "qlist.h"
  14. #include "qmessagebox.h"
  15. #include "qnamespace.h"
  16. #include "basemainTr.h"
  17. CWF::SqlDatabaseStorage storage("QSQLITE", "localhost", "postgres", "postgres", "1234", 5432);
  18. // 将 FILETIME 转换为 Unix 时间戳
  19. static qint64 fileTimeToUnixTimestamp(FILETIME ft)
  20. {
  21. // FILETIME 是自 1601 年以来的 100 毫微秒数
  22. ULARGE_INTEGER ull;
  23. ull.LowPart = ft.dwLowDateTime;
  24. ull.HighPart = ft.dwHighDateTime;
  25. // FILETIME 和 时间戳的 基础时间不一样
  26. // 转换为秒,并减去 1601 到 1970 年之间的秒数
  27. if (ull.QuadPart / 10000000LL > 11644473600LL) {
  28. qint64 timestamp = ull.QuadPart / 10000000LL - 11644473600LL;
  29. return timestamp;
  30. }
  31. return ull.QuadPart / 10000000LL;
  32. }
  33. QString convertSecondsToTimeFormat(int totalSeconds)
  34. {
  35. int days = totalSeconds / (24 * 3600); // 计算天数
  36. int hours = (totalSeconds % (24 * 3600)) / 3600; // 计算小时
  37. int minutes = (totalSeconds % 3600) / 60; // 计算分钟
  38. int seconds = totalSeconds % 60; // 计算秒数
  39. // 返回格式化后的字符串,并使用 tr() 标记可翻译的文本
  40. return QString(Tr::tr("%1days%2hours%3minutes%4seconds"))
  41. .arg(days)
  42. .arg(hours)
  43. .arg(minutes)
  44. .arg(seconds);
  45. }
  46. ///
  47. /// \brief 转换到分钟
  48. /// \param totalSeconds
  49. /// \return
  50. ///
  51. QString convertSecondsToMinutesTimeFormat(int totalSeconds)
  52. {
  53. // 只计算分钟
  54. int minutes = totalSeconds / 60;
  55. return QString::number(minutes);
  56. }
  57. void ProcessThread::sendExitTime(qint64 updataTime)
  58. {
  59. TC::ProcessNameApi processNameApi;
  60. QJsonArray serverArray = processNameApi.get();
  61. // 获取过滤名单
  62. QSet<QString> filter;
  63. QSet<QString> gameFilter;
  64. for (const QJsonValue &item : serverArray) {
  65. if (!item.isObject()) {
  66. return;
  67. }
  68. const QJsonObject object = item.toObject();
  69. const QString name = object["name"].toString();
  70. if (object.contains("is_confirmed")) {
  71. if (object["is_confirmed"].toInt() == 2) {
  72. filter.insert(name);
  73. }
  74. }
  75. if (object.contains("pid_type")) {
  76. const QString type = object["pid_type"].toString();
  77. if (type == "游戏" || type == "game") {
  78. gameFilter.insert(name);
  79. }
  80. }
  81. }
  82. ProcessModel processModel{storage};
  83. // 在数据库 获取 不是这个更新日期的数据
  84. CWF::SqlQueryManager qry(storage);
  85. qry.select("*", processModel.getTableName())
  86. .where(QString("updataTime != '%1' OR updataTime IS NULL").arg(updataTime));
  87. qry.prepare();
  88. QJsonObject jsonObject = qry.exec();
  89. QJsonArray jsonArray = qry.toJson();
  90. QJsonArray sendJsonArray;
  91. if (jsonArray.size() > 0) {
  92. for (const auto &json : jsonArray) {
  93. const QJsonObject object = json.toObject();
  94. const QString name = object["processName"].toString();
  95. QJsonObject sendObject;
  96. sendObject.insert("pid", object["pid"]);
  97. sendObject.insert("pid_name", object["processName"]);
  98. sendObject.insert("begin_time", object["creationTime"]);
  99. sendObject.insert("end_time", object["exitTime"]);
  100. sendObject.insert("last_check_time", object["updataTime"]);
  101. sendObject.insert("status", 1);
  102. sendObject.insert("notes", "");
  103. // 存在过滤里面的 不发送数据
  104. if (!filter.contains(name)) {
  105. sendJsonArray.append(sendObject);
  106. }
  107. }
  108. }
  109. // 上传 后 删除
  110. if (sendJsonArray.size() > 0) {
  111. TC::ProcessApi processApi(sendJsonArray);
  112. bool isSendok = processApi.post();
  113. if (isSendok) {
  114. // 移除发送到服务器的本地数据
  115. CWF::SqlQueryManager qry(storage);
  116. qry.remove(processModel.getTableName(),
  117. QString("updataTime != '%1' OR updataTime IS NULL").arg(updataTime));
  118. qry.prepare();
  119. QJsonObject jsonObject = qry.exec();
  120. QJsonArray jsonArray = qry.toJson();
  121. }
  122. }
  123. // 获取配置
  124. QVariant messageBoxPointX = QVariant();
  125. QVariant messageBoxPointY = QVariant();
  126. QVariant messageText = QVariant("");
  127. QVariant gameMessageText = QVariant("");
  128. QVariant messageTitle = QVariant(Tr::tr("Tips"));
  129. {
  130. TC::ConfigApi configApi("messageBoxPointX");
  131. std::optional<QVariant> value = configApi.get();
  132. if (value.has_value()) {
  133. messageBoxPointX = value.value();
  134. }
  135. }
  136. {
  137. TC::ConfigApi configApi("messageBoxPointY");
  138. std::optional<QVariant> value = configApi.get();
  139. if (value.has_value()) {
  140. messageBoxPointY = value.value();
  141. }
  142. }
  143. {
  144. TC::ConfigApi configApi("messageText");
  145. std::optional<QVariant> value = configApi.get();
  146. if (value.has_value()) {
  147. messageText = value.value();
  148. }
  149. }
  150. {
  151. TC::ConfigApi configApi("messageTitle");
  152. std::optional<QVariant> value = configApi.get();
  153. if (value.has_value()) {
  154. messageTitle = value.value();
  155. }
  156. }
  157. {
  158. TC::ConfigApi configApi("gameMessageText");
  159. std::optional<QVariant> value = configApi.get();
  160. if (value.has_value()) {
  161. gameMessageText = value.value();
  162. }
  163. }
  164. // 获取服务器 对应的时间信息
  165. for (const QJsonValue &item : serverArray) {
  166. if (!item.isObject()) {
  167. return;
  168. }
  169. const QJsonObject object = item.toObject();
  170. const QString name = object["name"].toString();
  171. const QString zhName = object["chinese_name"].toString();
  172. const QString type = object["pid_type"].toString();
  173. // 2 提示 1 不提示 0 未知
  174. if (object.contains("is_prompt")) { // 提示
  175. if (object["is_prompt"].toInt() == 2) {
  176. if (type == "游戏" || type == "game") {
  177. } else {
  178. // const QString name = object["name"].toString();
  179. // const QString zhName = object["chinese_name"].toString();
  180. const int time = object["prompt_time"].toInt();
  181. if (time > 0) {
  182. const QString whereTime = QString("(updataTime - lastAlertTime) > %1")
  183. .arg(time);
  184. const QString whereProcessName = QString("processName = '%1'").arg(name);
  185. const QString whereUpdataTime = QString("updataTime = '%1'").arg(updataTime);
  186. CWF::SqlQueryManager qry(storage);
  187. qry.select("*", processModel.getTableName())
  188. .where(QString("%1 AND %2 AND %3")
  189. .arg(whereTime)
  190. .arg(whereProcessName)
  191. .arg(whereUpdataTime));
  192. qry.prepare();
  193. QJsonObject jsonObject = qry.exec();
  194. QJsonArray jsonArray = qry.toJson();
  195. qDebug() << jsonArray;
  196. for (const auto &value : jsonArray) {
  197. const QJsonObject object = value.toObject();
  198. const QString processName = object["processName"].toString();
  199. const qint64 runTime = object["exitTime"].toVariant().toLongLong()
  200. - object["creationTime"].toVariant().toLongLong();
  201. const QString text = QString(
  202. tr("Program %1 has been used for %2 minutes%3")
  203. .arg(zhName)
  204. .arg(convertSecondsToMinutesTimeFormat(runTime))
  205. .arg(messageText.toString()));
  206. emit messageBox(text, messageBoxPointX, messageBoxPointY, messageTitle);
  207. // 更新 lastAlertTime
  208. ProcessModel processModel{storage};
  209. processModel.buildFromJson(object);
  210. // 更新 消息时间
  211. processModel.setLastAlertTime(updataTime);
  212. processModel.save();
  213. }
  214. }
  215. }
  216. }
  217. }
  218. if (object.contains("pid_type")) {
  219. if (type == "游戏" || type == "game") {
  220. const QString whereTime = QString("(updataTime - lastAlertTime) > %1").arg(1);
  221. const QString whereProcessName = QString("processName = '%1'").arg(name);
  222. const QString whereUpdataTime = QString("updataTime = '%1'").arg(updataTime);
  223. CWF::SqlQueryManager qry(storage);
  224. qry.select("*", processModel.getTableName())
  225. .where(QString("%1 AND %2 AND %3")
  226. .arg(whereTime)
  227. .arg(whereProcessName)
  228. .arg(whereUpdataTime));
  229. qry.prepare();
  230. QJsonObject jsonObject = qry.exec();
  231. QJsonArray jsonArray = qry.toJson();
  232. for (const auto &value : jsonArray) {
  233. const QJsonObject object = value.toObject();
  234. const QString processName = object["processName"].toString();
  235. const qint64 runTime = object["exitTime"].toVariant().toLongLong()
  236. - object["creationTime"].toVariant().toLongLong();
  237. const QString text = QString(tr("Program %1 has been used for %2 minutes%3")
  238. .arg(zhName)
  239. .arg(convertSecondsToMinutesTimeFormat(runTime))
  240. .arg(messageText.toString()));
  241. qDebug() << "玩游戏?" << text;
  242. QTemporaryFile tempFile("tempfile_XXXXXX.png");
  243. tempFile.setAutoRemove(true);
  244. tempFile.open();
  245. AppEvent::captureDesktop(&tempFile, "PNG");
  246. // qDebug() << "发送截图" << tempFile.fileName();
  247. TC::ProcessImageApi processImageApi;
  248. qDebug() << "发送截图" << processImageApi.post({tempFile.fileName()});
  249. tempFile.close();
  250. qDebug() << "发送截图" << tempFile.remove() << tempFile.errorString();
  251. // if (object.contains("is_prompt")) { // 提示
  252. // if (object["is_prompt"].toInt() == 2) {
  253. // }
  254. // }
  255. const QString message = gameMessageText.toString().isEmpty()
  256. ? text
  257. : gameMessageText.toString();
  258. emit messageBox(message, messageBoxPointX, messageBoxPointY, messageTitle);
  259. QProcess p;
  260. p.startDetached("taskkill", {"/im", name, "/f"});
  261. p.waitForFinished(3000);
  262. p.close();
  263. // 更新 lastAlertTime
  264. ProcessModel processModel{storage};
  265. processModel.buildFromJson(object);
  266. // 更新 消息时间
  267. processModel.setLastAlertTime(updataTime);
  268. processModel.save();
  269. }
  270. // 获取游戏 截图 并关闭进程
  271. // QTemporaryFile tempFile("tempfile_XXXXXX.png");
  272. // tempFile.setAutoRemove(true);
  273. // tempFile.open();
  274. // AppEvent::captureDesktop(&tempFile, "PNG");
  275. // qDebug() << "发送截图" << tempFile.fileName();
  276. // TC::ProcessImageApi processImageApi;
  277. // qDebug() << "发送截图" << processImageApi.post({tempFile.fileName()});
  278. // tempFile.close();
  279. // qDebug() << "发送截图" << tempFile.remove() << tempFile.errorString();
  280. // if (object.contains("is_prompt")) { // 提示
  281. // if (object["is_prompt"].toInt() == 2) {
  282. // }
  283. // }
  284. }
  285. }
  286. }
  287. }
  288. bool ProcessThread::upDataProcessSql()
  289. {
  290. std::vector<std::shared_ptr<ProcessMonitor::ProcessInfo>> timeProcessVector
  291. = processMonitor.checkProcesses();
  292. // 退出时间默认当前时间 然后更新
  293. auto exitTimestamp = QDateTime::currentSecsSinceEpoch();
  294. auto updataTimestamp = QDateTime::currentSecsSinceEpoch();
  295. QStringList sqlValues;
  296. for (auto timeProcess : timeProcessVector) {
  297. qint64 timestamp = fileTimeToUnixTimestamp(timeProcess->creationTime);
  298. ProcessModel processModel{storage};
  299. CWF::SqlQueryManager qry(storage);
  300. qry.select("*", processModel.getTableName())
  301. .where(QString("pid == '%1' AND processName == '%2'")
  302. .arg(timeProcess->pid)
  303. .arg(timeProcess->processName.c_str()));
  304. qry.prepare();
  305. QJsonObject jsonObject = qry.exec();
  306. if (jsonObject["success"].toBool()) {
  307. // 查询或者替换 ?
  308. QJsonArray array = qry.toJson();
  309. if (array.size() > 0) {
  310. // 替换
  311. for (const QJsonValue &info : array) {
  312. const QJsonObject object = info.toObject();
  313. //数据还原到 结构体
  314. processModel.buildFromJson(object);
  315. // 更新 结束时间
  316. processModel.setExitTime(exitTimestamp);
  317. processModel.setUpdataTime(updataTimestamp);
  318. processModel.save();
  319. }
  320. } else {
  321. // 插入数据
  322. processModel.setProcessName(timeProcess->processName.c_str());
  323. processModel.setPid(timeProcess->pid);
  324. processModel.setCreationTime(timestamp);
  325. processModel.setExitTime(exitTimestamp);
  326. processModel.setUpdataTime(updataTimestamp);
  327. processModel.setLastAlertTime(updataTimestamp);
  328. processModel.save();
  329. }
  330. }
  331. }
  332. // 发送更新后的数据
  333. sendExitTime(updataTimestamp);
  334. return false;
  335. }
  336. ProcessThread::ProcessThread(
  337. QObject *parent)
  338. : QThread{parent}
  339. {}
  340. void ProcessThread::run()
  341. {
  342. ProcessModel processModel{storage};
  343. processModel.updateDB();
  344. {
  345. const QString query = QString("CREATE UNIQUE INDEX %2_%3_unique ON %1 (%2, %3);")
  346. .arg(processModel.getTableName())
  347. .arg("pid")
  348. .arg("processName");
  349. CWF::SqlQuery qry(storage);
  350. qry.exec(query);
  351. }
  352. QElapsedTimer timer; // 创建高精度计时器
  353. timer.start(); // 启动计时器
  354. upDataProcessSql();
  355. while (true) {
  356. // 校验网络
  357. if (timer.elapsed() >= 10 * 1000) { // 检查是否经过1分钟
  358. timer.restart(); // 重新启动计时器
  359. upDataProcessSql();
  360. }
  361. msleep(1000); // 休息1秒
  362. }
  363. }