|
@@ -0,0 +1,178 @@
|
|
|
|
|
+"""账号域 DB 读写 —— demand_search_accounts + demand_search_article_relation.save_account_status"""
|
|
|
|
|
+
|
|
|
|
|
+from typing import Dict, List, Optional
|
|
|
|
|
+
|
|
|
|
|
+from src.infra.database.mysql.manager import MysqlManager
|
|
|
|
|
+
|
|
|
|
|
+from ._const import DemandSearchAccountConst
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class AccountMapper(DemandSearchAccountConst):
|
|
|
|
|
+ """demand_search_accounts 表 + relation.save_account_status 的读写"""
|
|
|
|
|
+
|
|
|
|
|
+ def __init__(self, pool: MysqlManager):
|
|
|
|
|
+ self.pool = pool
|
|
|
|
|
+
|
|
|
|
|
+ # ═══════════════════════════════════════════════════════════════
|
|
|
|
|
+ # relation 表 — 读取待抓账号
|
|
|
|
|
+ # ═══════════════════════════════════════════════════════════════
|
|
|
|
|
+
|
|
|
|
|
+ async def fetch_pending_relations(self, *, limit: int = 100) -> List[Dict]:
|
|
|
|
|
+ """取 save_account_status=0 的行(DISTINCT biz,按 id ASC)"""
|
|
|
|
|
+ query = f"""
|
|
|
|
|
+ SELECT MIN(id) AS id, biz, MIN(url) AS url
|
|
|
|
|
+ FROM {self.RELATION_TABLE}
|
|
|
|
|
+ WHERE save_account_status = %s AND biz IS NOT NULL AND biz != ''
|
|
|
|
|
+ GROUP BY biz
|
|
|
|
|
+ ORDER BY id ASC
|
|
|
|
|
+ LIMIT %s
|
|
|
|
|
+ """
|
|
|
|
|
+ return await self.pool.fetch(
|
|
|
|
|
+ query=query,
|
|
|
|
|
+ params=(self.AccountSaveStatus.INIT, limit),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ async def fetch_relation_ids_by_biz(self, biz: str) -> List[int]:
|
|
|
|
|
+ """取某个 biz 下所有 save_account_status=0 的 relation id"""
|
|
|
|
|
+ query = f"""
|
|
|
|
|
+ SELECT id
|
|
|
|
|
+ FROM {self.RELATION_TABLE}
|
|
|
|
|
+ WHERE biz = %s AND save_account_status = %s
|
|
|
|
|
+ """
|
|
|
|
|
+ rows = await self.pool.fetch(
|
|
|
|
|
+ query=query,
|
|
|
|
|
+ params=(biz, self.AccountSaveStatus.INIT),
|
|
|
|
|
+ )
|
|
|
|
|
+ return [r["id"] for r in rows]
|
|
|
|
|
+
|
|
|
|
|
+ async def fetch_relation_ids_by_biz_any_status(self, biz: str) -> List[int]:
|
|
|
|
|
+ """取某个 biz 下未完成(INIT 或 PROCESSING)的 relation id,用于异常恢复"""
|
|
|
|
|
+ query = f"""
|
|
|
|
|
+ SELECT id
|
|
|
|
|
+ FROM {self.RELATION_TABLE}
|
|
|
|
|
+ WHERE biz = %s
|
|
|
|
|
+ AND save_account_status IN (%s, %s)
|
|
|
|
|
+ """
|
|
|
|
|
+ rows = await self.pool.fetch(
|
|
|
|
|
+ query=query,
|
|
|
|
|
+ params=(
|
|
|
|
|
+ biz,
|
|
|
|
|
+ self.AccountSaveStatus.INIT,
|
|
|
|
|
+ self.AccountSaveStatus.PROCESSING,
|
|
|
|
|
+ ),
|
|
|
|
|
+ )
|
|
|
|
|
+ return [r["id"] for r in rows]
|
|
|
|
|
+
|
|
|
|
|
+ # ═══════════════════════════════════════════════════════════════
|
|
|
|
|
+ # relation 表 — save_account_status 状态更新
|
|
|
|
|
+ # ═══════════════════════════════════════════════════════════════
|
|
|
|
|
+
|
|
|
|
|
+ async def relation_mark_processing(self, ids: List[int]) -> int:
|
|
|
|
|
+ """INIT → PROCESSING,CAS 防重复"""
|
|
|
|
|
+ if not ids:
|
|
|
|
|
+ return 0
|
|
|
|
|
+ placeholders = ",".join(["%s"] * len(ids))
|
|
|
|
|
+ query = f"""
|
|
|
|
|
+ UPDATE {self.RELATION_TABLE}
|
|
|
|
|
+ SET save_account_status = %s
|
|
|
|
|
+ WHERE id IN ({placeholders}) AND save_account_status = %s
|
|
|
|
|
+ """
|
|
|
|
|
+ return await self.pool.save(
|
|
|
|
|
+ query=query,
|
|
|
|
|
+ params=(
|
|
|
|
|
+ self.AccountSaveStatus.PROCESSING,
|
|
|
|
|
+ *ids,
|
|
|
|
|
+ self.AccountSaveStatus.INIT,
|
|
|
|
|
+ ),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ async def relation_mark_success(self, ids: List[int]) -> int:
|
|
|
|
|
+ """PROCESSING → SUCCESS"""
|
|
|
|
|
+ if not ids:
|
|
|
|
|
+ return 0
|
|
|
|
|
+ placeholders = ",".join(["%s"] * len(ids))
|
|
|
|
|
+ query = f"""
|
|
|
|
|
+ UPDATE {self.RELATION_TABLE}
|
|
|
|
|
+ SET save_account_status = %s
|
|
|
|
|
+ WHERE id IN ({placeholders}) AND save_account_status = %s
|
|
|
|
|
+ """
|
|
|
|
|
+ return await self.pool.save(
|
|
|
|
|
+ query=query,
|
|
|
|
|
+ params=(
|
|
|
|
|
+ self.AccountSaveStatus.SUCCESS,
|
|
|
|
|
+ *ids,
|
|
|
|
|
+ self.AccountSaveStatus.PROCESSING,
|
|
|
|
|
+ ),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ async def relation_mark_success_direct(self, ids: List[int]) -> int:
|
|
|
|
|
+ """INIT → SUCCESS,跳过 PROCESSING(查重命中时用)"""
|
|
|
|
|
+ if not ids:
|
|
|
|
|
+ return 0
|
|
|
|
|
+ placeholders = ",".join(["%s"] * len(ids))
|
|
|
|
|
+ query = f"""
|
|
|
|
|
+ UPDATE {self.RELATION_TABLE}
|
|
|
|
|
+ SET save_account_status = %s
|
|
|
|
|
+ WHERE id IN ({placeholders}) AND save_account_status = %s
|
|
|
|
|
+ """
|
|
|
|
|
+ return await self.pool.save(
|
|
|
|
|
+ query=query,
|
|
|
|
|
+ params=(self.AccountSaveStatus.SUCCESS, *ids, self.AccountSaveStatus.INIT),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ async def relation_mark_failed(self, ids: List[int]) -> int:
|
|
|
|
|
+ """INIT 或 PROCESSING → FAIL,异常恢复用,不挑前置状态"""
|
|
|
|
|
+ if not ids:
|
|
|
|
|
+ return 0
|
|
|
|
|
+ placeholders = ",".join(["%s"] * len(ids))
|
|
|
|
|
+ query = f"""
|
|
|
|
|
+ UPDATE {self.RELATION_TABLE}
|
|
|
|
|
+ SET save_account_status = %s
|
|
|
|
|
+ WHERE id IN ({placeholders})
|
|
|
|
|
+ AND save_account_status IN (%s, %s)
|
|
|
|
|
+ """
|
|
|
|
|
+ return await self.pool.save(
|
|
|
|
|
+ query=query,
|
|
|
|
|
+ params=(
|
|
|
|
|
+ self.AccountSaveStatus.FAIL,
|
|
|
|
|
+ *ids,
|
|
|
|
|
+ self.AccountSaveStatus.INIT,
|
|
|
|
|
+ self.AccountSaveStatus.PROCESSING,
|
|
|
|
|
+ ),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # ═══════════════════════════════════════════════════════════════
|
|
|
|
|
+ # demand_search_accounts 表
|
|
|
|
|
+ # ═══════════════════════════════════════════════════════════════
|
|
|
|
|
+
|
|
|
|
|
+ async def find_by_biz(self, biz: str) -> Optional[Dict]:
|
|
|
|
|
+ """按 biz 查账号是否已存在"""
|
|
|
|
|
+ query = f"""
|
|
|
|
|
+ SELECT * FROM {self.ACCOUNT_TABLE}
|
|
|
|
|
+ WHERE biz = %s
|
|
|
|
|
+ LIMIT 1
|
|
|
|
|
+ """
|
|
|
|
|
+ return await self.pool.fetch_one(query=query, params=(biz,))
|
|
|
|
|
+
|
|
|
|
|
+ async def insert_account(self, account: Dict) -> int:
|
|
|
|
|
+ """写入账号,channel_account_id 冲突则跳过"""
|
|
|
|
|
+ query = f"""
|
|
|
|
|
+ INSERT IGNORE INTO {self.ACCOUNT_TABLE}
|
|
|
|
|
+ (channel_account_id, gh_id, account_name, biz, biz_info, description)
|
|
|
|
|
+ VALUES
|
|
|
|
|
+ (%s, %s, %s, %s, %s, %s)
|
|
|
|
|
+ """
|
|
|
|
|
+ return await self.pool.save(
|
|
|
|
|
+ query=query,
|
|
|
|
|
+ params=(
|
|
|
|
|
+ account["channel_account_id"],
|
|
|
|
|
+ account["gh_id"],
|
|
|
|
|
+ account["account_name"],
|
|
|
|
|
+ account["biz"],
|
|
|
|
|
+ account["biz_info"],
|
|
|
|
|
+ account["description"],
|
|
|
|
|
+ ),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+__all__ = ["AccountMapper"]
|