package com.pharmacopoeia.controller; import com.fasterxml.jackson.databind.ObjectMapper; import com.pharmacopoeia.config.UnifiedResponse; import com.pharmacopoeia.dto.*; import com.pharmacopoeia.entity.QuickAsk; import com.pharmacopoeia.service.AdminKnowledgeService; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.*; @UnifiedResponse @RestController @RequestMapping("/api/v1/admin/knowledge") public class AdminKnowledgeController { private final AdminKnowledgeService service; private final ObjectMapper mapper = new ObjectMapper(); public AdminKnowledgeController(AdminKnowledgeService service) { this.service = service; } // ==================== 药品管理 ==================== @GetMapping("/drugs") public ResponseEntity> listDrugs( @RequestParam(required = false) String keyword, @RequestParam(required = false) String category, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "20") int pageSize) { return ResponseEntity.ok(service.listDrugs(keyword, category, page, pageSize)); } @GetMapping("/drugs/{drugId}") public ResponseEntity> getDrug(@PathVariable String drugId) { return ResponseEntity.ok(service.getDrug(drugId) .map(d -> Map.of("data", (Object) d)) .orElse(Map.of("message", "数据入库后可用", "drug_id", drugId))); } @PostMapping("/drugs") public ResponseEntity> createDrug(@RequestBody DrugCreateRequest req) { return ResponseEntity.ok(service.createDrug(objectToMap(req))); } @PutMapping("/drugs/{drugId}") public ResponseEntity> updateDrug(@PathVariable String drugId, @RequestBody DrugUpdateRequest req) { return ResponseEntity.ok(service.updateDrug(drugId, objectToMap(req))); } @DeleteMapping("/drugs/{drugId}") public ResponseEntity> deleteDrug(@PathVariable String drugId) { return ResponseEntity.ok(service.deleteDrug(drugId)); } @PostMapping("/drugs/import") public ResponseEntity> importDrugs(@RequestBody ImportRequest req) { int count = parseCount(req); return ResponseEntity.ok(Map.of( "message", "导入任务已提交", "total", count, "success", 0, "failed", 0, "note", "Phase 2 实现异步批量写入 + 向量化" )); } @PostMapping("/drugs/reindex") public ResponseEntity> reindexDrugs(@RequestBody ReindexRequest req) { return ResponseEntity.ok(Map.of( "message", "重建索引任务已提交", "collection", req.getCollection(), "drug_ids", req.getDrugIds(), "status", "pending", "note", "Phase 2 实现:重新 chunk → 向量化 → 写入 pgvector" )); } // ==================== 知识点管理 ==================== @GetMapping("/knowledge-points") public ResponseEntity> listKnowledgePoints( @RequestParam(required = false) String subject, @RequestParam(required = false) String chapterId, @RequestParam(required = false) Integer difficulty, @RequestParam(required = false) String frequency, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "20") int pageSize) { return ResponseEntity.ok(service.listKnowledgePoints(subject, chapterId, difficulty, frequency, page, pageSize)); } @GetMapping("/knowledge-points/{pointId}") public ResponseEntity> getKnowledgePoint(@PathVariable String pointId) { return ResponseEntity.ok(service.getKnowledgePoint(pointId) .>map(kp -> Map.of( "point_id", kp.getPointId(), "subject", kp.getSubject(), "title", kp.getTitle(), "content", kp.getContent() )) .orElse(Map.of("message", "数据入库后可用", "point_id", pointId))); } @PostMapping("/knowledge-points") public ResponseEntity> createKnowledgePoint(@RequestBody KnowledgePointCreateRequest req) { return ResponseEntity.ok(service.createKnowledgePoint(objectToMap(req))); } @PutMapping("/knowledge-points/{pointId}") public ResponseEntity> updateKnowledgePoint(@PathVariable String pointId, @RequestBody KnowledgePointUpdateRequest req) { return ResponseEntity.ok(service.updateKnowledgePoint(pointId, objectToMap(req))); } @DeleteMapping("/knowledge-points/{pointId}") public ResponseEntity> deleteKnowledgePoint(@PathVariable String pointId) { return ResponseEntity.ok(service.deleteKnowledgePoint(pointId)); } @PostMapping("/knowledge-points/import") public ResponseEntity> importKnowledgePoints(@RequestBody ImportRequest req) { int count = parseCount(req); return ResponseEntity.ok(Map.of( "message", "导入任务已提交", "total", count, "success", 0, "failed", 0 )); } // ==================== 题库管理 ==================== @GetMapping("/questions") public ResponseEntity> listQuestions( @RequestParam(required = false) String subject, @RequestParam(required = false) String chapterId, @RequestParam(required = false) String questionType, @RequestParam(required = false) Integer difficulty, @RequestParam(required = false) Boolean audited, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "20") int pageSize) { return ResponseEntity.ok(service.listQuestions(subject, chapterId, questionType, difficulty, audited, page, pageSize)); } @GetMapping("/questions/{questionId}") public ResponseEntity> getQuestion(@PathVariable String questionId) { return ResponseEntity.ok(service.getQuestion(questionId) .>map(q -> Map.of( "question_id", q.getQuestionId(), "content", q.getContent(), "answer", q.getAnswer() )) .orElse(Map.of("message", "数据入库后可用", "question_id", questionId))); } @PostMapping("/questions") public ResponseEntity> createQuestion(@RequestBody QuestionCreateRequest req) { return ResponseEntity.ok(service.createQuestion(objectToMap(req))); } @PutMapping("/questions/{questionId}") public ResponseEntity> updateQuestion(@PathVariable String questionId, @RequestBody QuestionUpdateRequest req) { return ResponseEntity.ok(service.updateQuestion(questionId, objectToMap(req))); } @DeleteMapping("/questions/{questionId}") public ResponseEntity> deleteQuestion(@PathVariable String questionId) { return ResponseEntity.ok(service.deleteQuestion(questionId)); } @PostMapping("/questions/batch-audit") public ResponseEntity> batchAudit(@RequestBody BatchAuditRequest req) { return ResponseEntity.ok(service.batchAudit(req.getQuestionIds(), req.getAudited())); } @PostMapping("/questions/import") public ResponseEntity> importQuestions(@RequestBody ImportRequest req) { int count = parseCount(req); return ResponseEntity.ok(Map.of( "message", "导入任务已提交", "total", count, "success", 0, "failed", 0 )); } @PostMapping("/questions/generate") public ResponseEntity> generateQuestions( @RequestParam String pointId, @RequestParam(defaultValue = "10") int count, @RequestParam(defaultValue = "A") String questionType) { return ResponseEntity.ok(Map.of( "message", "AI 出题任务已提交", "point_id", pointId, "count", count, "question_type", questionType, "status", "pending", "note", "Phase 2 实现:LLM 根据知识点生成题目" )); } // ==================== 快捷提问标签管理(管理端,需要 JWT) ==================== @GetMapping("/quick-asks") public ResponseEntity> listAllQuickAsks() { return ResponseEntity.ok(service.listAllQuickAsks()); } @PostMapping("/quick-asks") public ResponseEntity> createQuickAsk(@RequestBody Map body) { String label = (String) body.getOrDefault("label", ""); String question = (String) body.getOrDefault("question", label); int sortOrder = body.get("sort_order") instanceof Integer i ? i : 0; return ResponseEntity.ok(service.createQuickAsk(label, question, sortOrder)); } @PutMapping("/quick-asks/{id}") public ResponseEntity> updateQuickAsk( @PathVariable int id, @RequestBody Map body) { String label = body.containsKey("label") ? (String) body.get("label") : null; String question = body.containsKey("question") ? (String) body.get("question") : null; Integer sortOrder = body.get("sort_order") instanceof Integer i ? i : null; Boolean isActive = body.get("is_active") instanceof Boolean b ? b : null; return ResponseEntity.ok(service.updateQuickAsk(id, label, question, sortOrder, isActive)); } @DeleteMapping("/quick-asks/{id}") public ResponseEntity> deleteQuickAsk(@PathVariable int id) { return ResponseEntity.ok(service.deleteQuickAsk(id)); } @PutMapping("/quick-asks/reorder") public ResponseEntity> reorderQuickAsks(@RequestBody List> items) { return ResponseEntity.ok(service.reorderQuickAsks(items)); } @GetMapping("/quick-asks/audit-log") public ResponseEntity> listQuickAskAuditLog( @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "20") int pageSize) { return ResponseEntity.ok(service.listQuickAskAuditLog(page, pageSize)); } // ==================== Helpers ==================== private int parseCount(ImportRequest req) { try { if ("json".equals(req.getFormat()) && req.getData() != null) { var items = mapper.readTree(req.getData()); return items.isArray() ? items.size() : 0; } if ("csv".equals(req.getFormat()) && req.getData() != null) { return req.getData().split("\n").length - 1; // rough count } } catch (Exception ignored) {} return 0; } private Map objectToMap(Object obj) { try { @SuppressWarnings("unchecked") Map map = mapper.convertValue(obj, Map.class); return map; } catch (Exception e) { return Map.of(); } } }