type: 收窄测试与响应模型类型
This commit is contained in:
@@ -35,7 +35,7 @@ class TestAccountSensitiveFields:
|
|||||||
def test_sensitive_fields_are_hidden_by_default_even_for_admin(self):
|
def test_sensitive_fields_are_hidden_by_default_even_for_admin(self):
|
||||||
result = list_accounts(
|
result = list_accounts(
|
||||||
assigned_only=False,
|
assigned_only=False,
|
||||||
tag=None,
|
tag="",
|
||||||
has_cookie=False,
|
has_cookie=False,
|
||||||
search="",
|
search="",
|
||||||
page=1,
|
page=1,
|
||||||
@@ -45,6 +45,7 @@ class TestAccountSensitiveFields:
|
|||||||
current=self.admin,
|
current=self.admin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert isinstance(result, dict)
|
||||||
item = result["items"][0]
|
item = result["items"][0]
|
||||||
assert item.password is None
|
assert item.password is None
|
||||||
assert item.email is None
|
assert item.email is None
|
||||||
@@ -53,7 +54,7 @@ class TestAccountSensitiveFields:
|
|||||||
def test_admin_can_explicitly_request_sensitive_fields(self):
|
def test_admin_can_explicitly_request_sensitive_fields(self):
|
||||||
result = list_accounts(
|
result = list_accounts(
|
||||||
assigned_only=False,
|
assigned_only=False,
|
||||||
tag=None,
|
tag="",
|
||||||
has_cookie=False,
|
has_cookie=False,
|
||||||
search="",
|
search="",
|
||||||
page=1,
|
page=1,
|
||||||
@@ -63,6 +64,7 @@ class TestAccountSensitiveFields:
|
|||||||
current=self.admin,
|
current=self.admin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert isinstance(result, dict)
|
||||||
item = result["items"][0]
|
item = result["items"][0]
|
||||||
assert item.password == "account-password"
|
assert item.password == "account-password"
|
||||||
assert item.email == "account@example.com"
|
assert item.email == "account@example.com"
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
from web.backend.database import Base
|
from web.backend.database import Base
|
||||||
from web.backend.models import Account, LoginTask, User
|
from web.backend.models import Account, LoginTask, ProxyConfig, User
|
||||||
from web.backend.services.login_service import (
|
from web.backend.services.login_service import (
|
||||||
LoginBatchRunner,
|
LoginBatchRunner,
|
||||||
cleanup_orphan_relogin_tasks,
|
cleanup_orphan_relogin_tasks,
|
||||||
@@ -137,6 +138,7 @@ class TestCookieOperation:
|
|||||||
current=admin,
|
current=admin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert isinstance(result, dict)
|
||||||
assert result["total"] == 1
|
assert result["total"] == 1
|
||||||
assert [item["id"] for item in result["items"]] == [self.owned_task.id]
|
assert [item["id"] for item in result["items"]] == [self.owned_task.id]
|
||||||
|
|
||||||
@@ -200,7 +202,7 @@ class TestCookieOperation:
|
|||||||
page=1,
|
page=1,
|
||||||
page_size=20,
|
page_size=20,
|
||||||
db=self.session,
|
db=self.session,
|
||||||
current=no_permission_user,
|
current=cast(User, no_permission_user),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_relogin_failed_record_stays_visible_and_can_be_checked(self):
|
def test_relogin_failed_record_stays_visible_and_can_be_checked(self):
|
||||||
@@ -235,7 +237,7 @@ class TestCookieOperation:
|
|||||||
assert self.other_task.status == "success"
|
assert self.other_task.status == "success"
|
||||||
|
|
||||||
def test_runner_copies_proxy_config_before_background_execution(self):
|
def test_runner_copies_proxy_config_before_background_execution(self):
|
||||||
proxy = SimpleNamespace(
|
proxy = ProxyConfig(
|
||||||
enabled=True,
|
enabled=True,
|
||||||
http="http://127.0.0.1:8080",
|
http="http://127.0.0.1:8080",
|
||||||
https="",
|
https="",
|
||||||
|
|||||||
@@ -94,16 +94,18 @@ class TestDouyuGoldRechargeChannel:
|
|||||||
assert str(supplier.create_order.call_args.kwargs["pay_amount"]) == "10"
|
assert str(supplier.create_order.call_args.kwargs["pay_amount"]) == "10"
|
||||||
self.session.refresh(self.task)
|
self.session.refresh(self.task)
|
||||||
assert self.task.status == "success"
|
assert self.task.status == "success"
|
||||||
assert self.task.result["recharge_channel"] == "supplier_api"
|
task_result = self.task.result
|
||||||
assert self.task.result["supplier_order_status"] == 2
|
assert isinstance(task_result, dict)
|
||||||
assert self.task.result["buy_num"] == 10
|
assert task_result["recharge_channel"] == "supplier_api"
|
||||||
assert self.task.result["out_order_id"] == expected_order_id
|
assert task_result["supplier_order_status"] == 2
|
||||||
assert self.task.result["recharge_account"] == "罗炅729"
|
assert task_result["buy_num"] == 10
|
||||||
assert self.task.result["douyu_uid"] == "10001"
|
assert task_result["out_order_id"] == expected_order_id
|
||||||
assert self.task.result["order_id"] == "supplier-001"
|
assert task_result["recharge_account"] == "罗炅729"
|
||||||
assert self.task.result["pay_amount"] == "10"
|
assert task_result["douyu_uid"] == "10001"
|
||||||
assert "pay_url" not in self.task.result
|
assert task_result["order_id"] == "supplier-001"
|
||||||
assert "sign" not in self.task.result["supplier_order"]
|
assert task_result["pay_amount"] == "10"
|
||||||
|
assert "pay_url" not in task_result
|
||||||
|
assert "sign" not in task_result["supplier_order"]
|
||||||
|
|
||||||
@patch("web.backend.services.douyu_runner_gold.FishFinRechargeClient")
|
@patch("web.backend.services.douyu_runner_gold.FishFinRechargeClient")
|
||||||
def test_supplier_channel_rejects_account_without_nickname(self, client_class):
|
def test_supplier_channel_rejects_account_without_nickname(self, client_class):
|
||||||
@@ -226,13 +228,14 @@ class TestDouyuGoldRechargeChannel:
|
|||||||
try:
|
try:
|
||||||
task = verify_session.get(DouyuTask, task_id)
|
task = verify_session.get(DouyuTask, task_id)
|
||||||
checked_login = verify_session.get(LoginTask, login_task_id)
|
checked_login = verify_session.get(LoginTask, login_task_id)
|
||||||
|
assert task is not None
|
||||||
|
assert checked_login is not None
|
||||||
assert task.status == "failed"
|
assert task.status == "failed"
|
||||||
assert "Cookie 已失效,请重新登录" in task.message
|
assert "Cookie 已失效,请重新登录" in task.message
|
||||||
assert checked_login.ck_check_status == "invalid"
|
assert checked_login.ck_check_status == "invalid"
|
||||||
assert (
|
check_result = checked_login.ck_check_result
|
||||||
checked_login.ck_check_result["message"]
|
assert isinstance(check_result, dict)
|
||||||
== "鱼丸接口: 未登录;等级接口: 未登录"
|
assert check_result["message"] == "鱼丸接口: 未登录;等级接口: 未登录"
|
||||||
)
|
|
||||||
finally:
|
finally:
|
||||||
verify_session.close()
|
verify_session.close()
|
||||||
|
|
||||||
@@ -273,8 +276,10 @@ class TestDouyuGoldRechargeChannel:
|
|||||||
assert self.task.message == "供应商直充成功(异步通知)"
|
assert self.task.message == "供应商直充成功(异步通知)"
|
||||||
self.session.refresh(self.account)
|
self.session.refresh(self.account)
|
||||||
assert self.account.bind_status == "gold_recharged"
|
assert self.account.bind_status == "gold_recharged"
|
||||||
assert self.task.result["order_id"] == "supplier-001"
|
task_result = self.task.result
|
||||||
assert self.task.result["supplier_order_status"] == 2
|
assert isinstance(task_result, dict)
|
||||||
|
assert task_result["order_id"] == "supplier-001"
|
||||||
|
assert task_result["supplier_order_status"] == 2
|
||||||
audit = (
|
audit = (
|
||||||
self.session.query(AuditLog)
|
self.session.query(AuditLog)
|
||||||
.filter(AuditLog.action == "recharge:douyu:callback")
|
.filter(AuditLog.action == "recharge:douyu:callback")
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
from typing import Any, cast
|
||||||
|
|
||||||
from core.douyu.login import DouyuLogin
|
from core.douyu.login import DouyuLogin
|
||||||
from core.douyu.login_api_wgapi import WgapiLoginAPI
|
from core.douyu.login_api_wgapi import WgapiLoginAPI
|
||||||
@@ -9,13 +10,14 @@ class TestMobileBindSkip:
|
|||||||
def setup_method(self):
|
def setup_method(self):
|
||||||
self.login = DouyuLogin.__new__(DouyuLogin)
|
self.login = DouyuLogin.__new__(DouyuLogin)
|
||||||
self.login.api = WgapiLoginAPI()
|
self.login.api = WgapiLoginAPI()
|
||||||
self.login.account = SimpleNamespace(
|
self.login.account = cast(
|
||||||
username="test-user", password="test-password"
|
Any, SimpleNamespace(username="test-user", password="test-password")
|
||||||
)
|
)
|
||||||
self.login._request_json = Mock()
|
self.request_json = Mock()
|
||||||
|
self.login.__dict__["_request_json"] = self.request_json
|
||||||
|
|
||||||
def test_second_login_skips_mobile_bind_with_unique_key(self):
|
def test_second_login_skips_mobile_bind_with_unique_key(self):
|
||||||
self.login._request_json.side_effect = [
|
self.request_json.side_effect = [
|
||||||
{
|
{
|
||||||
"error": 130014,
|
"error": 130014,
|
||||||
"msg": "需要进行手机号绑定",
|
"msg": "需要进行手机号绑定",
|
||||||
@@ -41,8 +43,8 @@ class TestMobileBindSkip:
|
|||||||
assert (
|
assert (
|
||||||
login_url == "https://www.douyu.com/api/passport/login?code=callback-code"
|
login_url == "https://www.douyu.com/api/passport/login?code=callback-code"
|
||||||
)
|
)
|
||||||
assert self.login._request_json.call_count == 2
|
assert self.request_json.call_count == 2
|
||||||
skip_call = self.login._request_json.call_args_list[1]
|
skip_call = self.request_json.call_args_list[1]
|
||||||
assert skip_call.args[:3] == (
|
assert skip_call.args[:3] == (
|
||||||
"post",
|
"post",
|
||||||
"https://passport.douyu.com/wgapi/member/passport/login",
|
"https://passport.douyu.com/wgapi/member/passport/login",
|
||||||
@@ -55,7 +57,7 @@ class TestMobileBindSkip:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def test_second_login_preserves_remote_email_flow_without_unique_key(self):
|
def test_second_login_preserves_remote_email_flow_without_unique_key(self):
|
||||||
self.login._request_json.return_value = {
|
self.request_json.return_value = {
|
||||||
"error": 130014,
|
"error": 130014,
|
||||||
"data": {"remoteLogin": {"code": "remote-code"}},
|
"data": {"remoteLogin": {"code": "remote-code"}},
|
||||||
}
|
}
|
||||||
@@ -69,4 +71,4 @@ class TestMobileBindSkip:
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert (next_step, remote_code) == ("remote_email", "remote-code")
|
assert (next_step, remote_code) == ("remote_email", "remote-code")
|
||||||
assert self.login._request_json.call_count == 1
|
assert self.request_json.call_count == 1
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from types import SimpleNamespace
|
|
||||||
|
|
||||||
from sqlalchemy import create_engine, event
|
from sqlalchemy import create_engine, event
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
@@ -54,12 +53,6 @@ class TestDouyuWorkbenchScope:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
self.current = SimpleNamespace(
|
|
||||||
id=self.user.id,
|
|
||||||
username=self.user.username,
|
|
||||||
role=self.user.role,
|
|
||||||
custom_permissions=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
def teardown_method(self):
|
def teardown_method(self):
|
||||||
self.session.close()
|
self.session.close()
|
||||||
@@ -72,12 +65,10 @@ class TestDouyuWorkbenchScope:
|
|||||||
handbook_scope="elite", account_ids=[self.account.id]
|
handbook_scope="elite", account_ids=[self.account.id]
|
||||||
),
|
),
|
||||||
db=self.session,
|
db=self.session,
|
||||||
current=self.current,
|
current=self.user,
|
||||||
)
|
|
||||||
elite = list_workbench_accounts("elite", db=self.session, current=self.current)
|
|
||||||
esports = list_workbench_accounts(
|
|
||||||
"esports", db=self.session, current=self.current
|
|
||||||
)
|
)
|
||||||
|
elite = list_workbench_accounts("elite", db=self.session, current=self.user)
|
||||||
|
esports = list_workbench_accounts("esports", db=self.session, current=self.user)
|
||||||
assert elite["account_ids"] == [self.account.id]
|
assert elite["account_ids"] == [self.account.id]
|
||||||
assert esports["account_ids"] == []
|
assert esports["account_ids"] == []
|
||||||
assert elite["configured"]
|
assert elite["configured"]
|
||||||
@@ -87,10 +78,10 @@ class TestDouyuWorkbenchScope:
|
|||||||
update_workbench_accounts(
|
update_workbench_accounts(
|
||||||
DouyuWorkbenchAccountsUpdate(handbook_scope="elite", account_ids=[]),
|
DouyuWorkbenchAccountsUpdate(handbook_scope="elite", account_ids=[]),
|
||||||
db=self.session,
|
db=self.session,
|
||||||
current=self.current,
|
current=self.user,
|
||||||
)
|
)
|
||||||
empty_elite = list_workbench_accounts(
|
empty_elite = list_workbench_accounts(
|
||||||
"elite", db=self.session, current=self.current
|
"elite", db=self.session, current=self.user
|
||||||
)
|
)
|
||||||
assert empty_elite["account_ids"] == []
|
assert empty_elite["account_ids"] == []
|
||||||
assert empty_elite["configured"]
|
assert empty_elite["configured"]
|
||||||
@@ -118,7 +109,7 @@ class TestDouyuWorkbenchScope:
|
|||||||
page=None,
|
page=None,
|
||||||
page_size=100,
|
page_size=100,
|
||||||
db=self.session,
|
db=self.session,
|
||||||
current=self.current,
|
current=self.user,
|
||||||
)
|
)
|
||||||
esports = list_tasks(
|
esports = list_tasks(
|
||||||
batch_id=None,
|
batch_id=None,
|
||||||
@@ -127,8 +118,10 @@ class TestDouyuWorkbenchScope:
|
|||||||
page=None,
|
page=None,
|
||||||
page_size=100,
|
page_size=100,
|
||||||
db=self.session,
|
db=self.session,
|
||||||
current=self.current,
|
current=self.user,
|
||||||
)
|
)
|
||||||
|
assert isinstance(elite, list)
|
||||||
|
assert isinstance(esports, list)
|
||||||
assert {item.batch_id for item in elite} == {elite_batch}
|
assert {item.batch_id for item in elite} == {elite_batch}
|
||||||
assert {item.batch_id for item in esports} == {esports_batch}
|
assert {item.batch_id for item in esports} == {esports_batch}
|
||||||
assert {
|
assert {
|
||||||
@@ -182,6 +175,6 @@ class TestDouyuWorkbenchScope:
|
|||||||
)
|
)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
delete_account(self.account.id, db=self.session, current=self.current)
|
delete_account(self.account.id, db=self.session, current=self.user)
|
||||||
|
|
||||||
assert self.session.query(DouyuWorkbenchAccount).count() == 0
|
assert self.session.query(DouyuWorkbenchAccount).count() == 0
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from types import SimpleNamespace
|
|
||||||
|
|
||||||
from sqlalchemy import create_engine, event
|
from sqlalchemy import create_engine, event
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
@@ -53,11 +51,12 @@ class TestUserDeletion:
|
|||||||
result = delete_user(
|
result = delete_user(
|
||||||
self.user.id,
|
self.user.id,
|
||||||
db=self.session,
|
db=self.session,
|
||||||
current=SimpleNamespace(id=self.admin.id, username=self.admin.username),
|
current=self.admin,
|
||||||
)
|
)
|
||||||
|
|
||||||
deleted = self.session.get(User, self.user.id)
|
deleted = self.session.get(User, self.user.id)
|
||||||
account = self.session.query(Account).one()
|
account = self.session.query(Account).one()
|
||||||
|
assert deleted is not None
|
||||||
assert result["success"]
|
assert result["success"]
|
||||||
assert result["released_accounts"] == 1
|
assert result["released_accounts"] == 1
|
||||||
assert not deleted.is_active
|
assert not deleted.is_active
|
||||||
@@ -74,7 +73,7 @@ class TestUserDeletion:
|
|||||||
delete_user(
|
delete_user(
|
||||||
self.user.id,
|
self.user.id,
|
||||||
db=self.session,
|
db=self.session,
|
||||||
current=SimpleNamespace(id=self.user.id, username=self.user.username),
|
current=self.user,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_rename_preserves_user_id_and_related_data(self):
|
def test_rename_preserves_user_id_and_related_data(self):
|
||||||
@@ -82,12 +81,13 @@ class TestUserDeletion:
|
|||||||
self.user.id,
|
self.user.id,
|
||||||
UserRename(username="renamed-support"),
|
UserRename(username="renamed-support"),
|
||||||
db=self.session,
|
db=self.session,
|
||||||
current=SimpleNamespace(id=self.admin.id, username=self.admin.username),
|
current=self.admin,
|
||||||
)
|
)
|
||||||
|
|
||||||
renamed = self.session.get(User, self.user.id)
|
renamed = self.session.get(User, self.user.id)
|
||||||
account = self.session.query(Account).one()
|
account = self.session.query(Account).one()
|
||||||
task = self.session.query(LoginTask).one()
|
task = self.session.query(LoginTask).one()
|
||||||
|
assert renamed is not None
|
||||||
assert result.username == "renamed-support"
|
assert result.username == "renamed-support"
|
||||||
assert renamed.id == self.user.id
|
assert renamed.id == self.user.id
|
||||||
assert account.assigned_to == self.user.id
|
assert account.assigned_to == self.user.id
|
||||||
|
|||||||
+41
-18
@@ -41,6 +41,11 @@ def _ensure_tz(dt: Optional[datetime]) -> Optional[datetime]:
|
|||||||
return dt
|
return dt
|
||||||
|
|
||||||
|
|
||||||
|
def _isoformat(dt: Optional[datetime]) -> str | None:
|
||||||
|
normalized = _ensure_tz(dt)
|
||||||
|
return normalized.isoformat() if normalized is not None else None
|
||||||
|
|
||||||
|
|
||||||
# ---- 认证 ----
|
# ---- 认证 ----
|
||||||
class LoginRequest(BaseModel):
|
class LoginRequest(BaseModel):
|
||||||
username: str
|
username: str
|
||||||
@@ -75,7 +80,7 @@ class UserInfo(BaseModel):
|
|||||||
"role": self.role,
|
"role": self.role,
|
||||||
"is_active": self.is_active,
|
"is_active": self.is_active,
|
||||||
"remark": self.remark,
|
"remark": self.remark,
|
||||||
"created_at": _ensure_tz(self.created_at).isoformat() if self.created_at else None,
|
"created_at": _isoformat(self.created_at),
|
||||||
"permissions": self.permissions,
|
"permissions": self.permissions,
|
||||||
"custom_permissions": self.custom_permissions,
|
"custom_permissions": self.custom_permissions,
|
||||||
}
|
}
|
||||||
@@ -108,6 +113,7 @@ class AccountImport(BaseModel):
|
|||||||
tag 为本次导入的统一标签兜底:某行未单独写标签时使用该值,
|
tag 为本次导入的统一标签兜底:某行未单独写标签时使用该值,
|
||||||
行内第5列标签优先。
|
行内第5列标签优先。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
text: str
|
text: str
|
||||||
tag: str = ""
|
tag: str = ""
|
||||||
|
|
||||||
@@ -123,6 +129,7 @@ class BatchAssign(BaseModel):
|
|||||||
|
|
||||||
class AccountBulkSelection(BaseModel):
|
class AccountBulkSelection(BaseModel):
|
||||||
"""批量操作选择范围:显式 ID 或当前筛选条件下全部账号。"""
|
"""批量操作选择范围:显式 ID 或当前筛选条件下全部账号。"""
|
||||||
|
|
||||||
account_ids: list[int] = Field(default_factory=list)
|
account_ids: list[int] = Field(default_factory=list)
|
||||||
all_matching: bool = False
|
all_matching: bool = False
|
||||||
search: str = ""
|
search: str = ""
|
||||||
@@ -167,7 +174,7 @@ class AccountOut(BaseModel):
|
|||||||
"assigned_to": self.assigned_to,
|
"assigned_to": self.assigned_to,
|
||||||
"assigned_username": self.assigned_username,
|
"assigned_username": self.assigned_username,
|
||||||
"remark": self.remark,
|
"remark": self.remark,
|
||||||
"created_at": _ensure_tz(self.created_at).isoformat() if self.created_at else None,
|
"created_at": _isoformat(self.created_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -178,7 +185,9 @@ class LoginBatchRequest(BaseModel):
|
|||||||
max_total_time: float = 300 # 单账号登录总时长上限(秒),0=不限制
|
max_total_time: float = 300 # 单账号登录总时长上限(秒),0=不限制
|
||||||
concurrency: int = 3 # 并发数,1-10
|
concurrency: int = 3 # 并发数,1-10
|
||||||
api_strategy: str = "wgapi" # 接口策略: wgapi(新版)或 iframe(旧版备选)
|
api_strategy: str = "wgapi" # 接口策略: wgapi(新版)或 iframe(旧版备选)
|
||||||
mode: str = Field("login", pattern="^(login|check)$") # login=登录取CK,check=账号状态检测
|
mode: str = Field(
|
||||||
|
"login", pattern="^(login|check)$"
|
||||||
|
) # login=登录取CK,check=账号状态检测
|
||||||
|
|
||||||
|
|
||||||
class CookieReloginRequest(BaseModel):
|
class CookieReloginRequest(BaseModel):
|
||||||
@@ -210,8 +219,8 @@ class LoginTaskOut(BaseModel):
|
|||||||
"cookie": self.cookie,
|
"cookie": self.cookie,
|
||||||
"message": self.message,
|
"message": self.message,
|
||||||
"created_by": self.created_by,
|
"created_by": self.created_by,
|
||||||
"created_at": _ensure_tz(self.created_at).isoformat() if self.created_at else None,
|
"created_at": _isoformat(self.created_at),
|
||||||
"finished_at": _ensure_tz(self.finished_at).isoformat() if self.finished_at else None,
|
"finished_at": _isoformat(self.finished_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -256,12 +265,14 @@ class AccountCheckBatchOut(BaseModel):
|
|||||||
# ---- 虎牙 ----
|
# ---- 虎牙 ----
|
||||||
class HuyaCookieImport(BaseModel):
|
class HuyaCookieImport(BaseModel):
|
||||||
"""批量导入虎牙 Cookie。支持纯 CK 或 账号----密码----CK。"""
|
"""批量导入虎牙 Cookie。支持纯 CK 或 账号----密码----CK。"""
|
||||||
|
|
||||||
text: str
|
text: str
|
||||||
tag: str = ""
|
tag: str = ""
|
||||||
|
|
||||||
|
|
||||||
class HuyaPasswordLoginRequest(BaseModel):
|
class HuyaPasswordLoginRequest(BaseModel):
|
||||||
"""虎牙账号 Web 密码登录并保存 Cookie(旧版)。"""
|
"""虎牙账号 Web 密码登录并保存 Cookie(旧版)。"""
|
||||||
|
|
||||||
username: str = Field(..., min_length=1, max_length=128)
|
username: str = Field(..., min_length=1, max_length=128)
|
||||||
password: str = Field(..., min_length=1, max_length=128)
|
password: str = Field(..., min_length=1, max_length=128)
|
||||||
tag: str = ""
|
tag: str = ""
|
||||||
@@ -270,6 +281,7 @@ class HuyaPasswordLoginRequest(BaseModel):
|
|||||||
|
|
||||||
class HuyaAppPasswordLoginRequest(BaseModel):
|
class HuyaAppPasswordLoginRequest(BaseModel):
|
||||||
"""虎牙账号 App 协议密码登录并保存 Cookie(推荐)。"""
|
"""虎牙账号 App 协议密码登录并保存 Cookie(推荐)。"""
|
||||||
|
|
||||||
username: str = Field(..., min_length=1, max_length=128)
|
username: str = Field(..., min_length=1, max_length=128)
|
||||||
password: str = Field(..., min_length=1, max_length=128)
|
password: str = Field(..., min_length=1, max_length=128)
|
||||||
tag: str = ""
|
tag: str = ""
|
||||||
@@ -278,12 +290,14 @@ class HuyaAppPasswordLoginRequest(BaseModel):
|
|||||||
|
|
||||||
class HuyaSmsCodeRequest(BaseModel):
|
class HuyaSmsCodeRequest(BaseModel):
|
||||||
"""发送虎牙短信验证码。"""
|
"""发送虎牙短信验证码。"""
|
||||||
|
|
||||||
phone: str = Field(..., min_length=5, max_length=32)
|
phone: str = Field(..., min_length=5, max_length=32)
|
||||||
cookie: str = ""
|
cookie: str = ""
|
||||||
|
|
||||||
|
|
||||||
class HuyaSmsLoginRequest(BaseModel):
|
class HuyaSmsLoginRequest(BaseModel):
|
||||||
"""提交虎牙短信验证码并保存 Cookie。"""
|
"""提交虎牙短信验证码并保存 Cookie。"""
|
||||||
|
|
||||||
authcode: str = Field(..., min_length=4, max_length=8)
|
authcode: str = Field(..., min_length=4, max_length=8)
|
||||||
state: str = Field(..., min_length=1)
|
state: str = Field(..., min_length=1)
|
||||||
phone: str = ""
|
phone: str = ""
|
||||||
@@ -292,6 +306,7 @@ class HuyaSmsLoginRequest(BaseModel):
|
|||||||
|
|
||||||
class HuyaAutoRegisterRequest(BaseModel):
|
class HuyaAutoRegisterRequest(BaseModel):
|
||||||
"""虎牙自动注册批次。每行格式:手机号----短信查询URL。"""
|
"""虎牙自动注册批次。每行格式:手机号----短信查询URL。"""
|
||||||
|
|
||||||
text: str = Field(..., min_length=1)
|
text: str = Field(..., min_length=1)
|
||||||
tag: str = ""
|
tag: str = ""
|
||||||
concurrency: int = Field(1, ge=1, le=5)
|
concurrency: int = Field(1, ge=1, le=5)
|
||||||
@@ -310,6 +325,7 @@ class HuyaAutoRegisterRetryRequest(BaseModel):
|
|||||||
- retry_failed: 只重试 error
|
- retry_failed: 只重试 error
|
||||||
- all_unfinished: 失败+未完成都跑
|
- all_unfinished: 失败+未完成都跑
|
||||||
"""
|
"""
|
||||||
|
|
||||||
mode: str = Field("continue", max_length=32)
|
mode: str = Field("continue", max_length=32)
|
||||||
concurrency: Optional[int] = Field(None, ge=1, le=5)
|
concurrency: Optional[int] = Field(None, ge=1, le=5)
|
||||||
wait_seconds: Optional[float] = Field(None, ge=15, le=600)
|
wait_seconds: Optional[float] = Field(None, ge=15, le=600)
|
||||||
@@ -382,12 +398,14 @@ class HuyaRegisterSuccessLogOut(BaseModel):
|
|||||||
|
|
||||||
class HuyaPasswordAccountImport(BaseModel):
|
class HuyaPasswordAccountImport(BaseModel):
|
||||||
"""导入虎牙账号密码,稍后再选择登录。"""
|
"""导入虎牙账号密码,稍后再选择登录。"""
|
||||||
|
|
||||||
text: str = Field(..., min_length=1)
|
text: str = Field(..., min_length=1)
|
||||||
tag: str = ""
|
tag: str = ""
|
||||||
|
|
||||||
|
|
||||||
class HuyaPasswordLoginSelectedRequest(BaseModel):
|
class HuyaPasswordLoginSelectedRequest(BaseModel):
|
||||||
"""选择已导入的虎牙账号执行密码登录。"""
|
"""选择已导入的虎牙账号执行密码登录。"""
|
||||||
|
|
||||||
account_ids: list[int] = Field(default_factory=list)
|
account_ids: list[int] = Field(default_factory=list)
|
||||||
all_matching: bool = False
|
all_matching: bool = False
|
||||||
search: str = ""
|
search: str = ""
|
||||||
@@ -399,6 +417,7 @@ class HuyaPasswordLoginSelectedRequest(BaseModel):
|
|||||||
|
|
||||||
class HuyaDeviceBindingsBatchDeleteRequest(BaseModel):
|
class HuyaDeviceBindingsBatchDeleteRequest(BaseModel):
|
||||||
"""批量解绑设备:删除账号画像与 hydevice 指纹状态。"""
|
"""批量解绑设备:删除账号画像与 hydevice 指纹状态。"""
|
||||||
|
|
||||||
accounts: list[str] = Field(..., min_length=1)
|
accounts: list[str] = Field(..., min_length=1)
|
||||||
|
|
||||||
|
|
||||||
@@ -447,8 +466,8 @@ class HuyaAccountOut(BaseModel):
|
|||||||
"game_phone": self.game_phone,
|
"game_phone": self.game_phone,
|
||||||
"assigned_to": self.assigned_to,
|
"assigned_to": self.assigned_to,
|
||||||
"assigned_username": self.assigned_username,
|
"assigned_username": self.assigned_username,
|
||||||
"created_at": _ensure_tz(self.created_at).isoformat() if self.created_at else None,
|
"created_at": _isoformat(self.created_at),
|
||||||
"updated_at": _ensure_tz(self.updated_at).isoformat() if self.updated_at else None,
|
"updated_at": _isoformat(self.updated_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -468,7 +487,7 @@ class HuyaConfigOut(BaseModel):
|
|||||||
"outer_act_id": self.outer_act_id,
|
"outer_act_id": self.outer_act_id,
|
||||||
"bind_act_id": self.bind_act_id,
|
"bind_act_id": self.bind_act_id,
|
||||||
"pay_channel": self.pay_channel,
|
"pay_channel": self.pay_channel,
|
||||||
"updated_at": _ensure_tz(self.updated_at).isoformat() if self.updated_at else None,
|
"updated_at": _isoformat(self.updated_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -516,8 +535,8 @@ class HuyaTaskOut(BaseModel):
|
|||||||
"message": self.message,
|
"message": self.message,
|
||||||
"result": self.result,
|
"result": self.result,
|
||||||
"created_by": self.created_by,
|
"created_by": self.created_by,
|
||||||
"created_at": _ensure_tz(self.created_at).isoformat() if self.created_at else None,
|
"created_at": _isoformat(self.created_at),
|
||||||
"finished_at": _ensure_tz(self.finished_at).isoformat() if self.finished_at else None,
|
"finished_at": _isoformat(self.finished_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -541,7 +560,7 @@ class HuyaGoodsOut(BaseModel):
|
|||||||
"price": self.price,
|
"price": self.price,
|
||||||
"remain_text": self.remain_text,
|
"remain_text": self.remain_text,
|
||||||
"raw": self.raw,
|
"raw": self.raw,
|
||||||
"updated_at": _ensure_tz(self.updated_at).isoformat() if self.updated_at else None,
|
"updated_at": _isoformat(self.updated_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -577,7 +596,7 @@ class HuyaRechargeGoodsOut(BaseModel):
|
|||||||
"task_id": self.task_id,
|
"task_id": self.task_id,
|
||||||
"task_name": self.task_name,
|
"task_name": self.task_name,
|
||||||
"raw": self.raw,
|
"raw": self.raw,
|
||||||
"updated_at": _ensure_tz(self.updated_at).isoformat() if self.updated_at else None,
|
"updated_at": _isoformat(self.updated_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -634,7 +653,7 @@ class DouyuConfigOut(BaseModel):
|
|||||||
"gold_api_account_template_name": self.gold_api_account_template_name,
|
"gold_api_account_template_name": self.gold_api_account_template_name,
|
||||||
"gift_id": self.gift_id,
|
"gift_id": self.gift_id,
|
||||||
"skin_id": self.skin_id,
|
"skin_id": self.skin_id,
|
||||||
"updated_at": _ensure_tz(self.updated_at).isoformat() if self.updated_at else None,
|
"updated_at": _isoformat(self.updated_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -657,7 +676,9 @@ class DouyuConfigUpdate(BaseModel):
|
|||||||
xpd_act_id: Optional[str] = None
|
xpd_act_id: Optional[str] = None
|
||||||
xpd_rid: Optional[str] = None
|
xpd_rid: Optional[str] = None
|
||||||
gold_pay_type: Optional[int] = Field(None, ge=1, le=9)
|
gold_pay_type: Optional[int] = Field(None, ge=1, le=9)
|
||||||
gold_recharge_channel: Optional[str] = Field(None, pattern="^(wechat_qr|supplier_api)$")
|
gold_recharge_channel: Optional[str] = Field(
|
||||||
|
None, pattern="^(wechat_qr|supplier_api)$"
|
||||||
|
)
|
||||||
gold_api_product_id: Optional[str] = Field(None, max_length=128)
|
gold_api_product_id: Optional[str] = Field(None, max_length=128)
|
||||||
gold_api_account_template_name: Optional[str] = Field(None, max_length=64)
|
gold_api_account_template_name: Optional[str] = Field(None, max_length=64)
|
||||||
gift_id: Optional[str] = None
|
gift_id: Optional[str] = None
|
||||||
@@ -710,8 +731,8 @@ class DouyuTaskOut(BaseModel):
|
|||||||
"message": self.message,
|
"message": self.message,
|
||||||
"result": self.result,
|
"result": self.result,
|
||||||
"created_by": self.created_by,
|
"created_by": self.created_by,
|
||||||
"created_at": _ensure_tz(self.created_at).isoformat() if self.created_at else None,
|
"created_at": _isoformat(self.created_at),
|
||||||
"finished_at": _ensure_tz(self.finished_at).isoformat() if self.finished_at else None,
|
"finished_at": _isoformat(self.finished_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -735,7 +756,7 @@ class DouyuGoodsOut(BaseModel):
|
|||||||
"score": self.score,
|
"score": self.score,
|
||||||
"status": self.status,
|
"status": self.status,
|
||||||
"raw": self.raw,
|
"raw": self.raw,
|
||||||
"updated_at": _ensure_tz(self.updated_at).isoformat() if self.updated_at else None,
|
"updated_at": _isoformat(self.updated_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -794,7 +815,7 @@ class DouyuXpdGoodsOut(BaseModel):
|
|||||||
"category": self.category,
|
"category": self.category,
|
||||||
"goods_left": self.goods_left,
|
"goods_left": self.goods_left,
|
||||||
"raw": self.raw,
|
"raw": self.raw,
|
||||||
"updated_at": _ensure_tz(self.updated_at).isoformat() if self.updated_at else None,
|
"updated_at": _isoformat(self.updated_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -828,6 +849,7 @@ class ProxyConfigUpdate(BaseModel):
|
|||||||
# ---- 代理平台元信息 ----
|
# ---- 代理平台元信息 ----
|
||||||
class PlatformFieldDef(BaseModel):
|
class PlatformFieldDef(BaseModel):
|
||||||
"""平台凭据字段定义。"""
|
"""平台凭据字段定义。"""
|
||||||
|
|
||||||
key: str
|
key: str
|
||||||
label: str
|
label: str
|
||||||
placeholder: str = ""
|
placeholder: str = ""
|
||||||
@@ -835,6 +857,7 @@ class PlatformFieldDef(BaseModel):
|
|||||||
|
|
||||||
class PlatformInfo(BaseModel):
|
class PlatformInfo(BaseModel):
|
||||||
"""平台元信息。"""
|
"""平台元信息。"""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
label: str
|
label: str
|
||||||
credential_fields: list[PlatformFieldDef]
|
credential_fields: list[PlatformFieldDef]
|
||||||
|
|||||||
Reference in New Issue
Block a user