dev-tmp1
luotaiqian 2026-08-19 15:16:41 +08:00
parent 8833a1d575
commit e71a2b774b
10 changed files with 284 additions and 108 deletions

View File

@ -1,22 +1,15 @@
package org.qinan.cedu.controller; package org.qinan.cedu.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.qinan.cedu.model.dto.PaperQuestionPageQueryDTO;
import org.qinan.cedu.model.dto.QuestionUpdateDTO; import org.qinan.cedu.model.dto.QuestionUpdateDTO;
import org.qinan.cedu.model.vo.PaperQuestionVO; import org.qinan.cedu.model.vo.QuestionVO;
import org.qinan.cedu.service.QuestionService; import org.qinan.cedu.service.QuestionService;
import org.qinan.safetyeval.client.dto.PageResponse;
import org.qinan.safetyeval.client.dto.SingleResponse; import org.qinan.safetyeval.client.dto.SingleResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/** /**
* *
@ -36,4 +29,11 @@ public class QuestionController {
return SingleResponse.success(); return SingleResponse.success();
} }
@ApiOperation("查询习题")
@GetMapping("/find")
public SingleResponse<QuestionVO> find(@RequestParam @ApiParam(value = "试题id", readOnly = true) Long questionId) {
QuestionVO questionVO = questionService.find(questionId);
return SingleResponse.success(questionVO);
}
} }

View File

@ -0,0 +1,39 @@
package org.qinan.cedu.convertor;
import org.mapstruct.Mapper;
import org.qinan.cedu.model.dto.QuestionOptionUpdateDTO;
import org.qinan.cedu.model.dto.QuestionUpdateDTO;
import org.qinan.cedu.model.entity.QuestionDO;
import org.qinan.cedu.model.entity.QuestionOptionDO;
import org.qinan.cedu.model.vo.QuestionOptionVO;
import org.qinan.cedu.model.vo.QuestionVO;
import java.util.List;
@Mapper(componentModel = "spring")
public interface QuestionConvertor {
QuestionVO convertEToVo(QuestionDO entity);
List<QuestionVO> convertEListToVoList(List<QuestionDO> list);
/**
*
*
* @param dto
* @return
*/
QuestionDO convertDtoToE(QuestionUpdateDTO dto);
/**
* DTO
*
* @param list DTO
* @return
*/
List<QuestionOptionDO> convertOptionDtoListToEList(List<QuestionOptionUpdateDTO> list);
QuestionOptionVO convertOptionEToVo(QuestionOptionDO entity);
List<QuestionOptionVO> convertOptionEListToVoList(List<QuestionOptionDO> list);
}

View File

@ -8,6 +8,7 @@ import org.apache.ibatis.annotations.Param;
import org.qinan.cedu.model.dto.PaperQuestionPageQueryDTO; import org.qinan.cedu.model.dto.PaperQuestionPageQueryDTO;
import org.qinan.cedu.model.entity.QuestionDO; import org.qinan.cedu.model.entity.QuestionDO;
import org.qinan.cedu.model.vo.PaperQuestionVO; import org.qinan.cedu.model.vo.PaperQuestionVO;
import org.qinan.cedu.model.vo.QuestionVO;
/** /**
* Mapper * Mapper
@ -23,4 +24,12 @@ public interface QuestionMapper extends BaseMapper<QuestionDO> {
* @return * @return
*/ */
IPage<PaperQuestionVO> pagePaperQuestions(Page<PaperQuestionVO> page, @Param("query") PaperQuestionPageQueryDTO query); IPage<PaperQuestionVO> pagePaperQuestions(Page<PaperQuestionVO> page, @Param("query") PaperQuestionPageQueryDTO query);
/**
*
*
* @param questionId id
* @return
*/
QuestionVO findQuestion(@Param("questionId") Long questionId);
} }

View File

@ -0,0 +1,33 @@
package org.qinan.cedu.model.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* idid
*/
@Data
@ApiModel(value = "QuestionOptionUpdateDTO", description = "试题选项编辑内容")
public class QuestionOptionUpdateDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "选项ID为空则新增")
private Long id;
@ApiModelProperty(value = "是否正确答案 true 是,false 不是", required = true)
@NotBlank(message = "是否正确答案不能为空")
private Boolean isCorrect;
@ApiModelProperty(value = "选项标识A,B,C,D", required = true)
@NotBlank(message = "选项标识不能为空")
private String optionKey;
@ApiModelProperty(value = "选项内容", required = true)
@NotBlank(message = "选项内容不能为空")
private String optionText;
}

View File

@ -4,17 +4,14 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.DecimalMax; import javax.validation.Valid;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
/** /**
* * QuestionVO
*/ */
@Data @Data
@ApiModel(value = "QuestionUpdateDTO", description = "编辑习题请求") @ApiModel(value = "QuestionUpdateDTO", description = "编辑习题请求")
@ -24,40 +21,29 @@ public class QuestionUpdateDTO {
@NotNull(message = "试题id不能为空") @NotNull(message = "试题id不能为空")
private Long id; private Long id;
@ApiModelProperty(value = "题", required = true) @ApiModelProperty(value = "题目内容", required = true)
@NotBlank(message = "题干不能为空") @NotBlank(message = "题干不能为空")
private String title; private String title;
@ApiModelProperty(value = "分值", required = true) @ApiModelProperty(value = "分值", required = true)
@NotNull(message = "分值不能为空") @NotNull(message = "分值不能为空")
@DecimalMin(value = "0.01", message = "分值必须大于0") @DecimalMin(value = "0.1", message = "分值必须大于0")
@DecimalMax(value = "999.99", message = "分值不能超过999.99") @DecimalMax(value = "999.9", message = "分值不能超过999.9")
@Digits(integer = 4, fraction = 1)
private BigDecimal score; private BigDecimal score;
@ApiModelProperty(value = "选项A内容", required = true)
@NotBlank(message = "选项A不能为空")
private String optionA;
@ApiModelProperty(value = "选项B内容", required = true)
@NotBlank(message = "选项B不能为空")
private String optionB;
@ApiModelProperty(value = "选项C内容")
@Size(max = 64, message = "选项C长度不能超过64个字符")
private String optionC;
@ApiModelProperty(value = "选项D内容")
@Size(max = 64, message = "选项D长度不能超过64个字符")
private String optionD;
@ApiModelProperty(value = "答案单选填写A多选填写A,B", required = true)
@NotEmpty(message = "答案不能为空")
private List<String> answer;
@ApiModelProperty(value = "关联课件id courseware_management_id", required = true)
@NotNull(message = "关联课件不能为空")
private Long coursewareManagementId;
@ApiModelProperty(value = "答案解析") @ApiModelProperty(value = "答案解析")
private String analysis; private String analysis;
@ApiModelProperty(value = "标签类型")
private String tagType;
@ApiModelProperty(value = "关联课件管理id", required = true)
@NotNull(message = "关联课件不能为空")
private Long coursewareManagementId;
@ApiModelProperty(value = "试题选项信息", required = true)
@NotEmpty(message = "试题选项不能为空")
@Valid
private List<QuestionOptionUpdateDTO> questionOptionList;
} }

View File

@ -0,0 +1,34 @@
package org.qinan.cedu.model.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
*
*/
@Data
@ApiModel(value = "QuestionOption 试题选项", description = "试题选项")
public class QuestionOptionVO {
@ApiModelProperty("选项ID")
private Long id;
@ApiModelProperty("所属试题ID")
private Long questionId;
@ApiModelProperty("选项标识A,B,C,D,E,F")
private String optionKey;
@ApiModelProperty("选项标志名称")
private String optionName;
@ApiModelProperty("选项内容")
private String optionText;
@ApiModelProperty("是否正确答案1是0否")
private Boolean isCorrect;
@ApiModelProperty("选项显示顺序")
private Integer sortOrder;
}

View File

@ -1,18 +1,30 @@
package org.qinan.cedu.model.vo; package org.qinan.cedu.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.qinan.cedu.model.entity.QuestionOptionDO;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/** /**
* *
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) public class QuestionVO {
public class QuestionVO extends BaseVO {
@ApiModelProperty("主键")
private String id;
@ApiModelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@ApiModelProperty("题目内容") @ApiModelProperty("题目内容")
private String title; private String title;
@ -40,4 +52,10 @@ public class QuestionVO extends BaseVO {
@ApiModelProperty("关联课件管理id") @ApiModelProperty("关联课件管理id")
private Long coursewareManagementId; private Long coursewareManagementId;
@ApiModelProperty("关联课件管理名称")
private String coursewareManagementName;
@ApiModelProperty("试题选项信息")
private List<QuestionOptionVO> questionOptionVOList;
} }

View File

@ -7,6 +7,7 @@ import org.qinan.cedu.model.dto.QuestionUpdateDTO;
import org.qinan.cedu.model.entity.QuestionDO; import org.qinan.cedu.model.entity.QuestionDO;
import org.qinan.cedu.model.entity.QuestionOptionDO; import org.qinan.cedu.model.entity.QuestionOptionDO;
import org.qinan.cedu.model.vo.PaperQuestionVO; import org.qinan.cedu.model.vo.PaperQuestionVO;
import org.qinan.cedu.model.vo.QuestionVO;
import java.util.List; import java.util.List;
@ -22,6 +23,14 @@ public interface QuestionService extends IService<QuestionDO> {
*/ */
void updateQuestion(QuestionUpdateDTO dto); void updateQuestion(QuestionUpdateDTO dto);
/**
*
*
* @param questionId id
* @return
*/
QuestionVO find(Long questionId);
/** /**
* XML * XML
* *

View File

@ -7,16 +7,19 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.qinan.cedu.convertor.QuestionConvertor;
import org.qinan.cedu.enums.DeleteEnum; import org.qinan.cedu.enums.DeleteEnum;
import org.qinan.cedu.enums.QuestionTypeEnum;
import org.qinan.cedu.mapper.QuestionMapper; import org.qinan.cedu.mapper.QuestionMapper;
import org.qinan.cedu.model.dto.PaperQuestionPageQueryDTO; import org.qinan.cedu.model.dto.PaperQuestionPageQueryDTO;
import org.qinan.cedu.model.dto.QuestionOptionUpdateDTO;
import org.qinan.cedu.model.dto.QuestionUpdateDTO; import org.qinan.cedu.model.dto.QuestionUpdateDTO;
import org.qinan.cedu.model.entity.QuestionDO; import org.qinan.cedu.model.entity.QuestionDO;
import org.qinan.cedu.model.entity.QuestionOptionDO; import org.qinan.cedu.model.entity.QuestionOptionDO;
import org.qinan.cedu.model.vo.PaperQuestionVO; import org.qinan.cedu.model.vo.PaperQuestionVO;
import org.qinan.cedu.model.vo.QuestionVO;
import org.qinan.cedu.service.QuestionOptionService; import org.qinan.cedu.service.QuestionOptionService;
import org.qinan.cedu.service.QuestionService; import org.qinan.cedu.service.QuestionService;
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -25,9 +28,8 @@ import org.springframework.util.StringUtils;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -41,11 +43,33 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
@Autowired @Autowired
private QuestionOptionService questionOptionService; private QuestionOptionService questionOptionService;
private final QuestionConvertor questionConvertor;
@Override @Override
public IPage<PaperQuestionVO> pagePaperQuestions(PaperQuestionPageQueryDTO query) { public IPage<PaperQuestionVO> pagePaperQuestions(PaperQuestionPageQueryDTO query) {
return this.baseMapper.pagePaperQuestions(new Page<>(query.getCurrent(), query.getSize()), query); return this.baseMapper.pagePaperQuestions(new Page<>(query.getCurrent(), query.getSize()), query);
} }
@Override
public QuestionVO find(Long questionId) {
QuestionVO questionVO = this.baseMapper.findQuestion(questionId);
if (questionVO == null) {
throw new IllegalArgumentException("试题不存在id=" + questionId);
}
// 查询选项信息仅查询VO需要的字段
List<QuestionOptionDO> options = questionOptionService.lambdaQuery()
.select(QuestionOptionDO::getId, QuestionOptionDO::getQuestionId,
QuestionOptionDO::getOptionKey, QuestionOptionDO::getOptionName,
QuestionOptionDO::getOptionText, QuestionOptionDO::getIsCorrect,
QuestionOptionDO::getSortOrder)
.eq(QuestionOptionDO::getQuestionId, questionId)
.eq(QuestionOptionDO::getDeleteEnum, DeleteEnum.FALSE.getCode())
.orderByAsc(QuestionOptionDO::getSortOrder)
.list();
questionVO.setQuestionOptionVOList(questionConvertor.convertOptionEListToVoList(options));
return questionVO;
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateQuestion(QuestionUpdateDTO dto) { public void updateQuestion(QuestionUpdateDTO dto) {
@ -58,75 +82,84 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
throw new IllegalArgumentException("试题不存在id=" + dto.getId()); throw new IllegalArgumentException("试题不存在id=" + dto.getId());
} }
// 组装选项按A-D空白列不生成选项 // 更新试题基本信息MapStruct转换答案归一化后回填
Map<String, String> optionTexts = new LinkedHashMap<>(); QuestionDO question = questionConvertor.convertDtoToE(dto);
optionTexts.put("A", dto.getOptionA().trim()); question.setAnswer(dto.getQuestionOptionList().stream()
optionTexts.put("B", dto.getOptionB().trim()); .filter(option -> Boolean.TRUE.equals(option.getIsCorrect()))
putIfHasText(optionTexts, "C", dto.getOptionC()); .map(QuestionOptionUpdateDTO::getOptionKey)
putIfHasText(optionTexts, "D", dto.getOptionD()); .collect(Collectors.joining(",")));
question.setUpdateTime(LocalDateTime.now());
// 校验答案必须能找到对应的选项 // 同步选项id在库的更新、无id的新增、多余的逻辑删除
List<String> answers = dto.getAnswer().stream() List<QuestionOptionDO> options = syncOptions(dto.getId(), dto.getQuestionOptionList());
.map(String::trim)
.map(String::toUpperCase)
.distinct()
.sorted()
.collect(Collectors.toList());
List<String> notMatched = answers.stream()
.filter(answer -> !optionTexts.containsKey(answer))
.collect(Collectors.toList());
if (!notMatched.isEmpty()) {
throw new IllegalArgumentException("答案[" + String.join(",", notMatched) + "]找不到对应的选项");
}
boolean single = QuestionTypeEnum.SINGLE.getCode().equals(exist.getQuestionType());
if (single && answers.size() != 1) {
throw new IllegalArgumentException("单选题答案只能有一个");
}
// 更新试题选项全量覆盖重建id预置雪花id
LocalDateTime now = LocalDateTime.now();
QuestionDO question = new QuestionDO();
question.setId(dto.getId());
question.setTitle(dto.getTitle().trim());
question.setAnswer(String.join(",", answers));
question.setScore(dto.getScore());
question.setAnalysis(StringUtils.hasText(dto.getAnalysis()) ? dto.getAnalysis().trim() : null);
question.setCoursewareManagementId(dto.getCoursewareManagementId());
question.setUpdateTime(now);
List<QuestionOptionDO> options = new ArrayList<>();
int sortOrder = 1;
for (Map.Entry<String, String> entry : optionTexts.entrySet()) {
QuestionOptionDO option = new QuestionOptionDO();
option.setId(IdUtil.getSnowflakeNextId());
option.setQuestionId(dto.getId());
option.setOptionKey(entry.getKey());
option.setOptionName(entry.getKey());
option.setOptionText(entry.getValue());
option.setIsCorrect(answers.contains(entry.getKey()));
option.setSortOrder(sortOrder++);
option.setCreateTime(now);
option.setUpdateTime(now);
options.add(option);
}
question.setAnswerQuestionOptionIds(options.stream() question.setAnswerQuestionOptionIds(options.stream()
.filter(option -> Boolean.TRUE.equals(option.getIsCorrect())) .filter(option -> Boolean.TRUE.equals(option.getIsCorrect()))
.map(option -> String.valueOf(option.getId())) .map(option -> String.valueOf(option.getId()))
.collect(Collectors.joining(","))); .collect(Collectors.joining(",")));
InsertFieldDefaults.applyForUpdate(question);
boolean updated = this.updateById(question); boolean updated = this.updateById(question);
if (!updated) { if (!updated) {
throw new IllegalArgumentException("编辑习题失败id=" + dto.getId()); throw new IllegalArgumentException("编辑习题失败id=" + dto.getId());
} }
questionOptionService.remove(Wrappers.<QuestionOptionDO>lambdaQuery()
.eq(QuestionOptionDO::getQuestionId, dto.getId()));
questionOptionService.saveBatch(options);
} }
private void putIfHasText(Map<String, String> optionTexts, String key, String value) {
if (StringUtils.hasText(value)) { /**
optionTexts.put(key, value.trim()); * ididid
*
* @param questionId id
* @param optionDTOs
* @return id
*/
private List<QuestionOptionDO> syncOptions(Long questionId, List<QuestionOptionUpdateDTO> optionDTOs) {
// 查询该试题现存未删除的选项id
Set<Long> existingIds = questionOptionService.lambdaQuery()
.select(QuestionOptionDO::getId)
.eq(QuestionOptionDO::getQuestionId, questionId)
.eq(QuestionOptionDO::getDeleteEnum, DeleteEnum.FALSE.getCode())
.list()
.stream()
.map(QuestionOptionDO::getId)
.collect(Collectors.toSet());
// 组装选项归一化标识、标记正确答案按id是否在库分为更新与新增两组
LocalDateTime now = LocalDateTime.now();
List<QuestionOptionDO> options = questionConvertor.convertOptionDtoListToEList(optionDTOs);
List<QuestionOptionDO> toUpdate = new ArrayList<>();
List<QuestionOptionDO> toInsert = new ArrayList<>();
int sortOrder = 1;
for (QuestionOptionDO option : options) {
String key = option.getOptionKey().trim().toUpperCase();
option.setQuestionId(questionId);
option.setOptionKey(key);
option.setOptionName(key);
option.setSortOrder(sortOrder++);
if (option.getId() != null && existingIds.contains(option.getId())) {
toUpdate.add(option);
InsertFieldDefaults.applyForUpdate(option);
} else {
option.setId(IdUtil.getSnowflakeNextId());
InsertFieldDefaults.applyIgnoreOrgId(option);
toInsert.add(option);
}
} }
// 库中多余(未出现在本次请求中)的选项逻辑删除
Set<Long> keepIds = toUpdate.stream().map(QuestionOptionDO::getId).collect(Collectors.toSet());
List<Long> toDeleteIds = existingIds.stream()
.filter(id -> !keepIds.contains(id))
.collect(Collectors.toList());
if (!toDeleteIds.isEmpty()) {
questionOptionService.lambdaUpdate()
.set(QuestionOptionDO::getDeleteEnum, DeleteEnum.TRUE.getCode())
.set(QuestionOptionDO::getUpdateTime, now)
.in(QuestionOptionDO::getId, toDeleteIds)
.update();
}
questionOptionService.updateBatchById(toUpdate);
questionOptionService.saveBatch(toInsert);
return options;
} }
@Override @Override
@ -200,7 +233,11 @@ public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, QuestionDO>
} }
/** /**
* * idid
*
* @param questionId id
* @param options
* @param now /
*/ */
private void saveOptions(Long questionId, List<QuestionOptionDO> options, LocalDateTime now) { private void saveOptions(Long questionId, List<QuestionOptionDO> options, LocalDateTime now) {
if (CollectionUtils.isEmpty(options)) { if (CollectionUtils.isEmpty(options)) {

View File

@ -10,8 +10,8 @@
JOIN question q ON pqr.question_id = q.id JOIN question q ON pqr.question_id = q.id
LEFT JOIN courseware_management cm ON cm.id = q.courseware_management_id LEFT JOIN courseware_management cm ON cm.id = q.courseware_management_id
WHERE pqr.paper_id = #{query.paperId} WHERE pqr.paper_id = #{query.paperId}
AND pqr.delete_enum = 0 AND pqr.delete_enum = 'false'
AND q.delete_enum = 0 AND q.delete_enum = 'false'
<if test="query.title != null and query.title != ''"> <if test="query.title != null and query.title != ''">
AND q.title LIKE CONCAT('%', #{query.title}, '%') AND q.title LIKE CONCAT('%', #{query.title}, '%')
</if> </if>
@ -20,4 +20,15 @@
</if> </if>
ORDER BY pqr.question_num ASC ORDER BY pqr.question_num ASC
</select> </select>
<select id="findQuestion" resultType="org.qinan.cedu.model.vo.QuestionVO">
SELECT q.id, q.create_time, q.title, q.question_type, q.answer
, q.answer_question_option_ids, q.children_question_ids, q.score
, q.analysis, q.tag_type, q.courseware_management_id
, cm.courseware_name AS coursewareManagementName
FROM question q
JOIN courseware_management cm ON q.courseware_management_id = cm.id
WHERE q.id = #{questionId}
AND q.delete_enum = 'false'
</select>
</mapper> </mapper>