init: 初始化项目
This commit is contained in:
commit
0852760852
161
backend/pom.xml
Normal file
161
backend/pom.xml
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>ltd.qubit</groupId>
|
||||||
|
<artifactId>llm-survey-api</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<packaging>war</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<spring.version>5.3.31</spring.version>
|
||||||
|
<mybatis.version>3.5.15</mybatis.version>
|
||||||
|
<mybatis-spring.version>2.1.2</mybatis-spring.version>
|
||||||
|
<mysql-connector.version>8.3.0</mysql-connector.version>
|
||||||
|
<hikaricp.version>5.1.0</hikaricp.version>
|
||||||
|
<jackson.version>2.16.1</jackson.version>
|
||||||
|
<lombok.version>1.18.30</lombok.version>
|
||||||
|
<slf4j.version>2.0.11</slf4j.version>
|
||||||
|
<logback.version>1.4.14</logback.version>
|
||||||
|
<servlet-api.version>4.0.1</servlet-api.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spring Framework -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-jdbc</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context-support</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-tx</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MyBatis -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis</groupId>
|
||||||
|
<artifactId>mybatis</artifactId>
|
||||||
|
<version>${mybatis.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis</groupId>
|
||||||
|
<artifactId>mybatis-spring</artifactId>
|
||||||
|
<version>${mybatis-spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MySQL Connector -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<version>${mysql-connector.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- HikariCP -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.zaxxer</groupId>
|
||||||
|
<artifactId>HikariCP</artifactId>
|
||||||
|
<version>${hikaricp.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Jackson -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Logging -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>${slf4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>${logback.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Servlet API -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<version>${servlet-api.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>llm-survey-api</finalName>
|
||||||
|
<plugins>
|
||||||
|
<!-- 编译插件 -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.11.0</version>
|
||||||
|
<configuration>
|
||||||
|
<release>${java.version}</release>
|
||||||
|
<encoding>${project.build.sourceEncoding}</encoding>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!-- WAR打包插件 -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-war-plugin</artifactId>
|
||||||
|
<version>3.4.0</version>
|
||||||
|
<configuration>
|
||||||
|
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
<!-- 资源文件配置 -->
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/java</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.xml</include>
|
||||||
|
</includes>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
package ltd.qubit.survey.controller;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import ltd.qubit.survey.model.ErrorInfo;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局异常处理器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理参数校验异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(BindException.class)
|
||||||
|
public ResponseEntity<ErrorInfo> handleBindException(BindException e) {
|
||||||
|
String message = e.getBindingResult().getFieldErrors().stream()
|
||||||
|
.map(error -> error.getField() + ": " + error.getDefaultMessage())
|
||||||
|
.reduce((a, b) -> a + "; " + b)
|
||||||
|
.orElse("参数错误");
|
||||||
|
|
||||||
|
ErrorInfo error = ErrorInfo.of(
|
||||||
|
"INVALID_ARGUMENT",
|
||||||
|
"参数校验失败",
|
||||||
|
message,
|
||||||
|
e.getObjectName());
|
||||||
|
|
||||||
|
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理业务异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(IllegalArgumentException.class)
|
||||||
|
public ResponseEntity<ErrorInfo> handleIllegalArgumentException(IllegalArgumentException e) {
|
||||||
|
log.warn("业务异常", e);
|
||||||
|
ErrorInfo error = ErrorInfo.of(
|
||||||
|
"BAD_REQUEST",
|
||||||
|
e.getMessage(),
|
||||||
|
e.getClass().getName());
|
||||||
|
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理数据访问异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(DataAccessException.class)
|
||||||
|
public ResponseEntity<ErrorInfo> handleDataAccessException(DataAccessException e) {
|
||||||
|
log.error("数据访问异常", e);
|
||||||
|
ErrorInfo error = ErrorInfo.of(
|
||||||
|
"DATABASE_ERROR",
|
||||||
|
"数据库访问错误",
|
||||||
|
e.getMessage(),
|
||||||
|
e.getClass().getName());
|
||||||
|
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理其他未知异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ResponseEntity<ErrorInfo> handleUnknownException(Exception e) {
|
||||||
|
log.error("系统异常", e);
|
||||||
|
ErrorInfo error = ErrorInfo.of(
|
||||||
|
"SYSTEM_ERROR",
|
||||||
|
"系统错误",
|
||||||
|
e.getMessage(),
|
||||||
|
e.getClass().getName());
|
||||||
|
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
package ltd.qubit.survey.controller;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import ltd.qubit.survey.model.Question;
|
||||||
|
import ltd.qubit.survey.model.Option;
|
||||||
|
import ltd.qubit.survey.service.QuestionService;
|
||||||
|
import ltd.qubit.survey.service.OptionService;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题控制器
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class QuestionController {
|
||||||
|
private final QuestionService questionService;
|
||||||
|
private final OptionService optionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户的问题列表
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 问题列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/question/user/{userId}")
|
||||||
|
public List<Question> getUserQuestions(@PathVariable Long userId) {
|
||||||
|
return questionService.getUserQuestions(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取问题的选项列表
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 选项列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/question/{questionId}/option")
|
||||||
|
public List<Option> getQuestionOptions(@PathVariable Long questionId) {
|
||||||
|
return optionService.findByQuestionId(questionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下一个问题
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param currentQuestionNumber 当前问题序号
|
||||||
|
* @param selectedOptions 选中的选项列表
|
||||||
|
* @return 下一个问题
|
||||||
|
*/
|
||||||
|
@GetMapping("/question/next")
|
||||||
|
public Question getNextQuestion(
|
||||||
|
@PathVariable Long userId,
|
||||||
|
@PathVariable Integer currentQuestionNumber,
|
||||||
|
@PathVariable List<String> selectedOptions) {
|
||||||
|
return questionService.getNextQuestion(userId, currentQuestionNumber, selectedOptions)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("没有更多问题了"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
package ltd.qubit.survey.controller;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import ltd.qubit.survey.model.SurveyResponse;
|
||||||
|
import ltd.qubit.survey.service.SurveyResponseService;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问卷控制器
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SurveyController {
|
||||||
|
private final SurveyResponseService surveyResponseService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交问卷答案
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param responses 答案列表
|
||||||
|
* @return 提交成功的答案列表
|
||||||
|
*/
|
||||||
|
@PostMapping("/survey/submit/{userId}")
|
||||||
|
public List<SurveyResponse> submitSurvey(
|
||||||
|
@PathVariable Long userId,
|
||||||
|
@RequestBody List<SurveyResponse> responses) {
|
||||||
|
return surveyResponseService.submitSurvey(userId, responses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户的问卷答案
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 答案列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/survey/user/{userId}")
|
||||||
|
public List<SurveyResponse> getUserResponses(@PathVariable Long userId) {
|
||||||
|
return surveyResponseService.findByUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取问题的所有答案
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 答案列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/survey/question/{questionId}")
|
||||||
|
public List<SurveyResponse> getQuestionResponses(@PathVariable Long questionId) {
|
||||||
|
return surveyResponseService.findByQuestionId(questionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package ltd.qubit.survey.controller;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import ltd.qubit.survey.model.User;
|
||||||
|
import ltd.qubit.survey.service.UserService;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户控制器
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserController {
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册
|
||||||
|
*
|
||||||
|
* @param user 用户信息
|
||||||
|
* @return 注册成功的用户信息
|
||||||
|
*/
|
||||||
|
@PostMapping("/user/register")
|
||||||
|
public User register(@RequestBody User user) {
|
||||||
|
return userService.register(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询用户
|
||||||
|
*
|
||||||
|
* @param id 用户ID
|
||||||
|
* @return 用户信息
|
||||||
|
*/
|
||||||
|
@GetMapping("/user/{id}")
|
||||||
|
public User findById(@PathVariable Long id) {
|
||||||
|
return userService.findById(id)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("用户不存在"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手机号是否已注册
|
||||||
|
*
|
||||||
|
* @param phone 手机号
|
||||||
|
* @return 是否已注册
|
||||||
|
*/
|
||||||
|
@GetMapping("/user/check/{phone}")
|
||||||
|
public boolean checkPhone(@PathVariable String phone) {
|
||||||
|
return userService.isPhoneRegistered(phone);
|
||||||
|
}
|
||||||
|
}
|
||||||
51
backend/src/main/java/ltd/qubit/survey/dao/BaseDao.java
Normal file
51
backend/src/main/java/ltd/qubit/survey/dao/BaseDao.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package ltd.qubit.survey.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础DAO接口,定义通用的CRUD操作
|
||||||
|
*
|
||||||
|
* @param <T> 实体类型
|
||||||
|
* @param <K> 主键类型
|
||||||
|
*/
|
||||||
|
public interface BaseDao<T, K> {
|
||||||
|
/**
|
||||||
|
* 插入一条记录
|
||||||
|
*
|
||||||
|
* @param entity 实体对象
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int insert(T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主键删除记录
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int deleteById(K id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新记录
|
||||||
|
*
|
||||||
|
* @param entity 实体对象
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int update(T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主键查询
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
* @return 实体对象
|
||||||
|
*/
|
||||||
|
Optional<T> findById(K id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有记录
|
||||||
|
*
|
||||||
|
* @return 实体对象列表
|
||||||
|
*/
|
||||||
|
List<T> findAll();
|
||||||
|
}
|
||||||
43
backend/src/main/java/ltd/qubit/survey/dao/OptionDao.java
Normal file
43
backend/src/main/java/ltd/qubit/survey/dao/OptionDao.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package ltd.qubit.survey.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.Option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项DAO接口
|
||||||
|
*/
|
||||||
|
public interface OptionDao extends BaseDao<Option, Long> {
|
||||||
|
/**
|
||||||
|
* 根据问题ID查询选项列表
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 选项列表
|
||||||
|
*/
|
||||||
|
List<Option> findByQuestionId(Long questionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据问题ID和选项代码查询
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @param optionCode 选项代码
|
||||||
|
* @return 选项对象
|
||||||
|
*/
|
||||||
|
Optional<Option> findByQuestionIdAndCode(Long questionId, String optionCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量插入选项
|
||||||
|
*
|
||||||
|
* @param options 选项列表
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int batchInsert(List<Option> options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据问题ID删除所有选项
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int deleteByQuestionId(Long questionId);
|
||||||
|
}
|
||||||
41
backend/src/main/java/ltd/qubit/survey/dao/QuestionDao.java
Normal file
41
backend/src/main/java/ltd/qubit/survey/dao/QuestionDao.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package ltd.qubit.survey.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.Question;
|
||||||
|
import ltd.qubit.survey.model.WorkArea;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题DAO接口
|
||||||
|
*/
|
||||||
|
public interface QuestionDao extends BaseDao<Question, Long> {
|
||||||
|
/**
|
||||||
|
* 根据问题序号查询
|
||||||
|
*
|
||||||
|
* @param questionNumber 问题序号
|
||||||
|
* @return 问题对象
|
||||||
|
*/
|
||||||
|
Optional<Question> findByQuestionNumber(Integer questionNumber);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据工作领域查询问题列表
|
||||||
|
*
|
||||||
|
* @param workArea 工作领域
|
||||||
|
* @return 问题列表
|
||||||
|
*/
|
||||||
|
List<Question> findByWorkArea(WorkArea workArea);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询通用问题列表(不针对特定工作领域)
|
||||||
|
*
|
||||||
|
* @return 问题列表
|
||||||
|
*/
|
||||||
|
List<Question> findCommonQuestions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下一个可用的问题序号
|
||||||
|
*
|
||||||
|
* @return 下一个问题序号
|
||||||
|
*/
|
||||||
|
Integer getNextQuestionNumber();
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
package ltd.qubit.survey.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.SurveyResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问卷答案DAO接口
|
||||||
|
*/
|
||||||
|
public interface SurveyResponseDao extends BaseDao<SurveyResponse, Long> {
|
||||||
|
/**
|
||||||
|
* 根据用户ID查询答案列表
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 答案列表
|
||||||
|
*/
|
||||||
|
List<SurveyResponse> findByUserId(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据问题ID查询答案列表
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 答案列表
|
||||||
|
*/
|
||||||
|
List<SurveyResponse> findByQuestionId(Long questionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户ID和问题ID查询答案
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 答案对象
|
||||||
|
*/
|
||||||
|
Optional<SurveyResponse> findByUserIdAndQuestionId(Long userId, Long questionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量插入答案
|
||||||
|
*
|
||||||
|
* @param responses 答案列表
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int batchInsert(List<SurveyResponse> responses);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户ID删除所有答案
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int deleteByUserId(Long userId);
|
||||||
|
}
|
||||||
27
backend/src/main/java/ltd/qubit/survey/dao/UserDao.java
Normal file
27
backend/src/main/java/ltd/qubit/survey/dao/UserDao.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package ltd.qubit.survey.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.User;
|
||||||
|
import ltd.qubit.survey.model.WorkArea;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户DAO接口
|
||||||
|
*/
|
||||||
|
public interface UserDao extends BaseDao<User, Long> {
|
||||||
|
/**
|
||||||
|
* 根据手机号查询用户
|
||||||
|
*
|
||||||
|
* @param phone 手机号
|
||||||
|
* @return 用户对象
|
||||||
|
*/
|
||||||
|
Optional<User> findByPhone(String phone);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据工作领域查询用户列表
|
||||||
|
*
|
||||||
|
* @param workArea 工作领域
|
||||||
|
* @return 用户列表
|
||||||
|
*/
|
||||||
|
List<User> findByWorkArea(WorkArea workArea);
|
||||||
|
}
|
||||||
65
backend/src/main/java/ltd/qubit/survey/model/ErrorInfo.java
Normal file
65
backend/src/main/java/ltd/qubit/survey/model/ErrorInfo.java
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ErrorInfo {
|
||||||
|
/**
|
||||||
|
* 错误代码
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误消息
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误详情
|
||||||
|
*/
|
||||||
|
private String detail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间戳
|
||||||
|
*/
|
||||||
|
private long timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求路径
|
||||||
|
*/
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建错误信息
|
||||||
|
*
|
||||||
|
* @param code 错误代码
|
||||||
|
* @param message 错误消息
|
||||||
|
* @param detail 错误详情
|
||||||
|
* @param path 请求路径
|
||||||
|
* @return 错误信息
|
||||||
|
*/
|
||||||
|
public static ErrorInfo of(String code, String message, String detail, String path) {
|
||||||
|
ErrorInfo error = new ErrorInfo();
|
||||||
|
error.setCode(code);
|
||||||
|
error.setMessage(message);
|
||||||
|
error.setDetail(detail);
|
||||||
|
error.setPath(path);
|
||||||
|
error.setTimestamp(System.currentTimeMillis());
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建错误信息(无详情)
|
||||||
|
*
|
||||||
|
* @param code 错误代码
|
||||||
|
* @param message 错误消息
|
||||||
|
* @param path 请求路径
|
||||||
|
* @return 错误信息
|
||||||
|
*/
|
||||||
|
public static ErrorInfo of(String code, String message, String path) {
|
||||||
|
return of(code, message, null, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
backend/src/main/java/ltd/qubit/survey/model/Option.java
Normal file
40
backend/src/main/java/ltd/qubit/survey/model/Option.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题选项实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class Option {
|
||||||
|
/**
|
||||||
|
* 选项ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联的问题ID
|
||||||
|
*/
|
||||||
|
private Long questionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项代码(如A、B、C)
|
||||||
|
*/
|
||||||
|
private String optionCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否需要填写文本
|
||||||
|
*/
|
||||||
|
private Boolean requiresText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 岗位性质枚举
|
||||||
|
*/
|
||||||
|
public enum PositionType {
|
||||||
|
/**
|
||||||
|
* 管理岗
|
||||||
|
*/
|
||||||
|
MANAGEMENT("管理岗"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 技术岗
|
||||||
|
*/
|
||||||
|
TECHNICAL("技术岗"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务岗
|
||||||
|
*/
|
||||||
|
BUSINESS("业务岗"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 职能支持岗
|
||||||
|
*/
|
||||||
|
SUPPORT("职能支持岗");
|
||||||
|
|
||||||
|
private final String displayName;
|
||||||
|
|
||||||
|
PositionType(String displayName) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
backend/src/main/java/ltd/qubit/survey/model/Question.java
Normal file
50
backend/src/main/java/ltd/qubit/survey/model/Question.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class Question {
|
||||||
|
/**
|
||||||
|
* 问题ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题序号
|
||||||
|
*/
|
||||||
|
private Integer questionNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题类型(单选、多选、文本)
|
||||||
|
*/
|
||||||
|
private QuestionType questionType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 针对的工作领域(为null表示通用问题)
|
||||||
|
*/
|
||||||
|
private WorkArea workArea;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否必答
|
||||||
|
*/
|
||||||
|
private Boolean isRequired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳转逻辑(JSON格式)
|
||||||
|
*/
|
||||||
|
private String nextQuestionLogic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题类型枚举
|
||||||
|
*/
|
||||||
|
public enum QuestionType {
|
||||||
|
/**
|
||||||
|
* 单选题
|
||||||
|
*/
|
||||||
|
SINGLE_CHOICE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多选题
|
||||||
|
*/
|
||||||
|
MULTIPLE_CHOICE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本题
|
||||||
|
*/
|
||||||
|
TEXT
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问卷答案实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class SurveyResponse {
|
||||||
|
/**
|
||||||
|
* 答案ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题ID
|
||||||
|
*/
|
||||||
|
private Long questionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选中的选项代码列表(JSON格式)
|
||||||
|
*/
|
||||||
|
private List<String> selectedOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本答案
|
||||||
|
*/
|
||||||
|
private String textAnswer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
40
backend/src/main/java/ltd/qubit/survey/model/User.java
Normal file
40
backend/src/main/java/ltd/qubit/survey/model/User.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户信息实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class User {
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 姓名
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手机号码
|
||||||
|
*/
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作领域
|
||||||
|
*/
|
||||||
|
private WorkArea workArea;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 岗位性质
|
||||||
|
*/
|
||||||
|
private PositionType positionType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
51
backend/src/main/java/ltd/qubit/survey/model/WorkArea.java
Normal file
51
backend/src/main/java/ltd/qubit/survey/model/WorkArea.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作领域枚举
|
||||||
|
*/
|
||||||
|
public enum WorkArea {
|
||||||
|
/**
|
||||||
|
* 研发领域
|
||||||
|
*/
|
||||||
|
RD("研发"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目领域
|
||||||
|
*/
|
||||||
|
PROJECT("项目"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保险领域
|
||||||
|
*/
|
||||||
|
INSURANCE("保险"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 财务领域
|
||||||
|
*/
|
||||||
|
FINANCE("财务"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运营领域
|
||||||
|
*/
|
||||||
|
OPERATION("运营"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客服领域
|
||||||
|
*/
|
||||||
|
CUSTOMER_SERVICE("客服"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 综合管理领域
|
||||||
|
*/
|
||||||
|
ADMIN("综合管理");
|
||||||
|
|
||||||
|
private final String displayName;
|
||||||
|
|
||||||
|
WorkArea(String displayName) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package ltd.qubit.survey.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础Service接口,定义通用的CRUD操作
|
||||||
|
*
|
||||||
|
* @param <T> 实体类型
|
||||||
|
* @param <K> 主键类型
|
||||||
|
*/
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public interface BaseService<T, K> {
|
||||||
|
/**
|
||||||
|
* 新增
|
||||||
|
*
|
||||||
|
* @param entity 实体对象
|
||||||
|
* @return 实体对象
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
T create(T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
void delete(K id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新
|
||||||
|
*
|
||||||
|
* @param entity 实体对象
|
||||||
|
* @return 实体对象
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
T update(T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
* @return 实体对象
|
||||||
|
*/
|
||||||
|
Optional<T> findById(K id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有
|
||||||
|
*
|
||||||
|
* @return 实体对象列表
|
||||||
|
*/
|
||||||
|
List<T> findAll();
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package ltd.qubit.survey.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.Option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项服务接口
|
||||||
|
*/
|
||||||
|
public interface OptionService extends BaseService<Option, Long> {
|
||||||
|
/**
|
||||||
|
* 根据问题ID查询选项列表
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 选项列表
|
||||||
|
*/
|
||||||
|
List<Option> findByQuestionId(Long questionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据问题ID和选项代码查询
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @param optionCode 选项代码
|
||||||
|
* @return 选项对象
|
||||||
|
*/
|
||||||
|
Optional<Option> findByQuestionIdAndCode(Long questionId, String optionCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量创建选项
|
||||||
|
*
|
||||||
|
* @param options 选项列表
|
||||||
|
* @return 创建成功的选项列表
|
||||||
|
*/
|
||||||
|
List<Option> batchCreate(List<Option> options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除问题的所有选项
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
*/
|
||||||
|
void deleteByQuestionId(Long questionId);
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package ltd.qubit.survey.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.Question;
|
||||||
|
import ltd.qubit.survey.model.WorkArea;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题服务接口
|
||||||
|
*/
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public interface QuestionService extends BaseService<Question, Long> {
|
||||||
|
/**
|
||||||
|
* 根据问题序号查询
|
||||||
|
*
|
||||||
|
* @param questionNumber 问题序号
|
||||||
|
* @return 问题对象
|
||||||
|
*/
|
||||||
|
Optional<Question> findByQuestionNumber(Integer questionNumber);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据工作领域查询问题列表
|
||||||
|
*
|
||||||
|
* @param workArea 工作领域
|
||||||
|
* @return 问题列表
|
||||||
|
*/
|
||||||
|
List<Question> findByWorkArea(WorkArea workArea);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询通用问题列表(不针对特定工作领域)
|
||||||
|
*
|
||||||
|
* @return 问题列表
|
||||||
|
*/
|
||||||
|
List<Question> findCommonQuestions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户的下一个问题
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param currentQuestionNumber 当前问题序号
|
||||||
|
* @param selectedOptions 当前问题的选择项
|
||||||
|
* @return 下一个问题
|
||||||
|
*/
|
||||||
|
Optional<Question> getNextQuestion(Long userId, Integer currentQuestionNumber, List<String> selectedOptions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户的问题列表(包括通用问题和针对其工作领域的问题)
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 问题列表
|
||||||
|
*/
|
||||||
|
List<Question> getUserQuestions(Long userId);
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
package ltd.qubit.survey.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.SurveyResponse;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问卷答案服务接口
|
||||||
|
*/
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public interface SurveyResponseService extends BaseService<SurveyResponse, Long> {
|
||||||
|
/**
|
||||||
|
* 根据用户ID查询答案列表
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 答案列表
|
||||||
|
*/
|
||||||
|
List<SurveyResponse> findByUserId(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据问题ID查询答案列表
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 答案列表
|
||||||
|
*/
|
||||||
|
List<SurveyResponse> findByQuestionId(Long questionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户ID和问题ID查询答案
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 答案对象
|
||||||
|
*/
|
||||||
|
Optional<SurveyResponse> findByUserIdAndQuestionId(Long userId, Long questionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存答案
|
||||||
|
*
|
||||||
|
* @param responses 答案列表
|
||||||
|
* @return 保存成功的答案列表
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
List<SurveyResponse> batchSave(List<SurveyResponse> responses);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除用户的所有答案
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
void deleteByUserId(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交问卷答案
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param responses 答案列表
|
||||||
|
* @return 提交成功的答案列表
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
List<SurveyResponse> submitSurvey(Long userId, List<SurveyResponse> responses);
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package ltd.qubit.survey.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.User;
|
||||||
|
import ltd.qubit.survey.model.WorkArea;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户服务接口
|
||||||
|
*/
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public interface UserService extends BaseService<User, Long> {
|
||||||
|
/**
|
||||||
|
* 根据手机号查询用户
|
||||||
|
*
|
||||||
|
* @param phone 手机号
|
||||||
|
* @return 用户对象
|
||||||
|
*/
|
||||||
|
Optional<User> findByPhone(String phone);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据工作领域查询用户列表
|
||||||
|
*
|
||||||
|
* @param workArea 工作领域
|
||||||
|
* @return 用户列表
|
||||||
|
*/
|
||||||
|
List<User> findByWorkArea(WorkArea workArea);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册
|
||||||
|
*
|
||||||
|
* @param user 用户信息
|
||||||
|
* @return 注册成功的用户
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
User register(User user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手机号是否已被注册
|
||||||
|
*
|
||||||
|
* @param phone 手机号
|
||||||
|
* @return 是否已注册
|
||||||
|
*/
|
||||||
|
boolean isPhoneRegistered(String phone);
|
||||||
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
package ltd.qubit.survey.service.impl;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import ltd.qubit.survey.dao.OptionDao;
|
||||||
|
import ltd.qubit.survey.model.Option;
|
||||||
|
import ltd.qubit.survey.service.OptionService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项服务实现类
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class OptionServiceImpl implements OptionService {
|
||||||
|
private final OptionDao optionDao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Option create(Option option) {
|
||||||
|
option.setCreatedAt(LocalDateTime.now());
|
||||||
|
optionDao.insert(option);
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(Long id) {
|
||||||
|
optionDao.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Option update(Option option) {
|
||||||
|
optionDao.update(option);
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Option> findById(Long id) {
|
||||||
|
return optionDao.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Option> findAll() {
|
||||||
|
return optionDao.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Option> findByQuestionId(Long questionId) {
|
||||||
|
return optionDao.findByQuestionId(questionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Option> findByQuestionIdAndCode(Long questionId, String optionCode) {
|
||||||
|
return optionDao.findByQuestionIdAndCode(questionId, optionCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Option> batchCreate(List<Option> options) {
|
||||||
|
// 设置创建时间
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
options.forEach(option -> option.setCreatedAt(now));
|
||||||
|
|
||||||
|
// 批量插入
|
||||||
|
optionDao.batchInsert(options);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteByQuestionId(Long questionId) {
|
||||||
|
optionDao.deleteByQuestionId(questionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,128 @@
|
|||||||
|
package ltd.qubit.survey.service.impl;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import ltd.qubit.survey.dao.QuestionDao;
|
||||||
|
import ltd.qubit.survey.model.Question;
|
||||||
|
import ltd.qubit.survey.model.User;
|
||||||
|
import ltd.qubit.survey.model.WorkArea;
|
||||||
|
import ltd.qubit.survey.service.QuestionService;
|
||||||
|
import ltd.qubit.survey.service.UserService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题服务实现类
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class QuestionServiceImpl implements QuestionService {
|
||||||
|
private final QuestionDao questionDao;
|
||||||
|
private final UserService userService;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Question create(Question question) {
|
||||||
|
// 如果没有指定问题序号,则自动生成
|
||||||
|
if (question.getQuestionNumber() == null) {
|
||||||
|
question.setQuestionNumber(questionDao.getNextQuestionNumber());
|
||||||
|
}
|
||||||
|
question.setCreatedAt(LocalDateTime.now());
|
||||||
|
questionDao.insert(question);
|
||||||
|
return question;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(Long id) {
|
||||||
|
questionDao.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Question update(Question question) {
|
||||||
|
questionDao.update(question);
|
||||||
|
return question;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Question> findById(Long id) {
|
||||||
|
return questionDao.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Question> findAll() {
|
||||||
|
return questionDao.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Question> findByQuestionNumber(Integer questionNumber) {
|
||||||
|
return questionDao.findByQuestionNumber(questionNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Question> findByWorkArea(WorkArea workArea) {
|
||||||
|
return questionDao.findByWorkArea(workArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Question> findCommonQuestions() {
|
||||||
|
return questionDao.findCommonQuestions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Question> getNextQuestion(Long userId, Integer currentQuestionNumber, List<String> selectedOptions) {
|
||||||
|
// 获取当前问题
|
||||||
|
Optional<Question> currentQuestion = findByQuestionNumber(currentQuestionNumber);
|
||||||
|
if (currentQuestion.isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果当前问题有跳转逻辑,则根据选项判断下一个问题
|
||||||
|
String nextQuestionLogic = currentQuestion.get().getNextQuestionLogic();
|
||||||
|
if (nextQuestionLogic != null && !nextQuestionLogic.isEmpty()) {
|
||||||
|
try {
|
||||||
|
// 解析跳转逻辑JSON
|
||||||
|
Map<String, Integer> logic = objectMapper.readValue(nextQuestionLogic,
|
||||||
|
new TypeReference<Map<String, Integer>>() {});
|
||||||
|
|
||||||
|
// 根据选项确定下一个问题序号
|
||||||
|
for (String option : selectedOptions) {
|
||||||
|
if (logic.containsKey(option)) {
|
||||||
|
return findByQuestionNumber(logic.get(option));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// JSON解析错误,继续使用默认的下一个问题
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有特殊跳转逻辑,则返回序号加1的问题
|
||||||
|
return findByQuestionNumber(currentQuestionNumber + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Question> getUserQuestions(Long userId) {
|
||||||
|
List<Question> questions = new ArrayList<>();
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
Optional<User> user = userService.findById(userId);
|
||||||
|
if (user.isEmpty()) {
|
||||||
|
return questions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加通用问题
|
||||||
|
questions.addAll(findCommonQuestions());
|
||||||
|
|
||||||
|
// 添加针对用户工作领域的问题
|
||||||
|
questions.addAll(findByWorkArea(user.get().getWorkArea()));
|
||||||
|
|
||||||
|
// 按问题序号排序
|
||||||
|
questions.sort((q1, q2) -> q1.getQuestionNumber().compareTo(q2.getQuestionNumber()));
|
||||||
|
|
||||||
|
return questions;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
package ltd.qubit.survey.service.impl;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import ltd.qubit.survey.dao.SurveyResponseDao;
|
||||||
|
import ltd.qubit.survey.model.Question;
|
||||||
|
import ltd.qubit.survey.model.SurveyResponse;
|
||||||
|
import ltd.qubit.survey.service.QuestionService;
|
||||||
|
import ltd.qubit.survey.service.SurveyResponseService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问卷答案服务实现类
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SurveyResponseServiceImpl implements SurveyResponseService {
|
||||||
|
private final SurveyResponseDao surveyResponseDao;
|
||||||
|
private final QuestionService questionService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SurveyResponse create(SurveyResponse response) {
|
||||||
|
response.setCreatedAt(LocalDateTime.now());
|
||||||
|
surveyResponseDao.insert(response);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(Long id) {
|
||||||
|
surveyResponseDao.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SurveyResponse update(SurveyResponse response) {
|
||||||
|
surveyResponseDao.update(response);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<SurveyResponse> findById(Long id) {
|
||||||
|
return surveyResponseDao.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SurveyResponse> findAll() {
|
||||||
|
return surveyResponseDao.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SurveyResponse> findByUserId(Long userId) {
|
||||||
|
return surveyResponseDao.findByUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SurveyResponse> findByQuestionId(Long questionId) {
|
||||||
|
return surveyResponseDao.findByQuestionId(questionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<SurveyResponse> findByUserIdAndQuestionId(Long userId, Long questionId) {
|
||||||
|
return surveyResponseDao.findByUserIdAndQuestionId(userId, questionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SurveyResponse> batchSave(List<SurveyResponse> responses) {
|
||||||
|
// 设置创建时间
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
responses.forEach(response -> response.setCreatedAt(now));
|
||||||
|
|
||||||
|
// 批量插入
|
||||||
|
surveyResponseDao.batchInsert(responses);
|
||||||
|
return responses;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteByUserId(Long userId) {
|
||||||
|
surveyResponseDao.deleteByUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SurveyResponse> submitSurvey(Long userId, List<SurveyResponse> responses) {
|
||||||
|
// 验证所有必答题是否已回答
|
||||||
|
List<Question> questions = questionService.getUserQuestions(userId);
|
||||||
|
for (Question question : questions) {
|
||||||
|
if (question.getIsRequired()) {
|
||||||
|
boolean answered = responses.stream()
|
||||||
|
.anyMatch(response -> response.getQuestionId().equals(question.getId()));
|
||||||
|
if (!answered) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format("问题 %d 为必答题,请填写答案", question.getQuestionNumber()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户之前的答案
|
||||||
|
deleteByUserId(userId);
|
||||||
|
|
||||||
|
// 设置用户ID和创建时间
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
responses.forEach(response -> {
|
||||||
|
response.setUserId(userId);
|
||||||
|
response.setCreatedAt(now);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量保存答案
|
||||||
|
surveyResponseDao.batchInsert(responses);
|
||||||
|
return responses;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
package ltd.qubit.survey.service.impl;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import ltd.qubit.survey.dao.UserDao;
|
||||||
|
import ltd.qubit.survey.model.User;
|
||||||
|
import ltd.qubit.survey.model.WorkArea;
|
||||||
|
import ltd.qubit.survey.service.UserService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户服务实现类
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserServiceImpl implements UserService {
|
||||||
|
private final UserDao userDao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User create(User user) {
|
||||||
|
user.setCreatedAt(LocalDateTime.now());
|
||||||
|
userDao.insert(user);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete(Long id) {
|
||||||
|
userDao.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User update(User user) {
|
||||||
|
userDao.update(user);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<User> findById(Long id) {
|
||||||
|
return userDao.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<User> findAll() {
|
||||||
|
return userDao.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<User> findByPhone(String phone) {
|
||||||
|
return userDao.findByPhone(phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<User> findByWorkArea(WorkArea workArea) {
|
||||||
|
return userDao.findByWorkArea(workArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User register(User user) {
|
||||||
|
// 检查手机号是否已注册
|
||||||
|
if (isPhoneRegistered(user.getPhone())) {
|
||||||
|
throw new IllegalArgumentException("手机号已被注册");
|
||||||
|
}
|
||||||
|
return create(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPhoneRegistered(String phone) {
|
||||||
|
return userDao.findByPhone(phone).isPresent();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
backend/src/main/resources/application.properties
Normal file
18
backend/src/main/resources/application.properties
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# 数据库配置
|
||||||
|
jdbc.driver=com.mysql.cj.jdbc.Driver
|
||||||
|
jdbc.url=jdbc:mysql://127.0.0.1:3306/llm_survey?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
||||||
|
jdbc.username=dev
|
||||||
|
jdbc.password=
|
||||||
|
|
||||||
|
# 连接池配置
|
||||||
|
jdbc.pool.minimumIdle=5
|
||||||
|
jdbc.pool.maximumPoolSize=20
|
||||||
|
jdbc.pool.idleTimeout=300000
|
||||||
|
jdbc.pool.connectionTimeout=20000
|
||||||
|
jdbc.pool.connectionTestQuery=SELECT 1
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
logging.level.root=INFO
|
||||||
|
logging.level.ltd.qubit.survey=DEBUG
|
||||||
|
logging.level.org.springframework=INFO
|
||||||
|
logging.level.org.mybatis=DEBUG
|
||||||
83
backend/src/main/resources/mybatis/mapper/OptionMapper.xml
Normal file
83
backend/src/main/resources/mybatis/mapper/OptionMapper.xml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="ltd.qubit.survey.dao.OptionDao">
|
||||||
|
<!-- 结果映射 -->
|
||||||
|
<resultMap id="optionMap" type="ltd.qubit.survey.model.Option">
|
||||||
|
<id property="id" column="id"/>
|
||||||
|
<result property="questionId" column="question_id"/>
|
||||||
|
<result property="optionCode" column="option_code"/>
|
||||||
|
<result property="content" column="content"/>
|
||||||
|
<result property="requiresText" column="requires_text"/>
|
||||||
|
<result property="createdAt" column="created_at"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 基础列 -->
|
||||||
|
<sql id="baseColumns">
|
||||||
|
id, question_id, option_code, content, requires_text, created_at
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- 插入 -->
|
||||||
|
<insert id="insert" parameterType="ltd.qubit.survey.model.Option" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
INSERT INTO options (question_id, option_code, content, requires_text)
|
||||||
|
VALUES (#{questionId}, #{optionCode}, #{content}, #{requiresText})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 批量插入 -->
|
||||||
|
<insert id="batchInsert" parameterType="java.util.List">
|
||||||
|
INSERT INTO options (question_id, option_code, content, requires_text)
|
||||||
|
VALUES
|
||||||
|
<foreach collection="list" item="item" separator=",">
|
||||||
|
(#{item.questionId}, #{item.optionCode}, #{item.content}, #{item.requiresText})
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 更新 -->
|
||||||
|
<update id="update" parameterType="ltd.qubit.survey.model.Option">
|
||||||
|
UPDATE options
|
||||||
|
SET question_id = #{questionId},
|
||||||
|
option_code = #{optionCode},
|
||||||
|
content = #{content},
|
||||||
|
requires_text = #{requiresText}
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 删除 -->
|
||||||
|
<delete id="deleteById" parameterType="long">
|
||||||
|
DELETE FROM options WHERE id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据问题ID删除 -->
|
||||||
|
<delete id="deleteByQuestionId" parameterType="long">
|
||||||
|
DELETE FROM options WHERE question_id = #{questionId}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据ID查询 -->
|
||||||
|
<select id="findById" parameterType="long" resultMap="optionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM options
|
||||||
|
WHERE id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询所有 -->
|
||||||
|
<select id="findAll" resultMap="optionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM options
|
||||||
|
ORDER BY question_id, option_code
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据问题ID查询 -->
|
||||||
|
<select id="findByQuestionId" parameterType="long" resultMap="optionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM options
|
||||||
|
WHERE question_id = #{questionId}
|
||||||
|
ORDER BY option_code
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据问题ID和选项代码查询 -->
|
||||||
|
<select id="findByQuestionIdAndCode" resultMap="optionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM options
|
||||||
|
WHERE question_id = #{questionId}
|
||||||
|
AND option_code = #{optionCode}
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
86
backend/src/main/resources/mybatis/mapper/QuestionMapper.xml
Normal file
86
backend/src/main/resources/mybatis/mapper/QuestionMapper.xml
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="ltd.qubit.survey.dao.QuestionDao">
|
||||||
|
<!-- 结果映射 -->
|
||||||
|
<resultMap id="questionMap" type="ltd.qubit.survey.model.Question">
|
||||||
|
<id property="id" column="id"/>
|
||||||
|
<result property="questionNumber" column="question_number"/>
|
||||||
|
<result property="content" column="content"/>
|
||||||
|
<result property="questionType" column="question_type"/>
|
||||||
|
<result property="workArea" column="work_area"/>
|
||||||
|
<result property="isRequired" column="is_required"/>
|
||||||
|
<result property="nextQuestionLogic" column="next_question_logic"/>
|
||||||
|
<result property="createdAt" column="created_at"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 基础列 -->
|
||||||
|
<sql id="baseColumns">
|
||||||
|
id, question_number, content, question_type, work_area, is_required, next_question_logic, created_at
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- 插入 -->
|
||||||
|
<insert id="insert" parameterType="ltd.qubit.survey.model.Question" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
INSERT INTO questions (question_number, content, question_type, work_area, is_required, next_question_logic)
|
||||||
|
VALUES (#{questionNumber}, #{content}, #{questionType}, #{workArea}, #{isRequired}, #{nextQuestionLogic})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 更新 -->
|
||||||
|
<update id="update" parameterType="ltd.qubit.survey.model.Question">
|
||||||
|
UPDATE questions
|
||||||
|
SET question_number = #{questionNumber},
|
||||||
|
content = #{content},
|
||||||
|
question_type = #{questionType},
|
||||||
|
work_area = #{workArea},
|
||||||
|
is_required = #{isRequired},
|
||||||
|
next_question_logic = #{nextQuestionLogic}
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 删除 -->
|
||||||
|
<delete id="deleteById" parameterType="long">
|
||||||
|
DELETE FROM questions WHERE id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据ID查询 -->
|
||||||
|
<select id="findById" parameterType="long" resultMap="questionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM questions
|
||||||
|
WHERE id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询所有 -->
|
||||||
|
<select id="findAll" resultMap="questionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM questions
|
||||||
|
ORDER BY question_number
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据问题序号查询 -->
|
||||||
|
<select id="findByQuestionNumber" parameterType="int" resultMap="questionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM questions
|
||||||
|
WHERE question_number = #{questionNumber}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据工作领域查询 -->
|
||||||
|
<select id="findByWorkArea" parameterType="string" resultMap="questionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM questions
|
||||||
|
WHERE work_area = #{workArea}
|
||||||
|
ORDER BY question_number
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询通用问题 -->
|
||||||
|
<select id="findCommonQuestions" resultMap="questionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM questions
|
||||||
|
WHERE work_area IS NULL
|
||||||
|
ORDER BY question_number
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 获取下一个问题序号 -->
|
||||||
|
<select id="getNextQuestionNumber" resultType="int">
|
||||||
|
SELECT COALESCE(MAX(question_number) + 1, 1)
|
||||||
|
FROM questions
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="ltd.qubit.survey.dao.SurveyResponseDao">
|
||||||
|
<!-- 结果映射 -->
|
||||||
|
<resultMap id="responseMap" type="ltd.qubit.survey.model.SurveyResponse">
|
||||||
|
<id property="id" column="id"/>
|
||||||
|
<result property="userId" column="user_id"/>
|
||||||
|
<result property="questionId" column="question_id"/>
|
||||||
|
<result property="selectedOptions" column="selected_options" typeHandler="org.apache.ibatis.type.JsonTypeHandler"/>
|
||||||
|
<result property="textAnswer" column="text_answer"/>
|
||||||
|
<result property="createdAt" column="created_at"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 基础列 -->
|
||||||
|
<sql id="baseColumns">
|
||||||
|
id, user_id, question_id, selected_options, text_answer, created_at
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- 插入 -->
|
||||||
|
<insert id="insert" parameterType="ltd.qubit.survey.model.SurveyResponse" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
INSERT INTO survey_responses (user_id, question_id, selected_options, text_answer)
|
||||||
|
VALUES (#{userId}, #{questionId}, #{selectedOptions,typeHandler=org.apache.ibatis.type.JsonTypeHandler}, #{textAnswer})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 批量插入 -->
|
||||||
|
<insert id="batchInsert" parameterType="java.util.List">
|
||||||
|
INSERT INTO survey_responses (user_id, question_id, selected_options, text_answer)
|
||||||
|
VALUES
|
||||||
|
<foreach collection="list" item="item" separator=",">
|
||||||
|
(#{item.userId}, #{item.questionId},
|
||||||
|
#{item.selectedOptions,typeHandler=org.apache.ibatis.type.JsonTypeHandler},
|
||||||
|
#{item.textAnswer})
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 更新 -->
|
||||||
|
<update id="update" parameterType="ltd.qubit.survey.model.SurveyResponse">
|
||||||
|
UPDATE survey_responses
|
||||||
|
SET user_id = #{userId},
|
||||||
|
question_id = #{questionId},
|
||||||
|
selected_options = #{selectedOptions,typeHandler=org.apache.ibatis.type.JsonTypeHandler},
|
||||||
|
text_answer = #{textAnswer}
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 删除 -->
|
||||||
|
<delete id="deleteById" parameterType="long">
|
||||||
|
DELETE FROM survey_responses WHERE id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据用户ID删除 -->
|
||||||
|
<delete id="deleteByUserId" parameterType="long">
|
||||||
|
DELETE FROM survey_responses WHERE user_id = #{userId}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据ID查询 -->
|
||||||
|
<select id="findById" parameterType="long" resultMap="responseMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM survey_responses
|
||||||
|
WHERE id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询所有 -->
|
||||||
|
<select id="findAll" resultMap="responseMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM survey_responses
|
||||||
|
ORDER BY user_id, question_id
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据用户ID查询 -->
|
||||||
|
<select id="findByUserId" parameterType="long" resultMap="responseMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM survey_responses
|
||||||
|
WHERE user_id = #{userId}
|
||||||
|
ORDER BY question_id
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据问题ID查询 -->
|
||||||
|
<select id="findByQuestionId" parameterType="long" resultMap="responseMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM survey_responses
|
||||||
|
WHERE question_id = #{questionId}
|
||||||
|
ORDER BY user_id
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据用户ID和问题ID查询 -->
|
||||||
|
<select id="findByUserIdAndQuestionId" resultMap="responseMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM survey_responses
|
||||||
|
WHERE user_id = #{userId}
|
||||||
|
AND question_id = #{questionId}
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
68
backend/src/main/resources/mybatis/mapper/UserMapper.xml
Normal file
68
backend/src/main/resources/mybatis/mapper/UserMapper.xml
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="ltd.qubit.survey.dao.UserDao">
|
||||||
|
<!-- 结果映射 -->
|
||||||
|
<resultMap id="userMap" type="ltd.qubit.survey.model.User">
|
||||||
|
<id property="id" column="id"/>
|
||||||
|
<result property="name" column="name"/>
|
||||||
|
<result property="phone" column="phone"/>
|
||||||
|
<result property="workArea" column="work_area"/>
|
||||||
|
<result property="positionType" column="position_type"/>
|
||||||
|
<result property="createdAt" column="created_at"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 基础列 -->
|
||||||
|
<sql id="baseColumns">
|
||||||
|
id, name, phone, work_area, position_type, created_at
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- 插入 -->
|
||||||
|
<insert id="insert" parameterType="ltd.qubit.survey.model.User" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
INSERT INTO users (name, phone, work_area, position_type)
|
||||||
|
VALUES (#{name}, #{phone}, #{workArea}, #{positionType})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 更新 -->
|
||||||
|
<update id="update" parameterType="ltd.qubit.survey.model.User">
|
||||||
|
UPDATE users
|
||||||
|
SET name = #{name},
|
||||||
|
phone = #{phone},
|
||||||
|
work_area = #{workArea},
|
||||||
|
position_type = #{positionType}
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 删除 -->
|
||||||
|
<delete id="deleteById" parameterType="long">
|
||||||
|
DELETE FROM users WHERE id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据ID查询 -->
|
||||||
|
<select id="findById" parameterType="long" resultMap="userMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM users
|
||||||
|
WHERE id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询所有 -->
|
||||||
|
<select id="findAll" resultMap="userMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM users
|
||||||
|
ORDER BY id
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据手机号查询 -->
|
||||||
|
<select id="findByPhone" parameterType="string" resultMap="userMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM users
|
||||||
|
WHERE phone = #{phone}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据工作领域查询 -->
|
||||||
|
<select id="findByWorkArea" parameterType="string" resultMap="userMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM users
|
||||||
|
WHERE work_area = #{workArea}
|
||||||
|
ORDER BY id
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
32
backend/src/main/resources/mybatis/mybatis-config.xml
Normal file
32
backend/src/main/resources/mybatis/mybatis-config.xml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
|
||||||
|
<configuration>
|
||||||
|
<settings>
|
||||||
|
<!-- 开启驼峰命名自动映射 -->
|
||||||
|
<setting name="mapUnderscoreToCamelCase" value="true"/>
|
||||||
|
<!-- 开启二级缓存 -->
|
||||||
|
<setting name="cacheEnabled" value="true"/>
|
||||||
|
<!-- 开启延迟加载 -->
|
||||||
|
<setting name="lazyLoadingEnabled" value="true"/>
|
||||||
|
<!-- 设置日志实现 -->
|
||||||
|
<setting name="logImpl" value="SLF4J"/>
|
||||||
|
</settings>
|
||||||
|
|
||||||
|
<typeHandlers>
|
||||||
|
<!-- 枚举类型处理器 -->
|
||||||
|
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"
|
||||||
|
javaType="ltd.qubit.survey.model.WorkArea"/>
|
||||||
|
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"
|
||||||
|
javaType="ltd.qubit.survey.model.PositionType"/>
|
||||||
|
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"
|
||||||
|
javaType="ltd.qubit.survey.model.QuestionType"/>
|
||||||
|
</typeHandlers>
|
||||||
|
|
||||||
|
<mappers>
|
||||||
|
<!-- 映射文件 -->
|
||||||
|
<mapper resource="mybatis/mapper/UserMapper.xml"/>
|
||||||
|
<mapper resource="mybatis/mapper/QuestionMapper.xml"/>
|
||||||
|
<mapper resource="mybatis/mapper/OptionMapper.xml"/>
|
||||||
|
<mapper resource="mybatis/mapper/SurveyResponseMapper.xml"/>
|
||||||
|
</mappers>
|
||||||
|
</configuration>
|
||||||
24
backend/src/main/resources/spring/applicationContext.xml
Normal file
24
backend/src/main/resources/spring/applicationContext.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:context="http://www.springframework.org/schema/context"
|
||||||
|
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/context
|
||||||
|
http://www.springframework.org/schema/context/spring-context.xsd
|
||||||
|
http://www.springframework.org/schema/tx
|
||||||
|
http://www.springframework.org/schema/tx/spring-tx.xsd">
|
||||||
|
|
||||||
|
<!-- 加载属性文件 -->
|
||||||
|
<context:property-placeholder location="classpath:application.properties"/>
|
||||||
|
|
||||||
|
<!-- 开启注解扫描,排除Controller -->
|
||||||
|
<context:component-scan base-package="ltd.qubit.survey">
|
||||||
|
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
|
||||||
|
</context:component-scan>
|
||||||
|
|
||||||
|
<!-- 配置事务注解驱动 -->
|
||||||
|
<tx:annotation-driven/>
|
||||||
|
|
||||||
|
</beans>
|
||||||
46
backend/src/main/resources/spring/spring-mvc.xml
Normal file
46
backend/src/main/resources/spring/spring-mvc.xml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:context="http://www.springframework.org/schema/context"
|
||||||
|
xmlns:mvc="http://www.springframework.org/schema/mvc"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/context
|
||||||
|
http://www.springframework.org/schema/context/spring-context.xsd
|
||||||
|
http://www.springframework.org/schema/mvc
|
||||||
|
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
|
||||||
|
|
||||||
|
<!-- 开启Controller注解扫描 -->
|
||||||
|
<context:component-scan base-package="ltd.qubit.survey.controller" use-default-filters="false">
|
||||||
|
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
|
||||||
|
</context:component-scan>
|
||||||
|
|
||||||
|
<!-- 开启SpringMVC注解驱动 -->
|
||||||
|
<mvc:annotation-driven>
|
||||||
|
<mvc:message-converters>
|
||||||
|
<!-- 配置JSON转换器 -->
|
||||||
|
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
|
||||||
|
<property name="objectMapper">
|
||||||
|
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
|
||||||
|
<property name="dateFormat">
|
||||||
|
<bean class="java.text.SimpleDateFormat">
|
||||||
|
<constructor-arg value="yyyy-MM-dd HH:mm:ss"/>
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
</mvc:message-converters>
|
||||||
|
</mvc:annotation-driven>
|
||||||
|
|
||||||
|
<!-- 配置跨域支持 -->
|
||||||
|
<mvc:cors>
|
||||||
|
<mvc:mapping path="/**"
|
||||||
|
allowed-origins="*"
|
||||||
|
allowed-methods="GET,POST,PUT,DELETE,OPTIONS"
|
||||||
|
allowed-headers="Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers"
|
||||||
|
allow-credentials="true"
|
||||||
|
max-age="3600"/>
|
||||||
|
</mvc:cors>
|
||||||
|
|
||||||
|
</beans>
|
||||||
39
backend/src/main/resources/spring/spring-mybatis.xml
Normal file
39
backend/src/main/resources/spring/spring-mybatis.xml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<!-- 配置数据源 -->
|
||||||
|
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
|
||||||
|
<property name="driverClassName" value="${jdbc.driver}"/>
|
||||||
|
<property name="jdbcUrl" value="${jdbc.url}"/>
|
||||||
|
<property name="username" value="${jdbc.username}"/>
|
||||||
|
<property name="password" value="${jdbc.password}"/>
|
||||||
|
<!-- 连接池配置 -->
|
||||||
|
<property name="minimumIdle" value="${jdbc.pool.minimumIdle}"/>
|
||||||
|
<property name="maximumPoolSize" value="${jdbc.pool.maximumPoolSize}"/>
|
||||||
|
<property name="idleTimeout" value="${jdbc.pool.idleTimeout}"/>
|
||||||
|
<property name="connectionTimeout" value="${jdbc.pool.connectionTimeout}"/>
|
||||||
|
<property name="connectionTestQuery" value="${jdbc.pool.connectionTestQuery}"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- 配置SqlSessionFactory -->
|
||||||
|
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
|
||||||
|
<property name="dataSource" ref="dataSource"/>
|
||||||
|
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
|
||||||
|
<property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- 配置Mapper扫描器 -->
|
||||||
|
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
|
||||||
|
<property name="basePackage" value="ltd.qubit.survey.dao"/>
|
||||||
|
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- 配置事务管理器 -->
|
||||||
|
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
|
||||||
|
<property name="dataSource" ref="dataSource"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
</beans>
|
||||||
57
backend/src/main/webapp/WEB-INF/web.xml
Normal file
57
backend/src/main/webapp/WEB-INF/web.xml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
|
||||||
|
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
|
||||||
|
version="4.0">
|
||||||
|
|
||||||
|
<display-name>LLM Survey API</display-name>
|
||||||
|
|
||||||
|
<!-- Spring配置文件位置 -->
|
||||||
|
<context-param>
|
||||||
|
<param-name>contextConfigLocation</param-name>
|
||||||
|
<param-value>
|
||||||
|
classpath:spring/applicationContext.xml
|
||||||
|
classpath:spring/spring-mybatis.xml
|
||||||
|
</param-value>
|
||||||
|
</context-param>
|
||||||
|
|
||||||
|
<!-- Spring监听器 -->
|
||||||
|
<listener>
|
||||||
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||||
|
</listener>
|
||||||
|
|
||||||
|
<!-- Spring MVC Servlet -->
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>springmvc</servlet-name>
|
||||||
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||||
|
<init-param>
|
||||||
|
<param-name>contextConfigLocation</param-name>
|
||||||
|
<param-value>classpath:spring/spring-mvc.xml</param-value>
|
||||||
|
</init-param>
|
||||||
|
<load-on-startup>1</load-on-startup>
|
||||||
|
</servlet>
|
||||||
|
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>springmvc</servlet-name>
|
||||||
|
<url-pattern>/</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<!-- 字符编码过滤器 -->
|
||||||
|
<filter>
|
||||||
|
<filter-name>characterEncodingFilter</filter-name>
|
||||||
|
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
|
||||||
|
<init-param>
|
||||||
|
<param-name>encoding</param-name>
|
||||||
|
<param-value>UTF-8</param-value>
|
||||||
|
</init-param>
|
||||||
|
<init-param>
|
||||||
|
<param-name>forceEncoding</param-name>
|
||||||
|
<param-value>true</param-value>
|
||||||
|
</init-param>
|
||||||
|
</filter>
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>characterEncodingFilter</filter-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</filter-mapping>
|
||||||
|
</web-app>
|
||||||
18
backend/target/classes/application.properties
Normal file
18
backend/target/classes/application.properties
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# 数据库配置
|
||||||
|
jdbc.driver=com.mysql.cj.jdbc.Driver
|
||||||
|
jdbc.url=jdbc:mysql://127.0.0.1:3306/llm_survey?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
||||||
|
jdbc.username=dev
|
||||||
|
jdbc.password=
|
||||||
|
|
||||||
|
# 连接池配置
|
||||||
|
jdbc.pool.minimumIdle=5
|
||||||
|
jdbc.pool.maximumPoolSize=20
|
||||||
|
jdbc.pool.idleTimeout=300000
|
||||||
|
jdbc.pool.connectionTimeout=20000
|
||||||
|
jdbc.pool.connectionTestQuery=SELECT 1
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
logging.level.root=INFO
|
||||||
|
logging.level.ltd.qubit.survey=DEBUG
|
||||||
|
logging.level.org.springframework=INFO
|
||||||
|
logging.level.org.mybatis=DEBUG
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
backend/target/classes/ltd/qubit/survey/dao/BaseDao.class
Normal file
BIN
backend/target/classes/ltd/qubit/survey/dao/BaseDao.class
Normal file
Binary file not shown.
BIN
backend/target/classes/ltd/qubit/survey/dao/OptionDao.class
Normal file
BIN
backend/target/classes/ltd/qubit/survey/dao/OptionDao.class
Normal file
Binary file not shown.
BIN
backend/target/classes/ltd/qubit/survey/dao/QuestionDao.class
Normal file
BIN
backend/target/classes/ltd/qubit/survey/dao/QuestionDao.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
backend/target/classes/ltd/qubit/survey/dao/UserDao.class
Normal file
BIN
backend/target/classes/ltd/qubit/survey/dao/UserDao.class
Normal file
Binary file not shown.
BIN
backend/target/classes/ltd/qubit/survey/model/ErrorInfo.class
Normal file
BIN
backend/target/classes/ltd/qubit/survey/model/ErrorInfo.class
Normal file
Binary file not shown.
BIN
backend/target/classes/ltd/qubit/survey/model/Option.class
Normal file
BIN
backend/target/classes/ltd/qubit/survey/model/Option.class
Normal file
Binary file not shown.
BIN
backend/target/classes/ltd/qubit/survey/model/PositionType.class
Normal file
BIN
backend/target/classes/ltd/qubit/survey/model/PositionType.class
Normal file
Binary file not shown.
BIN
backend/target/classes/ltd/qubit/survey/model/Question.class
Normal file
BIN
backend/target/classes/ltd/qubit/survey/model/Question.class
Normal file
Binary file not shown.
BIN
backend/target/classes/ltd/qubit/survey/model/QuestionType.class
Normal file
BIN
backend/target/classes/ltd/qubit/survey/model/QuestionType.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
backend/target/classes/ltd/qubit/survey/model/User.class
Normal file
BIN
backend/target/classes/ltd/qubit/survey/model/User.class
Normal file
Binary file not shown.
BIN
backend/target/classes/ltd/qubit/survey/model/WorkArea.class
Normal file
BIN
backend/target/classes/ltd/qubit/survey/model/WorkArea.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
83
backend/target/classes/mybatis/mapper/OptionMapper.xml
Normal file
83
backend/target/classes/mybatis/mapper/OptionMapper.xml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="ltd.qubit.survey.dao.OptionDao">
|
||||||
|
<!-- 结果映射 -->
|
||||||
|
<resultMap id="optionMap" type="ltd.qubit.survey.model.Option">
|
||||||
|
<id property="id" column="id"/>
|
||||||
|
<result property="questionId" column="question_id"/>
|
||||||
|
<result property="optionCode" column="option_code"/>
|
||||||
|
<result property="content" column="content"/>
|
||||||
|
<result property="requiresText" column="requires_text"/>
|
||||||
|
<result property="createdAt" column="created_at"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 基础列 -->
|
||||||
|
<sql id="baseColumns">
|
||||||
|
id, question_id, option_code, content, requires_text, created_at
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- 插入 -->
|
||||||
|
<insert id="insert" parameterType="ltd.qubit.survey.model.Option" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
INSERT INTO options (question_id, option_code, content, requires_text)
|
||||||
|
VALUES (#{questionId}, #{optionCode}, #{content}, #{requiresText})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 批量插入 -->
|
||||||
|
<insert id="batchInsert" parameterType="java.util.List">
|
||||||
|
INSERT INTO options (question_id, option_code, content, requires_text)
|
||||||
|
VALUES
|
||||||
|
<foreach collection="list" item="item" separator=",">
|
||||||
|
(#{item.questionId}, #{item.optionCode}, #{item.content}, #{item.requiresText})
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 更新 -->
|
||||||
|
<update id="update" parameterType="ltd.qubit.survey.model.Option">
|
||||||
|
UPDATE options
|
||||||
|
SET question_id = #{questionId},
|
||||||
|
option_code = #{optionCode},
|
||||||
|
content = #{content},
|
||||||
|
requires_text = #{requiresText}
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 删除 -->
|
||||||
|
<delete id="deleteById" parameterType="long">
|
||||||
|
DELETE FROM options WHERE id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据问题ID删除 -->
|
||||||
|
<delete id="deleteByQuestionId" parameterType="long">
|
||||||
|
DELETE FROM options WHERE question_id = #{questionId}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据ID查询 -->
|
||||||
|
<select id="findById" parameterType="long" resultMap="optionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM options
|
||||||
|
WHERE id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询所有 -->
|
||||||
|
<select id="findAll" resultMap="optionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM options
|
||||||
|
ORDER BY question_id, option_code
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据问题ID查询 -->
|
||||||
|
<select id="findByQuestionId" parameterType="long" resultMap="optionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM options
|
||||||
|
WHERE question_id = #{questionId}
|
||||||
|
ORDER BY option_code
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据问题ID和选项代码查询 -->
|
||||||
|
<select id="findByQuestionIdAndCode" resultMap="optionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM options
|
||||||
|
WHERE question_id = #{questionId}
|
||||||
|
AND option_code = #{optionCode}
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
86
backend/target/classes/mybatis/mapper/QuestionMapper.xml
Normal file
86
backend/target/classes/mybatis/mapper/QuestionMapper.xml
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="ltd.qubit.survey.dao.QuestionDao">
|
||||||
|
<!-- 结果映射 -->
|
||||||
|
<resultMap id="questionMap" type="ltd.qubit.survey.model.Question">
|
||||||
|
<id property="id" column="id"/>
|
||||||
|
<result property="questionNumber" column="question_number"/>
|
||||||
|
<result property="content" column="content"/>
|
||||||
|
<result property="questionType" column="question_type"/>
|
||||||
|
<result property="workArea" column="work_area"/>
|
||||||
|
<result property="isRequired" column="is_required"/>
|
||||||
|
<result property="nextQuestionLogic" column="next_question_logic"/>
|
||||||
|
<result property="createdAt" column="created_at"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 基础列 -->
|
||||||
|
<sql id="baseColumns">
|
||||||
|
id, question_number, content, question_type, work_area, is_required, next_question_logic, created_at
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- 插入 -->
|
||||||
|
<insert id="insert" parameterType="ltd.qubit.survey.model.Question" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
INSERT INTO questions (question_number, content, question_type, work_area, is_required, next_question_logic)
|
||||||
|
VALUES (#{questionNumber}, #{content}, #{questionType}, #{workArea}, #{isRequired}, #{nextQuestionLogic})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 更新 -->
|
||||||
|
<update id="update" parameterType="ltd.qubit.survey.model.Question">
|
||||||
|
UPDATE questions
|
||||||
|
SET question_number = #{questionNumber},
|
||||||
|
content = #{content},
|
||||||
|
question_type = #{questionType},
|
||||||
|
work_area = #{workArea},
|
||||||
|
is_required = #{isRequired},
|
||||||
|
next_question_logic = #{nextQuestionLogic}
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 删除 -->
|
||||||
|
<delete id="deleteById" parameterType="long">
|
||||||
|
DELETE FROM questions WHERE id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据ID查询 -->
|
||||||
|
<select id="findById" parameterType="long" resultMap="questionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM questions
|
||||||
|
WHERE id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询所有 -->
|
||||||
|
<select id="findAll" resultMap="questionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM questions
|
||||||
|
ORDER BY question_number
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据问题序号查询 -->
|
||||||
|
<select id="findByQuestionNumber" parameterType="int" resultMap="questionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM questions
|
||||||
|
WHERE question_number = #{questionNumber}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据工作领域查询 -->
|
||||||
|
<select id="findByWorkArea" parameterType="string" resultMap="questionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM questions
|
||||||
|
WHERE work_area = #{workArea}
|
||||||
|
ORDER BY question_number
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询通用问题 -->
|
||||||
|
<select id="findCommonQuestions" resultMap="questionMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM questions
|
||||||
|
WHERE work_area IS NULL
|
||||||
|
ORDER BY question_number
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 获取下一个问题序号 -->
|
||||||
|
<select id="getNextQuestionNumber" resultType="int">
|
||||||
|
SELECT COALESCE(MAX(question_number) + 1, 1)
|
||||||
|
FROM questions
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="ltd.qubit.survey.dao.SurveyResponseDao">
|
||||||
|
<!-- 结果映射 -->
|
||||||
|
<resultMap id="responseMap" type="ltd.qubit.survey.model.SurveyResponse">
|
||||||
|
<id property="id" column="id"/>
|
||||||
|
<result property="userId" column="user_id"/>
|
||||||
|
<result property="questionId" column="question_id"/>
|
||||||
|
<result property="selectedOptions" column="selected_options" typeHandler="org.apache.ibatis.type.JsonTypeHandler"/>
|
||||||
|
<result property="textAnswer" column="text_answer"/>
|
||||||
|
<result property="createdAt" column="created_at"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 基础列 -->
|
||||||
|
<sql id="baseColumns">
|
||||||
|
id, user_id, question_id, selected_options, text_answer, created_at
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- 插入 -->
|
||||||
|
<insert id="insert" parameterType="ltd.qubit.survey.model.SurveyResponse" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
INSERT INTO survey_responses (user_id, question_id, selected_options, text_answer)
|
||||||
|
VALUES (#{userId}, #{questionId}, #{selectedOptions,typeHandler=org.apache.ibatis.type.JsonTypeHandler}, #{textAnswer})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 批量插入 -->
|
||||||
|
<insert id="batchInsert" parameterType="java.util.List">
|
||||||
|
INSERT INTO survey_responses (user_id, question_id, selected_options, text_answer)
|
||||||
|
VALUES
|
||||||
|
<foreach collection="list" item="item" separator=",">
|
||||||
|
(#{item.userId}, #{item.questionId},
|
||||||
|
#{item.selectedOptions,typeHandler=org.apache.ibatis.type.JsonTypeHandler},
|
||||||
|
#{item.textAnswer})
|
||||||
|
</foreach>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 更新 -->
|
||||||
|
<update id="update" parameterType="ltd.qubit.survey.model.SurveyResponse">
|
||||||
|
UPDATE survey_responses
|
||||||
|
SET user_id = #{userId},
|
||||||
|
question_id = #{questionId},
|
||||||
|
selected_options = #{selectedOptions,typeHandler=org.apache.ibatis.type.JsonTypeHandler},
|
||||||
|
text_answer = #{textAnswer}
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 删除 -->
|
||||||
|
<delete id="deleteById" parameterType="long">
|
||||||
|
DELETE FROM survey_responses WHERE id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据用户ID删除 -->
|
||||||
|
<delete id="deleteByUserId" parameterType="long">
|
||||||
|
DELETE FROM survey_responses WHERE user_id = #{userId}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据ID查询 -->
|
||||||
|
<select id="findById" parameterType="long" resultMap="responseMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM survey_responses
|
||||||
|
WHERE id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询所有 -->
|
||||||
|
<select id="findAll" resultMap="responseMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM survey_responses
|
||||||
|
ORDER BY user_id, question_id
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据用户ID查询 -->
|
||||||
|
<select id="findByUserId" parameterType="long" resultMap="responseMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM survey_responses
|
||||||
|
WHERE user_id = #{userId}
|
||||||
|
ORDER BY question_id
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据问题ID查询 -->
|
||||||
|
<select id="findByQuestionId" parameterType="long" resultMap="responseMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM survey_responses
|
||||||
|
WHERE question_id = #{questionId}
|
||||||
|
ORDER BY user_id
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据用户ID和问题ID查询 -->
|
||||||
|
<select id="findByUserIdAndQuestionId" resultMap="responseMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM survey_responses
|
||||||
|
WHERE user_id = #{userId}
|
||||||
|
AND question_id = #{questionId}
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
68
backend/target/classes/mybatis/mapper/UserMapper.xml
Normal file
68
backend/target/classes/mybatis/mapper/UserMapper.xml
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="ltd.qubit.survey.dao.UserDao">
|
||||||
|
<!-- 结果映射 -->
|
||||||
|
<resultMap id="userMap" type="ltd.qubit.survey.model.User">
|
||||||
|
<id property="id" column="id"/>
|
||||||
|
<result property="name" column="name"/>
|
||||||
|
<result property="phone" column="phone"/>
|
||||||
|
<result property="workArea" column="work_area"/>
|
||||||
|
<result property="positionType" column="position_type"/>
|
||||||
|
<result property="createdAt" column="created_at"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<!-- 基础列 -->
|
||||||
|
<sql id="baseColumns">
|
||||||
|
id, name, phone, work_area, position_type, created_at
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- 插入 -->
|
||||||
|
<insert id="insert" parameterType="ltd.qubit.survey.model.User" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
INSERT INTO users (name, phone, work_area, position_type)
|
||||||
|
VALUES (#{name}, #{phone}, #{workArea}, #{positionType})
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<!-- 更新 -->
|
||||||
|
<update id="update" parameterType="ltd.qubit.survey.model.User">
|
||||||
|
UPDATE users
|
||||||
|
SET name = #{name},
|
||||||
|
phone = #{phone},
|
||||||
|
work_area = #{workArea},
|
||||||
|
position_type = #{positionType}
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!-- 删除 -->
|
||||||
|
<delete id="deleteById" parameterType="long">
|
||||||
|
DELETE FROM users WHERE id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- 根据ID查询 -->
|
||||||
|
<select id="findById" parameterType="long" resultMap="userMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM users
|
||||||
|
WHERE id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询所有 -->
|
||||||
|
<select id="findAll" resultMap="userMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM users
|
||||||
|
ORDER BY id
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据手机号查询 -->
|
||||||
|
<select id="findByPhone" parameterType="string" resultMap="userMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM users
|
||||||
|
WHERE phone = #{phone}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据工作领域查询 -->
|
||||||
|
<select id="findByWorkArea" parameterType="string" resultMap="userMap">
|
||||||
|
SELECT <include refid="baseColumns"/>
|
||||||
|
FROM users
|
||||||
|
WHERE work_area = #{workArea}
|
||||||
|
ORDER BY id
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
32
backend/target/classes/mybatis/mybatis-config.xml
Normal file
32
backend/target/classes/mybatis/mybatis-config.xml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
|
||||||
|
<configuration>
|
||||||
|
<settings>
|
||||||
|
<!-- 开启驼峰命名自动映射 -->
|
||||||
|
<setting name="mapUnderscoreToCamelCase" value="true"/>
|
||||||
|
<!-- 开启二级缓存 -->
|
||||||
|
<setting name="cacheEnabled" value="true"/>
|
||||||
|
<!-- 开启延迟加载 -->
|
||||||
|
<setting name="lazyLoadingEnabled" value="true"/>
|
||||||
|
<!-- 设置日志实现 -->
|
||||||
|
<setting name="logImpl" value="SLF4J"/>
|
||||||
|
</settings>
|
||||||
|
|
||||||
|
<typeHandlers>
|
||||||
|
<!-- 枚举类型处理器 -->
|
||||||
|
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"
|
||||||
|
javaType="ltd.qubit.survey.model.WorkArea"/>
|
||||||
|
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"
|
||||||
|
javaType="ltd.qubit.survey.model.PositionType"/>
|
||||||
|
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"
|
||||||
|
javaType="ltd.qubit.survey.model.QuestionType"/>
|
||||||
|
</typeHandlers>
|
||||||
|
|
||||||
|
<mappers>
|
||||||
|
<!-- 映射文件 -->
|
||||||
|
<mapper resource="mybatis/mapper/UserMapper.xml"/>
|
||||||
|
<mapper resource="mybatis/mapper/QuestionMapper.xml"/>
|
||||||
|
<mapper resource="mybatis/mapper/OptionMapper.xml"/>
|
||||||
|
<mapper resource="mybatis/mapper/SurveyResponseMapper.xml"/>
|
||||||
|
</mappers>
|
||||||
|
</configuration>
|
||||||
24
backend/target/classes/spring/applicationContext.xml
Normal file
24
backend/target/classes/spring/applicationContext.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:context="http://www.springframework.org/schema/context"
|
||||||
|
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/context
|
||||||
|
http://www.springframework.org/schema/context/spring-context.xsd
|
||||||
|
http://www.springframework.org/schema/tx
|
||||||
|
http://www.springframework.org/schema/tx/spring-tx.xsd">
|
||||||
|
|
||||||
|
<!-- 加载属性文件 -->
|
||||||
|
<context:property-placeholder location="classpath:application.properties"/>
|
||||||
|
|
||||||
|
<!-- 开启注解扫描,排除Controller -->
|
||||||
|
<context:component-scan base-package="ltd.qubit.survey">
|
||||||
|
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
|
||||||
|
</context:component-scan>
|
||||||
|
|
||||||
|
<!-- 配置事务注解驱动 -->
|
||||||
|
<tx:annotation-driven/>
|
||||||
|
|
||||||
|
</beans>
|
||||||
46
backend/target/classes/spring/spring-mvc.xml
Normal file
46
backend/target/classes/spring/spring-mvc.xml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:context="http://www.springframework.org/schema/context"
|
||||||
|
xmlns:mvc="http://www.springframework.org/schema/mvc"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/context
|
||||||
|
http://www.springframework.org/schema/context/spring-context.xsd
|
||||||
|
http://www.springframework.org/schema/mvc
|
||||||
|
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
|
||||||
|
|
||||||
|
<!-- 开启Controller注解扫描 -->
|
||||||
|
<context:component-scan base-package="ltd.qubit.survey.controller" use-default-filters="false">
|
||||||
|
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
|
||||||
|
</context:component-scan>
|
||||||
|
|
||||||
|
<!-- 开启SpringMVC注解驱动 -->
|
||||||
|
<mvc:annotation-driven>
|
||||||
|
<mvc:message-converters>
|
||||||
|
<!-- 配置JSON转换器 -->
|
||||||
|
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
|
||||||
|
<property name="objectMapper">
|
||||||
|
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
|
||||||
|
<property name="dateFormat">
|
||||||
|
<bean class="java.text.SimpleDateFormat">
|
||||||
|
<constructor-arg value="yyyy-MM-dd HH:mm:ss"/>
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
</mvc:message-converters>
|
||||||
|
</mvc:annotation-driven>
|
||||||
|
|
||||||
|
<!-- 配置跨域支持 -->
|
||||||
|
<mvc:cors>
|
||||||
|
<mvc:mapping path="/**"
|
||||||
|
allowed-origins="*"
|
||||||
|
allowed-methods="GET,POST,PUT,DELETE,OPTIONS"
|
||||||
|
allowed-headers="Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers"
|
||||||
|
allow-credentials="true"
|
||||||
|
max-age="3600"/>
|
||||||
|
</mvc:cors>
|
||||||
|
|
||||||
|
</beans>
|
||||||
39
backend/target/classes/spring/spring-mybatis.xml
Normal file
39
backend/target/classes/spring/spring-mybatis.xml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||||
|
|
||||||
|
<!-- 配置数据源 -->
|
||||||
|
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
|
||||||
|
<property name="driverClassName" value="${jdbc.driver}"/>
|
||||||
|
<property name="jdbcUrl" value="${jdbc.url}"/>
|
||||||
|
<property name="username" value="${jdbc.username}"/>
|
||||||
|
<property name="password" value="${jdbc.password}"/>
|
||||||
|
<!-- 连接池配置 -->
|
||||||
|
<property name="minimumIdle" value="${jdbc.pool.minimumIdle}"/>
|
||||||
|
<property name="maximumPoolSize" value="${jdbc.pool.maximumPoolSize}"/>
|
||||||
|
<property name="idleTimeout" value="${jdbc.pool.idleTimeout}"/>
|
||||||
|
<property name="connectionTimeout" value="${jdbc.pool.connectionTimeout}"/>
|
||||||
|
<property name="connectionTestQuery" value="${jdbc.pool.connectionTestQuery}"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- 配置SqlSessionFactory -->
|
||||||
|
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
|
||||||
|
<property name="dataSource" ref="dataSource"/>
|
||||||
|
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
|
||||||
|
<property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- 配置Mapper扫描器 -->
|
||||||
|
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
|
||||||
|
<property name="basePackage" value="ltd.qubit.survey.dao"/>
|
||||||
|
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- 配置事务管理器 -->
|
||||||
|
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
|
||||||
|
<property name="dataSource" ref="dataSource"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
</beans>
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
ltd/qubit/survey/controller/GlobalExceptionHandler.class
|
||||||
|
ltd/qubit/survey/dao/QuestionDao.class
|
||||||
|
ltd/qubit/survey/model/PositionType.class
|
||||||
|
ltd/qubit/survey/service/OptionService.class
|
||||||
|
ltd/qubit/survey/controller/UserController.class
|
||||||
|
ltd/qubit/survey/service/impl/SurveyResponseServiceImpl.class
|
||||||
|
ltd/qubit/survey/dao/SurveyResponseDao.class
|
||||||
|
ltd/qubit/survey/dao/OptionDao.class
|
||||||
|
ltd/qubit/survey/controller/QuestionController.class
|
||||||
|
ltd/qubit/survey/service/impl/UserServiceImpl.class
|
||||||
|
ltd/qubit/survey/service/impl/QuestionServiceImpl.class
|
||||||
|
ltd/qubit/survey/model/SurveyResponse.class
|
||||||
|
ltd/qubit/survey/model/User.class
|
||||||
|
ltd/qubit/survey/service/QuestionService.class
|
||||||
|
ltd/qubit/survey/service/impl/QuestionServiceImpl$1.class
|
||||||
|
ltd/qubit/survey/service/UserService.class
|
||||||
|
ltd/qubit/survey/dao/BaseDao.class
|
||||||
|
ltd/qubit/survey/service/impl/OptionServiceImpl.class
|
||||||
|
ltd/qubit/survey/dao/UserDao.class
|
||||||
|
ltd/qubit/survey/model/ErrorInfo.class
|
||||||
|
ltd/qubit/survey/controller/SurveyController.class
|
||||||
|
ltd/qubit/survey/model/Question.class
|
||||||
|
ltd/qubit/survey/model/WorkArea.class
|
||||||
|
ltd/qubit/survey/service/SurveyResponseService.class
|
||||||
|
ltd/qubit/survey/model/Option.class
|
||||||
|
ltd/qubit/survey/model/QuestionType.class
|
||||||
|
ltd/qubit/survey/service/BaseService.class
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/dao/QuestionDao.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/service/UserService.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/service/OptionService.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/model/Question.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/service/QuestionService.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/service/BaseService.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/model/User.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/service/impl/OptionServiceImpl.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/service/impl/QuestionServiceImpl.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/service/SurveyResponseService.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/model/Option.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/controller/GlobalExceptionHandler.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/model/QuestionType.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/dao/UserDao.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/model/SurveyResponse.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/service/impl/SurveyResponseServiceImpl.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/model/ErrorInfo.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/controller/UserController.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/service/impl/UserServiceImpl.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/dao/SurveyResponseDao.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/controller/QuestionController.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/model/WorkArea.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/model/PositionType.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/dao/BaseDao.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/controller/SurveyController.java
|
||||||
|
/Volumes/working/qubit/project/llm-survey/backend/src/main/java/ltd/qubit/survey/dao/OptionDao.java
|
||||||
30
database/init_database.sh
Executable file
30
database/init_database.sh
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 获取脚本所在目录的绝对路径
|
||||||
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
|
# 检查mysql命令是否可用
|
||||||
|
if ! command -v mysql &> /dev/null; then
|
||||||
|
echo "Error: mysql command not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 数据库连接信息
|
||||||
|
DB_HOST="127.0.0.1"
|
||||||
|
DB_USER="dev"
|
||||||
|
DB_NAME="llm_survey"
|
||||||
|
|
||||||
|
echo "开始初始化数据库..."
|
||||||
|
|
||||||
|
# 执行SQL脚本
|
||||||
|
mysql -h"$DB_HOST" -u"$DB_USER" < "$SCRIPT_DIR/init_database.sql"
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "数据库初始化成功!"
|
||||||
|
echo "数据库名称: $DB_NAME"
|
||||||
|
echo "数据库地址: $DB_HOST"
|
||||||
|
echo "数据库用户: $DB_USER"
|
||||||
|
else
|
||||||
|
echo "数据库初始化失败!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
73
database/init_database.sql
Normal file
73
database/init_database.sql
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
-- 创建数据库
|
||||||
|
CREATE DATABASE IF NOT EXISTS llm_survey DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
USE llm_survey;
|
||||||
|
|
||||||
|
-- 创建用户表
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
name VARCHAR(50) NOT NULL COMMENT '姓名',
|
||||||
|
phone VARCHAR(20) NOT NULL COMMENT '手机号码',
|
||||||
|
work_area VARCHAR(20) NOT NULL COMMENT '工作领域',
|
||||||
|
position_type VARCHAR(20) NOT NULL COMMENT '岗位性质',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE KEY uk_phone (phone)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
|
||||||
|
|
||||||
|
-- 创建问题表
|
||||||
|
CREATE TABLE IF NOT EXISTS questions (
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
question_number INT NOT NULL COMMENT '问题序号',
|
||||||
|
content TEXT NOT NULL COMMENT '问题内容',
|
||||||
|
question_type VARCHAR(20) NOT NULL COMMENT '问题类型',
|
||||||
|
work_area VARCHAR(20) COMMENT '针对的工作领域(NULL表示通用问题)',
|
||||||
|
is_required BOOLEAN DEFAULT TRUE COMMENT '是否必答',
|
||||||
|
next_question_logic JSON COMMENT '跳转逻辑',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE KEY uk_question_number (question_number)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='问题表';
|
||||||
|
|
||||||
|
-- 创建选项表
|
||||||
|
CREATE TABLE IF NOT EXISTS options (
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
question_id BIGINT NOT NULL COMMENT '关联的问题ID',
|
||||||
|
option_code VARCHAR(10) NOT NULL COMMENT '选项代码(如A、B、C)',
|
||||||
|
content TEXT NOT NULL COMMENT '选项内容',
|
||||||
|
requires_text BOOLEAN DEFAULT FALSE COMMENT '是否需要填写文本',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (question_id) REFERENCES questions(id),
|
||||||
|
UNIQUE KEY uk_question_option (question_id, option_code)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='问题选项表';
|
||||||
|
|
||||||
|
-- 创建答案表
|
||||||
|
CREATE TABLE IF NOT EXISTS survey_responses (
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user_id BIGINT NOT NULL COMMENT '用户ID',
|
||||||
|
question_id BIGINT NOT NULL COMMENT '问题ID',
|
||||||
|
selected_options JSON COMMENT '选中的选项代码列表',
|
||||||
|
text_answer TEXT COMMENT '文本答案',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id),
|
||||||
|
FOREIGN KEY (question_id) REFERENCES questions(id)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='问卷答案表';
|
||||||
|
|
||||||
|
-- 插入基础问题数据
|
||||||
|
INSERT INTO questions (question_number, content, question_type, is_required) VALUES
|
||||||
|
(1, '您的工作领域', 'SINGLE_CHOICE', TRUE),
|
||||||
|
(2, '岗位性质', 'SINGLE_CHOICE', TRUE);
|
||||||
|
|
||||||
|
-- 插入基础选项数据
|
||||||
|
INSERT INTO options (question_id, option_code, content) VALUES
|
||||||
|
(1, 'A', '研发'),
|
||||||
|
(1, 'B', '项目'),
|
||||||
|
(1, 'C', '保险'),
|
||||||
|
(1, 'D', '财务'),
|
||||||
|
(1, 'E', '运营'),
|
||||||
|
(1, 'F', '客服'),
|
||||||
|
(1, 'G', '综合管理');
|
||||||
|
|
||||||
|
INSERT INTO options (question_id, option_code, content) VALUES
|
||||||
|
(2, 'A', '管理岗'),
|
||||||
|
(2, 'B', '技术岗'),
|
||||||
|
(2, 'C', '业务岗'),
|
||||||
|
(2, 'D', '职能支持岗');
|
||||||
163
doc/api.md
Normal file
163
doc/api.md
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
# 在线调查问卷系统 API 文档
|
||||||
|
|
||||||
|
## 1. 数据结构
|
||||||
|
|
||||||
|
### 1.1 用户信息 (User)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "Long",
|
||||||
|
"name": "String", // 姓名
|
||||||
|
"phone": "String", // 手机号码
|
||||||
|
"workArea": "String", // 工作领域: RD/PROJECT/INSURANCE/FINANCE/OPERATION/CUSTOMER_SERVICE/ADMIN
|
||||||
|
"positionType": "String", // 岗位性质: MANAGEMENT/TECHNICAL/BUSINESS/SUPPORT
|
||||||
|
"createdAt": "DateTime" // 创建时间
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 问题 (Question)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "Long",
|
||||||
|
"questionNumber": "Integer", // 问题序号
|
||||||
|
"content": "String", // 问题内容
|
||||||
|
"questionType": "String", // 问题类型: SINGLE_CHOICE/MULTIPLE_CHOICE/TEXT
|
||||||
|
"workArea": "String", // 针对的工作领域(null表示通用问题)
|
||||||
|
"isRequired": "Boolean", // 是否必答
|
||||||
|
"nextQuestionLogic": "JSON", // 跳转逻辑
|
||||||
|
"createdAt": "DateTime" // 创建时间
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 选项 (Option)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "Long",
|
||||||
|
"questionId": "Long", // 关联的问题ID
|
||||||
|
"optionCode": "String", // 选项代码(如A、B、C)
|
||||||
|
"content": "String", // 选项内容
|
||||||
|
"requiresText": "Boolean", // 是否需要填写文本
|
||||||
|
"createdAt": "DateTime" // 创建时间
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.4 问卷答案 (SurveyResponse)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "Long",
|
||||||
|
"userId": "Long", // 用户ID
|
||||||
|
"questionId": "Long", // 问题ID
|
||||||
|
"selectedOptions": "List<String>", // 选中的选项代码列表
|
||||||
|
"textAnswer": "String", // 文本答案
|
||||||
|
"createdAt": "DateTime" // 创建时间
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.5 错误信息 (ErrorInfo)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": "String", // 错误代码
|
||||||
|
"message": "String", // 错误消息
|
||||||
|
"detail": "String", // 错误详情
|
||||||
|
"timestamp": "Long", // 时间戳
|
||||||
|
"path": "String" // 请求路径
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. API 接口
|
||||||
|
|
||||||
|
### 2.1 用户相关接口
|
||||||
|
|
||||||
|
#### 2.1.1 用户注册
|
||||||
|
- **URL**: `/user/register`
|
||||||
|
- **Method**: POST
|
||||||
|
- **请求体**: User对象(不需要id和createdAt)
|
||||||
|
- **响应**: User对象
|
||||||
|
- **错误码**:
|
||||||
|
- `INVALID_ARGUMENT`: 参数校验失败
|
||||||
|
- `BAD_REQUEST`: 手机号已被注册
|
||||||
|
|
||||||
|
#### 2.1.2 查询用户信息
|
||||||
|
- **URL**: `/user/{id}`
|
||||||
|
- **Method**: GET
|
||||||
|
- **参数**:
|
||||||
|
- `id`: 用户ID
|
||||||
|
- **响应**: User对象
|
||||||
|
- **错误码**:
|
||||||
|
- `BAD_REQUEST`: 用户不存在
|
||||||
|
|
||||||
|
#### 2.1.3 检查手机号是否已注册
|
||||||
|
- **URL**: `/user/check/{phone}`
|
||||||
|
- **Method**: GET
|
||||||
|
- **参数**:
|
||||||
|
- `phone`: 手机号
|
||||||
|
- **响应**: Boolean(true表示已注册)
|
||||||
|
|
||||||
|
### 2.2 问题相关接口
|
||||||
|
|
||||||
|
#### 2.2.1 获取用户的问题列表
|
||||||
|
- **URL**: `/question/user/{userId}`
|
||||||
|
- **Method**: GET
|
||||||
|
- **参数**:
|
||||||
|
- `userId`: 用户ID
|
||||||
|
- **响应**: Question对象列表
|
||||||
|
- **错误码**:
|
||||||
|
- `BAD_REQUEST`: 用户不存在
|
||||||
|
|
||||||
|
#### 2.2.2 获取问题的选项列表
|
||||||
|
- **URL**: `/question/{questionId}/option`
|
||||||
|
- **Method**: GET
|
||||||
|
- **参数**:
|
||||||
|
- `questionId`: 问题ID
|
||||||
|
- **响应**: Option对象列表
|
||||||
|
|
||||||
|
#### 2.2.3 获取下一个问题
|
||||||
|
- **URL**: `/question/next`
|
||||||
|
- **Method**: GET
|
||||||
|
- **参数**:
|
||||||
|
- `userId`: 用户ID
|
||||||
|
- `currentQuestionNumber`: 当前问题序号
|
||||||
|
- `selectedOptions`: 选中的选项代码列表
|
||||||
|
- **响应**: Question对象
|
||||||
|
- **错误码**:
|
||||||
|
- `BAD_REQUEST`: 没有更多问题了
|
||||||
|
|
||||||
|
### 2.3 问卷答案相关接口
|
||||||
|
|
||||||
|
#### 2.3.1 提交问卷答案
|
||||||
|
- **URL**: `/survey/submit/{userId}`
|
||||||
|
- **Method**: POST
|
||||||
|
- **参数**:
|
||||||
|
- `userId`: 用户ID
|
||||||
|
- **请求体**: SurveyResponse对象列表
|
||||||
|
- **响应**: SurveyResponse对象列表
|
||||||
|
- **错误码**:
|
||||||
|
- `BAD_REQUEST`: 必答题未回答
|
||||||
|
|
||||||
|
#### 2.3.2 获取用户的问卷答案
|
||||||
|
- **URL**: `/survey/user/{userId}`
|
||||||
|
- **Method**: GET
|
||||||
|
- **参数**:
|
||||||
|
- `userId`: 用户ID
|
||||||
|
- **响应**: SurveyResponse对象列表
|
||||||
|
|
||||||
|
#### 2.3.3 获取问题的所有答案
|
||||||
|
- **URL**: `/survey/question/{questionId}`
|
||||||
|
- **Method**: GET
|
||||||
|
- **参数**:
|
||||||
|
- `questionId`: 问题ID
|
||||||
|
- **响应**: SurveyResponse对象列表
|
||||||
|
|
||||||
|
## 3. 错误处理
|
||||||
|
|
||||||
|
所有接口在发生错误时都会返回统一格式的错误信息(ErrorInfo对象):
|
||||||
|
|
||||||
|
### 3.1 通用错误码
|
||||||
|
- `INVALID_ARGUMENT`: 参数校验失败
|
||||||
|
- `BAD_REQUEST`: 业务逻辑错误
|
||||||
|
- `DATABASE_ERROR`: 数据库访问错误
|
||||||
|
- `SYSTEM_ERROR`: 系统错误
|
||||||
|
|
||||||
|
### 3.2 HTTP状态码
|
||||||
|
- 200: 请求成功
|
||||||
|
- 400: 请求参数错误或业务逻辑错误
|
||||||
|
- 500: 服务器内部错误
|
||||||
62
doc/requirements.md
Normal file
62
doc/requirements.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# 在线调查问卷系统需求文档
|
||||||
|
|
||||||
|
## 1. 系统概述
|
||||||
|
本系统是一个在线调查问卷系统,用于收集用户对大模型应用的需求调研。系统包括前端问卷展示和后端数据处理两个部分。
|
||||||
|
|
||||||
|
## 2. 功能需求
|
||||||
|
|
||||||
|
### 2.1 用户信息收集
|
||||||
|
- 用户需填写基本信息(姓名和手机号码)
|
||||||
|
- 系统自动收集用户的问卷答案
|
||||||
|
|
||||||
|
### 2.2 问卷功能
|
||||||
|
- 支持问题跳转逻辑(根据用户选择自动跳转到相应问题)
|
||||||
|
- 支持多种题型:
|
||||||
|
- 单选题
|
||||||
|
- 多选题
|
||||||
|
- 文本输入题
|
||||||
|
- 答案自动保存到数据库
|
||||||
|
|
||||||
|
### 2.3 数据存储
|
||||||
|
- 使用MySQL数据库存储用户信息和答案
|
||||||
|
- 数据库配置:
|
||||||
|
- 地址:127.0.0.1
|
||||||
|
- 用户名:dev
|
||||||
|
- 密码:无
|
||||||
|
- 权限:可建库建表
|
||||||
|
|
||||||
|
## 3. 技术要求
|
||||||
|
|
||||||
|
### 3.1 前端技术栈
|
||||||
|
- 框架:Vue.js
|
||||||
|
- UI框架:移动端适配的主流框架
|
||||||
|
- HTTP请求:Axios(需要封装)
|
||||||
|
- 项目名称:llm-survey
|
||||||
|
|
||||||
|
### 3.2 后端技术栈
|
||||||
|
- 开发语言:Java
|
||||||
|
- 框架:Spring + MyBatis
|
||||||
|
- 打包方式:WAR
|
||||||
|
- 项目名称:llm-survey-api
|
||||||
|
- 架构:三层架构
|
||||||
|
- 领域对象(Domain Objects)
|
||||||
|
- DAO层
|
||||||
|
- Service层
|
||||||
|
- Controller层
|
||||||
|
|
||||||
|
### 3.3 部署要求
|
||||||
|
- 前端:llm-survey
|
||||||
|
- 后端:llm-survey-api
|
||||||
|
- Nginx配置:
|
||||||
|
- 反向代理Tomcat接口
|
||||||
|
- 将后端API重定向到127.0.0.1:80/llm-survey-api
|
||||||
|
|
||||||
|
## 4. 数据库初始化
|
||||||
|
- 需要提供shell脚本进行数据库初始化
|
||||||
|
- 脚本功能包括:
|
||||||
|
- 创建数据库
|
||||||
|
- 创建必要的表
|
||||||
|
- 设置必要的索引和约束
|
||||||
|
|
||||||
|
## 5. 问卷内容
|
||||||
|
问卷详细内容请参见 [大模型应用需求调研问卷.md](大模型应用需求调研问卷.md)
|
||||||
156
doc/大模型应用需求调研问卷.md
Normal file
156
doc/大模型应用需求调研问卷.md
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
**(根据部门自动跳转专属问题)**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第一部分:基本信息(必填)
|
||||||
|
1. 您所属部门:
|
||||||
|
A. 研发部
|
||||||
|
B. 项目部
|
||||||
|
C. 保险部
|
||||||
|
D. 财务部
|
||||||
|
E. 客服部
|
||||||
|
F. 运营部
|
||||||
|
G. 综合管理部
|
||||||
|
|
||||||
|
2. 岗位性质:
|
||||||
|
A. 管理岗
|
||||||
|
B. 技术岗
|
||||||
|
C. 业务岗
|
||||||
|
D. 职能支持岗
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第二部分:通用认知调研(必答)
|
||||||
|
1. 您对大模型(如ChatGPT、通义千问、DeepSeek)的了解程度:
|
||||||
|
A. 从未接触过
|
||||||
|
B. 仅简单使用过通用功能(如问答)
|
||||||
|
C. 在工作中尝试过基础应用
|
||||||
|
D. 深度研究过技术原理
|
||||||
|
|
||||||
|
2. 您认为以下哪些业务场景最需要效率提升?(多选)
|
||||||
|
A. 文档撰写/报告生成
|
||||||
|
B. 数据清洗与分析
|
||||||
|
C. 客户沟通与服务
|
||||||
|
D. 风险识别与预警
|
||||||
|
E. 流程自动化
|
||||||
|
F. 其他:____________
|
||||||
|
|
||||||
|
3. 您最关注大模型应用的哪些风险?(多选)
|
||||||
|
A. 数据隐私泄露
|
||||||
|
B. 生成内容不准确
|
||||||
|
C. 合规审查风险
|
||||||
|
D. 技术使用门槛高
|
||||||
|
E. 其他:____________ (需填写内容)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第三部分:部门专属问题
|
||||||
|
**(系统将根据第一部分选择的部门自动跳转)**
|
||||||
|
|
||||||
|
### 研发部
|
||||||
|
4. 您在开发过程中最耗时的重复性工作:
|
||||||
|
A. 保险条款文档编写
|
||||||
|
B. 医疗知识图谱维护
|
||||||
|
C. API接口调试
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
5. 您希望大模型如何与现有系统集成:
|
||||||
|
A. 自动生成代码片段(如DeepSeek-Coder)
|
||||||
|
B. 智能测试用例生成(如通义千问测试场景模拟)
|
||||||
|
C. 需求文档结构化(如ChatGPT生成PRD框架)
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
### 项目部
|
||||||
|
6. 惠民保产品设计中最需要数据支持的环节:
|
||||||
|
A. 参保人群画像分析
|
||||||
|
B. 竞品方案快速解析(如通义千问政策解读)
|
||||||
|
C. 定价模型优化
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
7. 您希望如何用大模型提升方案输出效率:
|
||||||
|
A. 自动生成PPT框架(ChatGPT生成大纲)
|
||||||
|
B. 从政策文件中提取关键条款(DeepSeek语义解析)
|
||||||
|
C. 风险测算报告自动化
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
### 保险部
|
||||||
|
8. 理赔处理中的主要效率瓶颈:
|
||||||
|
A. 材料完整性核验
|
||||||
|
B. 医疗票据信息提取(如OCR+通义千问核对)
|
||||||
|
C. 案件风险分级
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
9. 您认为大模型可优化的理赔环节:
|
||||||
|
A. 自动生成理赔告知书(ChatGPT模板生成)
|
||||||
|
B. 异常案件预警提示(DeepSeek数据分析)
|
||||||
|
C. 保险规则智能问答
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
### 财务部
|
||||||
|
10. 日常工作中重复性最高的任务:
|
||||||
|
A. 发票信息录入核对
|
||||||
|
B. 报销单据合规审查
|
||||||
|
C. 财务报表数据汇总
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
11. 您希望大模型协助完成的财务工作:
|
||||||
|
A. 自动提取票据关键字段(通义千问OCR识别)
|
||||||
|
B. 生成财务分析摘要(ChatGPT文本总结)
|
||||||
|
C. 异常收支模式检测(DeepSeek风险预测)
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
### 客服部
|
||||||
|
12. 客户咨询中最常遇到的重复性问题:
|
||||||
|
A. 理赔进度查询
|
||||||
|
B. 参保资格咨询
|
||||||
|
C. 材料补交通知
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
13. 您希望大模型如何辅助客服工作:
|
||||||
|
A. 自动生成个性化回复话术(ChatGPT对话生成)
|
||||||
|
B. 客户情绪实时识别(通义千问情感分析)
|
||||||
|
C. 咨询问题自动分类派单(DeepSeek意图分类)
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
### 运营部
|
||||||
|
14. 运营数据分析中的主要痛点:
|
||||||
|
A. 多平台数据整合
|
||||||
|
B. 参保率预测模型优化(DeepSeek时序预测)
|
||||||
|
C. 宣传文案创意生成(通义千问文案生成)
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
15. 您希望大模型赋能的运营场景:
|
||||||
|
A. 自动生成社交媒体图文(ChatGPT+通义万相)
|
||||||
|
B. 用户评论情感分析(DeepSeek语义分析)
|
||||||
|
C. 活动效果模拟预测
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
### 综合管理部
|
||||||
|
16. 日常工作中最耗时的行政事务:
|
||||||
|
A. 合同条款审查
|
||||||
|
B. 会议纪要整理
|
||||||
|
C. 制度文档更新
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
17. 您希望大模型协助完成的行政工作:
|
||||||
|
A. 自动生成招投标文件模板(ChatGPT框架生成)
|
||||||
|
B. 合同风险点智能排查(通义千问法律审查)
|
||||||
|
C. 流程说明书自动更新(DeepSeek版本迭代)
|
||||||
|
D. 其他:____________
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第四部分:开放建议(选填)
|
||||||
|
18. 您对大模型培训的具体期待:
|
||||||
|
____________________________
|
||||||
|
|
||||||
|
19. 您认为公司引入AI需提前防范的风险:
|
||||||
|
____________________________
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 问卷说明
|
||||||
|
20. **逻辑跳转**:系统将根据所选部门显示专属问题,总题量约10-12题。
|
||||||
|
21. **工具示例**:
|
||||||
|
- 通用大模型:ChatGPT(OpenAI)、通义千问(阿里云)、DeepSeek(深度求索)
|
||||||
|
22. **提交方式**:匿名填写,预计耗时5-8分钟。
|
||||||
161
pom.xml
Normal file
161
pom.xml
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>ltd.qubit</groupId>
|
||||||
|
<artifactId>llm-survey-api</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<packaging>war</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<spring.version>5.3.31</spring.version>
|
||||||
|
<mybatis.version>3.5.15</mybatis.version>
|
||||||
|
<mybatis-spring.version>2.1.2</mybatis-spring.version>
|
||||||
|
<mysql-connector.version>8.3.0</mysql-connector.version>
|
||||||
|
<hikaricp.version>5.1.0</hikaricp.version>
|
||||||
|
<jackson.version>2.16.1</jackson.version>
|
||||||
|
<lombok.version>1.18.30</lombok.version>
|
||||||
|
<slf4j.version>2.0.11</slf4j.version>
|
||||||
|
<logback.version>1.4.14</logback.version>
|
||||||
|
<servlet-api.version>4.0.1</servlet-api.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spring Framework -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-jdbc</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context-support</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-tx</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MyBatis -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis</groupId>
|
||||||
|
<artifactId>mybatis</artifactId>
|
||||||
|
<version>${mybatis.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis</groupId>
|
||||||
|
<artifactId>mybatis-spring</artifactId>
|
||||||
|
<version>${mybatis-spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MySQL Connector -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<version>${mysql-connector.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- HikariCP -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.zaxxer</groupId>
|
||||||
|
<artifactId>HikariCP</artifactId>
|
||||||
|
<version>${hikaricp.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Jackson -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Logging -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>${slf4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>${logback.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Servlet API -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<version>${servlet-api.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>llm-survey-api</finalName>
|
||||||
|
<plugins>
|
||||||
|
<!-- 编译插件 -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.11.0</version>
|
||||||
|
<configuration>
|
||||||
|
<release>${java.version}</release>
|
||||||
|
<encoding>${project.build.sourceEncoding}</encoding>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!-- WAR打包插件 -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-war-plugin</artifactId>
|
||||||
|
<version>3.4.0</version>
|
||||||
|
<configuration>
|
||||||
|
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
<!-- 资源文件配置 -->
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/java</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.xml</include>
|
||||||
|
</includes>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
package ltd.qubit.survey.controller;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import ltd.qubit.survey.model.ErrorInfo;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局异常处理器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理参数校验异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(BindException.class)
|
||||||
|
public ResponseEntity<ErrorInfo> handleBindException(BindException e) {
|
||||||
|
String message = e.getBindingResult().getFieldErrors().stream()
|
||||||
|
.map(error -> error.getField() + ": " + error.getDefaultMessage())
|
||||||
|
.reduce((a, b) -> a + "; " + b)
|
||||||
|
.orElse("参数错误");
|
||||||
|
|
||||||
|
ErrorInfo error = ErrorInfo.of(
|
||||||
|
"INVALID_ARGUMENT",
|
||||||
|
"参数校验失败",
|
||||||
|
message,
|
||||||
|
e.getObjectName());
|
||||||
|
|
||||||
|
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理业务异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(IllegalArgumentException.class)
|
||||||
|
public ResponseEntity<ErrorInfo> handleIllegalArgumentException(IllegalArgumentException e) {
|
||||||
|
log.warn("业务异常", e);
|
||||||
|
ErrorInfo error = ErrorInfo.of(
|
||||||
|
"BAD_REQUEST",
|
||||||
|
e.getMessage(),
|
||||||
|
e.getClass().getName());
|
||||||
|
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理数据访问异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(DataAccessException.class)
|
||||||
|
public ResponseEntity<ErrorInfo> handleDataAccessException(DataAccessException e) {
|
||||||
|
log.error("数据访问异常", e);
|
||||||
|
ErrorInfo error = ErrorInfo.of(
|
||||||
|
"DATABASE_ERROR",
|
||||||
|
"数据库访问错误",
|
||||||
|
e.getMessage(),
|
||||||
|
e.getClass().getName());
|
||||||
|
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理其他未知异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ResponseEntity<ErrorInfo> handleUnknownException(Exception e) {
|
||||||
|
log.error("系统异常", e);
|
||||||
|
ErrorInfo error = ErrorInfo.of(
|
||||||
|
"SYSTEM_ERROR",
|
||||||
|
"系统错误",
|
||||||
|
e.getMessage(),
|
||||||
|
e.getClass().getName());
|
||||||
|
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
package ltd.qubit.survey.controller;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import ltd.qubit.survey.model.Question;
|
||||||
|
import ltd.qubit.survey.model.Option;
|
||||||
|
import ltd.qubit.survey.service.QuestionService;
|
||||||
|
import ltd.qubit.survey.service.OptionService;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题控制器
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/questions")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class QuestionController {
|
||||||
|
private final QuestionService questionService;
|
||||||
|
private final OptionService optionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户的问题列表
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 问题列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/user/{userId}")
|
||||||
|
public List<Question> getUserQuestions(@PathVariable Long userId) {
|
||||||
|
return questionService.getUserQuestions(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取问题的选项列表
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 选项列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/{questionId}/options")
|
||||||
|
public List<Option> getQuestionOptions(@PathVariable Long questionId) {
|
||||||
|
return optionService.findByQuestionId(questionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下一个问题
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param currentQuestionNumber 当前问题序号
|
||||||
|
* @param selectedOptions 选中的选项列表
|
||||||
|
* @return 下一个问题
|
||||||
|
*/
|
||||||
|
@GetMapping("/next")
|
||||||
|
public Question getNextQuestion(
|
||||||
|
@PathVariable Long userId,
|
||||||
|
@PathVariable Integer currentQuestionNumber,
|
||||||
|
@PathVariable List<String> selectedOptions) {
|
||||||
|
return questionService.getNextQuestion(userId, currentQuestionNumber, selectedOptions)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("没有更多问题了"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
package ltd.qubit.survey.controller;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import ltd.qubit.survey.model.SurveyResponse;
|
||||||
|
import ltd.qubit.survey.service.SurveyResponseService;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问卷控制器
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/survey")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SurveyController {
|
||||||
|
private final SurveyResponseService surveyResponseService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交问卷答案
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param responses 答案列表
|
||||||
|
* @return 提交成功的答案列表
|
||||||
|
*/
|
||||||
|
@PostMapping("/submit/{userId}")
|
||||||
|
public List<SurveyResponse> submitSurvey(
|
||||||
|
@PathVariable Long userId,
|
||||||
|
@RequestBody List<SurveyResponse> responses) {
|
||||||
|
return surveyResponseService.submitSurvey(userId, responses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户的问卷答案
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 答案列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/user/{userId}")
|
||||||
|
public List<SurveyResponse> getUserResponses(@PathVariable Long userId) {
|
||||||
|
return surveyResponseService.findByUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取问题的所有答案
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 答案列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/question/{questionId}")
|
||||||
|
public List<SurveyResponse> getQuestionResponses(@PathVariable Long questionId) {
|
||||||
|
return surveyResponseService.findByQuestionId(questionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package ltd.qubit.survey.controller;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import ltd.qubit.survey.model.User;
|
||||||
|
import ltd.qubit.survey.service.UserService;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户控制器
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/users")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserController {
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户注册
|
||||||
|
*
|
||||||
|
* @param user 用户信息
|
||||||
|
* @return 注册成功的用户信息
|
||||||
|
*/
|
||||||
|
@PostMapping("/register")
|
||||||
|
public User register(@RequestBody User user) {
|
||||||
|
return userService.register(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询用户
|
||||||
|
*
|
||||||
|
* @param id 用户ID
|
||||||
|
* @return 用户信息
|
||||||
|
*/
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public User findById(@PathVariable Long id) {
|
||||||
|
return userService.findById(id)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("用户不存在"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手机号是否已注册
|
||||||
|
*
|
||||||
|
* @param phone 手机号
|
||||||
|
* @return 是否已注册
|
||||||
|
*/
|
||||||
|
@GetMapping("/check/{phone}")
|
||||||
|
public boolean checkPhone(@PathVariable String phone) {
|
||||||
|
return userService.isPhoneRegistered(phone);
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/main/java/ltd/qubit/survey/dao/BaseDao.java
Normal file
51
src/main/java/ltd/qubit/survey/dao/BaseDao.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package ltd.qubit.survey.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础DAO接口,定义通用的CRUD操作
|
||||||
|
*
|
||||||
|
* @param <T> 实体类型
|
||||||
|
* @param <K> 主键类型
|
||||||
|
*/
|
||||||
|
public interface BaseDao<T, K> {
|
||||||
|
/**
|
||||||
|
* 插入一条记录
|
||||||
|
*
|
||||||
|
* @param entity 实体对象
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int insert(T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主键删除记录
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int deleteById(K id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新记录
|
||||||
|
*
|
||||||
|
* @param entity 实体对象
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int update(T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主键查询
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
* @return 实体对象
|
||||||
|
*/
|
||||||
|
Optional<T> findById(K id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有记录
|
||||||
|
*
|
||||||
|
* @return 实体对象列表
|
||||||
|
*/
|
||||||
|
List<T> findAll();
|
||||||
|
}
|
||||||
43
src/main/java/ltd/qubit/survey/dao/OptionDao.java
Normal file
43
src/main/java/ltd/qubit/survey/dao/OptionDao.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package ltd.qubit.survey.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.Option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项DAO接口
|
||||||
|
*/
|
||||||
|
public interface OptionDao extends BaseDao<Option, Long> {
|
||||||
|
/**
|
||||||
|
* 根据问题ID查询选项列表
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 选项列表
|
||||||
|
*/
|
||||||
|
List<Option> findByQuestionId(Long questionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据问题ID和选项代码查询
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @param optionCode 选项代码
|
||||||
|
* @return 选项对象
|
||||||
|
*/
|
||||||
|
Optional<Option> findByQuestionIdAndCode(Long questionId, String optionCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量插入选项
|
||||||
|
*
|
||||||
|
* @param options 选项列表
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int batchInsert(List<Option> options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据问题ID删除所有选项
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int deleteByQuestionId(Long questionId);
|
||||||
|
}
|
||||||
41
src/main/java/ltd/qubit/survey/dao/QuestionDao.java
Normal file
41
src/main/java/ltd/qubit/survey/dao/QuestionDao.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package ltd.qubit.survey.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.Question;
|
||||||
|
import ltd.qubit.survey.model.WorkArea;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题DAO接口
|
||||||
|
*/
|
||||||
|
public interface QuestionDao extends BaseDao<Question, Long> {
|
||||||
|
/**
|
||||||
|
* 根据问题序号查询
|
||||||
|
*
|
||||||
|
* @param questionNumber 问题序号
|
||||||
|
* @return 问题对象
|
||||||
|
*/
|
||||||
|
Optional<Question> findByQuestionNumber(Integer questionNumber);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据工作领域查询问题列表
|
||||||
|
*
|
||||||
|
* @param workArea 工作领域
|
||||||
|
* @return 问题列表
|
||||||
|
*/
|
||||||
|
List<Question> findByWorkArea(WorkArea workArea);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询通用问题列表(不针对特定工作领域)
|
||||||
|
*
|
||||||
|
* @return 问题列表
|
||||||
|
*/
|
||||||
|
List<Question> findCommonQuestions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下一个可用的问题序号
|
||||||
|
*
|
||||||
|
* @return 下一个问题序号
|
||||||
|
*/
|
||||||
|
Integer getNextQuestionNumber();
|
||||||
|
}
|
||||||
51
src/main/java/ltd/qubit/survey/dao/SurveyResponseDao.java
Normal file
51
src/main/java/ltd/qubit/survey/dao/SurveyResponseDao.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package ltd.qubit.survey.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.SurveyResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问卷答案DAO接口
|
||||||
|
*/
|
||||||
|
public interface SurveyResponseDao extends BaseDao<SurveyResponse, Long> {
|
||||||
|
/**
|
||||||
|
* 根据用户ID查询答案列表
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 答案列表
|
||||||
|
*/
|
||||||
|
List<SurveyResponse> findByUserId(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据问题ID查询答案列表
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 答案列表
|
||||||
|
*/
|
||||||
|
List<SurveyResponse> findByQuestionId(Long questionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户ID和问题ID查询答案
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 答案对象
|
||||||
|
*/
|
||||||
|
Optional<SurveyResponse> findByUserIdAndQuestionId(Long userId, Long questionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量插入答案
|
||||||
|
*
|
||||||
|
* @param responses 答案列表
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int batchInsert(List<SurveyResponse> responses);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户ID删除所有答案
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 影响的行数
|
||||||
|
*/
|
||||||
|
int deleteByUserId(Long userId);
|
||||||
|
}
|
||||||
27
src/main/java/ltd/qubit/survey/dao/UserDao.java
Normal file
27
src/main/java/ltd/qubit/survey/dao/UserDao.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package ltd.qubit.survey.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.User;
|
||||||
|
import ltd.qubit.survey.model.WorkArea;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户DAO接口
|
||||||
|
*/
|
||||||
|
public interface UserDao extends BaseDao<User, Long> {
|
||||||
|
/**
|
||||||
|
* 根据手机号查询用户
|
||||||
|
*
|
||||||
|
* @param phone 手机号
|
||||||
|
* @return 用户对象
|
||||||
|
*/
|
||||||
|
Optional<User> findByPhone(String phone);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据工作领域查询用户列表
|
||||||
|
*
|
||||||
|
* @param workArea 工作领域
|
||||||
|
* @return 用户列表
|
||||||
|
*/
|
||||||
|
List<User> findByWorkArea(WorkArea workArea);
|
||||||
|
}
|
||||||
65
src/main/java/ltd/qubit/survey/model/ErrorInfo.java
Normal file
65
src/main/java/ltd/qubit/survey/model/ErrorInfo.java
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ErrorInfo {
|
||||||
|
/**
|
||||||
|
* 错误代码
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误消息
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误详情
|
||||||
|
*/
|
||||||
|
private String detail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间戳
|
||||||
|
*/
|
||||||
|
private long timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求路径
|
||||||
|
*/
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建错误信息
|
||||||
|
*
|
||||||
|
* @param code 错误代码
|
||||||
|
* @param message 错误消息
|
||||||
|
* @param detail 错误详情
|
||||||
|
* @param path 请求路径
|
||||||
|
* @return 错误信息
|
||||||
|
*/
|
||||||
|
public static ErrorInfo of(String code, String message, String detail, String path) {
|
||||||
|
ErrorInfo error = new ErrorInfo();
|
||||||
|
error.setCode(code);
|
||||||
|
error.setMessage(message);
|
||||||
|
error.setDetail(detail);
|
||||||
|
error.setPath(path);
|
||||||
|
error.setTimestamp(System.currentTimeMillis());
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建错误信息(无详情)
|
||||||
|
*
|
||||||
|
* @param code 错误代码
|
||||||
|
* @param message 错误消息
|
||||||
|
* @param path 请求路径
|
||||||
|
* @return 错误信息
|
||||||
|
*/
|
||||||
|
public static ErrorInfo of(String code, String message, String path) {
|
||||||
|
return of(code, message, null, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/main/java/ltd/qubit/survey/model/Option.java
Normal file
40
src/main/java/ltd/qubit/survey/model/Option.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题选项实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class Option {
|
||||||
|
/**
|
||||||
|
* 选项ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联的问题ID
|
||||||
|
*/
|
||||||
|
private Long questionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项代码(如A、B、C)
|
||||||
|
*/
|
||||||
|
private String optionCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否需要填写文本
|
||||||
|
*/
|
||||||
|
private Boolean requiresText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
36
src/main/java/ltd/qubit/survey/model/PositionType.java
Normal file
36
src/main/java/ltd/qubit/survey/model/PositionType.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 岗位性质枚举
|
||||||
|
*/
|
||||||
|
public enum PositionType {
|
||||||
|
/**
|
||||||
|
* 管理岗
|
||||||
|
*/
|
||||||
|
MANAGEMENT("管理岗"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 技术岗
|
||||||
|
*/
|
||||||
|
TECHNICAL("技术岗"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务岗
|
||||||
|
*/
|
||||||
|
BUSINESS("业务岗"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 职能支持岗
|
||||||
|
*/
|
||||||
|
SUPPORT("职能支持岗");
|
||||||
|
|
||||||
|
private final String displayName;
|
||||||
|
|
||||||
|
PositionType(String displayName) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/main/java/ltd/qubit/survey/model/Question.java
Normal file
50
src/main/java/ltd/qubit/survey/model/Question.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class Question {
|
||||||
|
/**
|
||||||
|
* 问题ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题序号
|
||||||
|
*/
|
||||||
|
private Integer questionNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题类型(单选、多选、文本)
|
||||||
|
*/
|
||||||
|
private QuestionType questionType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 针对的工作领域(为null表示通用问题)
|
||||||
|
*/
|
||||||
|
private WorkArea workArea;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否必答
|
||||||
|
*/
|
||||||
|
private Boolean isRequired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳转逻辑(JSON格式)
|
||||||
|
*/
|
||||||
|
private String nextQuestionLogic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
21
src/main/java/ltd/qubit/survey/model/QuestionType.java
Normal file
21
src/main/java/ltd/qubit/survey/model/QuestionType.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题类型枚举
|
||||||
|
*/
|
||||||
|
public enum QuestionType {
|
||||||
|
/**
|
||||||
|
* 单选题
|
||||||
|
*/
|
||||||
|
SINGLE_CHOICE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多选题
|
||||||
|
*/
|
||||||
|
MULTIPLE_CHOICE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本题
|
||||||
|
*/
|
||||||
|
TEXT
|
||||||
|
}
|
||||||
41
src/main/java/ltd/qubit/survey/model/SurveyResponse.java
Normal file
41
src/main/java/ltd/qubit/survey/model/SurveyResponse.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问卷答案实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class SurveyResponse {
|
||||||
|
/**
|
||||||
|
* 答案ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 问题ID
|
||||||
|
*/
|
||||||
|
private Long questionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选中的选项代码列表(JSON格式)
|
||||||
|
*/
|
||||||
|
private List<String> selectedOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本答案
|
||||||
|
*/
|
||||||
|
private String textAnswer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
40
src/main/java/ltd/qubit/survey/model/User.java
Normal file
40
src/main/java/ltd/qubit/survey/model/User.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户信息实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class User {
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 姓名
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手机号码
|
||||||
|
*/
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作领域
|
||||||
|
*/
|
||||||
|
private WorkArea workArea;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 岗位性质
|
||||||
|
*/
|
||||||
|
private PositionType positionType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
51
src/main/java/ltd/qubit/survey/model/WorkArea.java
Normal file
51
src/main/java/ltd/qubit/survey/model/WorkArea.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package ltd.qubit.survey.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作领域枚举
|
||||||
|
*/
|
||||||
|
public enum WorkArea {
|
||||||
|
/**
|
||||||
|
* 研发领域
|
||||||
|
*/
|
||||||
|
RD("研发"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目领域
|
||||||
|
*/
|
||||||
|
PROJECT("项目"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保险领域
|
||||||
|
*/
|
||||||
|
INSURANCE("保险"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 财务领域
|
||||||
|
*/
|
||||||
|
FINANCE("财务"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运营领域
|
||||||
|
*/
|
||||||
|
OPERATION("运营"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客服领域
|
||||||
|
*/
|
||||||
|
CUSTOMER_SERVICE("客服"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 综合管理领域
|
||||||
|
*/
|
||||||
|
ADMIN("综合管理");
|
||||||
|
|
||||||
|
private final String displayName;
|
||||||
|
|
||||||
|
WorkArea(String displayName) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/main/java/ltd/qubit/survey/service/BaseService.java
Normal file
55
src/main/java/ltd/qubit/survey/service/BaseService.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package ltd.qubit.survey.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础Service接口,定义通用的CRUD操作
|
||||||
|
*
|
||||||
|
* @param <T> 实体类型
|
||||||
|
* @param <K> 主键类型
|
||||||
|
*/
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public interface BaseService<T, K> {
|
||||||
|
/**
|
||||||
|
* 新增
|
||||||
|
*
|
||||||
|
* @param entity 实体对象
|
||||||
|
* @return 实体对象
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
T create(T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
void delete(K id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新
|
||||||
|
*
|
||||||
|
* @param entity 实体对象
|
||||||
|
* @return 实体对象
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
T update(T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
* @return 实体对象
|
||||||
|
*/
|
||||||
|
Optional<T> findById(K id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有
|
||||||
|
*
|
||||||
|
* @return 实体对象列表
|
||||||
|
*/
|
||||||
|
List<T> findAll();
|
||||||
|
}
|
||||||
42
src/main/java/ltd/qubit/survey/service/OptionService.java
Normal file
42
src/main/java/ltd/qubit/survey/service/OptionService.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package ltd.qubit.survey.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import ltd.qubit.survey.model.Option;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选项服务接口
|
||||||
|
*/
|
||||||
|
public interface OptionService extends BaseService<Option, Long> {
|
||||||
|
/**
|
||||||
|
* 根据问题ID查询选项列表
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @return 选项列表
|
||||||
|
*/
|
||||||
|
List<Option> findByQuestionId(Long questionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据问题ID和选项代码查询
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
* @param optionCode 选项代码
|
||||||
|
* @return 选项对象
|
||||||
|
*/
|
||||||
|
Optional<Option> findByQuestionIdAndCode(Long questionId, String optionCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量创建选项
|
||||||
|
*
|
||||||
|
* @param options 选项列表
|
||||||
|
* @return 创建成功的选项列表
|
||||||
|
*/
|
||||||
|
List<Option> batchCreate(List<Option> options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除问题的所有选项
|
||||||
|
*
|
||||||
|
* @param questionId 问题ID
|
||||||
|
*/
|
||||||
|
void deleteByQuestionId(Long questionId);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user