feat: implement automatic margin pricing and margin reverse engineering in sync
This commit is contained in:
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
|
||||
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP, ROUND_UP
|
||||
from typing import Any
|
||||
from urllib.parse import quote, urlparse
|
||||
|
||||
@@ -66,25 +66,16 @@ def calculate_target_rate(values: list[Any], percent: Any = 0, algorithm: str =
|
||||
elif algorithm == "max_plus_percent":
|
||||
base = max(rates)
|
||||
elif algorithm == "priority_weighted_plus_percent":
|
||||
rates_sorted = sorted(rates)
|
||||
N = len(rates_sorted)
|
||||
if N == 1:
|
||||
base = rates_sorted[0]
|
||||
else:
|
||||
w1 = Decimal("0.85")
|
||||
w_others = Decimal("0.15") / Decimal(N - 1)
|
||||
base = rates_sorted[0] * w1 + sum(rates_sorted[1:], Decimal("0")) * w_others
|
||||
min_rate = min(rates)
|
||||
max_rate = max(rates)
|
||||
raw_target = max(min_rate * Decimal("1.50"), max_rate * Decimal("1.10"))
|
||||
return raw_target.quantize(Decimal("0.01"), rounding=ROUND_UP)
|
||||
else:
|
||||
raise WebsiteError(f"不支持的算法:{algorithm}")
|
||||
pct = Decimal(str(percent or 0))
|
||||
if pct < 0:
|
||||
raise WebsiteError("百分比不能为负数")
|
||||
target_rate = (base * (Decimal("1") + pct / Decimal("100"))).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
|
||||
if algorithm == "priority_weighted_plus_percent":
|
||||
min_rate_2d = min(rates).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
|
||||
if target_rate < min_rate_2d:
|
||||
target_rate = min_rate_2d
|
||||
return target_rate
|
||||
return (base * (Decimal("1") + pct / Decimal("100"))).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
|
||||
|
||||
|
||||
def _unwrap_data(value: Any) -> Any:
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from decimal import Decimal
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
@@ -14,7 +14,7 @@ from app.models.upstream import Upstream
|
||||
from app.models.upstream_key import UpstreamGeneratedKey
|
||||
from app.models.website import Website, WebsiteGroupBinding, WebsiteSyncLog
|
||||
from app.services.auth_config import normalize_auth_config
|
||||
from app.services.website_client import Sub2ApiWebsiteClient, WebsiteError, _extract_id, calculate_target_rate
|
||||
from app.services.website_client import Sub2ApiWebsiteClient, WebsiteError, _extract_id, calculate_target_rate, parse_positive_decimal
|
||||
from app.services.upstream_client import UpstreamClient
|
||||
from app.services import webhook_service
|
||||
from app.utils.number import fixed_decimal_string
|
||||
@@ -168,6 +168,21 @@ def sync_binding(db: Session, binding: WebsiteGroupBinding, write: bool = True)
|
||||
})
|
||||
try:
|
||||
target_rate = calculate_target_rate([item.get("rate") for item in source_rates], binding.percent, binding.algorithm)
|
||||
if binding.algorithm == "priority_weighted_plus_percent":
|
||||
rates = [rate for rate in (parse_positive_decimal(item.get("rate")) for item in source_rates) if rate is not None]
|
||||
if rates:
|
||||
rates_sorted = sorted(rates)
|
||||
N = len(rates_sorted)
|
||||
if N == 1:
|
||||
base = rates_sorted[0]
|
||||
else:
|
||||
w1 = Decimal("0.85")
|
||||
w_others = Decimal("0.15") / Decimal(N - 1)
|
||||
base = rates_sorted[0] * w1 + sum(rates_sorted[1:], Decimal("0")) * w_others
|
||||
|
||||
if base > 0:
|
||||
effective_percent = (target_rate / base - Decimal("1")) * Decimal("100")
|
||||
binding.percent = fixed_decimal_string(effective_percent.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP), 2)
|
||||
except Exception as exc:
|
||||
return _log(db, binding, website, source_rates, "failed", str(exc))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user