250 lines
10 KiB
Python
Executable File
250 lines
10 KiB
Python
Executable File
"""互动记录接口"""
|
||
from typing import Optional
|
||
from fastapi import APIRouter, Depends, Query, HTTPException
|
||
from fastapi.responses import StreamingResponse
|
||
import io, pandas as pd
|
||
|
||
from app.core.database import get_db
|
||
from app.schemas import ApiResponse
|
||
from app.services.stats_service import stats_service
|
||
from app.models import InteractionRecord
|
||
from sqlalchemy import select, update
|
||
|
||
router = APIRouter()
|
||
|
||
|
||
@router.get("")
|
||
async def list_interactions(
|
||
page: int = Query(default=1, ge=1),
|
||
page_size: int = Query(default=20, ge=1, le=100),
|
||
user_id: Optional[int] = None,
|
||
interact_type: Optional[str] = None,
|
||
status: Optional[int] = None,
|
||
start_date: Optional[str] = None,
|
||
end_date: Optional[str] = None,
|
||
keyword: Optional[str] = None,
|
||
db=Depends(get_db)
|
||
):
|
||
result = await stats_service.get_interaction_records(
|
||
db, page, page_size, user_id, interact_type, status, start_date, end_date, keyword
|
||
)
|
||
return ApiResponse(data=result)
|
||
|
||
|
||
@router.post("/{record_id}/retry")
|
||
async def retry_interaction(record_id: int, db=Depends(get_db)):
|
||
"""手动重试失败任务"""
|
||
result = await db.execute(select(InteractionRecord).where(InteractionRecord.id == record_id))
|
||
record = result.scalar_one_or_none()
|
||
if not record:
|
||
raise HTTPException(status_code=404, detail="记录不存在")
|
||
if record.status != 2:
|
||
raise HTTPException(status_code=400, detail="只能重试失败的任务")
|
||
if record.retry_count >= 3:
|
||
raise HTTPException(status_code=400, detail="已超过最大重试次数(3次)")
|
||
|
||
from app.services.news_service import news_service
|
||
from app.services.ai_service import ai_service
|
||
from app.models import VirtualUser, UserPersonality
|
||
|
||
user_result = await db.execute(select(VirtualUser).where(VirtualUser.id == record.user_id))
|
||
user = user_result.scalar_one_or_none()
|
||
if not user or user.status != 2:
|
||
raise HTTPException(status_code=400, detail="用户未登录,无法重试")
|
||
|
||
success, err, platform_record_id = False, "未知类型", ""
|
||
if record.interact_type == "comment" and record.content:
|
||
success, err, platform_record_id = await news_service.post_comment_with_record_id(
|
||
db, user, record.article_id, record.article_title or "", record.content
|
||
)
|
||
elif record.interact_type == "like":
|
||
success, err = await news_service.like_news(db, user, record.article_id, org_id="", title=record.article_title or "")
|
||
elif record.interact_type == "collect":
|
||
success, err = await news_service.collect_news(db, user, record.article_id, title=record.article_title or "")
|
||
elif record.interact_type == "forward":
|
||
success, err = await news_service.forward_news(db, user, record.article_id)
|
||
elif record.interact_type == "reply" and record.content:
|
||
parent_comment = None
|
||
if record.parent_comment_id:
|
||
comments = await news_service.get_comments(db, user, record.article_id or "")
|
||
parent_comment = next(
|
||
(
|
||
c for c in comments
|
||
if str(c.get("id") or c.get("commentId") or "") == str(record.parent_comment_id)
|
||
),
|
||
None,
|
||
)
|
||
if not parent_comment:
|
||
success, err, platform_record_id = False, "未找到被回复的原评论,无法重试回复", ""
|
||
else:
|
||
success, err, platform_record_id = await news_service.post_reply_with_record_id(
|
||
db, user,
|
||
record.article_id or "",
|
||
record.parent_comment_id or "",
|
||
record.content,
|
||
parent_comment=parent_comment,
|
||
article_title=record.article_title or "",
|
||
)
|
||
|
||
await db.execute(
|
||
update(InteractionRecord).where(InteractionRecord.id == record_id).values(
|
||
status=1 if success else 2,
|
||
error_msg=None if success else err,
|
||
platform_record_id=platform_record_id or record.platform_record_id,
|
||
retry_count=record.retry_count + 1,
|
||
)
|
||
)
|
||
await db.commit()
|
||
return ApiResponse(message="重试成功" if success else f"重试失败: {err}")
|
||
|
||
|
||
@router.get("/export")
|
||
async def export_interactions(
|
||
user_id: Optional[int] = None,
|
||
interact_type: Optional[str] = None,
|
||
status: Optional[int] = None,
|
||
start_date: Optional[str] = None,
|
||
end_date: Optional[str] = None,
|
||
db=Depends(get_db)
|
||
):
|
||
"""导出互动记录"""
|
||
data = await stats_service.get_interaction_records(
|
||
db, 1, 10000, user_id, interact_type, status, start_date, end_date
|
||
)
|
||
rows = [{
|
||
"ID": r["id"], "用户昵称": r["user_nickname"], "用户账号": r["user_account"],
|
||
"文章标题": r["article_title"], "互动类型": r["interact_type_label"],
|
||
"内容": r["content"] or "", "Token消耗": r["token_consumed"],
|
||
"状态": r["status_label"], "失败原因": r["error_msg"] or "",
|
||
"重试次数": r["retry_count"], "执行时间": r["executed_at"],
|
||
} for r in data["items"]]
|
||
df = pd.DataFrame(rows)
|
||
buf = io.BytesIO()
|
||
df.to_excel(buf, index=False, sheet_name="互动记录")
|
||
buf.seek(0)
|
||
return StreamingResponse(
|
||
buf,
|
||
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||
headers={"Content-Disposition": "attachment; filename=interactions_export.xlsx"}
|
||
)
|
||
|
||
@router.post("/{record_id}/cancel")
|
||
async def cancel_interaction(record_id: int, db=Depends(get_db)):
|
||
"""取消互动(取消点赞/收藏/删除评论),转发不支持取消"""
|
||
from sqlalchemy import select, update
|
||
from app.models import InteractionRecord, VirtualUser
|
||
from app.services.news_service import news_service
|
||
|
||
# 查找互动记录
|
||
r = await db.execute(select(InteractionRecord).where(InteractionRecord.id == record_id))
|
||
record = r.scalar_one_or_none()
|
||
if not record:
|
||
return ApiResponse(code=404, message="记录不存在")
|
||
|
||
if record.status != 1:
|
||
return ApiResponse(code=400, message="只能取消成功的互动")
|
||
|
||
if record.interact_type == "forward":
|
||
return ApiResponse(code=400, message="转发互动无法取消")
|
||
|
||
if record.interact_type == "read":
|
||
return ApiResponse(code=400, message="阅读记录无法取消")
|
||
|
||
# 查找对应用户
|
||
ur = await db.execute(select(VirtualUser).where(VirtualUser.id == record.user_id))
|
||
user = ur.scalar_one_or_none()
|
||
if not user:
|
||
return ApiResponse(code=404, message="用户不存在")
|
||
|
||
# 执行取消
|
||
ok = False
|
||
err = ""
|
||
if record.interact_type in ("like",):
|
||
ok, err = await news_service.cancel_like(
|
||
db, user,
|
||
news_id=record.article_id or "",
|
||
org_id=record.session_id or "", # session_id 字段暂存 org_id
|
||
title=record.article_title or "",
|
||
)
|
||
elif record.interact_type == "collect":
|
||
ok, err = await news_service.cancel_collect(
|
||
db, user,
|
||
news_id=record.article_id or "",
|
||
title=record.article_title or "",
|
||
)
|
||
elif record.interact_type == "comment":
|
||
comment_id = record.platform_record_id or ""
|
||
if not comment_id:
|
||
comment_id, comment_count = await news_service.find_comment_id(
|
||
db, user,
|
||
news_id=record.article_id or "",
|
||
content=record.content or "",
|
||
)
|
||
if comment_id:
|
||
await db.execute(
|
||
update(InteractionRecord).where(InteractionRecord.id == record_id).values(
|
||
platform_record_id=comment_id
|
||
)
|
||
)
|
||
await db.commit()
|
||
elif comment_count == 0:
|
||
await db.execute(
|
||
update(InteractionRecord).where(InteractionRecord.id == record_id).values(
|
||
status=3,
|
||
error_msg="平台未查询到评论,已标记取消",
|
||
)
|
||
)
|
||
await db.commit()
|
||
return ApiResponse(message="平台未查询到评论,已标记取消")
|
||
else:
|
||
return ApiResponse(code=400, message="缺少评论ID,且未能从平台评论列表反查到该评论")
|
||
ok, err = await news_service.cancel_comment(
|
||
db, user,
|
||
news_id=record.article_id or "",
|
||
comment_id=comment_id,
|
||
)
|
||
elif record.interact_type == "reply":
|
||
reply_id = record.platform_record_id or ""
|
||
if not reply_id:
|
||
reply_id, reply_count = await news_service.find_reply_id(
|
||
db, user,
|
||
news_id=record.article_id or "",
|
||
content=record.content or "",
|
||
parent_comment_id=record.parent_comment_id or "",
|
||
)
|
||
if reply_id:
|
||
await db.execute(
|
||
update(InteractionRecord).where(InteractionRecord.id == record_id).values(
|
||
platform_record_id=reply_id
|
||
)
|
||
)
|
||
await db.commit()
|
||
elif reply_count == 0:
|
||
await db.execute(
|
||
update(InteractionRecord).where(InteractionRecord.id == record_id).values(
|
||
status=3,
|
||
error_msg="平台未查询到回复,已标记取消",
|
||
)
|
||
)
|
||
await db.commit()
|
||
return ApiResponse(message="平台未查询到回复,已标记取消")
|
||
else:
|
||
return ApiResponse(code=400, message="缺少回复ID,且未能从平台回复列表反查到该回复")
|
||
ok, err = await news_service.cancel_reply(
|
||
db, user,
|
||
news_id=record.article_id or "",
|
||
reply_id=reply_id,
|
||
)
|
||
|
||
if ok:
|
||
# 更新状态为手动取消(status=3)
|
||
await db.execute(
|
||
update(InteractionRecord).where(InteractionRecord.id == record_id).values(
|
||
status=3, error_msg="手动取消"
|
||
)
|
||
)
|
||
await db.commit()
|
||
return ApiResponse(message="取消成功")
|
||
else:
|
||
return ApiResponse(code=500, message=f"取消失败: {err}")
|