tijiao
This commit is contained in:
parent
2d3036ca6c
commit
813ac0a228
|
@ -14,7 +14,6 @@ buildscript {
|
|||
}
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.10")
|
||||
classpath("io.ktor.plugin:plugin:2.3.12")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,148 @@
|
|||
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// 问卷中的所有问题
|
||||
) {
|
||||
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, 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,
|
||||
targetQuestionId: Long
|
||||
) {
|
||||
val sourceOptionSet = questionnaire.getSelectedOption(sourceQuestionId)
|
||||
val targetQuestion = questionnaire.questions.map { it.question }.find { it.id == targetQuestionId }
|
||||
|
||||
if (sourceOptionSet == null || targetQuestion == null) return
|
||||
|
||||
for (sourceOption in sourceOptionSet) {
|
||||
targetQuestion.optionList.forEach { targetOption ->
|
||||
// Remove existing dependencies from the same source question
|
||||
targetOption.dependencies.removeAll { it.sourceQuestionId == sourceQuestionId }
|
||||
|
||||
val dependency = OptionDependency(
|
||||
sourceQuestionId = sourceQuestionId,
|
||||
sourceOptionId = sourceOption.id,
|
||||
targetOption = targetOption,
|
||||
condition = { q ->
|
||||
q.isOptionSelected(sourceQuestionId, sourceOption.id)
|
||||
},
|
||||
effect = { _ ->
|
||||
// Define your effect based on source and target options
|
||||
calculateEffectBasedOnOrder(sourceOption, targetOption)
|
||||
}
|
||||
)
|
||||
targetOption.dependencies.add(dependency)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateEffectBasedOnOrder(
|
||||
sourceOption: QuestionOption,
|
||||
targetOption: QuestionOption
|
||||
): Double {
|
||||
val orderDifference = abs(sourceOption.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 questionId: String,
|
||||
val id: Long,
|
||||
val title: String,
|
||||
val content: String,
|
||||
val optionList: List<QuestionOption>,
|
||||
|
@ -18,51 +150,27 @@ open class Question(
|
|||
val order: Int, // 表示问题顺序
|
||||
val isRequired: Boolean = true // 是否必答,默认为必答
|
||||
) {
|
||||
fun getOptionList(): List<QuestionOption> {
|
||||
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 updateDependenciesOnAnswer(answeredQuestion: SurveyQuestion, selectedOption: QuestionOption) {
|
||||
// 将已回答的问题和选项添加到记录中
|
||||
answeredQuestions[answeredQuestion] = selectedOption
|
||||
|
||||
// 遍历组内未回答的问题,并为每个未回答问题的选项更新依赖关系
|
||||
surveyQuestionList
|
||||
.filter { it != answeredQuestion && !answeredQuestions.containsKey(it) } // 过滤未回答的问题
|
||||
.flatMap { it.getQuestion().getOptionList() } // 将每个问题的选项展开成一个列表
|
||||
.forEach { option ->
|
||||
// 创建一个新的 OptionDependency,根据已回答的选项影响当前选项
|
||||
val dependency = OptionDependency(
|
||||
sourceOption = selectedOption,
|
||||
targetOption = option,
|
||||
condition = { _ ->
|
||||
// 定义条件,sourceOption 和 targetOption 必须同一问题组,且影响关系基于 markOrder 差值
|
||||
true
|
||||
},
|
||||
effect = { targetOpt ->
|
||||
val orderDifference = abs(selectedOption.markOrder - targetOpt.markOrder)
|
||||
when (orderDifference) {
|
||||
0 -> 1.0 // 相同 markOrder,权重最大
|
||||
1 -> 0.7 // 相邻 markOrder,权重较高
|
||||
2 -> 0.4 // 相差2,权重中等
|
||||
else -> 0.1 // 更远的,权重最低
|
||||
}
|
||||
}
|
||||
)
|
||||
// 将新的依赖关系添加到选项的 dependencies 列表中
|
||||
option.dependencies += dependency
|
||||
}
|
||||
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,
|
||||
|
@ -71,22 +179,32 @@ class QuestionOption(
|
|||
var dependencies: MutableList<OptionDependency> = mutableListOf() // 与其他选项的依赖关系
|
||||
) {
|
||||
// 计算依赖关系对权重的影响
|
||||
fun calculateWeightWithDependencies(): Double {
|
||||
var baseWeight = 1.0 // 基础权重
|
||||
fun calculateWeightWithDependencies(questionnaire: Questionnaire): Double {
|
||||
var baseWeight = 1.0
|
||||
dependencies.forEach { dependency ->
|
||||
if (dependency.condition(dependency.sourceOption)) {
|
||||
baseWeight += dependency.effect(dependency.sourceOption)
|
||||
if (dependency.condition(questionnaire)) {
|
||||
baseWeight += dependency.effect(questionnaire)
|
||||
}
|
||||
}
|
||||
return baseWeight.coerceAtLeast(0.0) // 确保权重不小于0
|
||||
return baseWeight.coerceAtLeast(0.0)
|
||||
}
|
||||
|
||||
fun calculateAndMapToWeight(questionnaire: Questionnaire): QuestionOptionWeight {
|
||||
return QuestionOptionWeight(id, calculateWeightWithDependencies(questionnaire))
|
||||
}
|
||||
}
|
||||
|
||||
class QuestionOptionWeight(
|
||||
val optionId: Long,
|
||||
val weight: Double,
|
||||
)
|
||||
|
||||
class OptionDependency(
|
||||
val sourceOption: QuestionOption, // 依赖的来源选项
|
||||
val targetOption: QuestionOption, // 目标选项,受影响的选项
|
||||
val condition: (QuestionOption) -> Boolean, // 判断条件,当 sourceOption 满足某个条件时影响 targetOption
|
||||
val effect: (QuestionOption) -> Double // 定义当条件满足时对目标选项的权重影响
|
||||
val sourceQuestionId: Long,
|
||||
val sourceOptionId: Long,
|
||||
val targetOption: QuestionOption,
|
||||
val condition: (Questionnaire) -> Boolean,
|
||||
val effect: (Questionnaire) -> Double
|
||||
)
|
||||
|
||||
|
||||
|
@ -96,16 +214,15 @@ enum class QuestionType {
|
|||
OPEN_ENDED // 开放式问题
|
||||
}
|
||||
|
||||
enum class Gender {
|
||||
MALE, FEMALE
|
||||
}
|
||||
|
||||
class SurveyQuestion(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
val content: String,
|
||||
val questionId: Long,
|
||||
val question: Question,
|
||||
) {
|
||||
fun getQuestion(): Question {
|
||||
fun getQuestionData(): Question {
|
||||
return this.question
|
||||
}
|
||||
}
|
||||
|
@ -117,8 +234,7 @@ class SurveyBackgroundSample(
|
|||
|
||||
class SurveyRespondent(
|
||||
val name: String,
|
||||
val age: Int,
|
||||
val gender: Gender
|
||||
val age: Int
|
||||
)
|
||||
|
||||
class SurveyBackground(
|
||||
|
@ -126,3 +242,35 @@ class SurveyBackground(
|
|||
val backgroundQuestion: Question
|
||||
)
|
||||
|
||||
class SurveyProcedure(
|
||||
val id: Long,
|
||||
val sourceOptionId: Long,
|
||||
val targetQuestionId: Long,
|
||||
val targetOptionId: Long,
|
||||
val computeMethod: ComputeMethod
|
||||
)
|
||||
|
||||
enum class ComputeMethod(
|
||||
val computeMethodName: String
|
||||
) {
|
||||
BaseComputeMethod("Default") {
|
||||
override fun calculateEffectBasedOnOrder(
|
||||
sourceOption: QuestionOption,
|
||||
targetOption: QuestionOption
|
||||
): Double {
|
||||
val orderDifference = abs(sourceOption.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
|
||||
}
|
Loading…
Reference in New Issue