fix: update frontend
This commit is contained in:
321
ts.py
Normal file
321
ts.py
Normal file
@@ -0,0 +1,321 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user