package jnpf.tmsService.impl;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import jnpf.exception.DataException;
|
import jnpf.permission.UserApi;
|
import jnpf.permission.entity.UserEntity;
|
import jnpf.tmsEntity.TmsQuestionBankEntity;
|
import jnpf.tmsEntity.TmsQuestionEntity;
|
import jnpf.tmsEntity.TmsQuestionOptionEntity;
|
import jnpf.tmsEntity.question.TmsQuestionBankOption;
|
import jnpf.tmsEntity.question.TmsQuestionForm;
|
import jnpf.tmsEntity.question.TmsQuestionOptionForm;
|
import jnpf.tmsEntity.question.TmsQuestionQuery;
|
import jnpf.tmsMapper.TmsQuestionBankMapper;
|
import jnpf.tmsMapper.TmsQuestionMapper;
|
import jnpf.tmsMapper.TmsQuestionOptionMapper;
|
import jnpf.tmsService.TmsQuestionService;
|
import jnpf.util.DateUtil;
|
import jnpf.util.RandomUtil;
|
import jnpf.util.StringUtil;
|
import jnpf.util.UserProvider;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.time.LocalDate;
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.Date;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.stream.Collectors;
|
|
@Service
|
@RequiredArgsConstructor
|
public class TmsQuestionServiceImpl implements TmsQuestionService {
|
|
private final TmsQuestionMapper questionMapper;
|
private final TmsQuestionOptionMapper optionMapper;
|
private final TmsQuestionBankMapper bankMapper;
|
private final UserApi userApi;
|
|
@Override
|
public List<TmsQuestionBankOption> listBanks() {
|
LambdaQueryWrapper<TmsQuestionBankEntity> qw = new LambdaQueryWrapper<>();
|
qw.orderByDesc(TmsQuestionBankEntity::getCreatorTime);
|
List<TmsQuestionBankEntity> list = bankMapper.selectList(qw);
|
List<TmsQuestionBankOption> result = new ArrayList<>(list.size());
|
for (TmsQuestionBankEntity bank : list) {
|
TmsQuestionBankOption opt = new TmsQuestionBankOption();
|
opt.setId(bank.getId());
|
opt.setFullName(bank.getBankName());
|
result.add(opt);
|
}
|
return result;
|
}
|
|
@Override
|
public List<Map<String, String>> listAdmins() {
|
QueryWrapper<TmsQuestionEntity> qw = new QueryWrapper<>();
|
qw.select("DISTINCT admin_user_id");
|
qw.isNotNull("admin_user_id");
|
qw.ne("admin_user_id", "");
|
List<TmsQuestionEntity> rows = questionMapper.selectList(qw);
|
List<String> userIds = rows.stream()
|
.map(TmsQuestionEntity::getAdminUserId)
|
.filter(StringUtil::isNotEmpty)
|
.distinct()
|
.collect(Collectors.toList());
|
if (userIds.isEmpty()) {
|
return Collections.emptyList();
|
}
|
Map<String, String> nameMap = loadUserNames(userIds);
|
List<Map<String, String>> result = new ArrayList<>();
|
for (String userId : userIds) {
|
Map<String, String> item = new HashMap<>(2);
|
item.put("id", userId);
|
item.put("fullName", nameMap.getOrDefault(userId, userId));
|
result.add(item);
|
}
|
return result;
|
}
|
|
@Override
|
public List<TmsQuestionForm> getList(TmsQuestionQuery query) {
|
LambdaQueryWrapper<TmsQuestionEntity> qw = new LambdaQueryWrapper<>();
|
if (StringUtil.isNotEmpty(query.getBankId())) {
|
qw.eq(TmsQuestionEntity::getBankId, query.getBankId());
|
}
|
if (StringUtil.isNotEmpty(query.getQuestionType())) {
|
qw.eq(TmsQuestionEntity::getQuestionType, query.getQuestionType());
|
}
|
if (StringUtil.isNotEmpty(query.getBizStatus())) {
|
qw.eq(TmsQuestionEntity::getBizStatus, query.getBizStatus());
|
}
|
if (StringUtil.isNotEmpty(query.getAdminUserId())) {
|
qw.eq(TmsQuestionEntity::getAdminUserId, query.getAdminUserId());
|
}
|
if (StringUtil.isNotEmpty(query.getKeyword()) && !"null".equalsIgnoreCase(query.getKeyword())) {
|
String kw = query.getKeyword().trim();
|
qw.and(w -> w.like(TmsQuestionEntity::getStem, kw)
|
.or()
|
.like(TmsQuestionEntity::getQuestionNo, kw));
|
}
|
qw.orderByDesc(TmsQuestionEntity::getCreatorTime);
|
|
Page<TmsQuestionEntity> page = new Page<>(query.getCurrentPage(), query.getPageSize());
|
Page<TmsQuestionEntity> result = questionMapper.selectPage(page, qw);
|
List<TmsQuestionEntity> records = result.getRecords();
|
|
Map<String, String> bankNames = loadBankNames(
|
records.stream().map(TmsQuestionEntity::getBankId).filter(StringUtil::isNotEmpty).distinct().collect(Collectors.toList()));
|
Map<String, String> userNames = loadUserNames(
|
records.stream().map(TmsQuestionEntity::getAdminUserId).filter(StringUtil::isNotEmpty).distinct().collect(Collectors.toList()));
|
|
List<TmsQuestionForm> list = new ArrayList<>(records.size());
|
for (TmsQuestionEntity entity : records) {
|
list.add(toForm(entity, bankNames, userNames, false));
|
}
|
query.setData(list, result.getTotal());
|
return list;
|
}
|
|
@Override
|
public TmsQuestionForm getInfo(String id) {
|
TmsQuestionEntity entity = questionMapper.selectById(id);
|
if (entity == null) {
|
throw new DataException("试题不存在");
|
}
|
Map<String, String> bankNames = loadBankNames(Collections.singletonList(entity.getBankId()));
|
Map<String, String> userNames = loadUserNames(
|
StringUtil.isEmpty(entity.getAdminUserId())
|
? Collections.emptyList()
|
: Collections.singletonList(entity.getAdminUserId()));
|
return toForm(entity, bankNames, userNames, true);
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public String create(TmsQuestionForm form) {
|
validateForm(form);
|
String id = RandomUtil.uuId();
|
Date now = new Date();
|
String userId = UserProvider.getLoginUserId();
|
|
TmsQuestionEntity entity = new TmsQuestionEntity();
|
entity.setId(id);
|
entity.setQuestionNo(nextQuestionNo());
|
entity.setBankId(form.getBankId());
|
entity.setQuestionType(form.getQuestionType());
|
entity.setDifficulty(form.getDifficulty());
|
entity.setSourceType(form.getSourceType());
|
entity.setStem(form.getStem());
|
entity.setAnalysis(form.getAnalysis());
|
entity.setAdminUserId(StringUtil.isNotEmpty(form.getAdminUserId()) ? form.getAdminUserId() : userId);
|
entity.setBizStatus(StringUtil.isNotEmpty(form.getBizStatus()) ? form.getBizStatus() : "closed");
|
entity.setCreatorUserId(userId);
|
entity.setCreatorTime(now);
|
questionMapper.insert(entity);
|
|
saveOptions(id, form.getOptions());
|
refreshBankCount(form.getBankId());
|
return id;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void update(String id, TmsQuestionForm form) {
|
TmsQuestionEntity entity = questionMapper.selectById(id);
|
if (entity == null) {
|
throw new DataException("试题不存在");
|
}
|
validateForm(form);
|
|
String oldBankId = entity.getBankId();
|
entity.setBankId(form.getBankId());
|
entity.setQuestionType(form.getQuestionType());
|
entity.setDifficulty(form.getDifficulty());
|
entity.setSourceType(form.getSourceType());
|
entity.setStem(form.getStem());
|
entity.setAnalysis(form.getAnalysis());
|
if (StringUtil.isNotEmpty(form.getAdminUserId())) {
|
entity.setAdminUserId(form.getAdminUserId());
|
}
|
if (StringUtil.isNotEmpty(form.getBizStatus())) {
|
entity.setBizStatus(form.getBizStatus());
|
}
|
entity.setLastModifyUserId(UserProvider.getLoginUserId());
|
entity.setLastModifyTime(new Date());
|
questionMapper.updateById(entity);
|
|
// 选项全量替换
|
LambdaQueryWrapper<TmsQuestionOptionEntity> delQw = new LambdaQueryWrapper<>();
|
delQw.eq(TmsQuestionOptionEntity::getForeignId, id);
|
optionMapper.delete(delQw);
|
saveOptions(id, form.getOptions());
|
|
refreshBankCount(oldBankId);
|
if (!Objects.equals(oldBankId, form.getBankId())) {
|
refreshBankCount(form.getBankId());
|
}
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void invalidate(String id) {
|
TmsQuestionEntity entity = questionMapper.selectById(id);
|
if (entity == null) {
|
return;
|
}
|
String bankId = entity.getBankId();
|
// TableLogic:写 f_delete_mark,列表/详情自动过滤
|
questionMapper.deleteById(id);
|
|
LambdaQueryWrapper<TmsQuestionOptionEntity> delQw = new LambdaQueryWrapper<>();
|
delQw.eq(TmsQuestionOptionEntity::getForeignId, id);
|
optionMapper.delete(delQw);
|
|
refreshBankCount(bankId);
|
}
|
|
private void validateForm(TmsQuestionForm form) {
|
if (form == null) {
|
throw new DataException("参数不能为空");
|
}
|
if (StringUtil.isEmpty(form.getBankId())) {
|
throw new DataException("请选择所属题库");
|
}
|
if (StringUtil.isEmpty(form.getQuestionType())) {
|
throw new DataException("请选择题型");
|
}
|
if (StringUtil.isEmpty(form.getStem())) {
|
throw new DataException("请填写题干");
|
}
|
TmsQuestionBankEntity bank = bankMapper.selectById(form.getBankId());
|
if (bank == null) {
|
throw new DataException("题库不存在或已删除");
|
}
|
}
|
|
private void saveOptions(String questionId, List<TmsQuestionOptionForm> options) {
|
if (options == null || options.isEmpty()) {
|
return;
|
}
|
Date now = new Date();
|
String userId = UserProvider.getLoginUserId();
|
int i = 0;
|
for (TmsQuestionOptionForm opt : options) {
|
TmsQuestionOptionEntity row = new TmsQuestionOptionEntity();
|
row.setId(RandomUtil.uuId());
|
row.setForeignId(questionId);
|
row.setSortNo(opt.getSortNo() != null ? opt.getSortNo() : ++i);
|
row.setOptionLabel(opt.getOptionLabel());
|
row.setOptionContent(opt.getOptionContent());
|
row.setIsCorrect(StringUtil.isNotEmpty(opt.getIsCorrect()) ? opt.getIsCorrect() : "0");
|
row.setCreatorUserId(userId);
|
row.setCreatorTime(now);
|
optionMapper.insert(row);
|
}
|
}
|
|
/** 按题库下未删除试题实计回写 question_count(创建 +1 / 删除 -1 / 换库两边重算) */
|
private void refreshBankCount(String bankId) {
|
if (StringUtil.isEmpty(bankId)) {
|
return;
|
}
|
TmsQuestionBankEntity bank = bankMapper.selectById(bankId);
|
if (bank == null) {
|
return;
|
}
|
LambdaQueryWrapper<TmsQuestionEntity> qw = new LambdaQueryWrapper<>();
|
qw.eq(TmsQuestionEntity::getBankId, bankId);
|
Long count = questionMapper.selectCount(qw);
|
bank.setQuestionCount(count == null ? 0 : count.intValue());
|
bank.setLastModifyUserId(UserProvider.getLoginUserId());
|
bank.setLastModifyTime(new Date());
|
bankMapper.updateById(bank);
|
}
|
|
/**
|
* 试题编号:年份后两位 + 4 位流水,如 260001。
|
* 按当年最大号 +1,含已删记录,避免流水回退。
|
*/
|
private synchronized String nextQuestionNo() {
|
String prefix = String.format("%02d", LocalDate.now().getYear() % 100);
|
String maxNo = questionMapper.selectMaxQuestionNo(prefix);
|
int next = 1;
|
if (StringUtil.isNotEmpty(maxNo) && maxNo.length() >= 6 && maxNo.startsWith(prefix)) {
|
try {
|
next = Integer.parseInt(maxNo.substring(prefix.length())) + 1;
|
} catch (NumberFormatException ignored) {
|
next = 1;
|
}
|
}
|
if (next > 9999) {
|
throw new DataException("当年试题编号已用尽(" + prefix + "9999)");
|
}
|
return prefix + String.format("%04d", next);
|
}
|
|
private TmsQuestionForm toForm(
|
TmsQuestionEntity entity,
|
Map<String, String> bankNames,
|
Map<String, String> userNames,
|
boolean withOptions) {
|
TmsQuestionForm form = new TmsQuestionForm();
|
form.setId(entity.getId());
|
form.setQuestionNo(entity.getQuestionNo());
|
form.setBankId(entity.getBankId());
|
form.setBankName(bankNames.get(entity.getBankId()));
|
form.setQuestionType(entity.getQuestionType());
|
form.setDifficulty(entity.getDifficulty());
|
form.setSourceType(entity.getSourceType());
|
form.setStem(entity.getStem());
|
form.setAnalysis(entity.getAnalysis());
|
form.setAdminUserId(entity.getAdminUserId());
|
form.setAdminUserName(userNames.get(entity.getAdminUserId()));
|
form.setBizStatus(entity.getBizStatus());
|
form.setCreatorTime(entity.getCreatorTime() == null
|
? null
|
: DateUtil.dateToString(entity.getCreatorTime(), "yyyy-MM-dd HH:mm:ss"));
|
if (withOptions) {
|
form.setOptions(loadOptions(entity.getId()));
|
}
|
return form;
|
}
|
|
private List<TmsQuestionOptionForm> loadOptions(String questionId) {
|
LambdaQueryWrapper<TmsQuestionOptionEntity> qw = new LambdaQueryWrapper<>();
|
qw.eq(TmsQuestionOptionEntity::getForeignId, questionId);
|
qw.orderByAsc(TmsQuestionOptionEntity::getSortNo);
|
List<TmsQuestionOptionEntity> rows = optionMapper.selectList(qw);
|
List<TmsQuestionOptionForm> list = new ArrayList<>(rows.size());
|
for (TmsQuestionOptionEntity row : rows) {
|
TmsQuestionOptionForm opt = new TmsQuestionOptionForm();
|
opt.setId(row.getId());
|
opt.setSortNo(row.getSortNo());
|
opt.setOptionLabel(row.getOptionLabel());
|
opt.setOptionContent(row.getOptionContent());
|
opt.setIsCorrect(row.getIsCorrect());
|
list.add(opt);
|
}
|
return list;
|
}
|
|
private Map<String, String> loadBankNames(List<String> bankIds) {
|
if (bankIds == null || bankIds.isEmpty()) {
|
return Collections.emptyMap();
|
}
|
List<TmsQuestionBankEntity> banks = bankMapper.selectBatchIds(bankIds);
|
Map<String, String> map = new HashMap<>();
|
for (TmsQuestionBankEntity bank : banks) {
|
map.put(bank.getId(), bank.getBankName());
|
}
|
return map;
|
}
|
|
private Map<String, String> loadUserNames(List<String> userIds) {
|
if (userIds == null || userIds.isEmpty()) {
|
return Collections.emptyMap();
|
}
|
try {
|
List<UserEntity> users = userApi.getUserName(userIds);
|
if (users == null || users.isEmpty()) {
|
return Collections.emptyMap();
|
}
|
return users.stream()
|
.filter(Objects::nonNull)
|
.collect(Collectors.toMap(UserEntity::getId, UserEntity::getRealName, (a, b) -> a));
|
} catch (Exception ex) {
|
return Collections.emptyMap();
|
}
|
}
|
}
|