mirror of
https://github.com/KenanZhu/AutoLibrary.git
synced 2026-08-02 22:19:37 +08:00
fix(bulletin): 修复公告栏系统多项逻辑缺陷与线程安全隐患
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+30
-15
@@ -130,7 +130,11 @@ class ALBulletinItemWidget(QWidget):
|
||||
self.ItemWidgetLayout.addLayout(self.BulletinTitleLayout)
|
||||
self.BulletinInfoLayout = QHBoxLayout()
|
||||
self.BulletinDateLabel = QLabel()
|
||||
date_time = datetime.fromisoformat(self.__bulletin.get("dateTime", ""))
|
||||
try:
|
||||
raw_dt = self.__bulletin.get("dateTime", "")
|
||||
date_time = datetime.fromisoformat(raw_dt) if raw_dt else datetime.now()
|
||||
except (ValueError, TypeError):
|
||||
date_time = datetime.now()
|
||||
self.BulletinDateLabel.setText(date_time.strftime("%Y-%m-%d %H:%M:%S"))
|
||||
self.BulletinDateLabel.setStyleSheet("color: #969696; font-size: 11px;")
|
||||
self.BulletinInfoLayout.addWidget(self.BulletinDateLabel)
|
||||
@@ -258,7 +262,11 @@ class ALBulletinDialog(QDialog, Ui_ALBulletinDialog):
|
||||
):
|
||||
|
||||
self.BulletinTitleLabel.setText(bulletin.get("title", "无标题"))
|
||||
date_time = datetime.fromisoformat(bulletin.get("dateTime", ""))
|
||||
try:
|
||||
raw_dt = bulletin.get("dateTime", "")
|
||||
date_time = datetime.fromisoformat(raw_dt) if raw_dt else datetime.now()
|
||||
except (ValueError, TypeError):
|
||||
date_time = datetime.now()
|
||||
self.BulletinDateLabel.setText(date_time.strftime("%Y-%m-%d %H:%M:%S"))
|
||||
self.BulletinAuthorLabel.setText(bulletin.get("author", "未知"))
|
||||
if bulletin.get("isEdited", False):
|
||||
@@ -306,12 +314,15 @@ class ALBulletinDialog(QDialog, Ui_ALBulletinDialog):
|
||||
data: dict
|
||||
):
|
||||
|
||||
if self.__fetch_worker:
|
||||
self.__fetch_worker.wait(2000)
|
||||
self.__fetch_worker.fetchWorkerIsFinished.disconnect(self.onBulletinsFetched)
|
||||
self.__fetch_worker.fetchWorkerFinishedWithError.disconnect(self.onBulletinsFetchError)
|
||||
self.__fetch_worker.deleteLater()
|
||||
self.__fetch_worker = None
|
||||
worker = self.sender()
|
||||
if worker is not self.__fetch_worker:
|
||||
return
|
||||
worker.fetchWorkerIsFinished.disconnect(self.onBulletinsFetched)
|
||||
worker.fetchWorkerFinishedWithError.disconnect(self.onBulletinsFetchError)
|
||||
worker.wait(2000)
|
||||
worker.deleteLater()
|
||||
self.__fetch_worker = None
|
||||
|
||||
bulletins = data.get("bulletins", [])
|
||||
delete_ids = data.get("delete_ids", [])
|
||||
merged = self.__bulletin_mgr.updateAndMergeBulletins(bulletins, delete_ids)
|
||||
@@ -336,12 +347,15 @@ class ALBulletinDialog(QDialog, Ui_ALBulletinDialog):
|
||||
error_message: str
|
||||
):
|
||||
|
||||
if self.__fetch_worker:
|
||||
self.__fetch_worker.wait(2000)
|
||||
self.__fetch_worker.fetchWorkerIsFinished.disconnect(self.onBulletinsFetched)
|
||||
self.__fetch_worker.fetchWorkerFinishedWithError.disconnect(self.onBulletinsFetchError)
|
||||
self.__fetch_worker.deleteLater()
|
||||
self.__fetch_worker = None
|
||||
worker = self.sender()
|
||||
if worker is not self.__fetch_worker:
|
||||
return
|
||||
worker.fetchWorkerIsFinished.disconnect(self.onBulletinsFetched)
|
||||
worker.fetchWorkerFinishedWithError.disconnect(self.onBulletinsFetchError)
|
||||
worker.wait(2000)
|
||||
worker.deleteLater()
|
||||
self.__fetch_worker = None
|
||||
|
||||
self.SyncButton.setEnabled(True)
|
||||
self.SyncButton.setText("重试")
|
||||
self.ALSyncStatusLabel.status = ALStatusLabel.Status.FAILURE
|
||||
@@ -376,7 +390,8 @@ class ALBulletinDialog(QDialog, Ui_ALBulletinDialog):
|
||||
if bulletin.get("isNew", False):
|
||||
bulletin["isNew"] = False
|
||||
widget = self.BulletinListWidget.itemWidget(item)
|
||||
widget.markAsRead()
|
||||
if widget:
|
||||
widget.markAsRead()
|
||||
item.setData(Qt.UserRole, bulletin)
|
||||
self.__bulletin_mgr.markBulletinAsRead(bulletin["id"])
|
||||
self.showBulletin(bulletin)
|
||||
|
||||
+52
-34
@@ -42,12 +42,14 @@ class ALBulletinPoller(QObject):
|
||||
self.__timer.timeout.connect(self.__poll)
|
||||
self.__worker = None
|
||||
self.__dialog_open = False
|
||||
self.__stopped = False
|
||||
self.__mgr = bulletinInstance()
|
||||
|
||||
def start(
|
||||
self
|
||||
):
|
||||
|
||||
self.__stopped = False
|
||||
interval_ms = self.__mgr.syncInterval()*60*1000
|
||||
self.__timer.start(interval_ms)
|
||||
|
||||
@@ -55,6 +57,7 @@ class ALBulletinPoller(QObject):
|
||||
self
|
||||
):
|
||||
|
||||
self.__stopped = True
|
||||
self.__timer.stop()
|
||||
self.__cleanupWorker()
|
||||
|
||||
@@ -88,6 +91,31 @@ class ALBulletinPoller(QObject):
|
||||
|
||||
self.__dialog_open = open
|
||||
|
||||
def __disconnectWorker(
|
||||
self,
|
||||
worker: ALBulletinFetchWorker
|
||||
):
|
||||
|
||||
try:
|
||||
worker.fetchWorkerIsFinished.disconnect(self.__onFetched)
|
||||
except (TypeError, RuntimeError):
|
||||
pass
|
||||
try:
|
||||
worker.fetchWorkerFinishedWithError.disconnect(self.__onError)
|
||||
except (TypeError, RuntimeError):
|
||||
pass
|
||||
|
||||
def __cleanupWorker(
|
||||
self
|
||||
):
|
||||
|
||||
if self.__worker is None:
|
||||
return
|
||||
self.__disconnectWorker(self.__worker)
|
||||
self.__worker.wait(2000)
|
||||
self.__worker.deleteLater()
|
||||
self.__worker = None
|
||||
|
||||
def __doFetch(
|
||||
self
|
||||
):
|
||||
@@ -117,23 +145,30 @@ class ALBulletinPoller(QObject):
|
||||
data: dict
|
||||
):
|
||||
|
||||
if self.__worker is None:
|
||||
worker = self.sender()
|
||||
if worker is not self.__worker:
|
||||
return
|
||||
self.__worker.fetchWorkerIsFinished.disconnect(self.__onFetched)
|
||||
self.__worker.fetchWorkerFinishedWithError.disconnect(self.__onError)
|
||||
self.__worker.wait(2000)
|
||||
self.__worker.deleteLater()
|
||||
self.__disconnectWorker(worker)
|
||||
worker.wait(2000)
|
||||
worker.deleteLater()
|
||||
self.__worker = None
|
||||
old_ids = {b["id"] for b in self.__mgr.bulletins()}
|
||||
old_ids = {str(b.get("id", "")) for b in self.__mgr.bulletins()}
|
||||
bulletins = data.get("bulletins", [])
|
||||
delete_ids = data.get("delete_ids", [])
|
||||
self.__mgr.updateAndMergeBulletins(bulletins, delete_ids)
|
||||
self.__mgr.setLastSyncTime(datetime.now().astimezone().isoformat())
|
||||
new_ids = {b["id"] for b in bulletins if b["id"] not in old_ids}
|
||||
delete_id_set = {str(d) for d in delete_ids}
|
||||
new_ids = {
|
||||
str(b.get("id", ""))
|
||||
for b in bulletins
|
||||
if str(b.get("id", "")) and str(b.get("id", "")) not in old_ids
|
||||
}
|
||||
new_ids -= delete_id_set
|
||||
if new_ids:
|
||||
self.newBulletinsDetected.emit(len(new_ids))
|
||||
interval_ms = self.__mgr.syncInterval()*60*1000
|
||||
self.__timer.start(interval_ms)
|
||||
if not self.__stopped:
|
||||
interval_ms = self.__mgr.syncInterval()*60*1000
|
||||
self.__timer.start(interval_ms)
|
||||
|
||||
@Slot(str)
|
||||
def __onError(
|
||||
@@ -141,30 +176,13 @@ class ALBulletinPoller(QObject):
|
||||
error_message: str
|
||||
):
|
||||
|
||||
if self.__worker is None:
|
||||
worker = self.sender()
|
||||
if worker is not self.__worker:
|
||||
return
|
||||
self.__worker.fetchWorkerIsFinished.disconnect(self.__onFetched)
|
||||
self.__worker.fetchWorkerFinishedWithError.disconnect(self.__onError)
|
||||
self.__worker.wait(2000)
|
||||
self.__worker.deleteLater()
|
||||
self.__worker = None
|
||||
interval_ms = self.__mgr.syncInterval()*60*1000
|
||||
self.__timer.start(interval_ms)
|
||||
|
||||
def __cleanupWorker(
|
||||
self
|
||||
):
|
||||
|
||||
if self.__worker is None:
|
||||
return
|
||||
try:
|
||||
self.__worker.fetchWorkerIsFinished.disconnect()
|
||||
except (TypeError, RuntimeError):
|
||||
pass
|
||||
try:
|
||||
self.__worker.fetchWorkerFinishedWithError.disconnect()
|
||||
except (TypeError, RuntimeError):
|
||||
pass
|
||||
self.__worker.wait(2000)
|
||||
self.__worker.deleteLater()
|
||||
self.__disconnectWorker(worker)
|
||||
worker.wait(2000)
|
||||
worker.deleteLater()
|
||||
self.__worker = None
|
||||
if not self.__stopped:
|
||||
interval_ms = self.__mgr.syncInterval()*60*1000
|
||||
self.__timer.start(interval_ms)
|
||||
|
||||
+55
-23
@@ -458,32 +458,64 @@ class ALSettingsWidget(CenterOnParentMixin, QWidget, Ui_ALSettingsWidget):
|
||||
self.BulletinTestStatusLabel.setText("请先输入服务器地址。")
|
||||
self.BulletinTestStatusLabel.setStyleSheet("color: red;")
|
||||
return
|
||||
if hasattr(self, '__bulletin_test_worker') and self.__bulletin_test_worker is not None:
|
||||
return
|
||||
self.BulletinTestButton.setEnabled(False)
|
||||
self.BulletinTestStatusLabel.setText("正在测试连接...")
|
||||
self.BulletinTestStatusLabel.setStyleSheet("")
|
||||
try:
|
||||
api_url = url.rstrip("/") + "/bulletins"
|
||||
t0 = time.monotonic()
|
||||
response = requests.get(api_url, timeout=5)
|
||||
elapsed_ms = (time.monotonic() - t0) * 1000
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
if data.get("code") == 200:
|
||||
self.BulletinTestStatusLabel.setText(
|
||||
f"连接成功!响应延迟 {elapsed_ms:.0f} ms"
|
||||
)
|
||||
self.BulletinTestStatusLabel.setStyleSheet("color: green;")
|
||||
else:
|
||||
self.BulletinTestStatusLabel.setText(
|
||||
f"服务器返回异常: [{data.get('code', '?')}] {data.get('msg', '未知错误')}"
|
||||
)
|
||||
self.BulletinTestStatusLabel.setStyleSheet("color: red;")
|
||||
except Exception as e:
|
||||
self.BulletinTestStatusLabel.setText(f"连接失败:{e}")
|
||||
self.BulletinTestStatusLabel.setStyleSheet("color: red;")
|
||||
finally:
|
||||
self.BulletinTestButton.setEnabled(True)
|
||||
QTimer.singleShot(3000, self.clearBulletinTestStatus)
|
||||
self.__bulletin_test_t0 = time.monotonic()
|
||||
from gui.ALBulletinDialog import ALBulletinFetchWorker
|
||||
api_url = url.rstrip("/") + "/bulletins"
|
||||
self.__bulletin_test_worker = ALBulletinFetchWorker(
|
||||
self, api_url, {"date": "", "time": "", "range_hour": "1"}
|
||||
)
|
||||
self.__bulletin_test_worker.fetchWorkerIsFinished.connect(
|
||||
self.__onBulletinTestFetched
|
||||
)
|
||||
self.__bulletin_test_worker.fetchWorkerFinishedWithError.connect(
|
||||
self.__onBulletinTestError
|
||||
)
|
||||
self.__bulletin_test_worker.start()
|
||||
|
||||
@Slot(dict)
|
||||
def __onBulletinTestFetched(
|
||||
self,
|
||||
data: dict
|
||||
):
|
||||
|
||||
self.__bulletin_test_worker.fetchWorkerIsFinished.disconnect(
|
||||
self.__onBulletinTestFetched
|
||||
)
|
||||
self.__bulletin_test_worker.fetchWorkerFinishedWithError.disconnect(
|
||||
self.__onBulletinTestError
|
||||
)
|
||||
self.__bulletin_test_worker.deleteLater()
|
||||
self.__bulletin_test_worker = None
|
||||
elapsed_ms = (time.monotonic() - self.__bulletin_test_t0) * 1000
|
||||
self.BulletinTestStatusLabel.setText(
|
||||
f"连接成功!响应延迟 {elapsed_ms:.0f} ms"
|
||||
)
|
||||
self.BulletinTestStatusLabel.setStyleSheet("color: green;")
|
||||
self.BulletinTestButton.setEnabled(True)
|
||||
QTimer.singleShot(3000, self.clearBulletinTestStatus)
|
||||
|
||||
@Slot(str)
|
||||
def __onBulletinTestError(
|
||||
self,
|
||||
error_message: str
|
||||
):
|
||||
|
||||
self.__bulletin_test_worker.fetchWorkerIsFinished.disconnect(
|
||||
self.__onBulletinTestFetched
|
||||
)
|
||||
self.__bulletin_test_worker.fetchWorkerFinishedWithError.disconnect(
|
||||
self.__onBulletinTestError
|
||||
)
|
||||
self.__bulletin_test_worker.deleteLater()
|
||||
self.__bulletin_test_worker = None
|
||||
self.BulletinTestStatusLabel.setText(f"连接失败:{error_message}")
|
||||
self.BulletinTestStatusLabel.setStyleSheet("color: red;")
|
||||
self.BulletinTestButton.setEnabled(True)
|
||||
|
||||
@Slot()
|
||||
def onCancelButtonClicked(
|
||||
|
||||
Reference in New Issue
Block a user