|
|
@@ -0,0 +1,229 @@
|
|
|
+package com.etotem.cfc.controller;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.etotem.cfc.common.Result;
|
|
|
+import com.etotem.cfc.dto.RecommendRequestDTO;
|
|
|
+import com.etotem.cfc.entity.ProblemDomain;
|
|
|
+import com.etotem.cfc.entity.SurveyTemplate;
|
|
|
+import com.etotem.cfc.entity.UserIntent;
|
|
|
+import com.etotem.cfc.mapper.ProblemDomainMapper;
|
|
|
+import com.etotem.cfc.mapper.SurveyTemplateMapper;
|
|
|
+import com.etotem.cfc.mapper.UserIntentMapper;
|
|
|
+import com.etotem.cfc.service.ContentRecommendService;
|
|
|
+import com.fasterxml.jackson.core.type.TypeReference;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import io.swagger.v3.oas.annotations.Operation;
|
|
|
+import io.swagger.v3.oas.annotations.tags.Tag;
|
|
|
+import javax.annotation.Resource;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.web.bind.annotation.PostMapping;
|
|
|
+import org.springframework.web.bind.annotation.RequestAttribute;
|
|
|
+import org.springframework.web.bind.annotation.RequestBody;
|
|
|
+import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
+import org.springframework.web.bind.annotation.RestController;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.LinkedHashSet;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 专项调研 — 改版v1(提交调研 → 汇总 resultTags → 推荐产品 → 存快照 → 推进旅程)
|
|
|
+ */
|
|
|
+@Tag(name = "专项调研", description = "问题域专项调研提交与结果推荐")
|
|
|
+@RestController
|
|
|
+@RequestMapping("/api/problem-survey")
|
|
|
+public class ProblemSurveyController {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private SurveyTemplateMapper surveyTemplateMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ProblemDomainMapper problemDomainMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private UserIntentMapper userIntentMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ContentRecommendService contentRecommendService;
|
|
|
+
|
|
|
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
|
|
+
|
|
|
+ @Operation(summary = "提交专项调研:聚合 resultTags → 推荐产品 → 存快照 → 旅程推进")
|
|
|
+ @PostMapping("/submit")
|
|
|
+ @Transactional
|
|
|
+ public Result<Map<String, Object>> submit(@RequestAttribute("userId") Long userId,
|
|
|
+ @RequestAttribute(value = "familyId", required = false) Long familyId,
|
|
|
+ @RequestBody Map<String, Object> params) {
|
|
|
+ Long surveyTemplateId = params.get("surveyTemplateId") != null
|
|
|
+ ? Long.valueOf(params.get("surveyTemplateId").toString()) : null;
|
|
|
+ String problemDomainCode = params.get("problemDomainCode") != null
|
|
|
+ ? params.get("problemDomainCode").toString() : null;
|
|
|
+ Long memberId = params.get("memberId") != null
|
|
|
+ ? Long.valueOf(params.get("memberId").toString()) : null;
|
|
|
+ Object answersObj = params.get("answers");
|
|
|
+
|
|
|
+ SurveyTemplate template = surveyTemplateId != null ? surveyTemplateMapper.selectById(surveyTemplateId) : null;
|
|
|
+ if (template == null) {
|
|
|
+ return Result.error("问卷模板不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 聚合 resultTags:从题目选项 tag/resultTag + 问题域 contentTags
|
|
|
+ Set<String> resultTags = new LinkedHashSet<>();
|
|
|
+ if (answersObj != null) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> answers = OBJECT_MAPPER.convertValue(answersObj,
|
|
|
+ new TypeReference<Map<String, Object>>() {});
|
|
|
+ resultTags.addAll(aggregateTagsFromAnswers(template.getQuestionsJson(), answers));
|
|
|
+ } catch (Exception ignored) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ProblemDomain domain = null;
|
|
|
+ if (problemDomainCode != null && !problemDomainCode.isEmpty()) {
|
|
|
+ domain = problemDomainMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<ProblemDomain>()
|
|
|
+ .eq(ProblemDomain::getCode, problemDomainCode)
|
|
|
+ .last("LIMIT 1"));
|
|
|
+ if (domain != null && domain.getContentTags() != null) {
|
|
|
+ for (String tag : domain.getContentTags().split(",")) {
|
|
|
+ if (!tag.trim().isEmpty()) {
|
|
|
+ resultTags.add(tag.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 推荐产品(按问卷维度 + 汇总标签)
|
|
|
+ Map<String, Object> recommendData = new HashMap<>();
|
|
|
+ RecommendRequestDTO recReq = new RecommendRequestDTO();
|
|
|
+ recReq.setType("product");
|
|
|
+ recReq.setDimensionCode(template.getDimension());
|
|
|
+ recReq.setChildId(memberId);
|
|
|
+ recReq.setSize(10);
|
|
|
+ try {
|
|
|
+ Map<String, Object> rec = contentRecommendService.recommend(recReq, userId, familyId);
|
|
|
+ recommendData = rec;
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 推荐失败不阻断提交
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 存快照到 user_intents(单用户唯一,up-sert)
|
|
|
+ UserIntent intent = userIntentMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<UserIntent>()
|
|
|
+ .eq(UserIntent::getUserId, userId)
|
|
|
+ .last("LIMIT 1"));
|
|
|
+ Date now = new Date();
|
|
|
+ Map<String, Object> snapshot = new HashMap<>();
|
|
|
+ snapshot.put("surveyTemplateId", template.getId());
|
|
|
+ snapshot.put("surveyTitle", template.getTitle());
|
|
|
+ snapshot.put("dimension", template.getDimension());
|
|
|
+ snapshot.put("resultTags", new ArrayList<>(resultTags));
|
|
|
+ snapshot.put("answers", answersObj);
|
|
|
+ snapshot.put("submittedAt", now);
|
|
|
+
|
|
|
+ if (intent == null) {
|
|
|
+ intent = new UserIntent();
|
|
|
+ intent.setUserId(userId);
|
|
|
+ intent.setIntentType(domain != null ? "C" : "B");
|
|
|
+ intent.setProblemDomainCode(problemDomainCode != null ? problemDomainCode : "");
|
|
|
+ intent.setJourneyStage(3);
|
|
|
+ intent.setStageUpdatedAt(now);
|
|
|
+ intent.setSurveyTemplateId(template.getId());
|
|
|
+ intent.setSurveySubmittedAt(now);
|
|
|
+ intent.setSurveyResultJson(toJson(snapshot));
|
|
|
+ intent.setCreatedAt(now);
|
|
|
+ intent.setUpdatedAt(now);
|
|
|
+ userIntentMapper.insert(intent);
|
|
|
+ } else {
|
|
|
+ intent.setSurveyTemplateId(template.getId());
|
|
|
+ intent.setSurveySubmittedAt(now);
|
|
|
+ intent.setSurveyResultJson(toJson(snapshot));
|
|
|
+ // 调研完成推进到阶段3
|
|
|
+ if (intent.getJourneyStage() == null || intent.getJourneyStage() < 3) {
|
|
|
+ intent.setJourneyStage(3);
|
|
|
+ intent.setStageUpdatedAt(now);
|
|
|
+ }
|
|
|
+ intent.setUpdatedAt(now);
|
|
|
+ userIntentMapper.updateById(intent);
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("resultTags", new ArrayList<>(resultTags));
|
|
|
+ data.put("recommendations", recommendData.get("recommendations") != null
|
|
|
+ ? recommendData.get("recommendations") : new ArrayList<>());
|
|
|
+ data.put("snapshot", snapshot);
|
|
|
+ data.put("journeyStage", intent.getJourneyStage());
|
|
|
+ return Result.success(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 从题目选项聚合 resultTags(兼容 questionsJson 不同结构) */
|
|
|
+ private Set<String> aggregateTagsFromAnswers(String questionsJson, Map<String, Object> answers) {
|
|
|
+ Set<String> tags = new LinkedHashSet<>();
|
|
|
+ if (questionsJson == null || questionsJson.isEmpty()) {
|
|
|
+ return tags;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ List<Map<String, Object>> questions = new ArrayList<>();
|
|
|
+ Object parsed = OBJECT_MAPPER.readValue(questionsJson, Object.class);
|
|
|
+ if (parsed instanceof List) {
|
|
|
+ questions = OBJECT_MAPPER.convertValue(parsed,
|
|
|
+ new TypeReference<List<Map<String, Object>>>() {});
|
|
|
+ } else if (parsed instanceof Map) {
|
|
|
+ Object qArr = ((Map<?, ?>) parsed).get("questions");
|
|
|
+ if (qArr instanceof List) {
|
|
|
+ questions = OBJECT_MAPPER.convertValue(qArr,
|
|
|
+ new TypeReference<List<Map<String, Object>>>() {});
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (Map<String, Object> question : questions) {
|
|
|
+ Object qId = question.get("id") != null ? question.get("id") : question.get("questionId");
|
|
|
+ Object answer = qId != null ? answers.get(String.valueOf(qId)) : null;
|
|
|
+ if (answer == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String answerStr = String.valueOf(answer);
|
|
|
+ // 多选数组答案
|
|
|
+ if (answer instanceof List) {
|
|
|
+ for (Object a : (List<?>) answer) {
|
|
|
+ collectOptionTag(question, String.valueOf(a), tags);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ collectOptionTag(question, answerStr, tags);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception ignored) {
|
|
|
+ }
|
|
|
+ return tags;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void collectOptionTag(Map<String, Object> question, String answerValue, Set<String> tags) {
|
|
|
+ Object optionsObj = question.get("options");
|
|
|
+ if (!(optionsObj instanceof List)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (Object o : (List<?>) optionsObj) {
|
|
|
+ if (!(o instanceof Map)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Map<?, ?> option = (Map<?, ?>) o;
|
|
|
+ Object optId = option.get("id") != null ? option.get("id") : option.get("value");
|
|
|
+ if (optId != null && String.valueOf(optId).equals(answerValue)) {
|
|
|
+ Object tag = option.get("tag") != null ? option.get("tag") : option.get("resultTag");
|
|
|
+ if (tag != null) {
|
|
|
+ tags.add(String.valueOf(tag));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String toJson(Object obj) {
|
|
|
+ try {
|
|
|
+ return OBJECT_MAPPER.writeValueAsString(obj);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return "{}";
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|