Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 84251770b4 |
@@ -25,6 +25,7 @@ from datetime import datetime, timedelta
|
||||
from sqlalchemy import delete, func, select, update
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.config import settings
|
||||
from app.core.rewards import CN_TZ
|
||||
from app.models.comparison import ComparisonRecord
|
||||
from app.models.ops_marquee_seed import OpsMarqueeSeed
|
||||
@@ -54,7 +55,7 @@ _SEED_MAX_CENTS = 100000
|
||||
# 展示层随机(抽样/金额/时间/名字合成)仍每次重算,缓存只省查询;新记录最多晚 30s 进轮播,可接受。
|
||||
_REAL_ROWS_TTL_SECONDS = 30
|
||||
_REAL_ROWS_FETCH_CAP = 600 # 一次多取些,够 limit≤30 去重后取数;命中缓存后复用
|
||||
_real_rows_cache: dict = {"at": None, "rows": None}
|
||||
_real_rows_cache: dict = {"at": None, "rows": None, "test_phones": None}
|
||||
|
||||
# ===== 用户标识脱敏(对齐 PRD) + 种子无真实昵称时的假名合成 =====
|
||||
# 脱敏规则(按字符数,中英文皆适用):有昵称→n≥5「首+***+末」、n=4「首+**+末」、n≤3「首+**」;
|
||||
@@ -198,10 +199,16 @@ def _recent_real_rows(db: Session) -> list[tuple[int, int, str | None]]:
|
||||
返回纯元组(脱离 session),可安全跨请求复用。极端并发下偶尔多查一次(无锁、幂等),纯门面无副作用。
|
||||
"""
|
||||
now = datetime.now(CN_TZ)
|
||||
test_phones = tuple(sorted(settings.test_account_phones))
|
||||
cached, at = _real_rows_cache["rows"], _real_rows_cache["at"]
|
||||
if cached is not None and at is not None and (now - at).total_seconds() < _REAL_ROWS_TTL_SECONDS:
|
||||
if (
|
||||
cached is not None
|
||||
and at is not None
|
||||
and _real_rows_cache["test_phones"] == test_phones
|
||||
and (now - at).total_seconds() < _REAL_ROWS_TTL_SECONDS
|
||||
):
|
||||
return cached
|
||||
rows = db.execute(
|
||||
stmt = (
|
||||
select(ComparisonRecord.user_id, ComparisonRecord.saved_amount_cents, User.nickname)
|
||||
.join(User, User.id == ComparisonRecord.user_id)
|
||||
.where(
|
||||
@@ -211,9 +218,12 @@ def _recent_real_rows(db: Session) -> list[tuple[int, int, str | None]]:
|
||||
)
|
||||
.order_by(ComparisonRecord.created_at.desc())
|
||||
.limit(_REAL_ROWS_FETCH_CAP)
|
||||
).all()
|
||||
)
|
||||
if test_phones:
|
||||
stmt = stmt.where(User.phone.not_in(test_phones))
|
||||
rows = db.execute(stmt).all()
|
||||
out = [(int(uid), int(sc), nick) for uid, sc, nick in rows]
|
||||
_real_rows_cache["rows"], _real_rows_cache["at"] = out, now
|
||||
_real_rows_cache.update(rows=out, at=now, test_phones=test_phones)
|
||||
return out
|
||||
|
||||
|
||||
@@ -340,7 +350,7 @@ def list_real_records(
|
||||
# pool: [(cluster_key, item)];cluster_key 供去连簇——真实=user_id、种子=各自唯一负数(互不聚簇)
|
||||
pool: list[tuple[int, dict]] = []
|
||||
if mode != "seed":
|
||||
rows = db.execute(
|
||||
stmt = (
|
||||
select(
|
||||
ComparisonRecord.user_id,
|
||||
ComparisonRecord.saved_amount_cents,
|
||||
@@ -355,7 +365,11 @@ def list_real_records(
|
||||
)
|
||||
.order_by(ComparisonRecord.created_at.desc())
|
||||
.limit(_REAL_BROWSE_CAP)
|
||||
).all()
|
||||
)
|
||||
test_phones = tuple(sorted(settings.test_account_phones))
|
||||
if test_phones:
|
||||
stmt = stmt.where(User.phone.not_in(test_phones))
|
||||
rows = db.execute(stmt).all()
|
||||
for uid, sc, nick, ca in rows:
|
||||
pool.append((
|
||||
int(uid),
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from app.core.config import settings
|
||||
from app.db.session import SessionLocal
|
||||
from app.models.comparison import ComparisonRecord
|
||||
from app.models.user import User
|
||||
from app.repositories import ops_marquee
|
||||
|
||||
|
||||
def _clear_real_rows_cache() -> None:
|
||||
ops_marquee._real_rows_cache.update(at=None, rows=None, test_phones=None)
|
||||
|
||||
|
||||
def test_real_marquee_records_exclude_configured_test_account(monkeypatch) -> None:
|
||||
legacy_test_phone = "19900009991"
|
||||
listed_test_phone = "19900009992"
|
||||
real_phone = "19900009993"
|
||||
monkeypatch.setattr(settings, "TEST_ACCOUNT_PHONE", legacy_test_phone)
|
||||
monkeypatch.setattr(settings, "TEST_ACCOUNT_PHONES", listed_test_phone)
|
||||
_clear_real_rows_cache()
|
||||
|
||||
try:
|
||||
with SessionLocal() as db:
|
||||
legacy_test_user = User(
|
||||
phone=legacy_test_phone,
|
||||
username="29900009991",
|
||||
register_channel="sms",
|
||||
)
|
||||
listed_test_user = User(
|
||||
phone=listed_test_phone,
|
||||
username="29900009992",
|
||||
register_channel="sms",
|
||||
)
|
||||
real_user = User(
|
||||
phone=real_phone,
|
||||
username="29900009993",
|
||||
register_channel="sms",
|
||||
)
|
||||
db.add_all([legacy_test_user, listed_test_user, real_user])
|
||||
db.flush()
|
||||
db.add_all([
|
||||
ComparisonRecord(
|
||||
user_id=legacy_test_user.id,
|
||||
trace_id="marquee-legacy-test-account-trace",
|
||||
status="success",
|
||||
saved_amount_cents=29_991,
|
||||
items=[],
|
||||
comparison_results=[],
|
||||
),
|
||||
ComparisonRecord(
|
||||
user_id=listed_test_user.id,
|
||||
trace_id="marquee-listed-test-account-trace",
|
||||
status="success",
|
||||
saved_amount_cents=29_992,
|
||||
items=[],
|
||||
comparison_results=[],
|
||||
),
|
||||
ComparisonRecord(
|
||||
user_id=real_user.id,
|
||||
trace_id="marquee-real-account-trace",
|
||||
status="success",
|
||||
saved_amount_cents=29_993,
|
||||
items=[],
|
||||
comparison_results=[],
|
||||
),
|
||||
])
|
||||
db.flush()
|
||||
|
||||
app_rows = ops_marquee._recent_real_rows(db)
|
||||
browse_rows, _ = ops_marquee.list_real_records(db, mode="real", limit=1_000)
|
||||
finally:
|
||||
_clear_real_rows_cache()
|
||||
|
||||
assert all(saved not in {29_991, 29_992} for _uid, saved, _nickname in app_rows)
|
||||
assert any(saved == 29_993 for _uid, saved, _nickname in app_rows)
|
||||
assert all(row["saved_amount_cents"] not in {29_991, 29_992} for row in browse_rows)
|
||||
assert any(row["saved_amount_cents"] == 29_993 for row in browse_rows)
|
||||
Reference in New Issue
Block a user