You've already forked FrameTour-BE
feat(integration): 添加问卷服务集成模块
- 新增问卷服务配置和客户端接口 - 实现问卷创建、查询、提交答案和统计分析等功能 - 添加问卷集成示例,演示各项功能的使用- 设计并实现问卷服务的 fallback 缓存管理策略
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.answer;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AnswerRequest {
|
||||
|
||||
@NotNull(message = "问题ID不能为空")
|
||||
@JsonProperty("questionId")
|
||||
private Long questionId;
|
||||
|
||||
@NotBlank(message = "答案内容不能为空")
|
||||
@JsonProperty("answer")
|
||||
private String answer;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.answer;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ResponseDetailResponse {
|
||||
|
||||
@JsonProperty("id")
|
||||
private Long id;
|
||||
|
||||
@JsonProperty("questionnaireId")
|
||||
private Long questionnaireId;
|
||||
|
||||
@JsonProperty("userId")
|
||||
private String userId;
|
||||
|
||||
@JsonProperty("ipAddress")
|
||||
private String ipAddress;
|
||||
|
||||
@JsonProperty("submittedAt")
|
||||
private String submittedAt;
|
||||
|
||||
@JsonProperty("answers")
|
||||
private List<AnswerDetailResponse> answers;
|
||||
}
|
||||
|
||||
@Data
|
||||
class AnswerDetailResponse {
|
||||
|
||||
@JsonProperty("questionId")
|
||||
private Long questionId;
|
||||
|
||||
@JsonProperty("questionTitle")
|
||||
private String questionTitle;
|
||||
|
||||
@JsonProperty("questionType")
|
||||
private Integer questionType;
|
||||
|
||||
@JsonProperty("answer")
|
||||
private String answer;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.answer;
|
||||
|
||||
import com.ycwl.basic.integration.common.response.PageResponse;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ResponseListResponse extends PageResponse<ResponseDetailResponse> {
|
||||
|
||||
public ResponseListResponse() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.answer;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class SubmitAnswerRequest {
|
||||
|
||||
@NotNull(message = "问卷ID不能为空")
|
||||
@JsonProperty("questionnaireId")
|
||||
private Long questionnaireId;
|
||||
|
||||
@JsonProperty("userId")
|
||||
private String userId; // 可选,用于非匿名问卷
|
||||
|
||||
@NotEmpty(message = "答案不能为空")
|
||||
@Valid
|
||||
@JsonProperty("answers")
|
||||
private List<AnswerRequest> answers;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.question;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CreateQuestionOptionRequest {
|
||||
|
||||
@NotBlank(message = "选项文本不能为空")
|
||||
@Size(max = 500, message = "选项文本长度不能超过500字符")
|
||||
@JsonProperty("text")
|
||||
private String text;
|
||||
|
||||
@NotBlank(message = "选项值不能为空")
|
||||
@Size(max = 100, message = "选项值长度不能超过100字符")
|
||||
@JsonProperty("value")
|
||||
private String value;
|
||||
|
||||
@JsonProperty("sort")
|
||||
private Integer sort = 0;
|
||||
|
||||
public CreateQuestionOptionRequest(String text, String value, Integer sort) {
|
||||
this.text = text;
|
||||
this.value = value;
|
||||
this.sort = sort;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.question;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class CreateQuestionRequest {
|
||||
|
||||
@NotBlank(message = "问题标题不能为空")
|
||||
@Size(max = 500, message = "问题标题长度不能超过500字符")
|
||||
@JsonProperty("title")
|
||||
private String title;
|
||||
|
||||
@NotNull(message = "问题类型不能为空")
|
||||
@Min(value = 1, message = "问题类型无效")
|
||||
@Max(value = 5, message = "问题类型无效")
|
||||
@JsonProperty("type")
|
||||
private Integer type; // 1:单选 2:多选 3:填空 4:文本域 5:评分
|
||||
|
||||
@JsonProperty("isRequired")
|
||||
private Boolean isRequired = false;
|
||||
|
||||
@JsonProperty("sort")
|
||||
private Integer sort = 0;
|
||||
|
||||
@Valid
|
||||
@JsonProperty("options")
|
||||
private List<CreateQuestionOptionRequest> options;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.question;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class QuestionOptionResponse {
|
||||
|
||||
@JsonProperty("id")
|
||||
private Long id;
|
||||
|
||||
@JsonProperty("text")
|
||||
private String text;
|
||||
|
||||
@JsonProperty("value")
|
||||
private String value;
|
||||
|
||||
@JsonProperty("sort")
|
||||
private Integer sort;
|
||||
|
||||
@JsonProperty("createdAt")
|
||||
private String createdAt;
|
||||
|
||||
@JsonProperty("updatedAt")
|
||||
private String updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.question;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class QuestionResponse {
|
||||
|
||||
@JsonProperty("id")
|
||||
private Long id;
|
||||
|
||||
@JsonProperty("title")
|
||||
private String title;
|
||||
|
||||
@JsonProperty("type")
|
||||
private Integer type;
|
||||
|
||||
@JsonProperty("typeText")
|
||||
private String typeText;
|
||||
|
||||
@JsonProperty("isRequired")
|
||||
private Boolean isRequired;
|
||||
|
||||
@JsonProperty("sort")
|
||||
private Integer sort;
|
||||
|
||||
@JsonProperty("createdAt")
|
||||
private String createdAt;
|
||||
|
||||
@JsonProperty("updatedAt")
|
||||
private String updatedAt;
|
||||
|
||||
@JsonProperty("options")
|
||||
private List<QuestionOptionResponse> options;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.questionnaire;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.ycwl.basic.integration.questionnaire.dto.question.CreateQuestionRequest;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class CreateQuestionnaireRequest {
|
||||
|
||||
@NotBlank(message = "问卷名称不能为空")
|
||||
@Size(max = 255, message = "问卷名称长度不能超过255字符")
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("description")
|
||||
private String description;
|
||||
|
||||
@JsonProperty("startTime")
|
||||
private String startTime; // 格式: "2024-01-01 00:00:00"
|
||||
|
||||
@JsonProperty("endTime")
|
||||
private String endTime; // 格式: "2024-12-31 23:59:59"
|
||||
|
||||
@JsonProperty("isAnonymous")
|
||||
private Boolean isAnonymous = true;
|
||||
|
||||
@JsonProperty("maxAnswers")
|
||||
private Integer maxAnswers = 0;
|
||||
|
||||
@Valid
|
||||
@JsonProperty("questions")
|
||||
private List<CreateQuestionRequest> questions;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.questionnaire;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.ycwl.basic.integration.common.response.PageResponse;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class QuestionnaireListResponse extends PageResponse<QuestionnaireResponse> {
|
||||
|
||||
public QuestionnaireListResponse() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.questionnaire;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.ycwl.basic.integration.questionnaire.dto.question.QuestionResponse;
|
||||
import com.ycwl.basic.integration.questionnaire.dto.statistics.QuestionnaireStatistics;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class QuestionnaireResponse {
|
||||
|
||||
@JsonProperty("id")
|
||||
private Long id;
|
||||
|
||||
@JsonProperty("name")
|
||||
private String name;
|
||||
|
||||
@JsonProperty("description")
|
||||
private String description;
|
||||
|
||||
@JsonProperty("status")
|
||||
private Integer status;
|
||||
|
||||
@JsonProperty("statusText")
|
||||
private String statusText;
|
||||
|
||||
@JsonProperty("createdBy")
|
||||
private String createdBy;
|
||||
|
||||
@JsonProperty("startTime")
|
||||
private String startTime;
|
||||
|
||||
@JsonProperty("endTime")
|
||||
private String endTime;
|
||||
|
||||
@JsonProperty("isAnonymous")
|
||||
private Boolean isAnonymous;
|
||||
|
||||
@JsonProperty("maxAnswers")
|
||||
private Integer maxAnswers;
|
||||
|
||||
@JsonProperty("createdAt")
|
||||
private String createdAt;
|
||||
|
||||
@JsonProperty("updatedAt")
|
||||
private String updatedAt;
|
||||
|
||||
@JsonProperty("questions")
|
||||
private List<QuestionResponse> questions;
|
||||
|
||||
@JsonProperty("statistics")
|
||||
private QuestionnaireStatistics statistics;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.ycwl.basic.integration.questionnaire.dto.statistics;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class QuestionnaireStatistics {
|
||||
|
||||
@JsonProperty("totalResponses")
|
||||
private Integer totalResponses;
|
||||
|
||||
@JsonProperty("completionRate")
|
||||
private Double completionRate;
|
||||
|
||||
@JsonProperty("averageTime")
|
||||
private Integer averageTime; // 平均答题时间(秒)
|
||||
|
||||
@JsonProperty("questionStats")
|
||||
private List<QuestionStatistics> questionStats;
|
||||
|
||||
@JsonProperty("responsesByDate")
|
||||
private Map<String, Integer> responsesByDate;
|
||||
|
||||
@JsonProperty("createdAt")
|
||||
private String createdAt;
|
||||
|
||||
@JsonProperty("updatedAt")
|
||||
private String updatedAt;
|
||||
}
|
||||
|
||||
@Data
|
||||
class QuestionStatistics {
|
||||
|
||||
@JsonProperty("questionId")
|
||||
private Long questionId;
|
||||
|
||||
@JsonProperty("questionTitle")
|
||||
private String questionTitle;
|
||||
|
||||
@JsonProperty("questionType")
|
||||
private Integer questionType;
|
||||
|
||||
@JsonProperty("totalAnswers")
|
||||
private Integer totalAnswers;
|
||||
|
||||
@JsonProperty("optionStats")
|
||||
private List<OptionStatistics> optionStats;
|
||||
|
||||
@JsonProperty("textAnswers")
|
||||
private List<String> textAnswers; // 用于填空题和文本域题
|
||||
}
|
||||
|
||||
@Data
|
||||
class OptionStatistics {
|
||||
|
||||
@JsonProperty("optionId")
|
||||
private Long optionId;
|
||||
|
||||
@JsonProperty("optionText")
|
||||
private String optionText;
|
||||
|
||||
@JsonProperty("optionValue")
|
||||
private String optionValue;
|
||||
|
||||
@JsonProperty("count")
|
||||
private Integer count;
|
||||
|
||||
@JsonProperty("percentage")
|
||||
private Double percentage;
|
||||
}
|
||||
Reference in New Issue
Block a user