代码加入保存数据库功能, 创建DLS语句来创建调查问卷
This commit is contained in:
parent
2347120cd3
commit
cebfde04b7
|
@ -12,5 +12,6 @@
|
|||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
<option name="offlineMode" value="true" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.0.10" />
|
||||
<option name="version" value="2.0.20" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,9 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jdk-17.0.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,5 +1,9 @@
|
|||
plugins {
|
||||
kotlin("jvm") version "2.0.10"
|
||||
kotlin("jvm") version "2.0.20"
|
||||
kotlin("plugin.spring") version "2.0.20"
|
||||
kotlin("plugin.serialization") version "2.0.20"
|
||||
id("org.springframework.boot") version "3.3.3"
|
||||
id("io.spring.dependency-management") version "1.1.6"
|
||||
}
|
||||
|
||||
group = "org.echo"
|
||||
|
@ -26,3 +30,23 @@ repositories {
|
|||
maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jdbc")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
|
||||
implementation("org.apache.httpcomponents:httpclient:4.5.14")
|
||||
implementation("com.google.code.gson:gson:2.10.1")
|
||||
|
||||
runtimeOnly("com.mysql:mysql-connector-j")
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform() // 确保使用 JUnit 平台
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package org.echo
|
||||
|
||||
fun main() {
|
||||
println("Hello World!")
|
||||
}
|
|
@ -0,0 +1,737 @@
|
|||
package org.echo.org.echo.questionnaire
|
||||
|
||||
import org.springframework.data.annotation.Id
|
||||
import org.springframework.data.annotation.Transient
|
||||
import org.springframework.data.relational.core.mapping.MappedCollection
|
||||
import org.springframework.data.relational.core.mapping.Table
|
||||
import org.springframework.data.repository.CrudRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.util.Optional
|
||||
import kotlin.math.abs
|
||||
import kotlin.random.Random
|
||||
|
||||
@Table("questionnaire")
|
||||
class Questionnaire(
|
||||
@Id val id: Long?,
|
||||
val title: String, // 问卷的标题
|
||||
val description: String, // 问卷的描述或说明
|
||||
@MappedCollection(idColumn = "questionnaire_id", keyColumn = "questionnaire_key")
|
||||
val questions: List<SurveyQuestion>,
|
||||
@MappedCollection(idColumn = "questionnaire_id", keyColumn = "questionnaire_key")
|
||||
val questionGroup: List<QuestionGroup>,
|
||||
@MappedCollection(idColumn = "questionnaire_id")
|
||||
val surveyBackground: SurveyBackground, // 问卷中的所有问题
|
||||
@MappedCollection(idColumn = "questionnaire_id", keyColumn = "questionnaire_key")
|
||||
val procedures: List<SurveyProcedure>,
|
||||
) {
|
||||
@Transient
|
||||
private val selectedOptions: MutableMap<Long, MutableSet<Long>> = mutableMapOf()// Map of questionId to optionId
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
title: String,
|
||||
description: String,
|
||||
questionGroup: List<QuestionGroup>,
|
||||
questions: List<SurveyQuestion>,
|
||||
surveyBackground: SurveyBackground,
|
||||
procedures: List<SurveyProcedure>
|
||||
): Questionnaire {
|
||||
return Questionnaire(null, title, description, questions, questionGroup, surveyBackground, procedures)
|
||||
}
|
||||
|
||||
fun questionnaire(block: QuestionnaireBuilder.() -> Unit): Questionnaire {
|
||||
val builder = QuestionnaireBuilder()
|
||||
builder.block()
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
fun selectOption(questionId: Long, optionId: Long) {
|
||||
val options = selectedOptions.getOrPut(questionId) { mutableSetOf() }
|
||||
options.add(optionId)
|
||||
onOptionSelected(questionId, optionId)
|
||||
}
|
||||
|
||||
fun isOptionSelected(questionId: Long, optionId: Long): Boolean {
|
||||
return selectedOptions[questionId]?.contains(optionId) == true
|
||||
}
|
||||
|
||||
fun getSelectedOption(questionId: Long): Set<QuestionOption>? {
|
||||
val optionIds = selectedOptions[questionId]
|
||||
return optionIds?.let {
|
||||
questions.map { it.question }
|
||||
.find { it.id == questionId }
|
||||
?.optionList
|
||||
?.filter { it.id in optionIds }
|
||||
?.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
fun onOptionSelected(questionId: Long, optionId: Long) {
|
||||
// Identify target questions influenced by the source question
|
||||
val influencedQuestionIds = getInfluencedQuestionIdsInGroup(questionId)
|
||||
for (targetQuestionId in influencedQuestionIds) {
|
||||
addDependenciesAfterAnswer(this, questionId, optionId, targetQuestionId)
|
||||
}
|
||||
}
|
||||
|
||||
fun getInfluencedQuestionIdsInGroup(questionId: Long): Set<Long> {
|
||||
return this.questionGroup.filter { it.checkIfInGroup(questionId) }
|
||||
.flatMap { it.surveyQuestionList.map { it.question.id!! }.toSet() }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
fun addDependenciesAfterAnswer(
|
||||
questionnaire: Questionnaire,
|
||||
sourceQuestionId: Long,
|
||||
sourceOptionId: Long,
|
||||
targetQuestionId: Long,
|
||||
) {
|
||||
val influencedOptionSet = questionnaire.getSelectedOption(sourceQuestionId)
|
||||
val targetQuestion = questionnaire.questions.map { it.question }.find { it.id == targetQuestionId }
|
||||
|
||||
if (influencedOptionSet == null || targetQuestion == null) return
|
||||
|
||||
for (influencedOption in influencedOptionSet) {
|
||||
targetQuestion.optionList.forEach { targetOption ->
|
||||
// Remove existing dependencies from the same source question
|
||||
val dependency = OptionDependency(
|
||||
sourceQuestionId = sourceQuestionId,
|
||||
sourceOptionId = sourceOptionId,
|
||||
influencedOptionId = influencedOption.id!!,
|
||||
targetOptionId = targetQuestion.id!!,
|
||||
condition = { q ->
|
||||
q.isOptionSelected(sourceQuestionId, sourceOptionId)
|
||||
},
|
||||
effect = { _ ->
|
||||
// 使用枚举类的 calculateEffectBasedOnOrder 方法
|
||||
val effectValue = ComputeMethod.BaseComputeMethod
|
||||
.calculateEffectBasedOnOrder(influencedOption, targetOption)
|
||||
// 根据计算结果进行相应处理
|
||||
println("Effect calculated: $effectValue")
|
||||
effectValue // 返回 effect 值,这个可以用于后续的逻辑
|
||||
}
|
||||
)
|
||||
targetOption.addDependency(dependency)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateEffectBasedOnOrder(
|
||||
influencedOption: QuestionOption,
|
||||
targetOption: QuestionOption, //是他自己
|
||||
): Double {
|
||||
val orderDifference = abs(influencedOption.markOrder - targetOption.markOrder)
|
||||
return when (orderDifference) {
|
||||
0 -> 1.0
|
||||
1 -> 0.7
|
||||
2 -> 0.4
|
||||
else -> 0.1
|
||||
}
|
||||
}
|
||||
|
||||
fun autoFillAnswers() {
|
||||
for (group in questionGroup) {
|
||||
group.surveyQuestionList
|
||||
.map { it.question }
|
||||
.forEach { question -> autoSelectAnswer(question) }
|
||||
}
|
||||
}
|
||||
|
||||
fun autoSelectAnswer(question: Question) {
|
||||
val weightedOptions = question.optionList
|
||||
.map { it.calculateAndMapToWeight(this) }
|
||||
|
||||
// 按照权重随机选择
|
||||
val selectedOption = weightedRandomSelection(weightedOptions)
|
||||
selectOption(question.id!!, selectedOption.optionId)
|
||||
|
||||
// 如果是多选题,随机选择两个选项(前提是至少有两个选项可选)
|
||||
if (question.questionType == QuestionType.MULTIPLE_CHOICE &&
|
||||
weightedOptions.size > 1
|
||||
) {
|
||||
val secondOption =
|
||||
weightedRandomSelection(weightedOptions.filter { it.optionId != selectedOption.optionId })
|
||||
selectOption(question.id, secondOption.optionId)
|
||||
}
|
||||
}
|
||||
|
||||
fun weightedRandomSelection(weightedOptions: List<QuestionOptionWeight>): QuestionOptionWeight {
|
||||
val totalWeight = weightedOptions.sumOf { it.weight }
|
||||
val randomValue = Random.nextDouble() * totalWeight
|
||||
var cumulativeWeight = 0.0
|
||||
for (option in weightedOptions) {
|
||||
cumulativeWeight += option.weight
|
||||
if (randomValue <= cumulativeWeight) {
|
||||
return option
|
||||
}
|
||||
}
|
||||
return weightedOptions.last()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Table("question")
|
||||
open class Question(
|
||||
@Id val id: Long?,
|
||||
val wrapperQuestionId: Long?,
|
||||
val title: String,
|
||||
val content: String,
|
||||
@MappedCollection(idColumn = "question_id", keyColumn = "question_key")
|
||||
val optionList: List<QuestionOption>,
|
||||
val questionType: QuestionType, // 表示问题类型:单选、多选、开放式等
|
||||
val order: Int, // 表示问题顺序
|
||||
val isRequired: Int = 1, // 是否必答,默认为必答
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
title: String,
|
||||
content: String,
|
||||
optionList: List<QuestionOption>,
|
||||
questionType: QuestionType,
|
||||
order: Int,
|
||||
isRequired: Int = 1
|
||||
): Question {
|
||||
return Question(
|
||||
id = null, // `id` 由数据库生成
|
||||
wrapperQuestionId = null,
|
||||
title = title,
|
||||
content = content,
|
||||
optionList = optionList,
|
||||
questionType = questionType,
|
||||
order = order,
|
||||
isRequired = isRequired
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getOptionListData(): List<QuestionOption> {
|
||||
return optionList
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Table("question_group")
|
||||
class QuestionGroup(
|
||||
@Id val id: Long?,
|
||||
val questionnaireId: Long?,
|
||||
val parentId: Long?,
|
||||
val groupName: String,
|
||||
@MappedCollection(idColumn = "question_group_id", keyColumn = "question_group_key")
|
||||
val surveyQuestionList: List<SurveyQuestion>
|
||||
) {
|
||||
@Transient
|
||||
private val answeredQuestions: MutableMap<SurveyQuestion, QuestionOption> = mutableMapOf()
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
parentId: Long?,
|
||||
groupName: String,
|
||||
surveyQuestionList: List<SurveyQuestion>
|
||||
): QuestionGroup {
|
||||
return QuestionGroup(
|
||||
id = null, // `id` 由数据库生成
|
||||
questionnaireId = null, //外键由数据库生成
|
||||
parentId = parentId,
|
||||
groupName = groupName,
|
||||
surveyQuestionList = surveyQuestionList
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkIfInGroup(questionId: Long): Boolean {
|
||||
return surveyQuestionList.map { it.question }.any { it.id == questionId }
|
||||
}
|
||||
}
|
||||
|
||||
@Table("question_option")
|
||||
class QuestionOption(
|
||||
@Id val id: Long?,
|
||||
val questionId: Long?, //外键
|
||||
val label: String,
|
||||
val content: String,
|
||||
val mark: String,
|
||||
val markOrder: Int,
|
||||
val score: Int? = null, // 可选的分数字段
|
||||
) {
|
||||
@Transient
|
||||
private val dependencies: MutableList<OptionDependency> = mutableListOf()
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
label: String,
|
||||
content: String,
|
||||
mark: String,
|
||||
markOrder: Int,
|
||||
score: Int? = null
|
||||
): QuestionOption {
|
||||
return QuestionOption(
|
||||
id = null, // `id` 由数据库生成
|
||||
questionId = null, //外键
|
||||
label = label,
|
||||
content = content,
|
||||
mark = mark,
|
||||
markOrder = markOrder,
|
||||
score = score
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 计算依赖关系对权重的影响
|
||||
fun calculateWeightWithDependencies(questionnaire: Questionnaire): Double {
|
||||
var baseWeight = 1.0
|
||||
dependencies.forEach { dependency ->
|
||||
if (dependency.condition(questionnaire)) {
|
||||
baseWeight += dependency.effect(questionnaire)
|
||||
}
|
||||
}
|
||||
return baseWeight.coerceAtLeast(0.0)
|
||||
}
|
||||
|
||||
fun calculateAndMapToWeight(questionnaire: Questionnaire): QuestionOptionWeight {
|
||||
return QuestionOptionWeight(id!!, calculateWeightWithDependencies(questionnaire))
|
||||
}
|
||||
|
||||
fun addDependency(dependency: OptionDependency) {
|
||||
//添加选项依赖,添加前会删除所有之前 问题的产生的依赖项,然后再进行添加
|
||||
val sourceQuestionId = dependency.sourceQuestionId
|
||||
this.dependencies.removeAll { it.sourceQuestionId == sourceQuestionId }
|
||||
this.dependencies.add(dependency)
|
||||
}
|
||||
}
|
||||
|
||||
class QuestionOptionWeight(
|
||||
val optionId: Long,
|
||||
val weight: Double,
|
||||
)
|
||||
|
||||
class OptionDependency(
|
||||
val sourceQuestionId: Long,
|
||||
val sourceOptionId: Long,
|
||||
val influencedOptionId: Long,
|
||||
val targetOptionId: Long,
|
||||
val condition: (Questionnaire) -> Boolean,
|
||||
val effect: (Questionnaire) -> Double,
|
||||
)
|
||||
|
||||
enum class QuestionType {
|
||||
SINGLE_CHOICE, // 单选
|
||||
MULTIPLE_CHOICE, // 多选
|
||||
OPEN_ENDED // 开放式问题
|
||||
}
|
||||
|
||||
@Table("survey_question")
|
||||
class SurveyQuestion(
|
||||
@Id val id: Long?,
|
||||
val questionnaireId: Long?,
|
||||
val title: String,
|
||||
val content: String,
|
||||
@MappedCollection(idColumn = "wrapper_question_id", keyColumn = "wrapper_question_key")
|
||||
val question: Question,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
title: String,
|
||||
content: String,
|
||||
question: Question
|
||||
): SurveyQuestion {
|
||||
return SurveyQuestion(
|
||||
id = null, // `id` 由数据库生成
|
||||
questionnaireId = null, //外键值由数据库生成
|
||||
title = title,
|
||||
content = content,
|
||||
question = question
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getQuestionData(): Question {
|
||||
return this.question
|
||||
}
|
||||
}
|
||||
|
||||
class SurveyBackgroundSample(
|
||||
val surveyRespondent: SurveyRespondent,
|
||||
val surveyBackgroundList: List<SurveyBackground>,
|
||||
)
|
||||
|
||||
class SurveyRespondent(
|
||||
val name: String,
|
||||
val age: Int,
|
||||
)
|
||||
|
||||
@Table("survey_background")
|
||||
class SurveyBackground(
|
||||
@Id val id: Long?,
|
||||
val questionnaireId: Long?,
|
||||
val title: String,
|
||||
@MappedCollection(idColumn = "wrapper_question_id", keyColumn = "wrapper_question_key")
|
||||
val backgroundQuestion: List<Question>,
|
||||
) {
|
||||
companion object {
|
||||
fun create(
|
||||
title: String,
|
||||
backgroundQuestion: List<Question>
|
||||
): SurveyBackground {
|
||||
return SurveyBackground(
|
||||
id = null, // `id` 由数据库生成
|
||||
questionnaireId = null,
|
||||
title = title,
|
||||
backgroundQuestion = backgroundQuestion
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Table("survey_procedure")
|
||||
class SurveyProcedure(
|
||||
@Id val id: Long?,
|
||||
val questionnaireId: Long?,
|
||||
val sourceQuestionId: Long,
|
||||
val sourceOptionId: Long,
|
||||
val influencedQuestionId: Long,
|
||||
val influencedOptionId: Long,
|
||||
var computeMethodName: String?,
|
||||
@Transient val computeMethod: ComputeMethod = ComputeMethod.BaseComputeMethod
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
sourceQuestionId: Long,
|
||||
sourceOptionId: Long,
|
||||
influencedQuestionId: Long,
|
||||
influencedOptionId: Long,
|
||||
computeMethod: ComputeMethod = ComputeMethod.BaseComputeMethod
|
||||
): SurveyProcedure {
|
||||
val surveyProcedure = SurveyProcedure(
|
||||
id = null, // `id` 由数据库生成
|
||||
questionnaireId = null,
|
||||
sourceQuestionId = sourceQuestionId,
|
||||
sourceOptionId = sourceOptionId,
|
||||
influencedQuestionId = influencedQuestionId,
|
||||
influencedOptionId = influencedOptionId,
|
||||
computeMethodName = null,
|
||||
computeMethod = computeMethod
|
||||
)
|
||||
surveyProcedure.computeMethodName = computeMethod.name
|
||||
return surveyProcedure
|
||||
}
|
||||
}
|
||||
|
||||
fun ifProcess(sourceOptionalId: Long, questionnaire: Questionnaire) {
|
||||
if (sourceOptionalId == sourceOptionId) generateOptionDependency(questionnaire)
|
||||
}
|
||||
|
||||
fun generateOptionDependency(questionnaire: Questionnaire) {
|
||||
val influencedQuestionOptions = questionnaire.questions
|
||||
.filter { it.question.id == influencedQuestionId }
|
||||
.map { it.question }
|
||||
.flatMap { it.optionList }
|
||||
val influencedOption = influencedQuestionOptions
|
||||
.filter { option -> option.id == influencedOptionId }
|
||||
.last()
|
||||
influencedQuestionOptions.forEach {
|
||||
//遍历所有的选项,向其中加入选项依赖
|
||||
val optionDependency = OptionDependency(
|
||||
sourceOptionId, sourceOptionId,
|
||||
influencedOption.id!!,
|
||||
it.id!!,
|
||||
condition = { q ->
|
||||
q.isOptionSelected(sourceQuestionId, sourceOptionId)
|
||||
},
|
||||
effect = { _ ->
|
||||
// Define your effect based on source and target options
|
||||
computeMethod.calculateEffectBasedOnOrder(influencedOption, it)
|
||||
}
|
||||
)
|
||||
it.addDependency(optionDependency)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ComputeMethod(
|
||||
val computeMethodName: String,
|
||||
) {
|
||||
BaseComputeMethod("Default") {
|
||||
override fun calculateEffectBasedOnOrder(
|
||||
influencedOption: QuestionOption,
|
||||
targetOption: QuestionOption,
|
||||
): Double {
|
||||
val orderDifference = abs(influencedOption.markOrder - targetOption.markOrder)
|
||||
return when (orderDifference) {
|
||||
0 -> 1.0
|
||||
1 -> 0.7
|
||||
2 -> 0.4
|
||||
else -> 0.1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 定义抽象方法,子类必须实现
|
||||
abstract fun calculateEffectBasedOnOrder(
|
||||
sourceOption: QuestionOption,
|
||||
targetOption: QuestionOption,
|
||||
): Double
|
||||
}
|
||||
|
||||
|
||||
interface QuestionnaireRepository {
|
||||
fun save(questionnaire: Questionnaire): Questionnaire
|
||||
fun findById(id: Long): Optional<Questionnaire>
|
||||
}
|
||||
|
||||
@Repository
|
||||
class QuestionnaireRepositoryImpl(private val jdbcQuestionnaireRepository: JdbcQuestionnaireRepository) :
|
||||
QuestionnaireRepository {
|
||||
override fun save(questionnaire: Questionnaire): Questionnaire {
|
||||
return jdbcQuestionnaireRepository.save(questionnaire)
|
||||
}
|
||||
|
||||
override fun findById(id: Long): Optional<Questionnaire> {
|
||||
return jdbcQuestionnaireRepository.findById(id)
|
||||
}
|
||||
}
|
||||
|
||||
@Repository
|
||||
interface JdbcQuestionnaireRepository : CrudRepository<Questionnaire, Long>
|
||||
|
||||
@DslMarker
|
||||
annotation class QuestionnaireDsl
|
||||
|
||||
@QuestionnaireDsl
|
||||
class QuestionnaireBuilder {
|
||||
var title: String = ""
|
||||
var description: String = ""
|
||||
private val questions = mutableListOf<SurveyQuestion>()
|
||||
private val questionGroups = mutableListOf<QuestionGroup>()
|
||||
private val procedures = mutableListOf<SurveyProcedure>()
|
||||
lateinit var surveyBackground: SurveyBackground
|
||||
|
||||
fun questionGroup(block: QuestionGroupBuilder.() -> Unit) {
|
||||
val groupBuilder = QuestionGroupBuilder()
|
||||
groupBuilder.block()
|
||||
val group = groupBuilder.build()
|
||||
questionGroups.add(group)
|
||||
questions.addAll(group.surveyQuestionList)
|
||||
}
|
||||
|
||||
fun procedures(block: ProceduresBuilder.() -> Unit) {
|
||||
val proceduresBuilder = ProceduresBuilder()
|
||||
proceduresBuilder.block()
|
||||
procedures.addAll(proceduresBuilder.build())
|
||||
}
|
||||
|
||||
fun background(block: BackgroundBuilder.() -> Unit) {
|
||||
val backgroundBuilder = BackgroundBuilder()
|
||||
backgroundBuilder.block()
|
||||
surveyBackground = backgroundBuilder.build()
|
||||
}
|
||||
|
||||
fun build(): Questionnaire {
|
||||
return Questionnaire.create(
|
||||
title = title,
|
||||
description = description,
|
||||
questionGroup = questionGroups,
|
||||
questions = questions,
|
||||
surveyBackground = surveyBackground,
|
||||
procedures = procedures
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@QuestionnaireDsl
|
||||
class QuestionGroupBuilder {
|
||||
var parentId: Long? = null
|
||||
var groupName: String = ""
|
||||
private val surveyQuestions = mutableListOf<SurveyQuestion>()
|
||||
|
||||
fun surveyQuestion(block: SurveyQuestionBuilder.() -> Unit) {
|
||||
val questionBuilder = SurveyQuestionBuilder()
|
||||
questionBuilder.block()
|
||||
surveyQuestions.add(questionBuilder.build())
|
||||
}
|
||||
|
||||
fun build(): QuestionGroup {
|
||||
return QuestionGroup.create(
|
||||
parentId = parentId,
|
||||
groupName = groupName,
|
||||
surveyQuestionList = surveyQuestions
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@QuestionnaireDsl
|
||||
class SurveyQuestionBuilder {
|
||||
var title: String = ""
|
||||
var content: String = ""
|
||||
lateinit var question: Question
|
||||
|
||||
fun question(block: QuestionBuilder.() -> Unit) {
|
||||
val questionBuilder = QuestionBuilder()
|
||||
questionBuilder.block()
|
||||
question = questionBuilder.build()
|
||||
}
|
||||
|
||||
fun build(): SurveyQuestion {
|
||||
return SurveyQuestion.create(
|
||||
title = title,
|
||||
content = content,
|
||||
question = question
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@QuestionnaireDsl
|
||||
class QuestionBuilder {
|
||||
var title: String = ""
|
||||
var content: String = ""
|
||||
var questionType: QuestionType = QuestionType.SINGLE_CHOICE
|
||||
var order: Int = 0
|
||||
var isRequired: Int = 1
|
||||
private val options = mutableListOf<QuestionOption>()
|
||||
|
||||
fun options(block: OptionsBuilder.() -> Unit) {
|
||||
val optionsBuilder = OptionsBuilder()
|
||||
optionsBuilder.block()
|
||||
options.addAll(optionsBuilder.build())
|
||||
}
|
||||
|
||||
fun build(): Question {
|
||||
return Question.create(
|
||||
title = title,
|
||||
content = content,
|
||||
optionList = options,
|
||||
questionType = questionType,
|
||||
order = order,
|
||||
isRequired = isRequired
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@QuestionnaireDsl
|
||||
class OptionsBuilder {
|
||||
private val options = mutableListOf<QuestionOption>()
|
||||
|
||||
fun option(block: OptionBuilder.() -> Unit) {
|
||||
val optionBuilder = OptionBuilder()
|
||||
optionBuilder.block()
|
||||
options.add(optionBuilder.build())
|
||||
}
|
||||
|
||||
fun build(): List<QuestionOption> = options
|
||||
}
|
||||
|
||||
@QuestionnaireDsl
|
||||
class OptionBuilder {
|
||||
var label: String = ""
|
||||
var content: String = ""
|
||||
var mark: String = ""
|
||||
var markOrder: Int = 0
|
||||
var score: Int? = null
|
||||
|
||||
fun build(): QuestionOption {
|
||||
return QuestionOption.create(
|
||||
label = label,
|
||||
content = content,
|
||||
mark = mark,
|
||||
markOrder = markOrder,
|
||||
score = score
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@QuestionnaireDsl
|
||||
class ProceduresBuilder {
|
||||
private val procedures = mutableListOf<SurveyProcedure>()
|
||||
|
||||
fun procedure(block: ProcedureBuilder.() -> Unit) {
|
||||
val procedureBuilder = ProcedureBuilder()
|
||||
procedureBuilder.block()
|
||||
procedures.add(procedureBuilder.build())
|
||||
}
|
||||
|
||||
fun build(): List<SurveyProcedure> = procedures
|
||||
}
|
||||
|
||||
@QuestionnaireDsl
|
||||
class ProcedureBuilder {
|
||||
var sourceQuestionId: Long = 0L
|
||||
var sourceOptionId: Long = 0L
|
||||
var influencedQuestionId: Long = 0L
|
||||
var influencedOptionId: Long = 0L
|
||||
var computeMethod: ComputeMethod = ComputeMethod.BaseComputeMethod
|
||||
|
||||
fun build(): SurveyProcedure {
|
||||
return SurveyProcedure.create(
|
||||
sourceQuestionId = sourceQuestionId,
|
||||
sourceOptionId = sourceOptionId,
|
||||
influencedQuestionId = influencedQuestionId,
|
||||
influencedOptionId = influencedOptionId,
|
||||
computeMethod = computeMethod
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@QuestionnaireDsl
|
||||
class SampleBuilder {
|
||||
lateinit var surveyRespondent: SurveyRespondent
|
||||
private val backgroundList = mutableListOf<SurveyBackground>()
|
||||
|
||||
fun respondent(block: RespondentBuilder.() -> Unit) {
|
||||
val respondentBuilder = RespondentBuilder()
|
||||
respondentBuilder.block()
|
||||
surveyRespondent = respondentBuilder.build()
|
||||
}
|
||||
|
||||
fun background(block: BackgroundBuilder.() -> Unit) {
|
||||
val backgroundBuilder = BackgroundBuilder()
|
||||
backgroundBuilder.block()
|
||||
backgroundList.add(backgroundBuilder.build())
|
||||
}
|
||||
|
||||
fun build(): SurveyBackgroundSample {
|
||||
return SurveyBackgroundSample(
|
||||
surveyRespondent = surveyRespondent,
|
||||
surveyBackgroundList = backgroundList
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@QuestionnaireDsl
|
||||
class RespondentBuilder {
|
||||
var name: String = ""
|
||||
var age: Int = 0
|
||||
|
||||
fun build(): SurveyRespondent {
|
||||
return SurveyRespondent(name = name, age = age)
|
||||
}
|
||||
}
|
||||
|
||||
@QuestionnaireDsl
|
||||
class BackgroundBuilder {
|
||||
var title: String = ""
|
||||
private val backgroundQuestions = mutableListOf<Question>()
|
||||
|
||||
fun question(block: QuestionBuilder.() -> Unit) {
|
||||
val questionBuilder = QuestionBuilder()
|
||||
questionBuilder.block()
|
||||
backgroundQuestions.add(questionBuilder.build())
|
||||
}
|
||||
|
||||
fun build(): SurveyBackground {
|
||||
return SurveyBackground.create(
|
||||
title = title,
|
||||
backgroundQuestion = backgroundQuestions
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,323 +0,0 @@
|
|||
package org.echo.questionnaire
|
||||
|
||||
import kotlin.math.abs
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
|
||||
class Questionnaire(
|
||||
val id: Long,
|
||||
val title: String, // 问卷的标题
|
||||
val description: String, // 问卷的描述或说明
|
||||
val questions: List<SurveyQuestion>,
|
||||
val questionGroup: List<QuestionGroup>,
|
||||
val sample: SurveyBackgroundSample, // 问卷中的所有问题
|
||||
val procedures: List<SurveyProcedure>,
|
||||
) {
|
||||
private val selectedOptions: MutableMap<Long, MutableSet<Long>> = mutableMapOf()// Map of questionId to optionId
|
||||
|
||||
fun selectOption(questionId: Long, optionId: Long) {
|
||||
val options = selectedOptions.getOrPut(questionId) { mutableSetOf() }
|
||||
options.add(optionId)
|
||||
onOptionSelected(questionId, optionId)
|
||||
}
|
||||
|
||||
fun isOptionSelected(questionId: Long, optionId: Long): Boolean {
|
||||
return selectedOptions[questionId]?.contains(optionId) == true
|
||||
}
|
||||
|
||||
fun getSelectedOption(questionId: Long): Set<QuestionOption>? {
|
||||
val optionIds = selectedOptions[questionId]
|
||||
return optionIds?.let {
|
||||
questions.map { it.question }
|
||||
.find { it.id == questionId }
|
||||
?.optionList
|
||||
?.filter { it.id in optionIds }
|
||||
?.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
fun onOptionSelected(questionId: Long, optionId: Long) {
|
||||
// Identify target questions influenced by the source question
|
||||
val influencedQuestionIds = getInfluencedQuestionIdsInGroup(questionId)
|
||||
for (targetQuestionId in influencedQuestionIds) {
|
||||
addDependenciesAfterAnswer(this, questionId, optionId, targetQuestionId)
|
||||
}
|
||||
|
||||
// Recalculate weights for all options that might be affected
|
||||
// this.questions.map { it.question }.forEach { question ->
|
||||
// question.optionList.forEach { option ->
|
||||
// val weight = option.calculateWeightWithDependencies(this)
|
||||
// // Use the weight as needed, e.g., adjust display order, filter out options, etc.
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fun getInfluencedQuestionIdsInGroup(questionId: Long): Set<Long> {
|
||||
return this.questionGroup.filter { it.checkIfInGroup(questionId) }
|
||||
.flatMap { it.surveyQuestionList.map { it.question.id }.toSet() }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
fun addDependenciesAfterAnswer(
|
||||
questionnaire: Questionnaire,
|
||||
sourceQuestionId: Long,
|
||||
sourceOptionId: Long,
|
||||
targetQuestionId: Long,
|
||||
) {
|
||||
val influencedOptionSet = questionnaire.getSelectedOption(sourceQuestionId)
|
||||
val targetQuestion = questionnaire.questions.map { it.question }.find { it.id == targetQuestionId }
|
||||
|
||||
if (influencedOptionSet == null || targetQuestion == null) return
|
||||
|
||||
for (influencedOption in influencedOptionSet) {
|
||||
targetQuestion.optionList.forEach { targetOption ->
|
||||
// Remove existing dependencies from the same source question
|
||||
val dependency = OptionDependency(
|
||||
sourceQuestionId = sourceQuestionId,
|
||||
sourceOptionId = sourceOptionId,
|
||||
influencedOptionId = influencedOption.id,
|
||||
targetOptionId = targetQuestion.id,
|
||||
condition = { q ->
|
||||
q.isOptionSelected(sourceQuestionId, sourceOptionId)
|
||||
},
|
||||
effect = { _ ->
|
||||
// 使用枚举类的 calculateEffectBasedOnOrder 方法
|
||||
val effectValue = ComputeMethod.BaseComputeMethod
|
||||
.calculateEffectBasedOnOrder(influencedOption, targetOption)
|
||||
// 根据计算结果进行相应处理
|
||||
println("Effect calculated: $effectValue")
|
||||
effectValue // 返回 effect 值,这个可以用于后续的逻辑
|
||||
}
|
||||
)
|
||||
targetOption.addDependency(dependency)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateEffectBasedOnOrder(
|
||||
influencedOption: QuestionOption,
|
||||
targetOption: QuestionOption, //是他自己
|
||||
): Double {
|
||||
val orderDifference = abs(influencedOption.markOrder - targetOption.markOrder)
|
||||
return when (orderDifference) {
|
||||
0 -> 1.0
|
||||
1 -> 0.7
|
||||
2 -> 0.4
|
||||
else -> 0.1
|
||||
}
|
||||
}
|
||||
|
||||
fun autoFillAnswers() {
|
||||
for (group in questionGroup) {
|
||||
group.surveyQuestionList
|
||||
.map { it.question }
|
||||
.forEach { question -> autoSelectAnswer(question) }
|
||||
}
|
||||
}
|
||||
|
||||
fun autoSelectAnswer(question: Question) {
|
||||
val weightedOptions = question.optionList
|
||||
.map { it.calculateAndMapToWeight(this) }
|
||||
|
||||
// 按照权重随机选择
|
||||
val selectedOption = weightedRandomSelection(weightedOptions)
|
||||
selectOption(question.id, selectedOption.optionId)
|
||||
|
||||
// 如果是多选题,随机选择两个选项(前提是至少有两个选项可选)
|
||||
if (question.questionType == QuestionType.MULTIPLE_CHOICE &&
|
||||
weightedOptions.size > 1
|
||||
) {
|
||||
val secondOption =
|
||||
weightedRandomSelection(weightedOptions.filter { it.optionId != selectedOption.optionId })
|
||||
selectOption(question.id, secondOption.optionId)
|
||||
}
|
||||
}
|
||||
|
||||
fun weightedRandomSelection(weightedOptions: List<QuestionOptionWeight>): QuestionOptionWeight {
|
||||
val totalWeight = weightedOptions.sumOf { it.weight }
|
||||
val randomValue = Random.nextDouble() * totalWeight
|
||||
var cumulativeWeight = 0.0
|
||||
for (option in weightedOptions) {
|
||||
cumulativeWeight += option.weight
|
||||
if (randomValue <= cumulativeWeight) {
|
||||
return option
|
||||
}
|
||||
}
|
||||
return weightedOptions.last()
|
||||
}
|
||||
}
|
||||
|
||||
open class Question(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
val content: String,
|
||||
val optionList: List<QuestionOption>,
|
||||
val questionType: QuestionType, // 表示问题类型:单选、多选、开放式等
|
||||
val order: Int, // 表示问题顺序
|
||||
val isRequired: Boolean = true, // 是否必答,默认为必答
|
||||
) {
|
||||
fun getOptionListData(): List<QuestionOption> {
|
||||
return optionList
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class QuestionGroup(
|
||||
val id: Long,
|
||||
val parentId: Long,
|
||||
val groupName: String,
|
||||
val surveyQuestionList: List<SurveyQuestion>,
|
||||
var answeredQuestions: MutableMap<SurveyQuestion, QuestionOption> = mutableMapOf(), // 已回答的问题及其分数
|
||||
) {
|
||||
fun checkIfInGroup(questionId: Long): Boolean {
|
||||
return surveyQuestionList.map { it.question }.any { it.id == questionId }
|
||||
}
|
||||
}
|
||||
|
||||
class QuestionOption(
|
||||
val id: Long,
|
||||
val label: String,
|
||||
val content: String,
|
||||
val mark: String,
|
||||
val markOrder: Int,
|
||||
val score: Int? = null, // 可选的分数字段
|
||||
var dependencies: MutableList<OptionDependency> = mutableListOf(), // 与其他选项的依赖关系
|
||||
) {
|
||||
// 计算依赖关系对权重的影响
|
||||
fun calculateWeightWithDependencies(questionnaire: Questionnaire): Double {
|
||||
var baseWeight = 1.0
|
||||
dependencies.forEach { dependency ->
|
||||
if (dependency.condition(questionnaire)) {
|
||||
baseWeight += dependency.effect(questionnaire)
|
||||
}
|
||||
}
|
||||
return baseWeight.coerceAtLeast(0.0)
|
||||
}
|
||||
|
||||
fun calculateAndMapToWeight(questionnaire: Questionnaire): QuestionOptionWeight {
|
||||
return QuestionOptionWeight(id, calculateWeightWithDependencies(questionnaire))
|
||||
}
|
||||
|
||||
fun addDependency(dependency: OptionDependency) {
|
||||
//添加选项依赖,添加前会删除所有之前 问题的产生的依赖项,然后再进行添加
|
||||
val sourceQuestionId = dependency.sourceQuestionId
|
||||
this.dependencies.removeAll { it.sourceQuestionId == sourceQuestionId }
|
||||
this.dependencies.add(dependency)
|
||||
}
|
||||
}
|
||||
|
||||
class QuestionOptionWeight(
|
||||
val optionId: Long,
|
||||
val weight: Double,
|
||||
)
|
||||
|
||||
class OptionDependency(
|
||||
val sourceQuestionId: Long,
|
||||
val sourceOptionId: Long,
|
||||
val influencedOptionId: Long,
|
||||
val targetOptionId: Long,
|
||||
val condition: (Questionnaire) -> Boolean,
|
||||
val effect: (Questionnaire) -> Double,
|
||||
)
|
||||
|
||||
|
||||
enum class QuestionType {
|
||||
SINGLE_CHOICE, // 单选
|
||||
MULTIPLE_CHOICE, // 多选
|
||||
OPEN_ENDED // 开放式问题
|
||||
}
|
||||
|
||||
|
||||
class SurveyQuestion(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
val content: String,
|
||||
val questionId: Long,
|
||||
val question: Question,
|
||||
) {
|
||||
fun getQuestionData(): Question {
|
||||
return this.question
|
||||
}
|
||||
}
|
||||
|
||||
class SurveyBackgroundSample(
|
||||
val surveyRespondent: SurveyRespondent,
|
||||
val surveyBackgroundList: List<SurveyBackground>,
|
||||
)
|
||||
|
||||
class SurveyRespondent(
|
||||
val name: String,
|
||||
val age: Int,
|
||||
)
|
||||
|
||||
class SurveyBackground(
|
||||
val title: String,
|
||||
val backgroundQuestion: Question,
|
||||
)
|
||||
|
||||
class SurveyProcedure(
|
||||
val id: Long,
|
||||
val sourceQuestionId: Long,
|
||||
val sourceOptionId: Long,
|
||||
val influencedQuestionId: Long,
|
||||
val influencedOptionId: Long,
|
||||
val computeMethod: ComputeMethod = ComputeMethod.BaseComputeMethod,
|
||||
) {
|
||||
|
||||
fun ifProcess(sourceOptionalId: Long, questionnaire: Questionnaire) {
|
||||
if (sourceOptionalId == sourceOptionId) generateOptionDependency(questionnaire)
|
||||
}
|
||||
|
||||
fun generateOptionDependency(questionnaire: Questionnaire) {
|
||||
val influencedQuestionOptions = questionnaire.questions
|
||||
.filter { it.questionId == influencedQuestionId }
|
||||
.map { it.question }
|
||||
.flatMap { it.optionList }
|
||||
val influencedOption = influencedQuestionOptions
|
||||
.filter { option -> option.id == influencedOptionId }
|
||||
.last()
|
||||
influencedQuestionOptions.forEach {
|
||||
//遍历所有的选项,向其中加入选项依赖
|
||||
val optionDependency = OptionDependency(
|
||||
sourceOptionId, sourceOptionId,
|
||||
influencedOption.id,
|
||||
it.id,
|
||||
condition = { q ->
|
||||
q.isOptionSelected(sourceQuestionId, sourceOptionId)
|
||||
},
|
||||
effect = { _ ->
|
||||
// Define your effect based on source and target options
|
||||
computeMethod.calculateEffectBasedOnOrder(influencedOption, it)
|
||||
}
|
||||
)
|
||||
it.addDependency(optionDependency)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ComputeMethod(
|
||||
val computeMethodName: String,
|
||||
) {
|
||||
BaseComputeMethod("Default") {
|
||||
override fun calculateEffectBasedOnOrder(
|
||||
influencedOption: QuestionOption,
|
||||
targetOption: QuestionOption,
|
||||
): Double {
|
||||
val orderDifference = abs(influencedOption.markOrder - targetOption.markOrder)
|
||||
return when (orderDifference) {
|
||||
0 -> 1.0
|
||||
1 -> 0.7
|
||||
2 -> 0.4
|
||||
else -> 0.1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 定义抽象方法,子类必须实现
|
||||
abstract fun calculateEffectBasedOnOrder(
|
||||
sourceOption: QuestionOption,
|
||||
targetOption: QuestionOption,
|
||||
): Double
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
server:
|
||||
port: 8083
|
||||
echo:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
host: 47.97.21.20
|
||||
port: 3306
|
||||
database: questionnaire
|
||||
username: root
|
||||
password: kerowsqw34
|
||||
redis:
|
||||
host: 10.10.103.131
|
||||
port: 6379
|
||||
password: Worktask@Redis2023
|
||||
database: 8
|
||||
|
||||
logging:
|
||||
level:
|
||||
org.springframework.jdbc.core.JdbcTemplate: info
|
||||
org.springframework.jdbc.core.StatementCreatorUtils: info
|
||||
root: info
|
||||
org.springframework.cache: info
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
spring:
|
||||
profiles:
|
||||
active: test
|
||||
datasource:
|
||||
driver-class-name: ${echo.datasource.driver-class-name}
|
||||
url: jdbc:mysql://${echo.datasource.host}:${echo.datasource.port}/${echo.datasource.database}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
|
||||
username: ${echo.datasource.username}
|
||||
password: ${echo.datasource.password}
|
||||
hikari:
|
||||
minimum-idle: 20
|
||||
maximum-pool-size: 60 # 连接池最大连接数
|
||||
idle-timeout: 300000 # 空闲连接超时时间(毫秒)
|
||||
max-lifetime: 1800000 # 连接的最长生命周期(毫秒)
|
||||
connection-timeout: 60000 # 连接超时时间(毫秒)
|
||||
pool-name: 'SpringBootHikariCP' # 连接池名字
|
||||
leak-detection-threshold: 30000 # 连接泄露检测阈值(毫秒)
|
||||
data:
|
||||
redis:
|
||||
host: ${echo.redis.host}
|
||||
port: ${echo.redis.port}
|
||||
password: ${echo.redis.password}
|
||||
database: ${echo.redis.database}
|
||||
aop:
|
||||
auto: true
|
||||
proxy-target-class: true
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package org.echo
|
||||
|
||||
|
||||
import org.echo.org.echo.questionnaire.ComputeMethod
|
||||
import org.echo.org.echo.questionnaire.QuestionType
|
||||
import org.echo.org.echo.questionnaire.Questionnaire
|
||||
import org.echo.org.echo.questionnaire.Questionnaire.Companion.questionnaire
|
||||
import org.echo.org.echo.questionnaire.QuestionnaireRepository
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
|
||||
@SpringBootTest
|
||||
class QuestionnaireApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private lateinit var questionnaireRepository: QuestionnaireRepository
|
||||
|
||||
@Test
|
||||
fun contextLoads() {
|
||||
var exampleQuestionnaire = exampleQuestionnaire()
|
||||
questionnaireRepository.save(exampleQuestionnaire)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun findQuestionnaire(){
|
||||
var findById = questionnaireRepository.findById(16L)
|
||||
println(findById)
|
||||
}
|
||||
}
|
||||
|
||||
fun exampleQuestionnaire(): Questionnaire {
|
||||
return questionnaire {
|
||||
title = "客户满意度调查"
|
||||
description = "一份用于测量客户满意度的调查问卷。"
|
||||
|
||||
background {
|
||||
title = "背景信息"
|
||||
question {
|
||||
title = "您的年龄是多少?"
|
||||
content = "请选择您的年龄范围。"
|
||||
questionType = QuestionType.SINGLE_CHOICE
|
||||
options {
|
||||
option {
|
||||
label = "18-25"
|
||||
content = "18-25 岁"
|
||||
mark = "A"
|
||||
markOrder = 1
|
||||
}
|
||||
option {
|
||||
label = "26-35"
|
||||
content = "26-35 岁"
|
||||
mark = "B"
|
||||
markOrder = 2
|
||||
}
|
||||
// 更多选项...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
questionGroup {
|
||||
groupName = "服务满意度"
|
||||
|
||||
surveyQuestion {
|
||||
title = "您对我们的服务满意吗?"
|
||||
content = "请选择一个选项。"
|
||||
question {
|
||||
title = "您对我们的服务满意吗?"
|
||||
content = "请选择一个选项。"
|
||||
questionType = QuestionType.SINGLE_CHOICE
|
||||
options {
|
||||
option {
|
||||
label = "非常满意"
|
||||
content = "非常满意"
|
||||
mark = "A"
|
||||
markOrder = 1
|
||||
}
|
||||
option {
|
||||
label = "满意"
|
||||
content = "满意"
|
||||
mark = "B"
|
||||
markOrder = 2
|
||||
}
|
||||
// 更多选项...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue