diff --git a/ts.py b/ts.py deleted file mode 100644 index 261d6a9..0000000 --- a/ts.py +++ /dev/null @@ -1,321 +0,0 @@ -import json -import os -import sys -import threading -import time -from pathlib import Path -from types import SimpleNamespace - -import requests - - -ROOT_DIR = Path(__file__).resolve().parent -APP_DIR = ROOT_DIR / "app" -sys.path.insert(0, str(APP_DIR)) - -from amazon.main import TaskMonitor # noqa: E402 -from amazon.patrol_delete import InventoryManage, PatrolDeleteTask # noqa: E402 -from config import ( # noqa: E402 - DELETE_BRAND_API_BASE, - JAVA_API_BASE, - JSON_TASK_QUEUE, - ZN_COMPANY, - ZN_PASSWORD, - ZN_USERNAME, - runing_task, -) -from main import WindowAPI # noqa: E402 - - -SHOP_NAME = "郭亚芳" -USER_ID = 1 -COUNTRIES = [item.strip() for item in os.environ.get("PATROL_COUNTRIES", "德国,西班牙,意大利").split(",") if item.strip()] -TS_MODE = os.environ.get("TS_MODE", "full").strip().lower() - - -class EventSlot: - def __iadd__(self, handler): - return self - - -class DummyWindow: - def __init__(self): - self.events = SimpleNamespace(maximized=EventSlot(), restored=EventSlot()) - - -def log(message, payload=None): - print(f"[ts] {message}", flush=True) - if payload is not None: - print(json.dumps(payload, ensure_ascii=False, indent=2), flush=True) - - -def unwrap(response): - log(f"HTTP {response.request.method} {response.url} -> {response.status_code}") - response.raise_for_status() - data = response.json() - if not data.get("success"): - raise RuntimeError(data.get("message") or data.get("error") or response.text) - return data.get("data") - - -def get_match_item(): - data = unwrap( - requests.post( - f"{JAVA_API_BASE}/api/patrol-delete/match-shops", - json={"user_id": USER_ID, "shop_names": [SHOP_NAME]}, - timeout=40, - ) - ) - items = data.get("items") or [] - if not items: - raise RuntimeError("match-shops did not return any items") - - item = items[0] - log("match result", item) - if not item.get("matched"): - raise RuntimeError(f"shop is not matched: {item}") - return item - - -def create_java_task(match_item): - country_sections = [ - { - "country": country, - "rows": [ - { - "status": "全部", - "quantity": "", - "deleteQuantity": "", - "processStatus": "", - } - ], - } - for country in COUNTRIES - ] - cart_ratios = [{"country": country, "ratio": ""} for country in COUNTRIES] - task_item = { - "shopName": match_item.get("shopName") or SHOP_NAME, - "matched": bool(match_item.get("matched")), - "shopId": match_item.get("shopId"), - "platform": match_item.get("platform"), - "companyName": match_item.get("companyName"), - "matchStatus": match_item.get("matchStatus"), - "matchMessage": match_item.get("matchMessage"), - "countrySections": country_sections, - "cartRatios": cart_ratios, - } - data = unwrap( - requests.post( - f"{JAVA_API_BASE}/api/patrol-delete/tasks", - json={"user_id": USER_ID, "items": [task_item]}, - timeout=40, - ) - ) - log("created task", data) - return data - - -def build_queue_payload(created_task): - task_id = created_task["taskId"] - created_item = (created_task.get("items") or [])[0] - sections = created_item.get("countrySections") or [] - ratios = created_item.get("cartRatios") or [] - payload = { - "type": "patrol-delete-run", - "ts": int(time.time() * 1000), - "data": { - "taskId": task_id, - "user_id": USER_ID, - "source": "ts-real-patrol-delete", - "items": [ - { - "shopName": created_item.get("shopName") or SHOP_NAME, - "shopId": created_item.get("shopId"), - "platform": created_item.get("platform"), - "companyName": created_item.get("companyName"), - "matched": created_item.get("matched"), - "matchStatus": created_item.get("matchStatus"), - "matchMessage": created_item.get("matchMessage"), - } - ], - "template_rows": [ - { - "shopName": created_item.get("shopName") or SHOP_NAME, - "countries": [ - { - "country": section.get("country"), - "status": (section.get("rows") or [{}])[0].get("status", ""), - "quantity": (section.get("rows") or [{}])[0].get("quantity", ""), - "deleteQuantity": (section.get("rows") or [{}])[0].get("deleteQuantity", ""), - "processStatus": (section.get("rows") or [{}])[0].get("processStatus", ""), - } - for section in sections - ], - "cartRatios": ratios, - } - ], - "country_sections": sections, - "cart_ratios": ratios, - }, - } - log("queue payload", payload) - return payload - - -def make_monitor_without_startup_kill(): - monitor = object.__new__(TaskMonitor) - monitor.running = True - monitor.user_info = { - "company": ZN_COMPANY, - "username": ZN_USERNAME, - "password": ZN_PASSWORD, - } - monitor.chunk_index = 1 - monitor.max_workers = 1 - monitor.executor = None - return monitor - - -def poll_java_task(task_id): - for _ in range(60): - try: - data = unwrap( - requests.post( - f"{JAVA_API_BASE}/api/patrol-delete/tasks/progress/batch", - json={"taskIds": [task_id]}, - timeout=40, - ) - ) - log("java progress", data) - items = data.get("items") or [] - if items and items[0].get("taskStatus") in {"SUCCESS", "FAILED", "COMPLETED"}: - return items[0] - except Exception as exc: - log(f"java progress poll failed: {exc}") - time.sleep(5) - return None - - -def drain_queue(): - while True: - try: - JSON_TASK_QUEUE.get_nowait() - except Exception: - return - - -def make_user_info(company_name=None): - return { - "company": company_name or ZN_COMPANY, - "username": ZN_USERNAME, - "password": ZN_PASSWORD, - } - - -def complete_draft_payload_rows(tags): - rows = [] - for item in tags: - rows.append( - { - "status": "补全草稿", - "tag": item.get("tag", ""), - "type": "completeDraft", - "quantity": str(item.get("quantity") if item.get("quantity") is not None else ""), - "deleteQuantity": str(item.get("quantity") if item.get("quantity") is not None else ""), - "processStatus": "已完成", - "raw_text": item.get("raw_text", ""), - } - ) - return rows - - -def run_complete_draft_real_browser(): - log("starting real complete-draft read test") - log("scope", {"shop": SHOP_NAME, "countries": COUNTRIES, "user_id": USER_ID}) - match_item = get_match_item() - driver = InventoryManage(make_user_info(match_item.get("companyName"))) - try: - browser = driver.open_shop(match_item.get("shopName") or SHOP_NAME) - if not browser or browser == "店铺不存在": - raise RuntimeError(f"open shop failed: {browser}") - if driver.need_login(): - raise RuntimeError("shop requires login; complete-draft read test cannot continue safely") - - normalized_sections = [] - for country in COUNTRIES: - log(f"switching country for complete-draft read: {country}") - if not driver.switch_to_country(country): - raise RuntimeError(f"switch country failed: {country}") - tags = driver.get_complete_draft_quick_view_tags(match_item.get("shopName") or SHOP_NAME, country) - payload_rows = complete_draft_payload_rows(tags) - normalized = PatrolDeleteTask._normalize_country_sections_for_result( - [{"country": country, "rows": payload_rows}] - )[0] - normalized_sections.append(normalized) - log( - "complete draft country result", - { - "country": country, - "rawTags": tags, - "payloadRowsBeforeNormalize": payload_rows, - "normalizedForSubmit": normalized, - }, - ) - - log("complete draft normalized sections", normalized_sections) - finally: - try: - driver.close_store() - except Exception as exc: - log(f"close store failed: {exc}") - - -def main(): - if TS_MODE == "complete-draft": - run_complete_draft_real_browser() - return - - log("starting real patrol-delete automation") - log("scope", {"shop": SHOP_NAME, "countries": COUNTRIES, "user_id": USER_ID}) - log("java api base", {"JAVA_API_BASE": JAVA_API_BASE, "DELETE_BRAND_API_BASE": DELETE_BRAND_API_BASE}) - log("ziniao env", {"company": bool(ZN_COMPANY), "username": bool(ZN_USERNAME), "password": bool(ZN_PASSWORD)}) - - drain_queue() - match_item = get_match_item() - created_task = create_java_task(match_item) - task_id = created_task["taskId"] - payload = build_queue_payload(created_task) - - monitor = make_monitor_without_startup_kill() - monitor_thread = threading.Thread(target=monitor.start, daemon=True) - monitor_thread.start() - time.sleep(0.5) - - enqueue_result = WindowAPI(DummyWindow()).enqueue_json(payload) - log("enqueue result", enqueue_result) - if not enqueue_result.get("success"): - raise RuntimeError(f"enqueue failed: {enqueue_result}") - - deadline = time.time() + 60 * 45 - last_status = None - while time.time() < deadline: - state = runing_task.get(task_id) - if state and state != last_status: - log("python task state", state) - last_status = dict(state) - if state and state.get("status") in {"completed", "failed", "stopped"}: - break - time.sleep(5) - - monitor.running = False - JSON_TASK_QUEUE.put({"type": "__stop__"}) - monitor_thread.join(timeout=10) - - log("python final state", runing_task.get(task_id)) - final_java = poll_java_task(task_id) - log("java final", final_java) - log("finished", {"taskId": task_id}) - - -if __name__ == "__main__": - main()