feat: 切换正式管理端默认地址 (#148)

This commit is contained in:
chengma
2026-08-11 11:50:29 +08:00
parent e4385ac2dc
commit 5c2f4f65d9
9 changed files with 77 additions and 24 deletions
+17
View File
@@ -6,12 +6,29 @@ import uuid
from dataclasses import dataclass from dataclasses import dataclass
from typing import Callable, Optional from typing import Callable, Optional
from .http_admin_gateway import DEFAULT_ADMIN_BASE_URL
from .settings_repository import SettingsRepository from .settings_repository import SettingsRepository
CLIENT_ID_KEY = "admin.client_id" CLIENT_ID_KEY = "admin.client_id"
CLIENT_NAME_KEY = "admin.client_name" CLIENT_NAME_KEY = "admin.client_name"
ADMIN_BASE_URL_KEY = "admin.base_url" ADMIN_BASE_URL_KEY = "admin.base_url"
LEGACY_ADMIN_BASE_URL = "http://127.0.0.1:8080"
def load_admin_base_url(repository: SettingsRepository) -> str:
"""读取管理端地址,并把空值或旧默认值迁移到当前默认地址。"""
saved_value = repository.get(ADMIN_BASE_URL_KEY)
normalized_value = (
saved_value.strip().rstrip("/")
if isinstance(saved_value, str)
else ""
)
if not normalized_value or normalized_value == LEGACY_ADMIN_BASE_URL:
repository.set(ADMIN_BASE_URL_KEY, DEFAULT_ADMIN_BASE_URL)
return DEFAULT_ADMIN_BASE_URL
return normalized_value
def generate_client_device_id() -> str: def generate_client_device_id() -> str:
+1 -1
View File
@@ -22,7 +22,7 @@ from .admin_gateway import (
from .task_models import TaskType from .task_models import TaskType
DEFAULT_ADMIN_BASE_URL = "http://127.0.0.1:8080" DEFAULT_ADMIN_BASE_URL = "https://buy.833729.com"
class HttpAdminGateway(AdminGateway): class HttpAdminGateway(AdminGateway):
+3 -3
View File
@@ -43,8 +43,8 @@ from .admin_gateway import (
AdminGateway, AdminGateway,
) )
from .collect_task_service import CollectServiceFactory, CollectTaskService from .collect_task_service import CollectServiceFactory, CollectTaskService
from .current_client_service import CurrentClientService from .current_client_service import CurrentClientService, load_admin_base_url
from .http_admin_gateway import DEFAULT_ADMIN_BASE_URL, HttpAdminGateway from .http_admin_gateway import HttpAdminGateway
from .pdd_ui import PDDTaskPage, TaskRow from .pdd_ui import PDDTaskPage, TaskRow
from .pdd_device_service import ( from .pdd_device_service import (
PersistentPddDeviceService, PersistentPddDeviceService,
@@ -630,7 +630,7 @@ class PDDTaskPageEvent(QObject):
self._owns_claim_gateway = claim_gateway is None self._owns_claim_gateway = claim_gateway is None
self._claim_gateway_error = "" self._claim_gateway_error = ""
if self._claim_gateway is None: if self._claim_gateway is None:
base_url = settings.get("admin.base_url", DEFAULT_ADMIN_BASE_URL) base_url = load_admin_base_url(settings)
timeout_value = settings.get("admin.request_timeout_seconds", 3.0) timeout_value = settings.get("admin.request_timeout_seconds", 3.0)
try: try:
timeout_seconds = float(timeout_value) timeout_seconds = float(timeout_value)
+1 -1
View File
@@ -244,7 +244,7 @@ class SettingsPage(QWidget):
self.deviceNameInput.setAccessibleName("当前客户端设备名") self.deviceNameInput.setAccessibleName("当前客户端设备名")
self.adminBaseUrlInput = LineEdit(self) self.adminBaseUrlInput = LineEdit(self)
self.adminBaseUrlInput.setPlaceholderText("http://127.0.0.1:8080") self.adminBaseUrlInput.setPlaceholderText("https://buy.833729.com")
self.adminBaseUrlInput.setClearButtonEnabled(True) self.adminBaseUrlInput.setClearButtonEnabled(True)
self.adminBaseUrlInput.setAccessibleName("Admin 管理端地址") self.adminBaseUrlInput.setAccessibleName("Admin 管理端地址")
+5 -11
View File
@@ -30,11 +30,11 @@ from .admin_gateway import (
ClientRegistrationGateway, ClientRegistrationGateway,
) )
from .current_client_service import ( from .current_client_service import (
ADMIN_BASE_URL_KEY,
CurrentClientService, CurrentClientService,
generate_client_device_id, generate_client_device_id,
load_admin_base_url,
) )
from .http_admin_gateway import DEFAULT_ADMIN_BASE_URL, HttpAdminGateway from .http_admin_gateway import HttpAdminGateway
from .selected_android_device_service import SelectedAndroidDeviceService from .selected_android_device_service import SelectedAndroidDeviceService
from .settings_repository import SettingsRepository from .settings_repository import SettingsRepository
from .settings_ui import AndroidDeviceRow from .settings_ui import AndroidDeviceRow
@@ -381,7 +381,7 @@ class SettingsPageEventBinder(QObject):
self._pending_admin_gateway = None self._pending_admin_gateway = None
self._gateway_error = "" self._gateway_error = ""
if self._admin_gateway is None: if self._admin_gateway is None:
base_url = repository.get("admin.base_url", DEFAULT_ADMIN_BASE_URL) base_url = load_admin_base_url(repository)
timeout_value = repository.get("admin.request_timeout_seconds", 3.0) timeout_value = repository.get("admin.request_timeout_seconds", 3.0)
try: try:
timeout_seconds = float(timeout_value) timeout_seconds = float(timeout_value)
@@ -946,14 +946,8 @@ class SettingsPageEventBinder(QObject):
"""页面创建时恢复本地 Client 信息。""" """页面创建时恢复本地 Client 信息。"""
try: try:
base_url = self._settings_repository.get( displayed_base_url = load_admin_base_url(
ADMIN_BASE_URL_KEY, self._settings_repository
DEFAULT_ADMIN_BASE_URL,
)
displayed_base_url = (
base_url.strip()
if isinstance(base_url, str) and base_url.strip()
else DEFAULT_ADMIN_BASE_URL
) )
saved = self._client_service.load() saved = self._client_service.load()
except Exception as exc: except Exception as exc:
+40 -1
View File
@@ -4,7 +4,11 @@ import tempfile
import unittest import unittest
from pathlib import Path from pathlib import Path
from src.current_client_service import CurrentClientService from src.current_client_service import (
CurrentClientService,
load_admin_base_url,
)
from src.http_admin_gateway import DEFAULT_ADMIN_BASE_URL
from src.settings_repository import SettingsRepository from src.settings_repository import SettingsRepository
@@ -52,6 +56,41 @@ class CurrentClientServiceTest(unittest.TestCase):
self.assertEqual(saved.client_id, "CLIENT-GENERATED-1") self.assertEqual(saved.client_id, "CLIENT-GENERATED-1")
self.assertEqual(saved.client_name, "新设备") self.assertEqual(saved.client_name, "新设备")
def test_missing_admin_base_url_is_saved_as_current_default(self):
self.assertEqual(
load_admin_base_url(self.repository),
DEFAULT_ADMIN_BASE_URL,
)
self.assertEqual(
self.repository.get("admin.base_url"),
DEFAULT_ADMIN_BASE_URL,
)
def test_legacy_admin_base_url_is_migrated(self):
self.repository.set("admin.base_url", "http://127.0.0.1:8080/")
self.assertEqual(
load_admin_base_url(self.repository),
DEFAULT_ADMIN_BASE_URL,
)
self.assertEqual(
self.repository.get("admin.base_url"),
DEFAULT_ADMIN_BASE_URL,
)
def test_custom_admin_base_url_is_preserved(self):
custom_url = "https://admin.example.com/service/"
self.repository.set("admin.base_url", custom_url)
self.assertEqual(
load_admin_base_url(self.repository),
"https://admin.example.com/service",
)
self.assertEqual(
self.repository.get("admin.base_url"),
custom_url,
)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
+3 -3
View File
@@ -13,7 +13,7 @@ from src.admin_gateway import (
ClaimCapabilities, ClaimCapabilities,
ClientInfo, ClientInfo,
) )
from src.http_admin_gateway import HttpAdminGateway from src.http_admin_gateway import DEFAULT_ADMIN_BASE_URL, HttpAdminGateway
from src.task_models import TaskType from src.task_models import TaskType
@@ -144,7 +144,7 @@ class HttpAdminGatewayTest(unittest.TestCase):
self.assertTrue(receipt.accepted) self.assertTrue(receipt.accepted)
self.assertEqual( self.assertEqual(
opener.request.full_url, opener.request.full_url,
"http://127.0.0.1:8080/api/v1/client/tasks/COL-001/result", f"{DEFAULT_ADMIN_BASE_URL}/api/v1/client/tasks/COL-001/result",
) )
headers = { headers = {
key.lower(): value for key, value in opener.request.header_items() key.lower(): value for key, value in opener.request.header_items()
@@ -256,7 +256,7 @@ class HttpAdminGatewayTest(unittest.TestCase):
self.assertEqual(opener.request.get_method(), "POST") self.assertEqual(opener.request.get_method(), "POST")
self.assertEqual( self.assertEqual(
opener.request.full_url, opener.request.full_url,
"http://127.0.0.1:8080/api/v1/client/tasks/claim", f"{DEFAULT_ADMIN_BASE_URL}/api/v1/client/tasks/claim",
) )
headers = { headers = {
key.lower(): value for key, value in opener.request.header_items() key.lower(): value for key, value in opener.request.header_items()
+6 -3
View File
@@ -290,7 +290,7 @@ class SettingsPageEventTest(unittest.TestCase):
{ {
"admin.client_id": "CLIENT-EXISTING", "admin.client_id": "CLIENT-EXISTING",
"admin.client_name": "原设备名", "admin.client_name": "原设备名",
"admin.base_url": "http://127.0.0.1:8080", "admin.base_url": "http://127.0.0.1:9090",
} }
) )
gateway = MockAdminGateway() gateway = MockAdminGateway()
@@ -309,7 +309,7 @@ class SettingsPageEventTest(unittest.TestCase):
self.assertEqual(self.repository.get("admin.client_name"), "原设备名") self.assertEqual(self.repository.get("admin.client_name"), "原设备名")
self.assertEqual( self.assertEqual(
self.repository.get("admin.base_url"), self.repository.get("admin.base_url"),
"http://127.0.0.1:8080", "http://127.0.0.1:9090",
) )
self.assertEqual(gateway.registration_count, 0) self.assertEqual(gateway.registration_count, 0)
self.assertIn("Admin 服务地址必须", page.currentDeviceStatusLabel.text()) self.assertIn("Admin 服务地址必须", page.currentDeviceStatusLabel.text())
@@ -330,7 +330,10 @@ class SettingsPageEventTest(unittest.TestCase):
page.currentDeviceSaveButton.click() page.currentDeviceSaveButton.click()
self.app.processEvents() self.app.processEvents()
self.assertIsNone(self.repository.get("admin.base_url")) self.assertEqual(
self.repository.get("admin.base_url"),
DEFAULT_ADMIN_BASE_URL,
)
self.assertEqual(gateway.registration_count, 0) self.assertEqual(gateway.registration_count, 0)
self.assertIn("不能包含账号、密码", page.currentDeviceStatusLabel.text()) self.assertIn("不能包含账号、密码", page.currentDeviceStatusLabel.text())
page.eventBinder.shutdown() page.eventBinder.shutdown()
+1 -1
View File
@@ -284,7 +284,7 @@ class TaskTableModel(QAbstractTableModel):
### 当前设备 ### 当前设备
- 第一行依次显示设备号和设备名;设备号只读,设备名可以修改,两组输入随窗口宽度平均伸缩; - 第一行依次显示设备号和设备名;设备号只读,设备名可以修改,两组输入随窗口宽度平均伸缩;
- 第二行显示“管理端地址”,对应 SQLite 中已有的 `admin.base_url`,没有保存值时显示默认地址; - 第二行显示“管理端地址”,对应 SQLite 中已有的 `admin.base_url`;默认地址为 `https://buy.833729.com`,没有保存值或保存值仍是旧默认地址 `http://127.0.0.1:8080` 时自动迁移,用户手动保存的其他地址不得覆盖;
- 点击“保存”前先验证地址;只接受有效的 HTTP/HTTPS 地址,不能包含账号、密码、查询参数或片段。验证失败时不保存设备号、设备名或地址,并把焦点放回地址输入框; - 点击“保存”前先验证地址;只接受有效的 HTTP/HTTPS 地址,不能包含账号、密码、查询参数或片段。验证失败时不保存设备号、设备名或地址,并把焦点放回地址输入框;
- 保存成功后立即用新地址登记 Client,后续任务领取、结果提交和失败提交也统一切换到新地址;已经发出的请求等待自身结束,不强制中断; - 保存成功后立即用新地址登记 Client,后续任务领取、结果提交和失败提交也统一切换到新地址;已经发出的请求等待自身结束,不强制中断;
- 保存不新增数据库表,不增加心跳,也不连接或操作 Android 设备。 - 保存不新增数据库表,不增加心跳,也不连接或操作 Android 设备。