feat(feedback): 补齐反馈历史审核发奖闭环
- 新增反馈审核字段和迁移,支持 pending/adopted/rejected、未采纳原因和采纳金币 - 增加用户端反馈历史 records 接口和 admin 采纳/拒绝接口 - 采纳时同事务写状态、金币流水和审计日志,拦截重复审核 - 验证:pytest tests/test_feedback.py tests/test_admin_write.py tests/test_admin_read.py
This commit is contained in:
@@ -74,7 +74,7 @@ def _seed_feedback(phone: str) -> int:
|
||||
uid = _seed_user(phone)
|
||||
db = SessionLocal()
|
||||
try:
|
||||
fb = Feedback(user_id=uid, content="测试", contact="wx", status="new")
|
||||
fb = Feedback(user_id=uid, content="测试", contact="wx", status="pending")
|
||||
db.add(fb)
|
||||
db.commit()
|
||||
return fb.id
|
||||
@@ -280,15 +280,90 @@ def test_super_admin_can_grant_coins(admin_client: TestClient, super_token: str)
|
||||
assert r.status_code == 200
|
||||
|
||||
|
||||
# ===== 反馈处理 =====
|
||||
# ===== 反馈审核 =====
|
||||
|
||||
def test_handle_feedback(admin_client: TestClient, operator_token: str) -> None:
|
||||
def test_approve_feedback_grants_coins_and_audit(
|
||||
admin_client: TestClient, operator_token: str
|
||||
) -> None:
|
||||
fid = _seed_feedback("13900000006")
|
||||
r = admin_client.post(f"/admin/api/feedbacks/{fid}/handle", headers=_auth(operator_token))
|
||||
assert r.status_code == 200
|
||||
r = admin_client.post(
|
||||
f"/admin/api/feedbacks/{fid}/approve",
|
||||
json={"reward_coins": 800, "note": "真实详细,已采纳"},
|
||||
headers=_auth(operator_token),
|
||||
)
|
||||
assert r.status_code == 200, r.text
|
||||
assert r.json()["status"] == "adopted"
|
||||
assert r.json()["reward_coins"] == 800
|
||||
db = SessionLocal()
|
||||
try:
|
||||
assert db.get(Feedback, fid).status == "handled"
|
||||
fb = db.get(Feedback, fid)
|
||||
assert fb is not None
|
||||
assert fb.status == "adopted"
|
||||
assert fb.reward_coins == 800
|
||||
assert fb.review_note == "真实详细,已采纳"
|
||||
assert fb.reviewed_by_admin_id is not None
|
||||
assert fb.reviewed_at is not None
|
||||
assert db.get(CoinAccount, fb.user_id).coin_balance == 800
|
||||
txns = db.execute(
|
||||
select(CoinTransaction).where(
|
||||
CoinTransaction.user_id == fb.user_id,
|
||||
CoinTransaction.biz_type == "feedback_reward",
|
||||
CoinTransaction.ref_id == str(fid),
|
||||
)
|
||||
).scalars().all()
|
||||
assert len(txns) == 1 and txns[0].amount == 800
|
||||
logs = db.execute(
|
||||
select(AdminAuditLog).where(
|
||||
AdminAuditLog.action == "feedback.approve",
|
||||
AdminAuditLog.target_id == str(fid),
|
||||
)
|
||||
).scalars().all()
|
||||
assert len(logs) == 1 and logs[0].detail["reward_coins"] == 800
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
r = admin_client.post(
|
||||
f"/admin/api/feedbacks/{fid}/approve",
|
||||
json={"reward_coins": 100},
|
||||
headers=_auth(operator_token),
|
||||
)
|
||||
assert r.status_code == 400
|
||||
|
||||
|
||||
def test_reject_feedback_records_reason_and_no_coin(
|
||||
admin_client: TestClient, operator_token: str
|
||||
) -> None:
|
||||
fid = _seed_feedback("13900000013")
|
||||
r = admin_client.post(
|
||||
f"/admin/api/feedbacks/{fid}/reject",
|
||||
json={"reason": "暂未提供可复现信息", "note": "信息不足"},
|
||||
headers=_auth(operator_token),
|
||||
)
|
||||
assert r.status_code == 200, r.text
|
||||
assert r.json()["status"] == "rejected"
|
||||
assert r.json()["reject_reason"] == "暂未提供可复现信息"
|
||||
db = SessionLocal()
|
||||
try:
|
||||
fb = db.get(Feedback, fid)
|
||||
assert fb is not None
|
||||
assert fb.status == "rejected"
|
||||
assert fb.reject_reason == "暂未提供可复现信息"
|
||||
assert fb.review_note == "信息不足"
|
||||
txns = db.execute(
|
||||
select(CoinTransaction).where(
|
||||
CoinTransaction.user_id == fb.user_id,
|
||||
CoinTransaction.biz_type == "feedback_reward",
|
||||
CoinTransaction.ref_id == str(fid),
|
||||
)
|
||||
).scalars().all()
|
||||
assert txns == []
|
||||
logs = db.execute(
|
||||
select(AdminAuditLog).where(
|
||||
AdminAuditLog.action == "feedback.reject",
|
||||
AdminAuditLog.target_id == str(fid),
|
||||
)
|
||||
).scalars().all()
|
||||
assert len(logs) == 1 and logs[0].detail["reason"] == "暂未提供可复现信息"
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user