package cn.surveyking.server.impl;
import cn.surveyking.server.core.common.Tuple2;
import cn.surveyking.server.core.constant.*;
import cn.surveyking.server.core.exception.ErrorCodeException;
import cn.surveyking.server.core.security.JwtTokenUtil;
import cn.surveyking.server.core.uitls.*;
import cn.surveyking.server.domain.dto.*;
import cn.surveyking.server.domain.mapper.ProjectViewMapper;
import cn.surveyking.server.domain.model.Answer;
import cn.surveyking.server.domain.model.CommDictItem;
import cn.surveyking.server.domain.model.ProjectPartner;
import cn.surveyking.server.domain.model.UserBook;
import cn.surveyking.server.mapper.ProjectPartnerMapper;
import cn.surveyking.server.service.ProjectService;
import cn.surveyking.server.service.SurveyService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.util.WebUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ValidationException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* @author javahuang
* @date 2021/8/22
*/
@Service
@Transactional
@RequiredArgsConstructor
@Slf4j
public class SurveyServiceImpl implements SurveyService {
private final ProjectService projectService;
private final ProjectViewMapper projectViewMapper;
private final AnswerServiceImpl answerService;
private final ProjectPartnerMapper projectPartnerMapper;
private final AuthenticationManager authenticationManager;
private final JwtTokenUtil jwtTokenUtil;
private final DictItemServiceImpl dictItemService;
private final RepoServiceImpl repoService;
private final UserBookServiceImpl userBookService;
private final TemplateServiceImpl templateService;
private final ObjectMapper objectMapper;
/**
* answerService 如果需要验证密码,则只有密码输入正确之后才开始加载 schema
* @param query
* @return
*/
@Override
public PublicProjectView loadProject(ProjectQuery query) {
ProjectView project = projectService.getProject(query.getId());
PublicProjectView projectView = projectViewMapper.toPublicProjectView(project);
if (project == null) {
throw new ErrorCodeException(ErrorCode.ProjectNotFound);
}
SurveySchema loginFormSchema = null;
if (query.getAnswerId() == null) {
// 表单需要验证
loginFormSchema = convertAndValidateLoginFormIfNeeded(project, null);
// 校验问卷
validateProject(project);
}
else {
// 直接根据答案加载出 schema
AnswerQuery answerQuery = new AnswerQuery();
answerQuery.setId(query.getAnswerId());
AnswerView answerView = answerService.getAnswer(answerQuery);
if (answerView != null) {
projectView.setAnswer(answerView.getAnswer());
if (answerView.getSurvey() != null) {
// 随机问题
projectView.setSurvey(answerView.getSurvey());
}
}
return projectView;
}
// 如果需要登录,将问卷 schema 替换成登录表单的 schema
if (loginFormSchema != null) {
projectView.setSurvey(loginFormSchema);
projectView.setLoginRequired(true);
}
else {
// 随机问题
replaceSchemaIfRandomSchema(project, projectView);
// 允许修改答案
projectView.setAnswer(getLatestAnswer(projectView, null));
}
projectView.setIsAuthenticated(SecurityContextUtils.isAuthenticated());
return projectView;
}
/**
* 验证问卷
* @param query
* @return
*/
@Override
public PublicProjectView validateProject(ProjectQuery query) {
String projectId = query.getId();
ProjectView project = projectService.getProject(projectId);
// 登录验证
convertAndValidateLoginFormIfNeeded(project, query.getAnswer());
PublicProjectView projectView = projectViewMapper.toPublicProjectView(project);
// 随机问题
replaceSchemaIfRandomSchema(project, projectView);
// 校验问卷
validateProject(project);
projectView.setAnswer(getLatestAnswer(projectView, (String) SchemaHelper.getLoginFormAnswer(query.getAnswer(),
SchemaHelper.LoginFormFieldEnum.whitelistName)));
return projectView;
}
@Override
public PublicStatisticsView statProject(ProjectQuery query) {
AnswerQuery answerQuery = new AnswerQuery();
answerQuery.setProjectId(query.getId());
answerQuery.setPageSize(-1);
List<AnswerView> answers = answerService.listAnswer(answerQuery).getList();
ProjectView project = projectService.getProject(query.getId());
return new ProjectStatHelper(project.getSurvey(), answers).stat();
}
@Override
@Transactional(rollbackFor = Exception.class)
public PublicAnswerView saveAnswer(AnswerRequest request) {
String projectId = request.getProjectId();
PublicAnswerView result = new PublicAnswerView();
ProjectView project = projectService.getProject(projectId);
String answerId = request.getId();
// 随机问卷更新答案
String randomSurveyCookieName = AppConsts.COOKIE_RANDOM_PROJECT_PREFIX + project.getId();
String answerIdFromCookie = ContextHelper.getCookie(randomSurveyCookieName);
if (isNotBlank(answerIdFromCookie)) {
answerId = answerIdFromCookie;
}
else if (isNotBlank(request.getQueryId())) {
// 公开查询修改答案
validateAndMergeAnswer(project, request);
}
else if (isNotBlank(request.getId())) {
// 传入答案ID 并且设置了允许修改答案,则可以修改问卷答案
boolean enableUpdate = Boolean.TRUE.equals(project.getSetting().getSubmittedSetting().getEnableUpdate());
if (!enableUpdate) {
throw new ErrorCodeException(ErrorCode.AnswerChangeDisabled);
}
}
else {
// 问卷允许修改答案 开关修改答案
AnswerView latestAnswer = validateAndGetLatestAnswer(project, request);
if (latestAnswer != null) {
answerId = latestAnswer.getId();
}
}
// 保存答案
request.setId(answerId);
AnswerView answerView = answerService.saveAnswer(request);
result.setAnswerId(answerView.getId());
// 考试模式,计算分值传给前端
if (ProjectModeEnum.exam.equals(project.getMode())) {
result.setExamScore(answerView.getExamScore());
result.setQuestionScore(answerView.getExamInfo().getQuestionScore());
// 计算错题
AnswerExamInfo answerExamInfo = answerView.getExamInfo();
LinkedHashMap<String, Double> questionScore = answerExamInfo.getQuestionScore();
userBookService.saveWrongQuestion(questionScore);
}
// 白名单更新答题信息
request.setId(answerView.getId());
updateProjectPartnerByAnswer(request, project);
// 完事儿删除 cookie 标识
if (isNotBlank(answerIdFromCookie)) {
Cookie cookie = new Cookie(randomSurveyCookieName, answerIdFromCookie);
cookie.setMaxAge(0);
ContextHelper.getCurrentHttpResponse().addCookie(cookie);
}
return result;
}
/**
* @param request
* @return
*/
@Override
@SneakyThrows
p