103 lines
3.3 KiB
Python
103 lines
3.3 KiB
Python
"""
|
||
维客线索路由。
|
||
|
||
- POST /api/retention-clue — 提交维客线索(UPSERT)
|
||
- GET /api/retention-clue/{member_id} — 查询某会员的全部维客线索
|
||
- DELETE /api/retention-clue/{clue_id} — 删除单条线索
|
||
"""
|
||
|
||
import logging
|
||
|
||
from fastapi import APIRouter, HTTPException, status
|
||
|
||
from app.database import get_connection
|
||
from app.schemas.member_retention_clue import RetentionClueSubmit, RetentionClueOut
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
router = APIRouter(prefix="/api", tags=["维客线索"])
|
||
|
||
|
||
@router.post("/retention-clue")
|
||
async def submit_retention_clue(body: RetentionClueSubmit):
|
||
"""
|
||
提交维客线索(INSERT)。
|
||
|
||
同一会员可有多条不同大类的线索。
|
||
"""
|
||
sql = """
|
||
INSERT INTO member_retention_clue
|
||
(member_id, category, summary, detail,
|
||
recorded_by_assistant_id, recorded_by_name, site_id, source)
|
||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
||
RETURNING id
|
||
"""
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(sql, (
|
||
body.member_id,
|
||
body.category.value,
|
||
body.summary,
|
||
body.detail,
|
||
body.recorded_by_assistant_id,
|
||
body.recorded_by_name,
|
||
body.site_id,
|
||
body.source.value,
|
||
))
|
||
row = cur.fetchone()
|
||
conn.commit()
|
||
return {"status": "ok", "id": row[0] if row else None}
|
||
except Exception:
|
||
conn.rollback()
|
||
logger.exception("维客线索写入失败: member_id=%s", body.member_id)
|
||
raise HTTPException(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
detail="线索提交失败,请稍后重试",
|
||
)
|
||
finally:
|
||
conn.close()
|
||
|
||
|
||
@router.get("/retention-clue/{member_id}", response_model=list[RetentionClueOut])
|
||
async def get_retention_clues(member_id: int, site_id: int):
|
||
"""查询某会员的全部维客线索,按录入时间倒序。"""
|
||
sql = """
|
||
SELECT id, member_id, category, summary, detail,
|
||
recorded_by_assistant_id, recorded_by_name, recorded_at, site_id, source
|
||
FROM member_retention_clue
|
||
WHERE member_id = %s AND site_id = %s
|
||
ORDER BY recorded_at DESC
|
||
"""
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(sql, (member_id, site_id))
|
||
rows = cur.fetchall()
|
||
cols = [d[0] for d in cur.description]
|
||
return [dict(zip(cols, r)) for r in rows]
|
||
finally:
|
||
conn.close()
|
||
|
||
|
||
@router.delete("/retention-clue/{clue_id}")
|
||
async def delete_retention_clue(clue_id: int):
|
||
"""删除单条维客线索。"""
|
||
sql = "DELETE FROM member_retention_clue WHERE id = %s"
|
||
conn = get_connection()
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(sql, (clue_id,))
|
||
if cur.rowcount == 0:
|
||
raise HTTPException(status_code=404, detail="线索不存在")
|
||
conn.commit()
|
||
return {"status": "ok"}
|
||
except HTTPException:
|
||
raise
|
||
except Exception:
|
||
conn.rollback()
|
||
logger.exception("维客线索删除失败: id=%s", clue_id)
|
||
raise HTTPException(status_code=500, detail="删除失败")
|
||
finally:
|
||
conn.close()
|