message.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. """消息通知 API"""
  2. from datetime import datetime
  3. from typing import Optional, List
  4. from fastapi import APIRouter, Depends, HTTPException
  5. from sqlalchemy import Column, String, DateTime, Integer, Text
  6. from sqlalchemy.orm import Session
  7. from pydantic import BaseModel, Field
  8. from app.core.database import get_db, Base
  9. from app.core.auth import get_current_user
  10. from app.core.response import success_response, error_response
  11. from app.core.utils import generate_id
  12. router = APIRouter(prefix="/api/web/admin/message", tags=["消息通知"])
  13. class BizMessage(Base):
  14. """消息通知表(临时定义)"""
  15. __tablename__ = "biz_message"
  16. id = Column(String(32), primary_key=True, comment="主键ID")
  17. receiver_id = Column(String(32), nullable=False, comment="接收人ID")
  18. receiver_name = Column(String(100), nullable=True, comment="接收人姓名")
  19. message_type = Column(String(20), nullable=False, comment="消息类型")
  20. title = Column(String(200), nullable=False, comment="消息标题")
  21. content = Column(Text, nullable=True, comment="消息内容")
  22. is_read = Column(Integer, default=0, comment="是否已读: 0-未读, 1-已读")
  23. read_time = Column(DateTime, nullable=True, comment="阅读时间")
  24. is_del = Column(Integer, default=0, comment="是否删除")
  25. create_at = Column(DateTime, default=datetime.now, comment="创建时间")
  26. create_by = Column(String(32), nullable=True, comment="创建人ID")
  27. create_name = Column(String(100), nullable=True, comment="创建人姓名")
  28. update_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="修改时间")
  29. update_by = Column(String(32), nullable=True, comment="修改人ID")
  30. class MessageCRUD:
  31. """消息通知CRUD"""
  32. def __init__(self, model):
  33. self.model = model
  34. def create(
  35. self,
  36. db: Session,
  37. receiver_id: str,
  38. receiver_name: str,
  39. message_type: str,
  40. title: str,
  41. content: str = None,
  42. create_by: str = None,
  43. create_name: str = None
  44. ):
  45. data = {
  46. "id": generate_id(),
  47. "receiver_id": receiver_id,
  48. "receiver_name": receiver_name,
  49. "message_type": message_type,
  50. "title": title,
  51. "content": content,
  52. "is_read": 0,
  53. "create_by": create_by,
  54. "create_name": create_name
  55. }
  56. obj = self.model(**data)
  57. db.add(obj)
  58. db.commit()
  59. db.refresh(obj)
  60. return obj
  61. def get_unread_count(self, db: Session, receiver_id: str) -> int:
  62. """获取未读消息数量"""
  63. return db.query(self.model).filter(
  64. self.model.receiver_id == receiver_id,
  65. self.model.is_read == 0,
  66. self.model.is_del == 0
  67. ).count()
  68. def get_list(
  69. self,
  70. db: Session,
  71. receiver_id: str = None,
  72. message_type: str = None,
  73. is_read: int = None,
  74. page: int = 1,
  75. page_size: int = 20
  76. ):
  77. """获取消息列表"""
  78. query = db.query(self.model).filter(self.model.is_del == 0)
  79. if receiver_id:
  80. query = query.filter(self.model.receiver_id == receiver_id)
  81. if message_type:
  82. query = query.filter(self.model.message_type == message_type)
  83. if is_read is not None:
  84. query = query.filter(self.model.is_read == is_read)
  85. total = query.count()
  86. items = query.order_by(self.model.create_at.desc()).offset((page - 1) * page_size).limit(page_size).all()
  87. return items, total
  88. def mark_read(self, db: Session, message_id: str) -> bool:
  89. """标记消息已读"""
  90. message = db.query(self.model).filter(
  91. self.model.id == message_id,
  92. self.model.is_del == 0
  93. ).first()
  94. if message:
  95. message.is_read = 1
  96. message.read_time = datetime.now()
  97. db.commit()
  98. return True
  99. return False
  100. def mark_all_read(self, db: Session, receiver_id: str) -> int:
  101. """标记所有消息已读"""
  102. count = db.query(self.model).filter(
  103. self.model.receiver_id == receiver_id,
  104. self.model.is_read == 0,
  105. self.model.is_del == 0
  106. ).update({
  107. "is_read": 1,
  108. "read_time": datetime.now()
  109. }, synchronize_session=False)
  110. db.commit()
  111. return count
  112. def delete(self, db: Session, message_id: str) -> bool:
  113. """删除消息"""
  114. message = db.query(self.model).filter(
  115. self.model.id == message_id,
  116. self.model.is_del == 0
  117. ).first()
  118. if message:
  119. message.is_del = 1
  120. db.commit()
  121. return True
  122. return False
  123. message_crud = MessageCRUD(BizMessage)
  124. class MessageCreateRequest(BaseModel):
  125. """创建消息请求"""
  126. receiver_id: str = Field(..., description="接收人ID")
  127. message_type: str = Field(..., description="消息类型: system/order/delivery/payment")
  128. title: str = Field(..., description="消息标题")
  129. content: str = Field(None, description="消息内容")
  130. @router.post("/send", response_model=dict)
  131. def send_message(
  132. request: MessageCreateRequest,
  133. db: Session = Depends(get_db),
  134. current_user: dict = Depends(get_current_user)
  135. ):
  136. """发送消息"""
  137. try:
  138. from app.crud.user import user_crud
  139. receiver = user_crud.get(db, request.receiver_id)
  140. receiver_name = receiver.real_name if receiver else None
  141. message = message_crud.create(
  142. db=db,
  143. receiver_id=request.receiver_id,
  144. receiver_name=receiver_name,
  145. message_type=request.message_type,
  146. title=request.title,
  147. content=request.content,
  148. create_by=current_user.get("user_id"),
  149. create_name=current_user.get("real_name")
  150. )
  151. return success_response({
  152. "id": message.id,
  153. "message": "消息发送成功"
  154. })
  155. except Exception as e:
  156. return error_response(500, f"发送消息失败: {str(e)}")
  157. @router.get("/list", response_model=dict)
  158. def get_message_list(
  159. page: int = 1,
  160. page_size: int = 20,
  161. receiver_id: str = None,
  162. message_type: str = None,
  163. is_read: int = None,
  164. start_date: str = None,
  165. end_date: str = None,
  166. db: Session = Depends(get_db),
  167. current_user: dict = Depends(get_current_user)
  168. ):
  169. """获取消息列表"""
  170. try:
  171. items, total = message_crud.get_list(
  172. db=db,
  173. receiver_id=receiver_id,
  174. message_type=message_type,
  175. is_read=is_read,
  176. page=page,
  177. page_size=page_size
  178. )
  179. return success_response({
  180. "list": [_to_response(item) for item in items],
  181. "total": total,
  182. "page": page,
  183. "page_size": page_size
  184. })
  185. except Exception as e:
  186. return error_response(500, f"获取消息列表失败: {str(e)}")
  187. @router.get("/unread-count", response_model=dict)
  188. def get_unread_count(
  189. receiver_id: str = None,
  190. db: Session = Depends(get_db),
  191. current_user: dict = Depends(get_current_user)
  192. ):
  193. """获取未读消息数量"""
  194. try:
  195. if not receiver_id:
  196. receiver_id = current_user.get("user_id")
  197. count = message_crud.get_unread_count(db, receiver_id)
  198. return success_response({"count": count})
  199. except Exception as e:
  200. return error_response(500, f"获取未读数量失败: {str(e)}")
  201. @router.post("/mark-read/{message_id}", response_model=dict)
  202. def mark_read(
  203. message_id: str,
  204. db: Session = Depends(get_db),
  205. current_user: dict = Depends(get_current_user)
  206. ):
  207. """标记消息已读"""
  208. try:
  209. success = message_crud.mark_read(db, message_id)
  210. if success:
  211. return success_response({"message": "标记成功"})
  212. return error_response(404, "消息不存在")
  213. except Exception as e:
  214. return error_response(500, f"标记失败: {str(e)}")
  215. @router.post("/mark-all-read", response_model=dict)
  216. def mark_all_read(
  217. receiver_id: str = None,
  218. db: Session = Depends(get_db),
  219. current_user: dict = Depends(get_current_user)
  220. ):
  221. """标记所有消息已读"""
  222. try:
  223. if not receiver_id:
  224. receiver_id = current_user.get("user_id")
  225. count = message_crud.mark_all_read(db, receiver_id)
  226. return success_response({"count": count, "message": "标记成功"})
  227. except Exception as e:
  228. return error_response(500, f"标记失败: {str(e)}")
  229. @router.delete("/delete/{message_id}", response_model=dict)
  230. def delete_message(
  231. message_id: str,
  232. db: Session = Depends(get_db),
  233. current_user: dict = Depends(get_current_user)
  234. ):
  235. """删除消息"""
  236. try:
  237. success = message_crud.delete(db, message_id)
  238. if success:
  239. return success_response({"message": "删除成功"})
  240. return error_response(404, "消息不存在")
  241. except Exception as e:
  242. return error_response(500, f"删除失败: {str(e)}")
  243. def _to_response(message: BizMessage) -> dict:
  244. """转换为响应格式"""
  245. return {
  246. "id": message.id,
  247. "receiver_id": message.receiver_id,
  248. "receiver_name": message.receiver_name,
  249. "message_type": message.message_type,
  250. "title": message.title,
  251. "content": message.content,
  252. "is_read": message.is_read,
  253. "read_time": str(message.read_time) if message.read_time else None,
  254. "create_at": str(message.create_at) if message.create_at else None
  255. }