AdminKnowledgeController.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package com.pharmacopoeia.controller;
  2. import com.fasterxml.jackson.databind.ObjectMapper;
  3. import com.pharmacopoeia.config.UnifiedResponse;
  4. import com.pharmacopoeia.dto.*;
  5. import com.pharmacopoeia.entity.QuickAsk;
  6. import com.pharmacopoeia.service.AdminKnowledgeService;
  7. import org.springframework.http.ResponseEntity;
  8. import org.springframework.web.bind.annotation.*;
  9. import java.util.*;
  10. @UnifiedResponse
  11. @RestController
  12. @RequestMapping("/api/v1/admin/knowledge")
  13. public class AdminKnowledgeController {
  14. private final AdminKnowledgeService service;
  15. private final ObjectMapper mapper = new ObjectMapper();
  16. public AdminKnowledgeController(AdminKnowledgeService service) {
  17. this.service = service;
  18. }
  19. // ==================== 药品管理 ====================
  20. @GetMapping("/drugs")
  21. public ResponseEntity<Map<String, Object>> listDrugs(
  22. @RequestParam(required = false) String keyword,
  23. @RequestParam(required = false) String category,
  24. @RequestParam(defaultValue = "1") int page,
  25. @RequestParam(defaultValue = "20") int pageSize) {
  26. return ResponseEntity.ok(service.listDrugs(keyword, category, page, pageSize));
  27. }
  28. @GetMapping("/drugs/{drugId}")
  29. public ResponseEntity<Map<String, Object>> getDrug(@PathVariable String drugId) {
  30. return ResponseEntity.ok(service.getDrug(drugId)
  31. .map(d -> Map.of("data", (Object) d))
  32. .orElse(Map.of("message", "数据入库后可用", "drug_id", drugId)));
  33. }
  34. @PostMapping("/drugs")
  35. public ResponseEntity<Map<String, Object>> createDrug(@RequestBody DrugCreateRequest req) {
  36. return ResponseEntity.ok(service.createDrug(objectToMap(req)));
  37. }
  38. @PutMapping("/drugs/{drugId}")
  39. public ResponseEntity<Map<String, Object>> updateDrug(@PathVariable String drugId,
  40. @RequestBody DrugUpdateRequest req) {
  41. return ResponseEntity.ok(service.updateDrug(drugId, objectToMap(req)));
  42. }
  43. @DeleteMapping("/drugs/{drugId}")
  44. public ResponseEntity<Map<String, Object>> deleteDrug(@PathVariable String drugId) {
  45. return ResponseEntity.ok(service.deleteDrug(drugId));
  46. }
  47. @PostMapping("/drugs/import")
  48. public ResponseEntity<Map<String, Object>> importDrugs(@RequestBody ImportRequest req) {
  49. int count = parseCount(req);
  50. return ResponseEntity.ok(Map.of(
  51. "message", "导入任务已提交",
  52. "total", count,
  53. "success", 0,
  54. "failed", 0,
  55. "note", "Phase 2 实现异步批量写入 + 向量化"
  56. ));
  57. }
  58. @PostMapping("/drugs/reindex")
  59. public ResponseEntity<Map<String, Object>> reindexDrugs(@RequestBody ReindexRequest req) {
  60. return ResponseEntity.ok(Map.of(
  61. "message", "重建索引任务已提交",
  62. "collection", req.getCollection(),
  63. "drug_ids", req.getDrugIds(),
  64. "status", "pending",
  65. "note", "Phase 2 实现:重新 chunk → 向量化 → 写入 pgvector"
  66. ));
  67. }
  68. // ==================== 知识点管理 ====================
  69. @GetMapping("/knowledge-points")
  70. public ResponseEntity<Map<String, Object>> listKnowledgePoints(
  71. @RequestParam(required = false) String subject,
  72. @RequestParam(required = false) String chapterId,
  73. @RequestParam(required = false) Integer difficulty,
  74. @RequestParam(required = false) String frequency,
  75. @RequestParam(defaultValue = "1") int page,
  76. @RequestParam(defaultValue = "20") int pageSize) {
  77. return ResponseEntity.ok(service.listKnowledgePoints(subject, chapterId, difficulty, frequency, page, pageSize));
  78. }
  79. @GetMapping("/knowledge-points/{pointId}")
  80. public ResponseEntity<Map<String, Object>> getKnowledgePoint(@PathVariable String pointId) {
  81. return ResponseEntity.ok(service.getKnowledgePoint(pointId)
  82. .<Map<String, Object>>map(kp -> Map.of(
  83. "point_id", kp.getPointId(),
  84. "subject", kp.getSubject(),
  85. "title", kp.getTitle(),
  86. "content", kp.getContent()
  87. ))
  88. .orElse(Map.of("message", "数据入库后可用", "point_id", pointId)));
  89. }
  90. @PostMapping("/knowledge-points")
  91. public ResponseEntity<Map<String, Object>> createKnowledgePoint(@RequestBody KnowledgePointCreateRequest req) {
  92. return ResponseEntity.ok(service.createKnowledgePoint(objectToMap(req)));
  93. }
  94. @PutMapping("/knowledge-points/{pointId}")
  95. public ResponseEntity<Map<String, Object>> updateKnowledgePoint(@PathVariable String pointId,
  96. @RequestBody KnowledgePointUpdateRequest req) {
  97. return ResponseEntity.ok(service.updateKnowledgePoint(pointId, objectToMap(req)));
  98. }
  99. @DeleteMapping("/knowledge-points/{pointId}")
  100. public ResponseEntity<Map<String, Object>> deleteKnowledgePoint(@PathVariable String pointId) {
  101. return ResponseEntity.ok(service.deleteKnowledgePoint(pointId));
  102. }
  103. @PostMapping("/knowledge-points/import")
  104. public ResponseEntity<Map<String, Object>> importKnowledgePoints(@RequestBody ImportRequest req) {
  105. int count = parseCount(req);
  106. return ResponseEntity.ok(Map.of(
  107. "message", "导入任务已提交",
  108. "total", count,
  109. "success", 0,
  110. "failed", 0
  111. ));
  112. }
  113. // ==================== 题库管理 ====================
  114. @GetMapping("/questions")
  115. public ResponseEntity<Map<String, Object>> listQuestions(
  116. @RequestParam(required = false) String subject,
  117. @RequestParam(required = false) String chapterId,
  118. @RequestParam(required = false) String questionType,
  119. @RequestParam(required = false) Integer difficulty,
  120. @RequestParam(required = false) Boolean audited,
  121. @RequestParam(defaultValue = "1") int page,
  122. @RequestParam(defaultValue = "20") int pageSize) {
  123. return ResponseEntity.ok(service.listQuestions(subject, chapterId, questionType, difficulty, audited, page, pageSize));
  124. }
  125. @GetMapping("/questions/{questionId}")
  126. public ResponseEntity<Map<String, Object>> getQuestion(@PathVariable String questionId) {
  127. return ResponseEntity.ok(service.getQuestion(questionId)
  128. .<Map<String, Object>>map(q -> Map.of(
  129. "question_id", q.getQuestionId(),
  130. "content", q.getContent(),
  131. "answer", q.getAnswer()
  132. ))
  133. .orElse(Map.of("message", "数据入库后可用", "question_id", questionId)));
  134. }
  135. @PostMapping("/questions")
  136. public ResponseEntity<Map<String, Object>> createQuestion(@RequestBody QuestionCreateRequest req) {
  137. return ResponseEntity.ok(service.createQuestion(objectToMap(req)));
  138. }
  139. @PutMapping("/questions/{questionId}")
  140. public ResponseEntity<Map<String, Object>> updateQuestion(@PathVariable String questionId,
  141. @RequestBody QuestionUpdateRequest req) {
  142. return ResponseEntity.ok(service.updateQuestion(questionId, objectToMap(req)));
  143. }
  144. @DeleteMapping("/questions/{questionId}")
  145. public ResponseEntity<Map<String, Object>> deleteQuestion(@PathVariable String questionId) {
  146. return ResponseEntity.ok(service.deleteQuestion(questionId));
  147. }
  148. @PostMapping("/questions/batch-audit")
  149. public ResponseEntity<Map<String, Object>> batchAudit(@RequestBody BatchAuditRequest req) {
  150. return ResponseEntity.ok(service.batchAudit(req.getQuestionIds(), req.getAudited()));
  151. }
  152. @PostMapping("/questions/import")
  153. public ResponseEntity<Map<String, Object>> importQuestions(@RequestBody ImportRequest req) {
  154. int count = parseCount(req);
  155. return ResponseEntity.ok(Map.of(
  156. "message", "导入任务已提交",
  157. "total", count,
  158. "success", 0,
  159. "failed", 0
  160. ));
  161. }
  162. @PostMapping("/questions/generate")
  163. public ResponseEntity<Map<String, Object>> generateQuestions(
  164. @RequestParam String pointId,
  165. @RequestParam(defaultValue = "10") int count,
  166. @RequestParam(defaultValue = "A") String questionType) {
  167. return ResponseEntity.ok(Map.of(
  168. "message", "AI 出题任务已提交",
  169. "point_id", pointId,
  170. "count", count,
  171. "question_type", questionType,
  172. "status", "pending",
  173. "note", "Phase 2 实现:LLM 根据知识点生成题目"
  174. ));
  175. }
  176. // ==================== 统计 ====================
  177. @GetMapping("/stats")
  178. public ResponseEntity<Map<String, Object>> knowledgeStats() {
  179. return ResponseEntity.ok(service.knowledgeStats());
  180. }
  181. // ==================== 快捷提问标签管理(管理端,需要 JWT) ====================
  182. @GetMapping("/quick-asks")
  183. public ResponseEntity<List<QuickAsk>> listAllQuickAsks() {
  184. return ResponseEntity.ok(service.listAllQuickAsks());
  185. }
  186. @PostMapping("/quick-asks")
  187. public ResponseEntity<Map<String, Object>> createQuickAsk(@RequestBody Map<String, Object> body) {
  188. String label = (String) body.getOrDefault("label", "");
  189. String question = (String) body.getOrDefault("question", label);
  190. int sortOrder = body.get("sort_order") instanceof Integer i ? i : 0;
  191. return ResponseEntity.ok(service.createQuickAsk(label, question, sortOrder));
  192. }
  193. @PutMapping("/quick-asks/{id}")
  194. public ResponseEntity<Map<String, Object>> updateQuickAsk(
  195. @PathVariable int id, @RequestBody Map<String, Object> body) {
  196. String label = body.containsKey("label") ? (String) body.get("label") : null;
  197. String question = body.containsKey("question") ? (String) body.get("question") : null;
  198. Integer sortOrder = body.get("sort_order") instanceof Integer i ? i : null;
  199. Boolean isActive = body.get("is_active") instanceof Boolean b ? b : null;
  200. return ResponseEntity.ok(service.updateQuickAsk(id, label, question, sortOrder, isActive));
  201. }
  202. @DeleteMapping("/quick-asks/{id}")
  203. public ResponseEntity<Map<String, Object>> deleteQuickAsk(@PathVariable int id) {
  204. return ResponseEntity.ok(service.deleteQuickAsk(id));
  205. }
  206. @PutMapping("/quick-asks/reorder")
  207. public ResponseEntity<Map<String, Object>> reorderQuickAsks(@RequestBody List<Map<String, Object>> items) {
  208. return ResponseEntity.ok(service.reorderQuickAsks(items));
  209. }
  210. @GetMapping("/quick-asks/audit-log")
  211. public ResponseEntity<Map<String, Object>> listQuickAskAuditLog(
  212. @RequestParam(defaultValue = "1") int page,
  213. @RequestParam(defaultValue = "20") int pageSize) {
  214. return ResponseEntity.ok(service.listQuickAskAuditLog(page, pageSize));
  215. }
  216. // ==================== Helpers ====================
  217. private int parseCount(ImportRequest req) {
  218. try {
  219. if ("json".equals(req.getFormat()) && req.getData() != null) {
  220. var items = mapper.readTree(req.getData());
  221. return items.isArray() ? items.size() : 0;
  222. }
  223. if ("csv".equals(req.getFormat()) && req.getData() != null) {
  224. return req.getData().split("\n").length - 1; // rough count
  225. }
  226. } catch (Exception ignored) {}
  227. return 0;
  228. }
  229. private Map<String, Object> objectToMap(Object obj) {
  230. try {
  231. @SuppressWarnings("unchecked")
  232. Map<String, Object> map = mapper.convertValue(obj, Map.class);
  233. return map;
  234. } catch (Exception e) {
  235. return Map.of();
  236. }
  237. }
  238. }