feat: 完成后端开发,包括项目规则文档、用户管理、问卷管理、答案管理功能,配置 Docker 环境,修复数据库连接配置
This commit is contained in:
parent
0852760852
commit
20c57a13d2
62
.cursorrules
Normal file
62
.cursorrules
Normal file
@ -0,0 +1,62 @@
|
||||
# 项目规则文档
|
||||
|
||||
## 1. 日志查看规则
|
||||
- 不允许使用 `tail -f` 命令查看日志文件
|
||||
- 应该使用 `tail` 命令查看日志内容
|
||||
- 查看日志直接查看项目本身的日志,而非tomcat的日志
|
||||
|
||||
## 2. 项目结构规则
|
||||
- 后端项目目录:`backend/`
|
||||
- 前端项目目录:`frontend/`
|
||||
- 数据库脚本目录:`database/`
|
||||
- 文档目录:`doc/`
|
||||
|
||||
## 3. 构建和部署规则
|
||||
- 使用 Maven 构建后端项目:`mvn clean package -DskipTests`
|
||||
- Tomcat 运行在 Docker 容器中:
|
||||
- 端口映射:18080
|
||||
- 数据目录:`$DOCKER_DATA_DIR/tomcat`
|
||||
- 日志目录:`$DOCKER_DATA_DIR/tomcat/logs`
|
||||
- WAR包目录:`$DOCKER_DATA_DIR/tomcat/webapps`
|
||||
- 应用上下文路径:`/llm-survey-api`
|
||||
- 复制文件时,使用 `command cp` 命令
|
||||
|
||||
## 4. 数据库规则
|
||||
- 数据库名称:`llm_survey`
|
||||
- 数据库用户:`dev`
|
||||
- 数据库地址:`127.0.0.1:3306`
|
||||
- 使用 `init_database.sh` 脚本初始化数据库
|
||||
|
||||
## 5. 代码规范
|
||||
- Java源代码使用UTF-8编码
|
||||
- 使用Lombok简化代码
|
||||
- DAO层继承BaseDao接口
|
||||
- Service层继承BaseService接口
|
||||
- 控制器使用RestController注解
|
||||
|
||||
## 6. Spring配置规则
|
||||
- 共享的bean定义放在 `applicationContext.xml`
|
||||
- MVC相关配置放在 `spring-mvc.xml`
|
||||
- MyBatis相关配置放在 `spring-mybatis.xml`
|
||||
- 避免重复的bean定义
|
||||
|
||||
## 7. 错误处理规则
|
||||
- 使用统一的错误处理格式(ErrorInfo)
|
||||
- 所有异常由GlobalExceptionHandler处理
|
||||
- 业务异常使用IllegalArgumentException
|
||||
|
||||
## 8. API规范
|
||||
- RESTful API设计
|
||||
- 统一的响应格式
|
||||
- 支持跨域访问
|
||||
- API文档位于 `doc/api.md`
|
||||
|
||||
## 9. 安全规则
|
||||
- 不在代码中硬编码敏感信息
|
||||
- 配置信息放在properties文件中
|
||||
- 使用prepared statement防止SQL注入
|
||||
|
||||
## 10. 版本控制
|
||||
- 使用Git进行版本控制
|
||||
- 遵循语义化版本规范
|
||||
- 重要配置文件加入版本控制
|
||||
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# Maven
|
||||
target/
|
||||
*.war
|
||||
*.jar
|
||||
*.ear
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
|
||||
# Node.js
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
dist/
|
||||
.cache/
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/
|
||||
*.code-workspace
|
||||
.history/
|
||||
|
||||
# IDE - IntelliJ IDEA
|
||||
.idea/
|
||||
*.iml
|
||||
*.iws
|
||||
*.ipr
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@ -14,16 +14,18 @@
|
||||
<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>
|
||||
<spring.version>6.1.4</spring.version>
|
||||
<mybatis.version>3.5.15</mybatis.version>
|
||||
<mybatis-spring.version>2.1.2</mybatis-spring.version>
|
||||
<mybatis-spring.version>3.0.3</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>
|
||||
<servlet-api.version>6.0.0</servlet-api.version>
|
||||
<pagehelper.version>5.3.3</pagehelper.version>
|
||||
<aspectj.version>1.9.21</aspectj.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@ -82,6 +84,13 @@
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- PageHelper -->
|
||||
<dependency>
|
||||
<groupId>com.github.pagehelper</groupId>
|
||||
<artifactId>pagehelper</artifactId>
|
||||
<version>${pagehelper.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
@ -104,11 +113,18 @@
|
||||
|
||||
<!-- Servlet API -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<version>${servlet-api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- AspectJ -->
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<version>${aspectj.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -122,6 +138,9 @@
|
||||
<configuration>
|
||||
<release>${java.version}</release>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
</compilerArgs>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
package com.fasterxml.jackson.databind;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
|
||||
/**
|
||||
* 自定义的 JSON 类型处理器
|
||||
*/
|
||||
public class JsonTypeHandler extends BaseTypeHandler<Object> {
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
|
||||
throws SQLException {
|
||||
try {
|
||||
ps.setString(i, MAPPER.writeValueAsString(parameter));
|
||||
} catch (Exception e) {
|
||||
throw new SQLException("Error converting JSON to String", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return parse(rs.getString(columnName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return parse(rs.getString(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return parse(cs.getString(columnIndex));
|
||||
}
|
||||
|
||||
private Object parse(String json) throws SQLException {
|
||||
try {
|
||||
if (json == null || json.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return MAPPER.readValue(json, Object.class);
|
||||
} catch (Exception e) {
|
||||
throw new SQLException("Error converting String to JSON", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package ltd.qubit.survey.common.mybatis;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* 自定义的 JSON 类型处理器
|
||||
*/
|
||||
public class JsonTypeHandler extends BaseTypeHandler<Object> {
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
|
||||
throws SQLException {
|
||||
try {
|
||||
ps.setString(i, MAPPER.writeValueAsString(parameter));
|
||||
} catch (Exception e) {
|
||||
throw new SQLException("Error converting JSON to String", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return parse(rs.getString(columnName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return parse(rs.getString(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return parse(cs.getString(columnIndex));
|
||||
}
|
||||
|
||||
private Object parse(String json) throws SQLException {
|
||||
try {
|
||||
if (json == null || json.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return MAPPER.readValue(json, Object.class);
|
||||
} catch (Exception e) {
|
||||
throw new SQLException("Error converting String to JSON", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
<result property="selectedOptions" column="selected_options" typeHandler="ltd.qubit.survey.common.mybatis.JsonTypeHandler"/>
|
||||
@ -1,6 +1,6 @@
|
||||
# 数据库配置
|
||||
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.url=jdbc:mysql://host.docker.internal:3306/llm_survey?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
||||
jdbc.username=dev
|
||||
jdbc.password=
|
||||
|
||||
|
||||
54
backend/src/main/resources/logback.xml
Normal file
54
backend/src/main/resources/logback.xml
Normal file
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<!-- 定义日志文件的存储地址 -->
|
||||
<property name="LOG_HOME" value="${catalina.base}/logs/llm-survey-api"/>
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 按照每天生成日志文件 -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/app.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件输出的文件名 -->
|
||||
<fileNamePattern>${LOG_HOME}/app.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志文件保留天数 -->
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 异步输出 -->
|
||||
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<!-- 不丢失日志,默认的,如果队列的80%已满,则会丢弃TRACE、DEBUG、INFO级别的日志 -->
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<!-- 更改默认的队列的深度,该值会影响性能,默认值为256 -->
|
||||
<queueSize>512</queueSize>
|
||||
<!-- 添加附加的appender,最多只能添加一个 -->
|
||||
<appender-ref ref="FILE"/>
|
||||
</appender>
|
||||
|
||||
<!-- MyBatis日志配置 -->
|
||||
<logger name="org.apache.ibatis" level="DEBUG"/>
|
||||
<logger name="java.sql" level="DEBUG"/>
|
||||
|
||||
<!-- 项目日志配置 -->
|
||||
<logger name="ltd.qubit.survey" level="DEBUG"/>
|
||||
|
||||
<!-- Spring日志配置 -->
|
||||
<logger name="org.springframework" level="INFO"/>
|
||||
|
||||
<!-- 日志输出级别 -->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="ASYNC_FILE"/>
|
||||
</root>
|
||||
</configuration>
|
||||
@ -2,7 +2,7 @@
|
||||
<!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">
|
||||
<resultMap id="baseOptionMap" type="ltd.qubit.survey.model.Option">
|
||||
<id property="id" column="id"/>
|
||||
<result property="questionId" column="question_id"/>
|
||||
<result property="optionCode" column="option_code"/>
|
||||
@ -52,21 +52,21 @@
|
||||
</delete>
|
||||
|
||||
<!-- 根据ID查询 -->
|
||||
<select id="findById" parameterType="long" resultMap="optionMap">
|
||||
<select id="findById" parameterType="long" resultMap="baseOptionMap">
|
||||
SELECT <include refid="baseColumns"/>
|
||||
FROM options
|
||||
WHERE id = #{id}
|
||||
</select>
|
||||
|
||||
<!-- 查询所有 -->
|
||||
<select id="findAll" resultMap="optionMap">
|
||||
<select id="findAll" resultMap="baseOptionMap">
|
||||
SELECT <include refid="baseColumns"/>
|
||||
FROM options
|
||||
ORDER BY question_id, option_code
|
||||
</select>
|
||||
|
||||
<!-- 根据问题ID查询 -->
|
||||
<select id="findByQuestionId" parameterType="long" resultMap="optionMap">
|
||||
<select id="findByQuestionId" parameterType="long" resultMap="baseOptionMap">
|
||||
SELECT <include refid="baseColumns"/>
|
||||
FROM options
|
||||
WHERE question_id = #{questionId}
|
||||
@ -74,7 +74,7 @@
|
||||
</select>
|
||||
|
||||
<!-- 根据问题ID和选项代码查询 -->
|
||||
<select id="findByQuestionIdAndCode" resultMap="optionMap">
|
||||
<select id="findByQuestionIdAndCode" resultMap="baseOptionMap">
|
||||
SELECT <include refid="baseColumns"/>
|
||||
FROM options
|
||||
WHERE question_id = #{questionId}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<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="selectedOptions" column="selected_options" typeHandler="com.fasterxml.jackson.databind.JsonTypeHandler"/>
|
||||
<result property="textAnswer" column="text_answer"/>
|
||||
<result property="createdAt" column="created_at"/>
|
||||
</resultMap>
|
||||
@ -19,7 +19,7 @@
|
||||
<!-- 插入 -->
|
||||
<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})
|
||||
VALUES (#{userId}, #{questionId}, #{selectedOptions,typeHandler=com.fasterxml.jackson.databind.JsonTypeHandler}, #{textAnswer})
|
||||
</insert>
|
||||
|
||||
<!-- 批量插入 -->
|
||||
@ -28,7 +28,7 @@
|
||||
VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.userId}, #{item.questionId},
|
||||
#{item.selectedOptions,typeHandler=org.apache.ibatis.type.JsonTypeHandler},
|
||||
#{item.selectedOptions,typeHandler=com.fasterxml.jackson.databind.JsonTypeHandler},
|
||||
#{item.textAnswer})
|
||||
</foreach>
|
||||
</insert>
|
||||
@ -38,7 +38,7 @@
|
||||
UPDATE survey_responses
|
||||
SET user_id = #{userId},
|
||||
question_id = #{questionId},
|
||||
selected_options = #{selectedOptions,typeHandler=org.apache.ibatis.type.JsonTypeHandler},
|
||||
selected_options = #{selectedOptions,typeHandler=com.fasterxml.jackson.databind.JsonTypeHandler},
|
||||
text_answer = #{textAnswer}
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
@ -12,6 +12,12 @@
|
||||
<setting name="logImpl" value="SLF4J"/>
|
||||
</settings>
|
||||
|
||||
<!-- 类型别名配置 -->
|
||||
<typeAliases>
|
||||
<package name="ltd.qubit.survey.model"/>
|
||||
</typeAliases>
|
||||
|
||||
<!-- 类型处理器配置 -->
|
||||
<typeHandlers>
|
||||
<!-- 枚举类型处理器 -->
|
||||
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"
|
||||
@ -20,13 +26,13 @@
|
||||
javaType="ltd.qubit.survey.model.PositionType"/>
|
||||
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"
|
||||
javaType="ltd.qubit.survey.model.QuestionType"/>
|
||||
<!-- JSON类型处理器 -->
|
||||
<typeHandler handler="ltd.qubit.survey.common.mybatis.JsonTypeHandler"
|
||||
javaType="java.util.List"/>
|
||||
</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"/>
|
||||
<package name="ltd.qubit.survey.dao"/>
|
||||
</mappers>
|
||||
</configuration>
|
||||
@ -3,16 +3,32 @@
|
||||
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"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
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">
|
||||
http://www.springframework.org/schema/tx/spring-tx.xsd
|
||||
http://www.springframework.org/schema/aop
|
||||
http://www.springframework.org/schema/aop/spring-aop.xsd">
|
||||
|
||||
<!-- 加载属性文件 -->
|
||||
<context:property-placeholder location="classpath:application.properties"/>
|
||||
|
||||
<!-- 配置ObjectMapper -->
|
||||
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" primary="true">
|
||||
<property name="dateFormat">
|
||||
<bean class="java.text.SimpleDateFormat">
|
||||
<constructor-arg value="yyyy-MM-dd HH:mm:ss"/>
|
||||
</bean>
|
||||
</property>
|
||||
<!-- 配置JSON序列化特性 -->
|
||||
<property name="serializationInclusion">
|
||||
<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- 开启注解扫描,排除Controller -->
|
||||
<context:component-scan base-package="ltd.qubit.survey">
|
||||
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
|
||||
@ -21,4 +37,21 @@
|
||||
<!-- 配置事务注解驱动 -->
|
||||
<tx:annotation-driven/>
|
||||
|
||||
<!-- 配置事务通知 -->
|
||||
<tx:advice id="txAdvice" transaction-manager="transactionManager">
|
||||
<tx:attributes>
|
||||
<tx:method name="get*" read-only="true"/>
|
||||
<tx:method name="find*" read-only="true"/>
|
||||
<tx:method name="list*" read-only="true"/>
|
||||
<tx:method name="query*" read-only="true"/>
|
||||
<tx:method name="*" propagation="REQUIRED"/>
|
||||
</tx:attributes>
|
||||
</tx:advice>
|
||||
|
||||
<!-- 配置事务切面 -->
|
||||
<aop:config>
|
||||
<aop:pointcut id="txPointcut" expression="execution(* ltd.qubit.survey.service..*.*(..))"/>
|
||||
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
|
||||
</aop:config>
|
||||
|
||||
</beans>
|
||||
@ -20,27 +20,39 @@
|
||||
<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 name="objectMapper" ref="objectMapper"/>
|
||||
<property name="supportedMediaTypes">
|
||||
<list>
|
||||
<value>application/json;charset=UTF-8</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
<!-- 配置字符串转换器 -->
|
||||
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
|
||||
<property name="supportedMediaTypes">
|
||||
<list>
|
||||
<value>text/plain;charset=UTF-8</value>
|
||||
<value>text/html;charset=UTF-8</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
</mvc:message-converters>
|
||||
</mvc:annotation-driven>
|
||||
|
||||
<!-- 配置静态资源处理 -->
|
||||
<mvc:default-servlet-handler/>
|
||||
|
||||
<!-- 配置跨域支持 -->
|
||||
<mvc:cors>
|
||||
<mvc:mapping path="/**"
|
||||
allowed-origins="*"
|
||||
allowed-origins="http://localhost:3000,http://localhost:8080"
|
||||
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>
|
||||
|
||||
<!-- 配置异常处理器 -->
|
||||
<bean class="ltd.qubit.survey.controller.GlobalExceptionHandler"/>
|
||||
|
||||
</beans>
|
||||
@ -1,8 +1,11 @@
|
||||
<?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: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/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/tx
|
||||
http://www.springframework.org/schema/tx/spring-tx.xsd">
|
||||
|
||||
<!-- 配置数据源 -->
|
||||
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
|
||||
@ -16,13 +19,35 @@
|
||||
<property name="idleTimeout" value="${jdbc.pool.idleTimeout}"/>
|
||||
<property name="connectionTimeout" value="${jdbc.pool.connectionTimeout}"/>
|
||||
<property name="connectionTestQuery" value="${jdbc.pool.connectionTestQuery}"/>
|
||||
<!-- 其他配置 -->
|
||||
<property name="autoCommit" value="true"/>
|
||||
<property name="poolName" value="HikariPool-Survey"/>
|
||||
<property name="maxLifetime" value="1800000"/>
|
||||
<property name="validationTimeout" value="5000"/>
|
||||
<property name="leakDetectionThreshold" value="60000"/>
|
||||
</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"/>
|
||||
<property name="mapperLocations" value="classpath*:mybatis/mapper/*.xml"/>
|
||||
<!-- 配置插件 -->
|
||||
<property name="plugins">
|
||||
<array>
|
||||
<!-- 分页插件 -->
|
||||
<bean class="com.github.pagehelper.PageInterceptor">
|
||||
<property name="properties">
|
||||
<value>
|
||||
helperDialect=mysql
|
||||
reasonable=true
|
||||
supportMethodsArguments=true
|
||||
params=count=countSql
|
||||
</value>
|
||||
</property>
|
||||
</bean>
|
||||
</array>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- 配置Mapper扫描器 -->
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
|
||||
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">
|
||||
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
|
||||
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
|
||||
version="6.0">
|
||||
|
||||
<display-name>LLM Survey API</display-name>
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# 数据库配置
|
||||
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.url=jdbc:mysql://host.docker.internal:3306/llm_survey?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
||||
jdbc.username=dev
|
||||
jdbc.password=
|
||||
|
||||
|
||||
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.
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -2,7 +2,7 @@
|
||||
<!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">
|
||||
<resultMap id="baseOptionMap" type="ltd.qubit.survey.model.Option">
|
||||
<id property="id" column="id"/>
|
||||
<result property="questionId" column="question_id"/>
|
||||
<result property="optionCode" column="option_code"/>
|
||||
@ -52,21 +52,21 @@
|
||||
</delete>
|
||||
|
||||
<!-- 根据ID查询 -->
|
||||
<select id="findById" parameterType="long" resultMap="optionMap">
|
||||
<select id="findById" parameterType="long" resultMap="baseOptionMap">
|
||||
SELECT <include refid="baseColumns"/>
|
||||
FROM options
|
||||
WHERE id = #{id}
|
||||
</select>
|
||||
|
||||
<!-- 查询所有 -->
|
||||
<select id="findAll" resultMap="optionMap">
|
||||
<select id="findAll" resultMap="baseOptionMap">
|
||||
SELECT <include refid="baseColumns"/>
|
||||
FROM options
|
||||
ORDER BY question_id, option_code
|
||||
</select>
|
||||
|
||||
<!-- 根据问题ID查询 -->
|
||||
<select id="findByQuestionId" parameterType="long" resultMap="optionMap">
|
||||
<select id="findByQuestionId" parameterType="long" resultMap="baseOptionMap">
|
||||
SELECT <include refid="baseColumns"/>
|
||||
FROM options
|
||||
WHERE question_id = #{questionId}
|
||||
@ -74,7 +74,7 @@
|
||||
</select>
|
||||
|
||||
<!-- 根据问题ID和选项代码查询 -->
|
||||
<select id="findByQuestionIdAndCode" resultMap="optionMap">
|
||||
<select id="findByQuestionIdAndCode" resultMap="baseOptionMap">
|
||||
SELECT <include refid="baseColumns"/>
|
||||
FROM options
|
||||
WHERE question_id = #{questionId}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<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="selectedOptions" column="selected_options" typeHandler="com.fasterxml.jackson.databind.JsonTypeHandler"/>
|
||||
<result property="textAnswer" column="text_answer"/>
|
||||
<result property="createdAt" column="created_at"/>
|
||||
</resultMap>
|
||||
@ -19,7 +19,7 @@
|
||||
<!-- 插入 -->
|
||||
<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})
|
||||
VALUES (#{userId}, #{questionId}, #{selectedOptions,typeHandler=com.fasterxml.jackson.databind.JsonTypeHandler}, #{textAnswer})
|
||||
</insert>
|
||||
|
||||
<!-- 批量插入 -->
|
||||
@ -28,7 +28,7 @@
|
||||
VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(#{item.userId}, #{item.questionId},
|
||||
#{item.selectedOptions,typeHandler=org.apache.ibatis.type.JsonTypeHandler},
|
||||
#{item.selectedOptions,typeHandler=com.fasterxml.jackson.databind.JsonTypeHandler},
|
||||
#{item.textAnswer})
|
||||
</foreach>
|
||||
</insert>
|
||||
@ -38,7 +38,7 @@
|
||||
UPDATE survey_responses
|
||||
SET user_id = #{userId},
|
||||
question_id = #{questionId},
|
||||
selected_options = #{selectedOptions,typeHandler=org.apache.ibatis.type.JsonTypeHandler},
|
||||
selected_options = #{selectedOptions,typeHandler=com.fasterxml.jackson.databind.JsonTypeHandler},
|
||||
text_answer = #{textAnswer}
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
@ -12,6 +12,12 @@
|
||||
<setting name="logImpl" value="SLF4J"/>
|
||||
</settings>
|
||||
|
||||
<!-- 类型别名配置 -->
|
||||
<typeAliases>
|
||||
<package name="ltd.qubit.survey.model"/>
|
||||
</typeAliases>
|
||||
|
||||
<!-- 类型处理器配置 -->
|
||||
<typeHandlers>
|
||||
<!-- 枚举类型处理器 -->
|
||||
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"
|
||||
@ -20,13 +26,13 @@
|
||||
javaType="ltd.qubit.survey.model.PositionType"/>
|
||||
<typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"
|
||||
javaType="ltd.qubit.survey.model.QuestionType"/>
|
||||
<!-- JSON类型处理器 -->
|
||||
<typeHandler handler="ltd.qubit.survey.common.mybatis.JsonTypeHandler"
|
||||
javaType="java.util.List"/>
|
||||
</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"/>
|
||||
<package name="ltd.qubit.survey.dao"/>
|
||||
</mappers>
|
||||
</configuration>
|
||||
@ -3,16 +3,32 @@
|
||||
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"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
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">
|
||||
http://www.springframework.org/schema/tx/spring-tx.xsd
|
||||
http://www.springframework.org/schema/aop
|
||||
http://www.springframework.org/schema/aop/spring-aop.xsd">
|
||||
|
||||
<!-- 加载属性文件 -->
|
||||
<context:property-placeholder location="classpath:application.properties"/>
|
||||
|
||||
<!-- 配置ObjectMapper -->
|
||||
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" primary="true">
|
||||
<property name="dateFormat">
|
||||
<bean class="java.text.SimpleDateFormat">
|
||||
<constructor-arg value="yyyy-MM-dd HH:mm:ss"/>
|
||||
</bean>
|
||||
</property>
|
||||
<!-- 配置JSON序列化特性 -->
|
||||
<property name="serializationInclusion">
|
||||
<value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- 开启注解扫描,排除Controller -->
|
||||
<context:component-scan base-package="ltd.qubit.survey">
|
||||
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
|
||||
@ -21,4 +37,21 @@
|
||||
<!-- 配置事务注解驱动 -->
|
||||
<tx:annotation-driven/>
|
||||
|
||||
<!-- 配置事务通知 -->
|
||||
<tx:advice id="txAdvice" transaction-manager="transactionManager">
|
||||
<tx:attributes>
|
||||
<tx:method name="get*" read-only="true"/>
|
||||
<tx:method name="find*" read-only="true"/>
|
||||
<tx:method name="list*" read-only="true"/>
|
||||
<tx:method name="query*" read-only="true"/>
|
||||
<tx:method name="*" propagation="REQUIRED"/>
|
||||
</tx:attributes>
|
||||
</tx:advice>
|
||||
|
||||
<!-- 配置事务切面 -->
|
||||
<aop:config>
|
||||
<aop:pointcut id="txPointcut" expression="execution(* ltd.qubit.survey.service..*.*(..))"/>
|
||||
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
|
||||
</aop:config>
|
||||
|
||||
</beans>
|
||||
@ -20,27 +20,39 @@
|
||||
<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 name="objectMapper" ref="objectMapper"/>
|
||||
<property name="supportedMediaTypes">
|
||||
<list>
|
||||
<value>application/json;charset=UTF-8</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
<!-- 配置字符串转换器 -->
|
||||
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
|
||||
<property name="supportedMediaTypes">
|
||||
<list>
|
||||
<value>text/plain;charset=UTF-8</value>
|
||||
<value>text/html;charset=UTF-8</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
</mvc:message-converters>
|
||||
</mvc:annotation-driven>
|
||||
|
||||
<!-- 配置静态资源处理 -->
|
||||
<mvc:default-servlet-handler/>
|
||||
|
||||
<!-- 配置跨域支持 -->
|
||||
<mvc:cors>
|
||||
<mvc:mapping path="/**"
|
||||
allowed-origins="*"
|
||||
allowed-origins="http://localhost:3000,http://localhost:8080"
|
||||
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>
|
||||
|
||||
<!-- 配置异常处理器 -->
|
||||
<bean class="ltd.qubit.survey.controller.GlobalExceptionHandler"/>
|
||||
|
||||
</beans>
|
||||
@ -1,8 +1,11 @@
|
||||
<?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: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/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/tx
|
||||
http://www.springframework.org/schema/tx/spring-tx.xsd">
|
||||
|
||||
<!-- 配置数据源 -->
|
||||
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
|
||||
@ -16,13 +19,35 @@
|
||||
<property name="idleTimeout" value="${jdbc.pool.idleTimeout}"/>
|
||||
<property name="connectionTimeout" value="${jdbc.pool.connectionTimeout}"/>
|
||||
<property name="connectionTestQuery" value="${jdbc.pool.connectionTestQuery}"/>
|
||||
<!-- 其他配置 -->
|
||||
<property name="autoCommit" value="true"/>
|
||||
<property name="poolName" value="HikariPool-Survey"/>
|
||||
<property name="maxLifetime" value="1800000"/>
|
||||
<property name="validationTimeout" value="5000"/>
|
||||
<property name="leakDetectionThreshold" value="60000"/>
|
||||
</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"/>
|
||||
<property name="mapperLocations" value="classpath*:mybatis/mapper/*.xml"/>
|
||||
<!-- 配置插件 -->
|
||||
<property name="plugins">
|
||||
<array>
|
||||
<!-- 分页插件 -->
|
||||
<bean class="com.github.pagehelper.PageInterceptor">
|
||||
<property name="properties">
|
||||
<value>
|
||||
helperDialect=mysql
|
||||
reasonable=true
|
||||
supportMethodsArguments=true
|
||||
params=count=countSql
|
||||
</value>
|
||||
</property>
|
||||
</bean>
|
||||
</array>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- 配置Mapper扫描器 -->
|
||||
|
||||
@ -12,6 +12,7 @@ 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/common/mybatis/JsonTypeHandler.class
|
||||
ltd/qubit/survey/service/impl/QuestionServiceImpl$1.class
|
||||
ltd/qubit/survey/service/UserService.class
|
||||
ltd/qubit/survey/dao/BaseDao.class
|
||||
@ -25,3 +26,4 @@ ltd/qubit/survey/service/SurveyResponseService.class
|
||||
ltd/qubit/survey/model/Option.class
|
||||
ltd/qubit/survey/model/QuestionType.class
|
||||
ltd/qubit/survey/service/BaseService.class
|
||||
com/fasterxml/jackson/databind/JsonTypeHandler.class
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
/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/common/mybatis/JsonTypeHandler.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
|
||||
@ -8,6 +9,7 @@
|
||||
/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/com/fasterxml/jackson/databind/JsonTypeHandler.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
|
||||
|
||||
74
doc/deployment.md
Normal file
74
doc/deployment.md
Normal file
@ -0,0 +1,74 @@
|
||||
# 部署文档
|
||||
|
||||
## 1. 开发环境配置
|
||||
|
||||
### 1.1 环境变量
|
||||
- `DOCKER_DATA_DIR`: Docker数据目录,默认为 `/Volumes/working/docker`
|
||||
|
||||
### 1.2 开发环境组件
|
||||
- Tomcat: 运行在Docker容器中
|
||||
- 数据目录: `$DOCKER_DATA_DIR/tomcat`
|
||||
- 日志目录: `$DOCKER_DATA_DIR/tomcat/logs`
|
||||
- 应用目录: `$DOCKER_DATA_DIR/tomcat/webapps`
|
||||
|
||||
## 2. 项目构建
|
||||
|
||||
### 2.1 编译打包
|
||||
```bash
|
||||
# 进入后端项目目录
|
||||
cd backend
|
||||
|
||||
# 清理并打包项目(跳过测试)
|
||||
mvn clean package -DskipTests
|
||||
|
||||
# 打包结果
|
||||
# - WAR包位置:backend/target/llm-survey-api.war
|
||||
```
|
||||
|
||||
### 2.2 数据库初始化
|
||||
```bash
|
||||
# 进入数据库脚本目录
|
||||
cd database
|
||||
|
||||
# 添加执行权限
|
||||
chmod +x init_database.sh
|
||||
|
||||
# 执行初始化脚本
|
||||
./init_database.sh
|
||||
|
||||
# 初始化内容
|
||||
# - 创建数据库:llm_survey
|
||||
# - 创建表:users, questions, options, survey_responses
|
||||
# - 插入基础数据:工作领域和岗位性质相关的问题和选项
|
||||
```
|
||||
|
||||
## 3. 开发环境部署
|
||||
|
||||
### 3.1 部署WAR包
|
||||
```bash
|
||||
# 复制WAR包到Tomcat的webapps目录
|
||||
cp backend/target/llm-survey-api.war $DOCKER_DATA_DIR/tomcat/webapps/
|
||||
|
||||
# 部署后的访问地址
|
||||
# - 上下文路径:/llm-survey-api
|
||||
# - API基础路径:/llm-survey-api/api
|
||||
```
|
||||
|
||||
### 3.2 查看部署结果
|
||||
```bash
|
||||
# 查看Tomcat日志
|
||||
tail -f $DOCKER_DATA_DIR/tomcat/logs/catalina.out
|
||||
|
||||
# 检查应用是否成功部署
|
||||
ls -l $DOCKER_DATA_DIR/tomcat/webapps/llm-survey-api/
|
||||
```
|
||||
|
||||
### 3.3 验证部署
|
||||
- 访问测试接口:`http://localhost:8080/llm-survey-api/user/check/13800000000`
|
||||
- 预期返回:`false`(表示手机号未注册)
|
||||
|
||||
## 4. 注意事项
|
||||
1. 确保MySQL服务已启动且能够通过localhost:3306访问
|
||||
2. 确保Tomcat容器已启动且8080端口可访问
|
||||
3. 部署前确保数据库已正确初始化
|
||||
4. 如需重新部署,可直接覆盖webapps目录下的WAR包,Tomcat会自动重新部署
|
||||
161
pom.xml
161
pom.xml
@ -1,161 +0,0 @@
|
||||
<?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>
|
||||
@ -1,78 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
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("没有更多问题了"));
|
||||
}
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,65 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
package ltd.qubit.survey.model;
|
||||
|
||||
/**
|
||||
* 问题类型枚举
|
||||
*/
|
||||
public enum QuestionType {
|
||||
/**
|
||||
* 单选题
|
||||
*/
|
||||
SINGLE_CHOICE,
|
||||
|
||||
/**
|
||||
* 多选题
|
||||
*/
|
||||
MULTIPLE_CHOICE,
|
||||
|
||||
/**
|
||||
* 文本题
|
||||
*/
|
||||
TEXT
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,128 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,72 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
# 数据库配置
|
||||
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
|
||||
@ -1,83 +0,0 @@
|
||||
<?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>
|
||||
@ -1,86 +0,0 @@
|
||||
<?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>
|
||||
@ -1,93 +0,0 @@
|
||||
<?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>
|
||||
@ -1,68 +0,0 @@
|
||||
<?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>
|
||||
@ -1,32 +0,0 @@
|
||||
<?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>
|
||||
@ -1,24 +0,0 @@
|
||||
<?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>
|
||||
@ -1,46 +0,0 @@
|
||||
<?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>
|
||||
@ -1,39 +0,0 @@
|
||||
<?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>
|
||||
@ -1,57 +0,0 @@
|
||||
<?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>
|
||||
@ -1,18 +0,0 @@
|
||||
# 数据库配置
|
||||
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user