java_client.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import httpx
  2. from typing import Optional
  3. from app.config import settings
  4. import logging
  5. logger = logging.getLogger(__name__)
  6. class JavaClient:
  7. """Java 后端 HTTP 客户端 (单例, 复用连接池)"""
  8. _instance: Optional["JavaClient"] = None
  9. def __new__(cls):
  10. if cls._instance is None:
  11. cls._instance = super().__new__(cls)
  12. cls._instance._client = None
  13. cls._instance._base_url = settings.java_base_url
  14. return cls._instance
  15. async def _get_client(self) -> httpx.AsyncClient:
  16. if self._client is None:
  17. limits = httpx.Limits(
  18. max_connections=10,
  19. max_keepalive_connections=5,
  20. keepalive_expiry=30,
  21. )
  22. self._client = httpx.AsyncClient(
  23. base_url=self._base_url,
  24. timeout=httpx.Timeout(120.0, connect=3.0),
  25. limits=limits,
  26. )
  27. return self._client
  28. async def close(self):
  29. if self._client:
  30. await self._client.aclose()
  31. self._client = None
  32. JavaClient._instance = None
  33. async def get_published_articles(self) -> list[dict]:
  34. client = await self._get_client()
  35. resp = await client.post("/api/article/list", json={"status": "published", "limit": 1000})
  36. data = resp.json()
  37. if data.get("code") == 200:
  38. return data.get("data", [])
  39. return []
  40. async def search_products(self, keyword: str, limit: int = 5) -> list[dict]:
  41. client = await self._get_client()
  42. resp = await client.post("/api/product/search", json={
  43. "keyword": keyword, "status": "上架", "limit": limit
  44. })
  45. data = resp.json()
  46. if data.get("code") == 200:
  47. return data.get("data", [])
  48. return []
  49. async def search_activities(self, keyword: str, limit: int = 5) -> list[dict]:
  50. client = await self._get_client()
  51. resp = await client.post("/api/activity/search", json={
  52. "keyword": keyword, "status": "published", "limit": limit
  53. })
  54. data = resp.json()
  55. if data.get("code") == 200:
  56. return data.get("data", [])
  57. return []
  58. async def search_articles(self, keyword: str, limit: int = 5) -> list[dict]:
  59. client = await self._get_client()
  60. resp = await client.post("/api/article/search", json={
  61. "keyword": keyword, "status": "published", "limit": limit
  62. })
  63. data = resp.json()
  64. if data.get("code") == 200:
  65. return data.get("data", [])
  66. return []
  67. async def get_user_context(self, user_id: int, params: Optional[dict] = None) -> dict:
  68. client = await self._get_client()
  69. resp = await client.post(settings.effective_java_context_url, json={
  70. "user_id": str(user_id),
  71. "params": params or {},
  72. })
  73. data = resp.json()
  74. if data.get("code") == 200:
  75. return data.get("data", {})
  76. return {}
  77. async def get_microbiome_articles(self) -> list[dict]:
  78. """拉取菌群知识库文章(分页,避免单次响应过大导致 Broken pipe)"""
  79. client = await self._get_client()
  80. page = 1
  81. size = 50
  82. records = []
  83. while True:
  84. resp = await client.post("/api/microbiome/article/list",
  85. json={"page": page, "size": size, "keyword": "", "category": ""})
  86. data = resp.json()
  87. if data.get("code") != 200:
  88. break
  89. body = data.get("data", {}) or {}
  90. items = body.get("records", []) or []
  91. records.extend(items)
  92. total = body.get("total", 0)
  93. if page * size >= int(total or 0) or not items:
  94. break
  95. page += 1
  96. return records
  97. async def get_dan_knowledge_base(self) -> list[dict]:
  98. """拉取知识库内容(dan_knowledge_base 表,仅启用 status=1)"""
  99. client = await self._get_client()
  100. page = 1
  101. size = 1000
  102. records = []
  103. while True:
  104. resp = await client.post("/api/admin/knowledge-base/list", json={"page": page, "size": size, "status": 1})
  105. data = resp.json()
  106. if data.get("code") != 200:
  107. break
  108. body = data.get("data", {}) or {}
  109. items = body.get("records", []) or []
  110. records.extend(items)
  111. total = body.get("total", 0)
  112. if page * size >= int(total or 0) or not items:
  113. break
  114. page += 1
  115. return records
  116. async def get_member_reports(self, member_id: int) -> list[dict]:
  117. """获取某个家庭成员的健康报告列表"""
  118. client = await self._get_client()
  119. resp = await client.post("/api/health/report/list", json={"memberId": member_id})
  120. data = resp.json()
  121. if data.get("code") == 200:
  122. return data.get("data", [])
  123. return []
  124. async def get_report_indicators(self, report_id: int) -> list[dict]:
  125. """获取健康报告的所有指标明细"""
  126. client = await self._get_client()
  127. resp = await client.post("/api/health/report/indicator/list", json={"reportId": report_id})
  128. data = resp.json()
  129. if data.get("code") == 200:
  130. return data.get("data", [])
  131. return []
  132. async def query_health_knowledge(self, item_type: str, item_name: str) -> dict | None:
  133. """查询健康知识库中某个指标/菌属/营养素的定义"""
  134. client = await self._get_client()
  135. resp = await client.post("/api/health/knowledge/query", json={
  136. "itemType": item_type, "itemName": item_name,
  137. })
  138. data = resp.json()
  139. if data.get("code") == 200 and data.get("data"):
  140. return data["data"]
  141. return None
  142. async def batch_query_health_knowledge(self, queries: list[dict]) -> list[dict]:
  143. """批量查询健康知识库"""
  144. client = await self._get_client()
  145. resp = await client.post("/api/health/knowledge/batch-query", json={"queries": queries})
  146. data = resp.json()
  147. if data.get("code") == 200:
  148. return data.get("data", [])
  149. return []
  150. async def get_family_context(self, user_id: int, intent_type: str = "child_info", params: dict | None = None) -> dict:
  151. """获取家庭上下文数据(通过公开的 /api/ai/context 端点)"""
  152. client = await self._get_client()
  153. body = {"user_id": str(user_id), "intent_type": intent_type}
  154. if params:
  155. body["params"] = params
  156. resp = await client.post("/api/ai/context", json=body)
  157. data = resp.json()
  158. if data.get("code") == 200:
  159. return data.get("data", {})
  160. return {}