题目架构设计
题目分为单选,多选,判断,简单,四种数据类型,在设计数据的时候,我们去拆分成了题目的主表和其他对应的表来做。
题目主表:
单选表:
新增题目
注意:采取工厂+策略的模式去做扩展,现在有四种题型,未来无论加多少种,我们都可以不用动主流程。
后期会结合es 做题目的查重。为搜索做准备。
工厂 + 策略实现
1.首先在common公共层定义题目类型枚举类
package com.ssm.subject.common.enums;
import lombok.Getter;
/**
* 题目类型枚举
* 1.单选 2.多选 3.判断 4.简答
*/
@Getter
public enum SubjectInfoTypeEnum {
RADIO(1, "单选"),
MULTIPLE(2, "多选"),
JUDGE(3, "判断"),
BRIEF(4, "简答");
private Integer code;
private String desc;
SubjectInfoTypeEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
public static SubjectInfoTypeEnum getByCode(int code) {
for(SubjectInfoTypeEnum subjectInfoTypeEnum : SubjectInfoTypeEnum.values()) {
if(subjectInfoTypeEnum.code == code) {
return subjectInfoTypeEnum;
}
}
return null;
}
}
2.使用策略模式定义题目父类和实现类
父接口中定义规则,实现类中实现每种题目的具体逻辑
package com.ssm.subject.domain.handler.subejct;
import com.ssm.subject.common.enums.SubjectInfoTypeEnum;
import com.ssm.subject.domain.bo.SubjectInfoBO;
public interface SubejctTypeHandler {
/**
* 获取每个实体类对应哪个枚举属性(枚举身份的识别)
* @return
*/
SubjectInfoTypeEnum getHandlerType();
/**
* 每个实体类真正的新增题目方法
* @param subjectInfoBO
*/
void add(SubjectInfoBO subjectInfoBO);
}
package com.ssm.subject.domain.handler.subejct;
import com.ssm.subject.common.enums.SubjectInfoTypeEnum;
import com.ssm.subject.domain.bo.SubjectAnswerBO;
import com.ssm.subject.domain.bo.SubjectInfoBO;
import com.ssm.subject.domain.convert.SubjectRadioConvert;
import com.ssm.subject.infra.basic.entity.SubjectRadio;
import com.ssm.subject.infra.basic.service.SubjectRadioService;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* 单选策略类
*/
@Component
public class RadioTypeHandle implements SubejctTypeHandler {
@Resource
private SubjectRadioService subjectRadioService;
@Override
public SubjectInfoTypeEnum getHandlerType() {
return SubjectInfoTypeEnum.RADIO;
}
@Override
public void add(SubjectInfoBO subjectInfoBO) {
//单选题目的插入
List<SubjectRadio> subjectRadioList = new ArrayList<>(); //一个选项为一条数据,用List存
List<SubjectAnswerBO> optionList = subjectInfoBO.getOptionList();
//为空,直接返回,空集合使用foreach会空指针
if(CollectionUtils.isEmpty(optionList)) {
return;
}
optionList.forEach(subjectAnswerBO -> {
SubjectRadio subjectRadio = SubjectRadioConvert.INSTANCE.subjectAnswerBoToRadio(subjectAnswerBO);
subjectRadio.setSubjectId(subjectInfoBO.getId());
subjectRadioList.add(subjectRadio);
});
subjectRadioService.batchInsert(subjectRadioList);
}
}
package com.ssm.subject.domain.handler.subejct;
import com.ssm.subject.common.enums.SubjectInfoTypeEnum;
import com.ssm.subject.domain.bo.SubjectAnswerBO;
import com.ssm.subject.domain.bo.SubjectInfoBO;
import com.ssm.subject.domain.convert.SubjectMultipleConvert;
import com.ssm.subject.infra.basic.entity.SubjectMultiple;
import com.ssm.subject.infra.basic.service.SubjectMappingService;
import com.ssm.subject.infra.basic.service.SubjectMultipleService;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* 多选策略类
*/
@Component
public class MultipleTypeHandle implements SubejctTypeHandler{
@Resource
private SubjectMultipleService subjectMultipleService;
@Override
public SubjectInfoTypeEnum getHandlerType() {
return SubjectInfoTypeEnum.MULTIPLE;
}
@Override
public void add(SubjectInfoBO subjectInfoBO) {
//多选题目的插入
List<SubjectAnswerBO> optionList = subjectInfoBO.getOptionList();
List<SubjectMultiple> subjectMultipleList = new ArrayList<>();
if(CollectionUtils.isEmpty(optionList)) {
return;
}
optionList.forEach(subjectAnswerBO -> {
SubjectMultiple subjectMultiple = SubjectMultipleConvert.INSTANCE.subjectAnswerBoToMultiple(subjectAnswerBO);
subjectMultiple.setSubjectId(subjectInfoBO.getId());
subjectMultipleList.add(subjectMultiple);
});
subjectMultipleService.batchInsert(subjectMultipleList);
}
}
package com.ssm.subject.domain.handler.subejct;
import com.ssm.subject.common.enums.SubjectInfoTypeEnum;
import com.ssm.subject.domain.bo.SubjectAnswerBO;
import com.ssm.subject.domain.bo.SubjectInfoBO;
import com.ssm.subject.domain.convert.SubjectJudgeConvert;
import com.ssm.subject.infra.basic.entity.SubjectJudge;
import com.ssm.subject.infra.basic.service.SubjectJudgeService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 判断策略类
*/
@Component
public class JudgeTypeHandle implements SubejctTypeHandler{
@Resource
private SubjectJudgeService subjectJudgeService;
@Override
public SubjectInfoTypeEnum getHandlerType() {
return SubjectInfoTypeEnum.JUDGE;
}
@Override
public void add(SubjectInfoBO subjectInfoBO) {
//判断题目的插入(判断题,列表中只有一条是否正确数据)
SubjectAnswerBO subjectAnswerBO = subjectInfoBO.getOptionList().get(0);
SubjectJudge subjectJudge = SubjectJudgeConvert.INSTANCE.subjectAnswerBoToJudge(subjectAnswerBO);
subjectJudge.setSubjectId(subjectInfoBO.getId());
subjectJudgeService.insert(subjectJudge);
}
}
package com.ssm.subject.domain.handler.subejct;
import com.ssm.subject.common.enums.SubjectInfoTypeEnum;
import com.ssm.subject.domain.bo.SubjectInfoBO;
import com.ssm.subject.domain.convert.SubjectBriefConvert;
import com.ssm.subject.infra.basic.entity.SubjectBrief;
import com.ssm.subject.infra.basic.service.SubjectBriefService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 简答策略类
*/
@Component
public class BriefTypeHandle implements SubejctTypeHandler{
@Resource
private SubjectBriefService subjectBriefService;
@Override
public SubjectInfoTypeEnum getHandlerType() {
return SubjectInfoTypeEnum.BRIEF;
}
@Override
public void add(SubjectInfoBO subjectInfoBO) {
//简答题目的插入
SubjectBrief subjectBrief = SubjectBriefConvert.INSTANCE.subjectInfoBoToBrief(subjectInfoBO);
subjectBrief.setSubjectId(subjectInfoBO.getId());
subjectBriefService.insert(subjectBrief);
}
}
3.最后使用工厂模式根据入参时的题目类型来获取不同题目
package com.ssm.subject.domain.handler.subejct;
import com.ssm.subject.common.enums.SubjectInfoTypeEnum;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 题目类型工厂类
*/
@Component
public class SubjectTypeHandlerFactory implements InitializingBean {
@Resource //可获取所有实现类(实现类要被加入到IOC容器)
private List<SubejctTypeHandler> subejctTypeHandlerList;
private Map<SubjectInfoTypeEnum, SubejctTypeHandler> subejctTypeHandlerMap = new HashMap<>();
/**
* 对外提供根据id获取对应的题目策略类
* @param subjectType
* @return
*/
public SubejctTypeHandler getHandler(int subjectType) {
SubjectInfoTypeEnum subjectInfoTypeEnum = SubjectInfoTypeEnum.getByCode(subjectType);
return subejctTypeHandlerMap.get(subjectInfoTypeEnum);
}
/**
* 实现InitializingBean后可重写该方法,实现当前bean初始化完成后执行该方法为handlerMap赋值
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
for (SubejctTypeHandler subjectTypeHandler : subejctTypeHandlerList) {
subejctTypeHandlerMap.put(subjectTypeHandler.getHandlerType(), subjectTypeHandler);
}
}
}
优势
在外部domain层中,不需嵌套大量if进行判断题目类型和每个类型的具体逻辑,解耦,可维护性强
@Override
@Transactional(rollbackFor = Exception.class)
public void add(SubjectInfoBO subjectInfoBO) {
if(log.isInfoEnabled()) {
log.info("SubjectInfoDomainService.add.subjectInfoBO:{}", JSON.toJSONString(subjectInfoBO));
}
//先插入主表info数据
SubjectInfo subjectInfo = SubjectInfoBOConvert.INSTANCE.subjectInfoBoToInfo(subjectInfoBO);
subjectInfoServices.insert(subjectInfo);
//工厂 + 策略设计模式
SubejctTypeHandler handler = subjectTypeHandlerFactory.getHandler(subjectInfo.getSubjectType());
subjectInfoBO.setId(subjectInfo.getId()); //info表开启了主键映射,新增完会把id值赋值到实体类上
handler.add(subjectInfoBO);
//把题目、标签、分类的映射关系同步到mapping表中(每个题目对应的每个分类对应的每个标签都为一条数据,即1个题目在有3个标签且属于两个分类就有6条数据)
List<Long> categoryIds = subjectInfoBO.getCategoryIds();
List<Long> labelIds = subjectInfoBO.getLabelIds();
//分类id和标签id在入口已经判空过了,在domain层肯定有值
List<SubjectMapping> subjectMappingList = new ArrayList<>();
categoryIds.forEach(categoryId -> {
labelIds.forEach(labelId -> {
SubjectMapping subjectMapping = new SubjectMapping();
subjectMapping.setCategoryId(categoryId);
subjectMapping.setLabelId(labelId);
subjectMapping.setSubjectId(subjectInfo.getId());
subjectMappingList.add(subjectMapping);
});
});
subjectMappingService.batchInsert(subjectMappingList);
}