feat: 实现题目服务基础架构

- 创建题目服务模块 aioj-backend-question-service
- 实现 Question、TestCase、QuestionSubmit 实体类
- 实现 RESTful 风格的 Controller 接口
- 添加完善的 Swagger 注解和校验
- 配置 Nacos 服务发现和 Redis 缓存
- 实现分页查询和条件过滤功能

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-01-21 22:47:36 +08:00
parent 61fb847ac1
commit cf0e326b0c
37 changed files with 1568 additions and 20 deletions

View File

@@ -0,0 +1,19 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.0.0.10/aioj_dev
username: root
password: root
data:
redis:
host: 10.0.0.10
port: 6379
password: 123456
cloud:
nacos:
discovery:
enabled: true
register-enabled: true
server-addr: 10.0.0.10:8848
username: nacos
password: nacos

View File

@@ -0,0 +1,11 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.0.0.10/aioj_prod
username: root
password: root
data:
redis:
host: 10.0.0.10
port: 6379
password: 123456

View File

@@ -0,0 +1,11 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.0.0.10/aioj_test
username: root
password: 123456
data:
redis:
host: 10.0.0.10
port: 6379
password: 123456

View File

@@ -0,0 +1,49 @@
spring:
application:
name: aioj-question-service
profiles:
active: @env@
server:
port: 18083
servlet:
context-path: /api
error:
include-stacktrace: never
springdoc:
api-docs:
enabled: true
path: /v3/api-docs
default-flat-param-object: true
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
group-configs:
- group: 'default'
paths-to-match: '/api/**'
packages-to-scan: cn.meowrain.aioj.backend.questionservice.controller
knife4j:
basic:
enable: true
setting:
language: zh_cn
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
mapper-locations: classpath*:/mapper/**/*.xml
# JWT 配置(必须与 auth-service 保持一致)
jwt:
enabled: true
secret: "12345678901234567890123456789012" # 至少32字节
access-expire: 900000 # 15分钟
refresh-expire: 604800000 # 7天
aioj:
log:
enabled: true
max-length: 20000
logging:
file:
path: ./logs/${spring.application.name}

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds" debug="false">
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
<timestamp key="time-month" datePattern="yyyy-MM"/>
<timestamp key="time-month-day" datePattern="yyyy-MM-dd"/>
<property name="LOG_FILE_PATH" value="${LOG_PATH:-./logs/${APP_NAME}}"/>
<property name="FILE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -[%X{traceId:-}] %msg%n"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE_PATH}/${time-month}/${time-month-day}/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>31</maxHistory>
<totalSizeCap>100GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE_PATH}/${time-month}/${time-month-day}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>31</maxHistory>
<totalSizeCap>100GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="ASYNC_INFO"
class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>512</queueSize>
<appender-ref ref="INFO_FILE"/>
</appender>
<appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>512</queueSize>
<appender-ref ref="ERROR_FILE"/>
</appender>
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC_ERROR"/>
<appender-ref ref="ASYNC_INFO"/>
</root>
</springProfile>
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC_ERROR"/>
<appender-ref ref="ASYNC_INFO"/>
</root>
</springProfile>
</configuration>