fix(priority-sync): handle missing rate data and backfill target group on re-import
P1 - Missing rate data now skips account instead of falling back to 1.0:
In sync_account_priorities_for_upstream(), the rated list now filters
out accounts whose upstream snapshot has no rate entry for their group_id.
If after filtering a competitive bucket has fewer than 2 accounts with
valid rate data, the entire bucket is silently skipped (no update_account
call, no webhook) rather than treating missing rates as 1.0 and
potentially triggering spurious notifications.
P2 - Re-importing an existing account now backfills imported_target_group_id:
In the exists-is-True idempotency branch of import_upstream_keys_as_accounts(),
if the current request supplies a target_group_id for the account's source group
and it differs from what is stored, the field is written back and committed.
This lets operators fix old data by simply re-running the import dialog.
Tests added:
- test_missing_rate_skips_entire_competitive_group: all accounts in
competitive group lack snapshot → bucket skipped, no update called
- test_partial_missing_rate_sufficient_accounts_still_updates: 3 accounts
in same bucket, 1 missing rate → the 2 with rates still compete normally
All 27 tests pass.
This commit is contained in:
@@ -558,6 +558,11 @@ def import_upstream_keys_as_accounts(
|
||||
old_account_id = row.imported_account_id
|
||||
exists = c.account_exists(row.imported_account_id)
|
||||
if exists is True:
|
||||
# 顺手回填 imported_target_group_id(老数据升级后可通过重导自动补齐)
|
||||
new_tgid = body.target_group_map.get(row.group_id) or None
|
||||
if new_tgid and row.imported_target_group_id != new_tgid:
|
||||
row.imported_target_group_id = new_tgid
|
||||
db.commit()
|
||||
items.append(ImportAccountItem(
|
||||
upstream_key_id=row.id,
|
||||
source_group_id=row.group_id,
|
||||
|
||||
@@ -372,11 +372,19 @@ def sync_account_priorities_for_upstream(db: Session, upstream_id: int) -> list[
|
||||
# priority_assignment: account_id → new_priority
|
||||
priority_assignment: dict[str, int] = {}
|
||||
for comp_key, comp_rows in competitive_buckets.items():
|
||||
# 取每行的倍率(查不到则 fallback 1.0)
|
||||
# 只保留快照中能查到倍率的账号;无数据的账号不参与排序
|
||||
rated = [
|
||||
(row, raw_rate_map.get(f"{row.upstream_id}:{row.group_id}", 1.0))
|
||||
(row, raw_rate_map[f"{row.upstream_id}:{row.group_id}"])
|
||||
for row in comp_rows
|
||||
if f"{row.upstream_id}:{row.group_id}" in raw_rate_map
|
||||
]
|
||||
# 过滤后有效账号不足 2 个 → 此分组无竞争意义,整组跳过
|
||||
if len(rated) < 2:
|
||||
logger.info(
|
||||
"skip competitive bucket %s for website %s: only %d account(s) have rate data",
|
||||
comp_key, wid, len(rated),
|
||||
)
|
||||
continue
|
||||
# 组内按倍率升序排序(倍率低 → priority 小 → 优先)
|
||||
unique_rates = sorted(set(r for _, r in rated))
|
||||
rate_to_prio = {rate: idx + 1 for idx, rate in enumerate(unique_rates)}
|
||||
|
||||
Reference in New Issue
Block a user