f868414966
- 新增反馈审核字段和迁移,支持 pending/adopted/rejected、未采纳原因和采纳金币 - 增加用户端反馈历史 records 接口和 admin 采纳/拒绝接口 - 采纳时同事务写状态、金币流水和审计日志,拦截重复审核 - 验证:pytest tests/test_feedback.py tests/test_admin_write.py tests/test_admin_read.py --------- Co-authored-by: lowmaster-chen <1119780489@qq.com> Reviewed-on: #66 Co-authored-by: chenshuobo <chenshuobo@wonderable.ai> Co-committed-by: chenshuobo <chenshuobo@wonderable.ai>
137 lines
4.2 KiB
Python
137 lines
4.2 KiB
Python
"""admin 写操作 repo(状态改写)。
|
|
|
|
涉钱的金币/提现复用 app.repositories.wallet(grant_coins / refresh_withdraw_status /
|
|
reconcile_pending_withdraws),不在这里重写——重写涉钱逻辑就是给自己埋雷。
|
|
|
|
set_user_status / update_feedback_status 支持 commit=False,让 router 把"业务写 + 审计写"
|
|
放进同一事务一起 commit(原子:改了就有审计、有审计就真改了,见 plan 风险点 1)。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import delete
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.core.rewards import CN_TZ
|
|
from app.models.feedback import Feedback
|
|
from app.models.onboarding import OnboardingCompletion
|
|
from app.models.price_report import PriceReport
|
|
from app.models.user import User
|
|
|
|
|
|
def set_user_status(db: Session, user: User, *, status: str, commit: bool = True) -> User:
|
|
user.status = status
|
|
if commit:
|
|
db.commit()
|
|
db.refresh(user)
|
|
else:
|
|
db.flush()
|
|
return user
|
|
|
|
|
|
def set_user_debug_trace(
|
|
db: Session, user: User, *, enabled: bool, commit: bool = True
|
|
) -> User:
|
|
"""开关用户「复制调试链接」权限。同 set_user_status:支持 commit=False 让 router 把
|
|
业务写 + 审计写放进同一事务。"""
|
|
user.debug_trace_enabled = enabled
|
|
if commit:
|
|
db.commit()
|
|
db.refresh(user)
|
|
else:
|
|
db.flush()
|
|
return user
|
|
|
|
|
|
def update_feedback_status(
|
|
db: Session, feedback: Feedback, *, status: str, commit: bool = True
|
|
) -> Feedback:
|
|
feedback.status = status
|
|
if commit:
|
|
db.commit()
|
|
db.refresh(feedback)
|
|
else:
|
|
db.flush()
|
|
return feedback
|
|
|
|
|
|
def review_feedback(
|
|
db: Session,
|
|
feedback: Feedback,
|
|
*,
|
|
status: str,
|
|
reviewed_by_admin_id: int,
|
|
reward_coins: int | None = None,
|
|
reject_reason: str | None = None,
|
|
review_note: str | None = None,
|
|
commit: bool = True,
|
|
) -> Feedback:
|
|
"""审核反馈:置 adopted/rejected + 记录奖励/原因/审核人/审核时间。
|
|
|
|
发金币(wallet.grant_coins)由 router 在同一事务里调,确保状态、金币流水、审计一起提交。
|
|
"""
|
|
feedback.status = status
|
|
feedback.reward_coins = reward_coins
|
|
feedback.reject_reason = reject_reason
|
|
feedback.review_note = review_note
|
|
feedback.reviewed_by_admin_id = reviewed_by_admin_id
|
|
feedback.reviewed_at = datetime.now(CN_TZ).replace(tzinfo=None)
|
|
if commit:
|
|
db.commit()
|
|
db.refresh(feedback)
|
|
else:
|
|
db.flush()
|
|
return feedback
|
|
|
|
|
|
def review_price_report(
|
|
db: Session,
|
|
report: PriceReport,
|
|
*,
|
|
status: str,
|
|
reward_coins: int | None = None,
|
|
reject_reason: str | None = None,
|
|
commit: bool = True,
|
|
) -> PriceReport:
|
|
"""审核上报:置 approved/rejected + 记审核时间;通过填 reward_coins、拒绝填 reject_reason。
|
|
|
|
发金币(wallet.grant_coins)不在这里——由 router 在同一事务里调,涉钱逻辑不重写(见模块头注释)。
|
|
"""
|
|
report.status = status
|
|
if reward_coins is not None:
|
|
report.reward_coins = reward_coins
|
|
if reject_reason is not None:
|
|
report.reject_reason = reject_reason
|
|
report.reviewed_at = datetime.now(CN_TZ).replace(tzinfo=None) # 北京 wall-clock,同 created_at
|
|
if commit:
|
|
db.commit()
|
|
db.refresh(report)
|
|
else:
|
|
db.flush()
|
|
return report
|
|
|
|
|
|
def delete_device_onboarding(db: Session, device_id: str, *, commit: bool = True) -> int:
|
|
"""删某设备(device_id)的全部 onboarding 完成记录 → 该设备上所有账号下次登录重走引导。
|
|
返回删除行数。支持 commit=False 让 router 把业务写 + 审计写放进同一事务。"""
|
|
n = db.execute(
|
|
delete(OnboardingCompletion).where(OnboardingCompletion.device_id == device_id)
|
|
).rowcount
|
|
if commit:
|
|
db.commit()
|
|
else:
|
|
db.flush()
|
|
return n
|
|
|
|
|
|
def delete_all_onboarding(db: Session, *, commit: bool = True) -> int:
|
|
"""清空 onboarding_completion 表 → 所有用户在所有设备下次登录都重走引导。返回删除行数。
|
|
支持 commit=False(同 delete_device_onboarding)。"""
|
|
n = db.execute(delete(OnboardingCompletion)).rowcount
|
|
if commit:
|
|
db.commit()
|
|
else:
|
|
db.flush()
|
|
return n
|