Compare commits
2 Commits
9215465bd5
...
096d06af3d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
096d06af3d | ||
|
|
bead1cf4dc |
5
backend-java/.gitignore
vendored
Normal file
5
backend-java/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
target/
|
||||||
|
uploads/
|
||||||
|
*.log
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
198
backend-java/pom.xml
Normal file
198
backend-java/pom.xml
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.2.5</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>com.competition</groupId>
|
||||||
|
<artifactId>competition-management-system</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<name>competition-management-system</name>
|
||||||
|
<description>少儿绘本创作活动管理平台 - Java 后端</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<mybatis-plus.version>3.5.7</mybatis-plus.version>
|
||||||
|
<druid.version>1.2.23</druid.version>
|
||||||
|
<jjwt.version>0.12.6</jjwt.version>
|
||||||
|
<knife4j.version>4.5.0</knife4j.version>
|
||||||
|
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||||
|
<hutool.version>5.8.32</hutool.version>
|
||||||
|
<fastjson2.version>2.0.53</fastjson2.version>
|
||||||
|
<cos.version>5.6.227</cos.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spring Boot Starters -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MyBatis-Plus -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Druid 连接池 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>druid-spring-boot-3-starter</artifactId>
|
||||||
|
<version>${druid.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MySQL 驱动 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Flyway 数据库迁移 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-mysql</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JWT -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-api</artifactId>
|
||||||
|
<version>${jjwt.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-impl</artifactId>
|
||||||
|
<version>${jjwt.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-jackson</artifactId>
|
||||||
|
<version>${jjwt.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Knife4j API 文档 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.xiaoymin</groupId>
|
||||||
|
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||||
|
<version>${knife4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MapStruct -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct</artifactId>
|
||||||
|
<version>${mapstruct.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Hutool 工具集 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>${hutool.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- FastJSON2 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
<artifactId>fastjson2</artifactId>
|
||||||
|
<version>${fastjson2.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 腾讯云 COS -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.qcloud</groupId>
|
||||||
|
<artifactId>cos_api</artifactId>
|
||||||
|
<version>${cos.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Test -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>${java.version}</source>
|
||||||
|
<target>${java.version}</target>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</path>
|
||||||
|
<path>
|
||||||
|
<groupId>org.mapstruct</groupId>
|
||||||
|
<artifactId>mapstruct-processor</artifactId>
|
||||||
|
<version>${mapstruct.version}</version>
|
||||||
|
</path>
|
||||||
|
<!-- Lombok-MapStruct 绑定,确保 MapStruct 能识别 Lombok 生成的方法 -->
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||||
|
<version>0.2.0</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.competition;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@MapperScan({
|
||||||
|
"com.competition.modules.sys.mapper",
|
||||||
|
"com.competition.modules.biz.contest.mapper",
|
||||||
|
"com.competition.modules.biz.review.mapper",
|
||||||
|
"com.competition.modules.biz.homework.mapper",
|
||||||
|
"com.competition.modules.biz.judge.mapper",
|
||||||
|
"com.competition.modules.user.mapper",
|
||||||
|
"com.competition.modules.ugc.mapper"
|
||||||
|
})
|
||||||
|
public class CompetitionApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(CompetitionApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.competition.common.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
import org.springframework.web.filter.CorsFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跨域配置
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CorsConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CorsFilter corsFilter() {
|
||||||
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
|
config.setAllowCredentials(true);
|
||||||
|
config.addAllowedOriginPattern("*");
|
||||||
|
config.addAllowedHeader("*");
|
||||||
|
config.addAllowedMethod("*");
|
||||||
|
config.addExposedHeader("X-Trace-Id");
|
||||||
|
|
||||||
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
|
source.registerCorsConfiguration("/**", config);
|
||||||
|
return new CorsFilter(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.competition.common.config;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
|
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||||
|
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Knife4j / OpenAPI 配置
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class Knife4jConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OpenAPI openAPI() {
|
||||||
|
return new OpenAPI()
|
||||||
|
.info(new Info()
|
||||||
|
.title("少儿绘本创作活动管理平台 API")
|
||||||
|
.description("Competition Management System - Java Backend")
|
||||||
|
.version("1.0.0"))
|
||||||
|
.addSecurityItem(new SecurityRequirement().addList("Bearer"))
|
||||||
|
.schemaRequirement("Bearer", new SecurityScheme()
|
||||||
|
.type(SecurityScheme.Type.HTTP)
|
||||||
|
.scheme("bearer")
|
||||||
|
.bearerFormat("JWT"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.competition.common.config;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.DbType;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyBatis-Plus 配置
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class MyBatisPlusConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||||
|
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||||
|
// 分页插件
|
||||||
|
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.competition.common.config;
|
||||||
|
|
||||||
|
import com.competition.common.interceptor.TraceIdInterceptor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebMvc 配置
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class WebMvcConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
private final TraceIdInterceptor traceIdInterceptor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
registry.addInterceptor(traceIdInterceptor).addPathPatterns("/**");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package com.competition.common.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础实体类,所有实体继承此类
|
||||||
|
* 当前阶段使用现有数据库列名,新审计字段暂不映射(待 Flyway 迁移后启用)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public abstract class BaseEntity implements Serializable {
|
||||||
|
|
||||||
|
/** 主键 ID(自增) */
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
// ====== 新审计字段(列尚未存在,暂不映射到数据库) ======
|
||||||
|
|
||||||
|
/** 创建人账号(待 Flyway V2 后启用) */
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String createBy;
|
||||||
|
|
||||||
|
/** 更新人账号(待 Flyway V2 后启用) */
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String updateBy;
|
||||||
|
|
||||||
|
/** 逻辑删除标识(待 Flyway V2 后启用) */
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
// ====== 现有审计字段(与当前数据库一致) ======
|
||||||
|
|
||||||
|
/** 创建人 ID */
|
||||||
|
@TableField(value = "creator", fill = FieldFill.INSERT)
|
||||||
|
private Integer creator;
|
||||||
|
|
||||||
|
/** 修改人 ID */
|
||||||
|
@TableField(value = "modifier", fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Integer modifier;
|
||||||
|
|
||||||
|
/** 创建时间 */
|
||||||
|
@TableField(value = "create_time", fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/** 修改时间(数据库列名为 modify_time) */
|
||||||
|
@TableField(value = "modify_time", fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime modifyTime;
|
||||||
|
|
||||||
|
/** 有效状态:1-有效,2-失效 */
|
||||||
|
@TableField(value = "valid_state", fill = FieldFill.INSERT)
|
||||||
|
private Integer validState;
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package com.competition.common.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误码枚举
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum ErrorCode {
|
||||||
|
|
||||||
|
SUCCESS(200, "success"),
|
||||||
|
BAD_REQUEST(400, "请求参数错误"),
|
||||||
|
UNAUTHORIZED(401, "未登录或 Token 已过期"),
|
||||||
|
FORBIDDEN(403, "没有访问权限"),
|
||||||
|
NOT_FOUND(404, "资源不存在"),
|
||||||
|
CONFLICT(409, "数据冲突"),
|
||||||
|
INTERNAL_ERROR(500, "系统内部错误");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String message;
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.competition.common.exception;
|
||||||
|
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务异常
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class BusinessException extends RuntimeException {
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
|
||||||
|
public BusinessException(Integer code, String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessException(ErrorCode errorCode) {
|
||||||
|
super(errorCode.getMessage());
|
||||||
|
this.code = errorCode.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessException(ErrorCode errorCode, String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = errorCode.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BusinessException of(ErrorCode errorCode) {
|
||||||
|
return new BusinessException(errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BusinessException of(ErrorCode errorCode, String message) {
|
||||||
|
return new BusinessException(errorCode, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
package com.competition.common.exception;
|
||||||
|
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局异常处理器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
/** 业务异常 */
|
||||||
|
@ExceptionHandler(BusinessException.class)
|
||||||
|
public Result<Void> handleBusinessException(BusinessException e, HttpServletRequest request) {
|
||||||
|
log.warn("业务异常,路径:{},消息:{}", request.getRequestURI(), e.getMessage());
|
||||||
|
return Result.error(e.getCode(), e.getMessage(), request.getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 参数校验异常(@Valid) */
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
|
public Result<Void> handleValidationException(MethodArgumentNotValidException e, HttpServletRequest request) {
|
||||||
|
String message = e.getBindingResult().getFieldErrors().stream()
|
||||||
|
.map(error -> error.getField() + ": " + error.getDefaultMessage())
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
log.warn("参数校验失败,路径:{},消息:{}", request.getRequestURI(), message);
|
||||||
|
return Result.error(400, message, request.getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 参数绑定异常 */
|
||||||
|
@ExceptionHandler(BindException.class)
|
||||||
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
|
public Result<Void> handleBindException(BindException e, HttpServletRequest request) {
|
||||||
|
String message = e.getFieldErrors().stream()
|
||||||
|
.map(error -> error.getField() + ": " + error.getDefaultMessage())
|
||||||
|
.collect(Collectors.joining(", "));
|
||||||
|
log.warn("参数绑定失败,路径:{},消息:{}", request.getRequestURI(), message);
|
||||||
|
return Result.error(400, message, request.getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 认证异常 */
|
||||||
|
@ExceptionHandler(AuthenticationException.class)
|
||||||
|
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||||
|
public Result<Void> handleAuthenticationException(AuthenticationException e, HttpServletRequest request) {
|
||||||
|
return Result.error(401, ErrorCode.UNAUTHORIZED.getMessage(), request.getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 授权异常 */
|
||||||
|
@ExceptionHandler(AccessDeniedException.class)
|
||||||
|
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||||
|
public Result<Void> handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) {
|
||||||
|
return Result.error(403, ErrorCode.FORBIDDEN.getMessage(), request.getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 资源不存在 */
|
||||||
|
@ExceptionHandler(NoResourceFoundException.class)
|
||||||
|
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||||
|
public Result<Void> handleNotFoundException(NoResourceFoundException e, HttpServletRequest request) {
|
||||||
|
return Result.error(404, ErrorCode.NOT_FOUND.getMessage(), request.getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 兜底:未知异常 */
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
public Result<Void> handleException(Exception e, HttpServletRequest request) {
|
||||||
|
log.error("系统异常,路径:{},消息:{}", request.getRequestURI(), e.getMessage(), e);
|
||||||
|
return Result.error(500, ErrorCode.INTERNAL_ERROR.getMessage(), request.getRequestURI());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package com.competition.common.handler;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.ibatis.reflection.MetaObject;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyBatis-Plus 审计字段自动填充
|
||||||
|
* 同时填充新字段(create_by/update_by/deleted)和旧字段(creator/modifier/valid_state)
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class AuditMetaObjectHandler implements MetaObjectHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertFill(MetaObject metaObject) {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
Long userId = SecurityUtil.getCurrentUserIdOrNull();
|
||||||
|
|
||||||
|
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, now);
|
||||||
|
this.strictInsertFill(metaObject, "modifyTime", LocalDateTime.class, now);
|
||||||
|
this.strictInsertFill(metaObject, "validState", Integer.class, 1);
|
||||||
|
|
||||||
|
if (userId != null) {
|
||||||
|
this.strictInsertFill(metaObject, "creator", Integer.class, userId.intValue());
|
||||||
|
this.strictInsertFill(metaObject, "modifier", Integer.class, userId.intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateFill(MetaObject metaObject) {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
Long userId = SecurityUtil.getCurrentUserIdOrNull();
|
||||||
|
|
||||||
|
this.strictUpdateFill(metaObject, "modifyTime", LocalDateTime.class, now);
|
||||||
|
|
||||||
|
if (userId != null) {
|
||||||
|
this.strictUpdateFill(metaObject, "modifier", Integer.class, userId.intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.competition.common.interceptor;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.slf4j.MDC;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TraceId 链路追踪拦截器
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class TraceIdInterceptor implements HandlerInterceptor {
|
||||||
|
|
||||||
|
private static final String TRACE_ID = "traceId";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||||
|
String traceId = request.getHeader("X-Trace-Id");
|
||||||
|
if (traceId == null || traceId.isBlank()) {
|
||||||
|
traceId = IdUtil.fastSimpleUUID();
|
||||||
|
}
|
||||||
|
MDC.put(TRACE_ID, traceId);
|
||||||
|
response.setHeader("X-Trace-Id", traceId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
|
||||||
|
MDC.remove(TRACE_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
package com.competition.common.result;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页结果,格式与前端完全兼容:{ list, total, page, pageSize }
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PageResult<T> implements Serializable {
|
||||||
|
|
||||||
|
/** 数据列表 */
|
||||||
|
private List<T> list;
|
||||||
|
|
||||||
|
/** 总记录数 */
|
||||||
|
private Long total;
|
||||||
|
|
||||||
|
/** 当前页码 */
|
||||||
|
private Long page;
|
||||||
|
|
||||||
|
/** 每页大小 */
|
||||||
|
private Long pageSize;
|
||||||
|
|
||||||
|
public PageResult() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageResult(List<T> list, Long total, Long page, Long pageSize) {
|
||||||
|
this.list = list;
|
||||||
|
this.total = total;
|
||||||
|
this.page = page;
|
||||||
|
this.pageSize = pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 MyBatis-Plus 的 IPage 转换
|
||||||
|
*/
|
||||||
|
public static <T> PageResult<T> from(IPage<T> page) {
|
||||||
|
return new PageResult<>(
|
||||||
|
page.getRecords(),
|
||||||
|
page.getTotal(),
|
||||||
|
page.getCurrent(),
|
||||||
|
page.getSize()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 MyBatis-Plus 的 IPage 转换,支持 VO 列表替换
|
||||||
|
*/
|
||||||
|
public static <T> PageResult<T> from(IPage<?> page, List<T> voList) {
|
||||||
|
return new PageResult<>(
|
||||||
|
voList,
|
||||||
|
page.getTotal(),
|
||||||
|
page.getCurrent(),
|
||||||
|
page.getSize()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
package com.competition.common.result;
|
||||||
|
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一响应结果
|
||||||
|
* 格式与 NestJS 前端完全兼容:{ code, message, data, timestamp, path }
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class Result<T> implements Serializable {
|
||||||
|
|
||||||
|
private Integer code;
|
||||||
|
private String message;
|
||||||
|
private T data;
|
||||||
|
private String timestamp;
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
private Result() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> success(T data) {
|
||||||
|
Result<T> result = new Result<>();
|
||||||
|
result.setCode(200);
|
||||||
|
result.setMessage("success");
|
||||||
|
result.setData(data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> success() {
|
||||||
|
return success(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> success(String message, T data) {
|
||||||
|
Result<T> result = new Result<>();
|
||||||
|
result.setCode(200);
|
||||||
|
result.setMessage(message);
|
||||||
|
result.setData(data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error(Integer code, String message) {
|
||||||
|
Result<T> result = new Result<>();
|
||||||
|
result.setCode(code);
|
||||||
|
result.setMessage(message);
|
||||||
|
result.setTimestamp(LocalDateTime.now().toString());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error(ErrorCode errorCode) {
|
||||||
|
return error(errorCode.getCode(), errorCode.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error(Integer code, String message, String path) {
|
||||||
|
Result<T> result = error(code, message);
|
||||||
|
result.setPath(path);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
package com.competition.common.util;
|
||||||
|
|
||||||
|
import com.competition.security.model.LoginUser;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全工具类 - 获取当前登录用户信息
|
||||||
|
*/
|
||||||
|
public final class SecurityUtil {
|
||||||
|
|
||||||
|
private SecurityUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录用户
|
||||||
|
*/
|
||||||
|
public static LoginUser getCurrentUser() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (authentication != null && authentication.getPrincipal() instanceof LoginUser loginUser) {
|
||||||
|
return loginUser;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户 ID(未登录返回 null)
|
||||||
|
*/
|
||||||
|
public static Long getCurrentUserIdOrNull() {
|
||||||
|
LoginUser user = getCurrentUser();
|
||||||
|
return user != null ? user.getUserId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户 ID(未登录抛异常)
|
||||||
|
*/
|
||||||
|
public static Long getCurrentUserId() {
|
||||||
|
LoginUser user = getCurrentUser();
|
||||||
|
if (user == null) {
|
||||||
|
throw new RuntimeException("用户未登录");
|
||||||
|
}
|
||||||
|
return user.getUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户名(未登录返回 "system")
|
||||||
|
*/
|
||||||
|
public static String getCurrentUsername() {
|
||||||
|
LoginUser user = getCurrentUser();
|
||||||
|
return user != null ? user.getUsername() : "system";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前租户 ID(未登录返回 null)
|
||||||
|
*/
|
||||||
|
public static Long getCurrentTenantId() {
|
||||||
|
LoginUser user = getCurrentUser();
|
||||||
|
return user != null ? user.getTenantId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前用户是否为超级管理员
|
||||||
|
*/
|
||||||
|
public static boolean isSuperAdmin() {
|
||||||
|
LoginUser user = getCurrentUser();
|
||||||
|
return user != null && user.isSuperAdmin();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
package com.competition.modules.biz.contest.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestAttachment;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestAttachmentService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name = "赛事附件")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/contests/attachments")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestAttachmentController {
|
||||||
|
|
||||||
|
private final IContestAttachmentService attachmentService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "上传附件")
|
||||||
|
public Result<BizContestAttachment> create(@RequestBody BizContestAttachment attachment) {
|
||||||
|
attachmentService.save(attachment);
|
||||||
|
return Result.success(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/contest/{contestId}")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询赛事下的附件列表")
|
||||||
|
public Result<List<BizContestAttachment>> findByContest(@PathVariable Long contestId) {
|
||||||
|
List<BizContestAttachment> list = attachmentService.list(
|
||||||
|
new LambdaQueryWrapper<BizContestAttachment>()
|
||||||
|
.eq(BizContestAttachment::getContestId, contestId)
|
||||||
|
.orderByDesc(BizContestAttachment::getCreateTime));
|
||||||
|
return Result.success(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询附件详情")
|
||||||
|
public Result<BizContestAttachment> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(attachmentService.getById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "更新附件")
|
||||||
|
public Result<Void> update(@PathVariable Long id, @RequestBody BizContestAttachment attachment) {
|
||||||
|
attachment.setId(id);
|
||||||
|
attachmentService.updateById(attachment);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "删除附件")
|
||||||
|
public Result<Void> remove(@PathVariable Long id) {
|
||||||
|
attachmentService.removeById(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,115 @@
|
|||||||
|
package com.competition.modules.biz.contest.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.contest.dto.CreateContestDto;
|
||||||
|
import com.competition.modules.biz.contest.dto.QueryContestDto;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContest;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestService;
|
||||||
|
import com.competition.modules.sys.service.ISysTenantService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "赛事管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/contests")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestController {
|
||||||
|
|
||||||
|
private final IContestService contestService;
|
||||||
|
private final ISysTenantService tenantService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission("contest:create")
|
||||||
|
@Operation(summary = "创建赛事")
|
||||||
|
public Result<BizContest> create(@Valid @RequestBody CreateContestDto dto) {
|
||||||
|
return Result.success(contestService.createContest(dto, SecurityUtil.getCurrentUserId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/stats")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "获取赛事统计")
|
||||||
|
public Result<Map<String, Object>> getStats() {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
boolean isSuperTenant = tenantService.isSuperTenant(tenantId);
|
||||||
|
return Result.success(contestService.getStats(tenantId, isSuperTenant));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/dashboard")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "获取赛事看板")
|
||||||
|
public Result<Map<String, Object>> getDashboard() {
|
||||||
|
return Result.success(contestService.getDashboard(SecurityUtil.getCurrentTenantId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询赛事列表")
|
||||||
|
public Result<PageResult<Map<String, Object>>> findAll(QueryContestDto dto) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
boolean isSuperTenant = tenantService.isSuperTenant(tenantId);
|
||||||
|
return Result.success(contestService.findAll(dto, tenantId, isSuperTenant));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/my-contests")
|
||||||
|
@RequirePermission({"contest:read", "contest:activity:read"})
|
||||||
|
@Operation(summary = "获取我的赛事")
|
||||||
|
public Result<PageResult<Map<String, Object>>> getMyContests(QueryContestDto dto) {
|
||||||
|
Long userId = SecurityUtil.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(contestService.getMyContests(dto, userId, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询赛事详情")
|
||||||
|
public Result<Map<String, Object>> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(contestService.findDetail(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "更新赛事")
|
||||||
|
public Result<BizContest> update(@PathVariable Long id, @RequestBody CreateContestDto dto) {
|
||||||
|
return Result.success(contestService.updateContest(id, dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}/publish")
|
||||||
|
@RequirePermission("contest:publish")
|
||||||
|
@Operation(summary = "发布/撤回赛事")
|
||||||
|
public Result<Void> publish(@PathVariable Long id, @RequestBody Map<String, String> body) {
|
||||||
|
contestService.publishContest(id, body.get("contestState"));
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}/finish")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "结束赛事")
|
||||||
|
public Result<Void> finish(@PathVariable Long id) {
|
||||||
|
contestService.finishContest(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}/reopen")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "重新开放赛事")
|
||||||
|
public Result<Void> reopen(@PathVariable Long id) {
|
||||||
|
contestService.reopenContest(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@RequirePermission("contest:delete")
|
||||||
|
@Operation(summary = "删除赛事")
|
||||||
|
public Result<Void> remove(@PathVariable Long id) {
|
||||||
|
contestService.removeContest(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,102 @@
|
|||||||
|
package com.competition.modules.biz.contest.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.modules.biz.contest.dto.CreateNoticeDto;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestNotice;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestNoticeService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Tag(name = "赛事公告")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/contests/notices")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestNoticeController {
|
||||||
|
|
||||||
|
private final IContestNoticeService noticeService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission("notice:create")
|
||||||
|
@Operation(summary = "创建公告")
|
||||||
|
public Result<BizContestNotice> create(@Valid @RequestBody CreateNoticeDto dto) {
|
||||||
|
BizContestNotice notice = new BizContestNotice();
|
||||||
|
notice.setContestId(dto.getContestId());
|
||||||
|
notice.setTitle(dto.getTitle());
|
||||||
|
notice.setContent(dto.getContent());
|
||||||
|
notice.setNoticeType(dto.getNoticeType());
|
||||||
|
notice.setPriority(dto.getPriority());
|
||||||
|
if (StringUtils.hasText(dto.getPublishTime())) {
|
||||||
|
notice.setPublishTime(LocalDateTime.parse(dto.getPublishTime()));
|
||||||
|
}
|
||||||
|
noticeService.save(notice);
|
||||||
|
return Result.success(notice);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/contest/{contestId}")
|
||||||
|
@RequirePermission("notice:read")
|
||||||
|
@Operation(summary = "查询赛事下的公告列表")
|
||||||
|
public Result<List<BizContestNotice>> findByContest(@PathVariable Long contestId) {
|
||||||
|
List<BizContestNotice> list = noticeService.list(
|
||||||
|
new LambdaQueryWrapper<BizContestNotice>()
|
||||||
|
.eq(BizContestNotice::getContestId, contestId)
|
||||||
|
.orderByDesc(BizContestNotice::getCreateTime));
|
||||||
|
return Result.success(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission("notice:read")
|
||||||
|
@Operation(summary = "分页查询公告列表")
|
||||||
|
public Result<PageResult<BizContestNotice>> findAll(
|
||||||
|
@RequestParam(defaultValue = "1") Long page,
|
||||||
|
@RequestParam(defaultValue = "10") Long pageSize,
|
||||||
|
@RequestParam(required = false) String title) {
|
||||||
|
LambdaQueryWrapper<BizContestNotice> wrapper = new LambdaQueryWrapper<BizContestNotice>()
|
||||||
|
.like(StringUtils.hasText(title), BizContestNotice::getTitle, title)
|
||||||
|
.orderByDesc(BizContestNotice::getCreateTime);
|
||||||
|
Page<BizContestNotice> result = noticeService.page(new Page<>(page, pageSize), wrapper);
|
||||||
|
return Result.success(PageResult.from(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission("notice:read")
|
||||||
|
@Operation(summary = "查询公告详情")
|
||||||
|
public Result<BizContestNotice> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(noticeService.getById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}")
|
||||||
|
@RequirePermission("notice:update")
|
||||||
|
@Operation(summary = "更新公告")
|
||||||
|
public Result<Void> update(@PathVariable Long id, @RequestBody CreateNoticeDto dto) {
|
||||||
|
BizContestNotice notice = new BizContestNotice();
|
||||||
|
notice.setId(id);
|
||||||
|
notice.setTitle(dto.getTitle());
|
||||||
|
notice.setContent(dto.getContent());
|
||||||
|
notice.setNoticeType(dto.getNoticeType());
|
||||||
|
notice.setPriority(dto.getPriority());
|
||||||
|
if (StringUtils.hasText(dto.getPublishTime())) {
|
||||||
|
notice.setPublishTime(LocalDateTime.parse(dto.getPublishTime()));
|
||||||
|
}
|
||||||
|
noticeService.updateById(notice);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@RequirePermission("notice:delete")
|
||||||
|
@Operation(summary = "删除公告")
|
||||||
|
public Result<Void> remove(@PathVariable Long id) {
|
||||||
|
noticeService.removeById(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,125 @@
|
|||||||
|
package com.competition.modules.biz.contest.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.contest.dto.CreateRegistrationDto;
|
||||||
|
import com.competition.modules.biz.contest.dto.QueryRegistrationDto;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestRegistrationService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "报名管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/contests/registrations")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestRegistrationController {
|
||||||
|
|
||||||
|
private final IContestRegistrationService registrationService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission("contest:register")
|
||||||
|
@Operation(summary = "创建报名")
|
||||||
|
public Result<Map<String, Object>> create(@Valid @RequestBody CreateRegistrationDto dto) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
Long userId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(registrationService.createRegistration(dto, tenantId, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/stats")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "获取报名统计")
|
||||||
|
public Result<Map<String, Object>> getStats(@RequestParam(required = false) Long contestId) {
|
||||||
|
return Result.success(registrationService.getStats(contestId, SecurityUtil.getCurrentTenantId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询报名列表")
|
||||||
|
public Result<PageResult<Map<String, Object>>> findAll(QueryRegistrationDto dto) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
boolean isSuperTenant = SecurityUtil.isSuperAdmin();
|
||||||
|
return Result.success(registrationService.findAll(dto, tenantId, isSuperTenant));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/my/{contestId}")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "获取我的报名信息")
|
||||||
|
public Result<Map<String, Object>> getMyRegistration(@PathVariable Long contestId) {
|
||||||
|
Long userId = SecurityUtil.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(registrationService.getMyRegistration(contestId, userId, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询报名详情")
|
||||||
|
public Result<Map<String, Object>> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(registrationService.findDetail(id, SecurityUtil.getCurrentTenantId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}/review")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "审核报名")
|
||||||
|
public Result<Void> review(@PathVariable Long id, @RequestBody Map<String, String> body) {
|
||||||
|
Long operatorId = SecurityUtil.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
registrationService.reviewRegistration(id, body.get("registrationState"), body.get("reason"), operatorId, tenantId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}/revoke")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "撤回审核")
|
||||||
|
public Result<Void> revoke(@PathVariable Long id) {
|
||||||
|
registrationService.revokeReview(id, SecurityUtil.getCurrentTenantId());
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/batch-review")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "批量审核报名")
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Result<Void> batchReview(@RequestBody Map<String, Object> body) {
|
||||||
|
List<Long> ids = (List<Long>) body.get("ids");
|
||||||
|
String registrationState = (String) body.get("registrationState");
|
||||||
|
String reason = (String) body.get("reason");
|
||||||
|
Long operatorId = SecurityUtil.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
registrationService.batchReview(ids, registrationState, reason, operatorId, tenantId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/teachers")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "添加指导老师")
|
||||||
|
public Result<Void> addTeacher(@PathVariable Long id, @RequestBody Map<String, Long> body) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
Long creatorId = SecurityUtil.getCurrentUserId();
|
||||||
|
registrationService.addTeacher(id, body.get("teacherUserId"), tenantId, creatorId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}/teachers/{teacherUserId}")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "移除指导老师")
|
||||||
|
public Result<Void> removeTeacher(@PathVariable Long id, @PathVariable Long teacherUserId) {
|
||||||
|
registrationService.removeTeacher(id, teacherUserId, SecurityUtil.getCurrentTenantId());
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "删除报名")
|
||||||
|
public Result<Void> remove(@PathVariable Long id) {
|
||||||
|
registrationService.removeRegistration(id, SecurityUtil.getCurrentTenantId());
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
package com.competition.modules.biz.contest.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.contest.dto.CreateTeamDto;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestTeam;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestTeamService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "团队管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/contests/teams")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestTeamController {
|
||||||
|
|
||||||
|
private final IContestTeamService teamService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission("team:create")
|
||||||
|
@Operation(summary = "创建团队")
|
||||||
|
public Result<Map<String, Object>> create(@Valid @RequestBody CreateTeamDto dto) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
Long creatorId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(teamService.createTeam(dto, tenantId, creatorId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/contest/{contestId}")
|
||||||
|
@RequirePermission("team:read")
|
||||||
|
@Operation(summary = "查询赛事下的团队列表")
|
||||||
|
public Result<List<Map<String, Object>>> findByContest(@PathVariable Long contestId) {
|
||||||
|
return Result.success(teamService.findByContest(contestId, SecurityUtil.getCurrentTenantId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission("team:read")
|
||||||
|
@Operation(summary = "查询团队详情")
|
||||||
|
public Result<Map<String, Object>> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(teamService.findDetail(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}")
|
||||||
|
@RequirePermission("team:update")
|
||||||
|
@Operation(summary = "更新团队")
|
||||||
|
public Result<BizContestTeam> update(@PathVariable Long id, @RequestBody CreateTeamDto dto) {
|
||||||
|
return Result.success(teamService.updateTeam(id, dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/members")
|
||||||
|
@RequirePermission("team:update")
|
||||||
|
@Operation(summary = "添加团队成员")
|
||||||
|
public Result<Void> addMember(@PathVariable Long id, @RequestBody Map<String, Object> body) {
|
||||||
|
Long userId = ((Number) body.get("userId")).longValue();
|
||||||
|
String role = (String) body.get("role");
|
||||||
|
teamService.addMember(id, userId, role, SecurityUtil.getCurrentTenantId());
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}/members/{userId}")
|
||||||
|
@RequirePermission("team:update")
|
||||||
|
@Operation(summary = "移除团队成员")
|
||||||
|
public Result<Void> removeMember(@PathVariable Long id, @PathVariable Long userId) {
|
||||||
|
teamService.removeMember(id, userId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@RequirePermission("team:delete")
|
||||||
|
@Operation(summary = "删除团队")
|
||||||
|
public Result<Void> remove(@PathVariable Long id) {
|
||||||
|
teamService.removeTeam(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
package com.competition.modules.biz.contest.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.contest.dto.QueryWorkDto;
|
||||||
|
import com.competition.modules.biz.contest.dto.SubmitWorkDto;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestWorkService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "作品管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/contests/works")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestWorkController {
|
||||||
|
|
||||||
|
private final IContestWorkService workService;
|
||||||
|
|
||||||
|
@PostMapping("/submit")
|
||||||
|
@RequirePermission("work:submit")
|
||||||
|
@Operation(summary = "提交作品")
|
||||||
|
public Result<Map<String, Object>> submit(@Valid @RequestBody SubmitWorkDto dto) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
Long submitterId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(workService.submitWork(dto, tenantId, submitterId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/stats")
|
||||||
|
@RequirePermission("work:read")
|
||||||
|
@Operation(summary = "获取作品统计")
|
||||||
|
public Result<Map<String, Object>> getStats(@RequestParam(required = false) Long contestId) {
|
||||||
|
return Result.success(workService.getStats(contestId, SecurityUtil.getCurrentTenantId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission("work:read")
|
||||||
|
@Operation(summary = "查询作品列表")
|
||||||
|
public Result<PageResult<Map<String, Object>>> findAll(QueryWorkDto dto) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
boolean isSuperTenant = SecurityUtil.isSuperAdmin();
|
||||||
|
return Result.success(workService.findAll(dto, tenantId, isSuperTenant));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/guided")
|
||||||
|
@RequirePermission("activity:read")
|
||||||
|
@Operation(summary = "查询辅导作品")
|
||||||
|
public Result<PageResult<Map<String, Object>>> getGuidedWorks(
|
||||||
|
@RequestParam(required = false) Long contestId,
|
||||||
|
@RequestParam(required = false) String workNo,
|
||||||
|
@RequestParam(required = false) String playerName,
|
||||||
|
@RequestParam(required = false) String accountNo,
|
||||||
|
@RequestParam(defaultValue = "1") Long page,
|
||||||
|
@RequestParam(defaultValue = "10") Long pageSize) {
|
||||||
|
Long userId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(workService.getGuidedWorks(contestId, workNo, playerName, accountNo, page, pageSize, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission("work:read")
|
||||||
|
@Operation(summary = "查询作品详情")
|
||||||
|
public Result<Map<String, Object>> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(workService.findDetail(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/registration/{registrationId}/versions")
|
||||||
|
@RequirePermission("work:read")
|
||||||
|
@Operation(summary = "查询作品版本历史")
|
||||||
|
public Result<List<Map<String, Object>>> getWorkVersions(@PathVariable Long registrationId) {
|
||||||
|
return Result.success(workService.getWorkVersions(registrationId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@RequirePermission("work:update")
|
||||||
|
@Operation(summary = "删除作品")
|
||||||
|
public Result<Void> remove(@PathVariable Long id) {
|
||||||
|
workService.removeWork(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
package com.competition.modules.biz.contest.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建赛事DTO")
|
||||||
|
public class CreateContestDto {
|
||||||
|
|
||||||
|
@NotBlank(message = "赛事名称不能为空")
|
||||||
|
@Schema(description = "赛事名称")
|
||||||
|
private String contestName;
|
||||||
|
|
||||||
|
@NotBlank(message = "赛事类型不能为空")
|
||||||
|
@Schema(description = "赛事类型")
|
||||||
|
private String contestType;
|
||||||
|
|
||||||
|
@Schema(description = "可见性")
|
||||||
|
private String visibility;
|
||||||
|
|
||||||
|
@Schema(description = "目标城市列表")
|
||||||
|
private List<String> targetCities;
|
||||||
|
|
||||||
|
@Schema(description = "最小年龄")
|
||||||
|
private Integer ageMin;
|
||||||
|
|
||||||
|
@Schema(description = "最大年龄")
|
||||||
|
private Integer ageMax;
|
||||||
|
|
||||||
|
@NotBlank(message = "开始时间不能为空")
|
||||||
|
@Schema(description = "开始时间")
|
||||||
|
private String startTime;
|
||||||
|
|
||||||
|
@NotBlank(message = "结束时间不能为空")
|
||||||
|
@Schema(description = "结束时间")
|
||||||
|
private String endTime;
|
||||||
|
|
||||||
|
@Schema(description = "地址")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "赛事内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "赛事关联租户ID列表")
|
||||||
|
private List<Integer> contestTenants;
|
||||||
|
|
||||||
|
@Schema(description = "封面图URL")
|
||||||
|
private String coverUrl;
|
||||||
|
|
||||||
|
@Schema(description = "海报URL")
|
||||||
|
private String posterUrl;
|
||||||
|
|
||||||
|
@Schema(description = "联系人姓名")
|
||||||
|
private String contactName;
|
||||||
|
|
||||||
|
@Schema(description = "联系人电话")
|
||||||
|
private String contactPhone;
|
||||||
|
|
||||||
|
@Schema(description = "联系人二维码")
|
||||||
|
private String contactQrcode;
|
||||||
|
|
||||||
|
@Schema(description = "主办方")
|
||||||
|
private Object organizers;
|
||||||
|
|
||||||
|
@Schema(description = "协办方")
|
||||||
|
private Object coOrganizers;
|
||||||
|
|
||||||
|
@Schema(description = "赞助方")
|
||||||
|
private Object sponsors;
|
||||||
|
|
||||||
|
@NotBlank(message = "报名开始时间不能为空")
|
||||||
|
@Schema(description = "报名开始时间")
|
||||||
|
private String registerStartTime;
|
||||||
|
|
||||||
|
@NotBlank(message = "报名结束时间不能为空")
|
||||||
|
@Schema(description = "报名结束时间")
|
||||||
|
private String registerEndTime;
|
||||||
|
|
||||||
|
@Schema(description = "报名状态")
|
||||||
|
private String registerState;
|
||||||
|
|
||||||
|
@Schema(description = "是否需要审核")
|
||||||
|
private Boolean requireAudit;
|
||||||
|
|
||||||
|
@Schema(description = "允许的年级列表")
|
||||||
|
private List<Integer> allowedGrades;
|
||||||
|
|
||||||
|
@Schema(description = "允许的班级列表")
|
||||||
|
private List<Integer> allowedClasses;
|
||||||
|
|
||||||
|
@Schema(description = "团队最小人数")
|
||||||
|
private Integer teamMinMembers;
|
||||||
|
|
||||||
|
@Schema(description = "团队最大人数")
|
||||||
|
private Integer teamMaxMembers;
|
||||||
|
|
||||||
|
@Schema(description = "提交规则")
|
||||||
|
private String submitRule;
|
||||||
|
|
||||||
|
@NotBlank(message = "提交开始时间不能为空")
|
||||||
|
@Schema(description = "提交开始时间")
|
||||||
|
private String submitStartTime;
|
||||||
|
|
||||||
|
@NotBlank(message = "提交结束时间不能为空")
|
||||||
|
@Schema(description = "提交结束时间")
|
||||||
|
private String submitEndTime;
|
||||||
|
|
||||||
|
@Schema(description = "作品类型")
|
||||||
|
private String workType;
|
||||||
|
|
||||||
|
@Schema(description = "作品要求")
|
||||||
|
private String workRequirement;
|
||||||
|
|
||||||
|
@Schema(description = "评审规则ID")
|
||||||
|
private Long reviewRuleId;
|
||||||
|
|
||||||
|
@NotBlank(message = "评审开始时间不能为空")
|
||||||
|
@Schema(description = "评审开始时间")
|
||||||
|
private String reviewStartTime;
|
||||||
|
|
||||||
|
@NotBlank(message = "评审结束时间不能为空")
|
||||||
|
@Schema(description = "评审结束时间")
|
||||||
|
private String reviewEndTime;
|
||||||
|
|
||||||
|
@Schema(description = "结果发布时间")
|
||||||
|
private String resultPublishTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.competition.modules.biz.contest.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建公告DTO")
|
||||||
|
public class CreateNoticeDto {
|
||||||
|
|
||||||
|
@NotNull(message = "赛事ID不能为空")
|
||||||
|
@Schema(description = "赛事ID")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
@NotBlank(message = "公告标题不能为空")
|
||||||
|
@Schema(description = "公告标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@NotBlank(message = "公告内容不能为空")
|
||||||
|
@Schema(description = "公告内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "公告类型")
|
||||||
|
private String noticeType;
|
||||||
|
|
||||||
|
@Schema(description = "优先级")
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
|
@Schema(description = "发布时间")
|
||||||
|
private String publishTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.competition.modules.biz.contest.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建报名DTO")
|
||||||
|
public class CreateRegistrationDto {
|
||||||
|
|
||||||
|
@NotNull(message = "赛事ID不能为空")
|
||||||
|
@Schema(description = "赛事ID")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
@NotBlank(message = "报名类型不能为空")
|
||||||
|
@Schema(description = "报名类型")
|
||||||
|
private String registrationType;
|
||||||
|
|
||||||
|
@Schema(description = "团队ID")
|
||||||
|
private Long teamId;
|
||||||
|
|
||||||
|
@NotNull(message = "用户ID不能为空")
|
||||||
|
@Schema(description = "用户ID")
|
||||||
|
private Long userId;
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.competition.modules.biz.contest.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建团队DTO")
|
||||||
|
public class CreateTeamDto {
|
||||||
|
|
||||||
|
@NotNull(message = "赛事ID不能为空")
|
||||||
|
@Schema(description = "赛事ID")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
@NotBlank(message = "团队名称不能为空")
|
||||||
|
@Schema(description = "团队名称")
|
||||||
|
private String teamName;
|
||||||
|
|
||||||
|
@NotNull(message = "队长ID不能为空")
|
||||||
|
@Schema(description = "队长ID")
|
||||||
|
private Long leaderId;
|
||||||
|
|
||||||
|
@Schema(description = "成员ID列表")
|
||||||
|
private List<Long> memberIds;
|
||||||
|
|
||||||
|
@Schema(description = "指导老师ID列表")
|
||||||
|
private List<Long> teacherIds;
|
||||||
|
|
||||||
|
@Schema(description = "最大成员数")
|
||||||
|
private Integer maxMembers;
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package com.competition.modules.biz.contest.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "查询赛事DTO")
|
||||||
|
public class QueryContestDto {
|
||||||
|
|
||||||
|
@Schema(description = "页码", defaultValue = "1")
|
||||||
|
private Long page = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "每页条数", defaultValue = "10")
|
||||||
|
private Long pageSize = 10L;
|
||||||
|
|
||||||
|
@Schema(description = "赛事名称")
|
||||||
|
private String contestName;
|
||||||
|
|
||||||
|
@Schema(description = "赛事状态")
|
||||||
|
private String contestState;
|
||||||
|
|
||||||
|
@Schema(description = "状态")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "赛事类型")
|
||||||
|
private String contestType;
|
||||||
|
|
||||||
|
@Schema(description = "可见性")
|
||||||
|
private String visibility;
|
||||||
|
|
||||||
|
@Schema(description = "赛事阶段")
|
||||||
|
private String stage;
|
||||||
|
|
||||||
|
@Schema(description = "创建者租户ID")
|
||||||
|
private Long creatorTenantId;
|
||||||
|
|
||||||
|
@Schema(description = "角色")
|
||||||
|
private String role;
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.competition.modules.biz.contest.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "查询报名DTO")
|
||||||
|
public class QueryRegistrationDto {
|
||||||
|
|
||||||
|
@Schema(description = "页码", defaultValue = "1")
|
||||||
|
private Long page = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "每页条数", defaultValue = "10")
|
||||||
|
private Long pageSize = 10L;
|
||||||
|
|
||||||
|
@Schema(description = "赛事ID")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
@Schema(description = "报名状态")
|
||||||
|
private String registrationState;
|
||||||
|
|
||||||
|
@Schema(description = "报名类型")
|
||||||
|
private String registrationType;
|
||||||
|
|
||||||
|
@Schema(description = "用户ID")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@Schema(description = "参赛者类型")
|
||||||
|
private String participantType;
|
||||||
|
|
||||||
|
@Schema(description = "关键词")
|
||||||
|
private String keyword;
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
package com.competition.modules.biz.contest.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "查询作品DTO")
|
||||||
|
public class QueryWorkDto {
|
||||||
|
|
||||||
|
@Schema(description = "页码", defaultValue = "1")
|
||||||
|
private Long page = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "每页条数", defaultValue = "10")
|
||||||
|
private Long pageSize = 10L;
|
||||||
|
|
||||||
|
@Schema(description = "赛事ID")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
@Schema(description = "报名ID")
|
||||||
|
private Long registrationId;
|
||||||
|
|
||||||
|
@Schema(description = "状态")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "作品标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Schema(description = "作品编号")
|
||||||
|
private String workNo;
|
||||||
|
|
||||||
|
@Schema(description = "用户名")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@Schema(description = "关键词")
|
||||||
|
private String keyword;
|
||||||
|
|
||||||
|
@Schema(description = "姓名")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "分配状态")
|
||||||
|
private String assignStatus;
|
||||||
|
|
||||||
|
@Schema(description = "租户ID")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "提交开始时间")
|
||||||
|
private String submitStartTime;
|
||||||
|
|
||||||
|
@Schema(description = "提交结束时间")
|
||||||
|
private String submitEndTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
package com.competition.modules.biz.contest.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "提交作品DTO")
|
||||||
|
public class SubmitWorkDto {
|
||||||
|
|
||||||
|
@NotNull(message = "报名ID不能为空")
|
||||||
|
@Schema(description = "报名ID")
|
||||||
|
private Long registrationId;
|
||||||
|
|
||||||
|
@NotBlank(message = "作品标题不能为空")
|
||||||
|
@Schema(description = "作品标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Schema(description = "作品描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "文件信息")
|
||||||
|
private Object files;
|
||||||
|
|
||||||
|
@Schema(description = "预览图URL")
|
||||||
|
private String previewUrl;
|
||||||
|
|
||||||
|
@Schema(description = "预览图URL列表")
|
||||||
|
private List<String> previewUrls;
|
||||||
|
|
||||||
|
@Schema(description = "AI模型元数据")
|
||||||
|
private Object aiModelMeta;
|
||||||
|
|
||||||
|
@Schema(description = "附件列表")
|
||||||
|
private List<AttachmentItem> attachments;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "附件项")
|
||||||
|
public static class AttachmentItem {
|
||||||
|
|
||||||
|
@Schema(description = "文件名")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@Schema(description = "文件URL")
|
||||||
|
private String fileUrl;
|
||||||
|
|
||||||
|
@Schema(description = "文件类型")
|
||||||
|
private String fileType;
|
||||||
|
|
||||||
|
@Schema(description = "文件大小")
|
||||||
|
private String size;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,152 @@
|
|||||||
|
package com.competition.modules.biz.contest.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
|
import com.competition.common.entity.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 赛事实体(35+ 字段,7 个 JSON 列)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName(value = "t_contest", autoResultMap = true)
|
||||||
|
public class BizContest extends BaseEntity {
|
||||||
|
|
||||||
|
/** 赛事名称(唯一) */
|
||||||
|
@TableField("contest_name")
|
||||||
|
private String contestName;
|
||||||
|
|
||||||
|
/** 赛事类型:individual/team */
|
||||||
|
@TableField("contest_type")
|
||||||
|
private String contestType;
|
||||||
|
|
||||||
|
/** 赛事发布状态:unpublished/published */
|
||||||
|
@TableField("contest_state")
|
||||||
|
private String contestState;
|
||||||
|
|
||||||
|
/** 赛事进度状态:ongoing/finished */
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/** 开始时间 */
|
||||||
|
@TableField("start_time")
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
|
/** 结束时间 */
|
||||||
|
@TableField("end_time")
|
||||||
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
|
/** 线下地址 */
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
/** 赛事详情(富文本) */
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/** 可见范围:public/designated/internal */
|
||||||
|
private String visibility;
|
||||||
|
|
||||||
|
// ====== 授权租户(JSON) ======
|
||||||
|
/** 授权租户 ID 数组 */
|
||||||
|
@TableField(value = "contest_tenants", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<Integer> contestTenants;
|
||||||
|
|
||||||
|
// ====== 封面和联系方式 ======
|
||||||
|
@TableField("cover_url")
|
||||||
|
private String coverUrl;
|
||||||
|
|
||||||
|
@TableField("poster_url")
|
||||||
|
private String posterUrl;
|
||||||
|
|
||||||
|
@TableField("contact_name")
|
||||||
|
private String contactName;
|
||||||
|
|
||||||
|
@TableField("contact_phone")
|
||||||
|
private String contactPhone;
|
||||||
|
|
||||||
|
@TableField("contact_qrcode")
|
||||||
|
private String contactQrcode;
|
||||||
|
|
||||||
|
// ====== 主办/协办/赞助(JSON) ======
|
||||||
|
@TableField(value = "organizers", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private Object organizers;
|
||||||
|
|
||||||
|
@TableField(value = "co_organizers", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private Object coOrganizers;
|
||||||
|
|
||||||
|
@TableField(value = "sponsors", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private Object sponsors;
|
||||||
|
|
||||||
|
// ====== 报名配置 ======
|
||||||
|
@TableField("register_start_time")
|
||||||
|
private LocalDateTime registerStartTime;
|
||||||
|
|
||||||
|
@TableField("register_end_time")
|
||||||
|
private LocalDateTime registerEndTime;
|
||||||
|
|
||||||
|
@TableField("register_state")
|
||||||
|
private String registerState;
|
||||||
|
|
||||||
|
@TableField("require_audit")
|
||||||
|
private Boolean requireAudit;
|
||||||
|
|
||||||
|
@TableField(value = "allowed_grades", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<Integer> allowedGrades;
|
||||||
|
|
||||||
|
@TableField(value = "allowed_classes", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<Integer> allowedClasses;
|
||||||
|
|
||||||
|
@TableField("team_min_members")
|
||||||
|
private Integer teamMinMembers;
|
||||||
|
|
||||||
|
@TableField("team_max_members")
|
||||||
|
private Integer teamMaxMembers;
|
||||||
|
|
||||||
|
// ====== 目标筛选 ======
|
||||||
|
@TableField(value = "target_cities", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<String> targetCities;
|
||||||
|
|
||||||
|
@TableField("age_min")
|
||||||
|
private Integer ageMin;
|
||||||
|
|
||||||
|
@TableField("age_max")
|
||||||
|
private Integer ageMax;
|
||||||
|
|
||||||
|
// ====== 提交配置 ======
|
||||||
|
@TableField("submit_rule")
|
||||||
|
private String submitRule;
|
||||||
|
|
||||||
|
@TableField("submit_start_time")
|
||||||
|
private LocalDateTime submitStartTime;
|
||||||
|
|
||||||
|
@TableField("submit_end_time")
|
||||||
|
private LocalDateTime submitEndTime;
|
||||||
|
|
||||||
|
@TableField("work_type")
|
||||||
|
private String workType;
|
||||||
|
|
||||||
|
@TableField("work_requirement")
|
||||||
|
private String workRequirement;
|
||||||
|
|
||||||
|
// ====== 评审配置 ======
|
||||||
|
@TableField("review_rule_id")
|
||||||
|
private Long reviewRuleId;
|
||||||
|
|
||||||
|
@TableField("review_start_time")
|
||||||
|
private LocalDateTime reviewStartTime;
|
||||||
|
|
||||||
|
@TableField("review_end_time")
|
||||||
|
private LocalDateTime reviewEndTime;
|
||||||
|
|
||||||
|
// ====== 成果发布 ======
|
||||||
|
@TableField("result_state")
|
||||||
|
private String resultState;
|
||||||
|
|
||||||
|
@TableField("result_publish_time")
|
||||||
|
private LocalDateTime resultPublishTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.competition.modules.biz.contest.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.competition.common.entity.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("t_contest_attachment")
|
||||||
|
public class BizContestAttachment extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("contest_id")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
@TableField("file_name")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@TableField("file_url")
|
||||||
|
private String fileUrl;
|
||||||
|
|
||||||
|
private String format;
|
||||||
|
|
||||||
|
@TableField("file_type")
|
||||||
|
private String fileType;
|
||||||
|
|
||||||
|
private String size;
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.competition.modules.biz.contest.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.competition.common.entity.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("t_contest_notice")
|
||||||
|
public class BizContestNotice extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("contest_id")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/** system/manual/urgent */
|
||||||
|
@TableField("notice_type")
|
||||||
|
private String noticeType;
|
||||||
|
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
|
@TableField("publish_time")
|
||||||
|
private LocalDateTime publishTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
package com.competition.modules.biz.contest.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.competition.common.entity.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("t_contest_registration")
|
||||||
|
public class BizContestRegistration extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("contest_id")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
@TableField("tenant_id")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
/** 报名类型:individual/team */
|
||||||
|
@TableField("registration_type")
|
||||||
|
private String registrationType;
|
||||||
|
|
||||||
|
@TableField("team_id")
|
||||||
|
private Long teamId;
|
||||||
|
|
||||||
|
/** 团队名称快照 */
|
||||||
|
@TableField("team_name")
|
||||||
|
private String teamName;
|
||||||
|
|
||||||
|
@TableField("user_id")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/** 账号快照 */
|
||||||
|
@TableField("account_no")
|
||||||
|
private String accountNo;
|
||||||
|
|
||||||
|
@TableField("account_name")
|
||||||
|
private String accountName;
|
||||||
|
|
||||||
|
/** 角色快照:leader/member/mentor */
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
/** 报名状态:pending/passed/rejected/withdrawn */
|
||||||
|
@TableField("registration_state")
|
||||||
|
private String registrationState;
|
||||||
|
|
||||||
|
/** 参与者类型:self/child */
|
||||||
|
@TableField("participant_type")
|
||||||
|
private String participantType;
|
||||||
|
|
||||||
|
@TableField("child_id")
|
||||||
|
private Long childId;
|
||||||
|
|
||||||
|
/** 实际提交人 ID */
|
||||||
|
private Integer registrant;
|
||||||
|
|
||||||
|
@TableField("registration_time")
|
||||||
|
private LocalDateTime registrationTime;
|
||||||
|
|
||||||
|
/** 审核原因 */
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
/** 审核操作人 */
|
||||||
|
private Integer operator;
|
||||||
|
|
||||||
|
@TableField("operation_date")
|
||||||
|
private LocalDateTime operationDate;
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.competition.modules.biz.contest.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("t_contest_registration_teacher")
|
||||||
|
public class BizContestRegistrationTeacher implements Serializable {
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@TableField("registration_id")
|
||||||
|
private Long registrationId;
|
||||||
|
|
||||||
|
@TableField("tenant_id")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
@TableField("user_id")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@TableField("is_default")
|
||||||
|
private Boolean isDefault;
|
||||||
|
|
||||||
|
private Integer creator;
|
||||||
|
private Integer modifier;
|
||||||
|
|
||||||
|
@TableField("create_time")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@TableField("modify_time")
|
||||||
|
private LocalDateTime modifyTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.competition.modules.biz.contest.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.competition.common.entity.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("t_contest_team")
|
||||||
|
public class BizContestTeam extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("tenant_id")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
@TableField("contest_id")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
@TableField("team_name")
|
||||||
|
private String teamName;
|
||||||
|
|
||||||
|
@TableField("leader_user_id")
|
||||||
|
private Long leaderUserId;
|
||||||
|
|
||||||
|
@TableField("max_members")
|
||||||
|
private Integer maxMembers;
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.competition.modules.biz.contest.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("t_contest_team_member")
|
||||||
|
public class BizContestTeamMember implements Serializable {
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@TableField("tenant_id")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
@TableField("team_id")
|
||||||
|
private Long teamId;
|
||||||
|
|
||||||
|
@TableField("user_id")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/** 角色:member/leader/mentor */
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
private Integer creator;
|
||||||
|
private Integer modifier;
|
||||||
|
|
||||||
|
@TableField("create_time")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@TableField("modify_time")
|
||||||
|
private LocalDateTime modifyTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
package com.competition.modules.biz.contest.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
|
import com.competition.common.entity.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName(value = "t_contest_work", autoResultMap = true)
|
||||||
|
public class BizContestWork extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("tenant_id")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
@TableField("contest_id")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
@TableField("registration_id")
|
||||||
|
private Long registrationId;
|
||||||
|
|
||||||
|
/** 作品编号(唯一展示编号) */
|
||||||
|
@TableField("work_no")
|
||||||
|
private String workNo;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@TableField(value = "files", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private Object files;
|
||||||
|
|
||||||
|
private Integer version;
|
||||||
|
|
||||||
|
@TableField("is_latest")
|
||||||
|
private Boolean isLatest;
|
||||||
|
|
||||||
|
/** submitted/locked/reviewing/rejected/accepted */
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@TableField("submit_time")
|
||||||
|
private LocalDateTime submitTime;
|
||||||
|
|
||||||
|
@TableField("submitter_user_id")
|
||||||
|
private Long submitterUserId;
|
||||||
|
|
||||||
|
@TableField("submitter_account_no")
|
||||||
|
private String submitterAccountNo;
|
||||||
|
|
||||||
|
/** teacher/student/team_leader */
|
||||||
|
@TableField("submit_source")
|
||||||
|
private String submitSource;
|
||||||
|
|
||||||
|
@TableField("preview_url")
|
||||||
|
private String previewUrl;
|
||||||
|
|
||||||
|
@TableField(value = "preview_urls", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<String> previewUrls;
|
||||||
|
|
||||||
|
@TableField(value = "ai_model_meta", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private Object aiModelMeta;
|
||||||
|
|
||||||
|
@TableField("user_work_id")
|
||||||
|
private Long userWorkId;
|
||||||
|
|
||||||
|
// ====== 赛果字段 ======
|
||||||
|
@TableField("final_score")
|
||||||
|
private BigDecimal finalScore;
|
||||||
|
|
||||||
|
@TableField("`rank`")
|
||||||
|
private Integer rank;
|
||||||
|
|
||||||
|
/** first/second/third/excellent/none */
|
||||||
|
@TableField("award_level")
|
||||||
|
private String awardLevel;
|
||||||
|
|
||||||
|
@TableField("award_name")
|
||||||
|
private String awardName;
|
||||||
|
|
||||||
|
@TableField("certificate_url")
|
||||||
|
private String certificateUrl;
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package com.competition.modules.biz.contest.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("t_contest_work_attachment")
|
||||||
|
public class BizContestWorkAttachment implements Serializable {
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@TableField("tenant_id")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
@TableField("contest_id")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
@TableField("work_id")
|
||||||
|
private Long workId;
|
||||||
|
|
||||||
|
@TableField("file_name")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@TableField("file_url")
|
||||||
|
private String fileUrl;
|
||||||
|
|
||||||
|
private String format;
|
||||||
|
|
||||||
|
@TableField("file_type")
|
||||||
|
private String fileType;
|
||||||
|
|
||||||
|
private String size;
|
||||||
|
|
||||||
|
private Integer creator;
|
||||||
|
private Integer modifier;
|
||||||
|
|
||||||
|
@TableField("create_time")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@TableField("modify_time")
|
||||||
|
private LocalDateTime modifyTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.contest.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestAttachment;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ContestAttachmentMapper extends BaseMapper<BizContestAttachment> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.contest.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContest;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ContestMapper extends BaseMapper<BizContest> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.contest.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestNotice;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ContestNoticeMapper extends BaseMapper<BizContestNotice> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.contest.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestRegistration;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ContestRegistrationMapper extends BaseMapper<BizContestRegistration> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.contest.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestRegistrationTeacher;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ContestRegistrationTeacherMapper extends BaseMapper<BizContestRegistrationTeacher> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.contest.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestTeam;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ContestTeamMapper extends BaseMapper<BizContestTeam> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.contest.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestTeamMember;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ContestTeamMemberMapper extends BaseMapper<BizContestTeamMember> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.contest.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestWorkAttachment;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ContestWorkAttachmentMapper extends BaseMapper<BizContestWorkAttachment> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.contest.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestWork;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ContestWorkMapper extends BaseMapper<BizContestWork> {
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package com.competition.modules.biz.contest.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestAttachment;
|
||||||
|
|
||||||
|
public interface IContestAttachmentService extends IService<BizContestAttachment> {
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package com.competition.modules.biz.contest.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestNotice;
|
||||||
|
|
||||||
|
public interface IContestNoticeService extends IService<BizContestNotice> {
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.competition.modules.biz.contest.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.contest.dto.CreateRegistrationDto;
|
||||||
|
import com.competition.modules.biz.contest.dto.QueryRegistrationDto;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestRegistration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IContestRegistrationService extends IService<BizContestRegistration> {
|
||||||
|
|
||||||
|
Map<String, Object> createRegistration(CreateRegistrationDto dto, Long tenantId, Long creatorId);
|
||||||
|
|
||||||
|
PageResult<Map<String, Object>> findAll(QueryRegistrationDto dto, Long tenantId, boolean isSuperTenant);
|
||||||
|
|
||||||
|
Map<String, Object> getStats(Long contestId, Long tenantId);
|
||||||
|
|
||||||
|
Map<String, Object> findDetail(Long id, Long tenantId);
|
||||||
|
|
||||||
|
Map<String, Object> getMyRegistration(Long contestId, Long userId, Long tenantId);
|
||||||
|
|
||||||
|
void reviewRegistration(Long id, String state, String reason, Long operatorId, Long tenantId);
|
||||||
|
|
||||||
|
void revokeReview(Long id, Long tenantId);
|
||||||
|
|
||||||
|
void batchReview(List<Long> ids, String state, String reason, Long operatorId, Long tenantId);
|
||||||
|
|
||||||
|
void addTeacher(Long registrationId, Long teacherUserId, Long tenantId, Long creatorId);
|
||||||
|
|
||||||
|
void removeTeacher(Long registrationId, Long teacherUserId, Long tenantId);
|
||||||
|
|
||||||
|
void removeRegistration(Long id, Long tenantId);
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.competition.modules.biz.contest.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.contest.dto.CreateContestDto;
|
||||||
|
import com.competition.modules.biz.contest.dto.QueryContestDto;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContest;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IContestService extends IService<BizContest> {
|
||||||
|
|
||||||
|
BizContest createContest(CreateContestDto dto, Long creatorId);
|
||||||
|
|
||||||
|
PageResult<Map<String, Object>> findAll(QueryContestDto dto, Long tenantId, boolean isSuperTenant);
|
||||||
|
|
||||||
|
PageResult<Map<String, Object>> getMyContests(QueryContestDto dto, Long userId, Long tenantId);
|
||||||
|
|
||||||
|
Map<String, Object> findDetail(Long id);
|
||||||
|
|
||||||
|
BizContest updateContest(Long id, CreateContestDto dto);
|
||||||
|
|
||||||
|
void publishContest(Long id, String contestState);
|
||||||
|
|
||||||
|
void finishContest(Long id);
|
||||||
|
|
||||||
|
void reopenContest(Long id);
|
||||||
|
|
||||||
|
void removeContest(Long id);
|
||||||
|
|
||||||
|
Map<String, Object> getStats(Long tenantId, boolean isSuperTenant);
|
||||||
|
|
||||||
|
Map<String, Object> getDashboard(Long tenantId);
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.competition.modules.biz.contest.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.competition.modules.biz.contest.dto.CreateTeamDto;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestTeam;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IContestTeamService extends IService<BizContestTeam> {
|
||||||
|
|
||||||
|
Map<String, Object> createTeam(CreateTeamDto dto, Long tenantId, Long creatorId);
|
||||||
|
|
||||||
|
List<Map<String, Object>> findByContest(Long contestId, Long tenantId);
|
||||||
|
|
||||||
|
Map<String, Object> findDetail(Long id);
|
||||||
|
|
||||||
|
BizContestTeam updateTeam(Long id, CreateTeamDto dto);
|
||||||
|
|
||||||
|
void addMember(Long teamId, Long userId, String role, Long tenantId);
|
||||||
|
|
||||||
|
void removeMember(Long teamId, Long userId);
|
||||||
|
|
||||||
|
void removeTeam(Long id);
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.competition.modules.biz.contest.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.contest.dto.QueryWorkDto;
|
||||||
|
import com.competition.modules.biz.contest.dto.SubmitWorkDto;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestWork;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IContestWorkService extends IService<BizContestWork> {
|
||||||
|
|
||||||
|
Map<String, Object> submitWork(SubmitWorkDto dto, Long tenantId, Long submitterId);
|
||||||
|
|
||||||
|
PageResult<Map<String, Object>> findAll(QueryWorkDto dto, Long tenantId, boolean isSuperTenant);
|
||||||
|
|
||||||
|
Map<String, Object> getStats(Long contestId, Long tenantId);
|
||||||
|
|
||||||
|
Map<String, Object> findDetail(Long id);
|
||||||
|
|
||||||
|
List<Map<String, Object>> getWorkVersions(Long registrationId);
|
||||||
|
|
||||||
|
PageResult<Map<String, Object>> getGuidedWorks(Long contestId, String workNo, String playerName, String accountNo, Long page, Long pageSize, Long userId);
|
||||||
|
|
||||||
|
void removeWork(Long id);
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.competition.modules.biz.contest.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestAttachment;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestAttachmentMapper;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestAttachmentService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ContestAttachmentServiceImpl extends ServiceImpl<ContestAttachmentMapper, BizContestAttachment> implements IContestAttachmentService {
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.competition.modules.biz.contest.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestNotice;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestNoticeMapper;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestNoticeService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ContestNoticeServiceImpl extends ServiceImpl<ContestNoticeMapper, BizContestNotice> implements IContestNoticeService {
|
||||||
|
}
|
||||||
@ -0,0 +1,332 @@
|
|||||||
|
package com.competition.modules.biz.contest.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import com.competition.common.exception.BusinessException;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.contest.dto.CreateRegistrationDto;
|
||||||
|
import com.competition.modules.biz.contest.dto.QueryRegistrationDto;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContest;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestRegistration;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestRegistrationTeacher;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestMapper;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestRegistrationTeacherMapper;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestRegistrationService;
|
||||||
|
import com.competition.modules.sys.entity.SysUser;
|
||||||
|
import com.competition.modules.sys.mapper.SysUserMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestRegistrationServiceImpl extends ServiceImpl<ContestRegistrationMapper, BizContestRegistration>
|
||||||
|
implements IContestRegistrationService {
|
||||||
|
|
||||||
|
private final ContestRegistrationMapper contestRegistrationMapper;
|
||||||
|
private final ContestRegistrationTeacherMapper contestRegistrationTeacherMapper;
|
||||||
|
private final ContestMapper contestMapper;
|
||||||
|
private final SysUserMapper sysUserMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> createRegistration(CreateRegistrationDto dto, Long tenantId, Long creatorId) {
|
||||||
|
log.info("开始创建报名,赛事ID:{},用户ID:{}", dto.getContestId(), dto.getUserId());
|
||||||
|
|
||||||
|
// 验证赛事存在且已发布
|
||||||
|
BizContest contest = contestMapper.selectById(dto.getContestId());
|
||||||
|
if (contest == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "赛事不存在");
|
||||||
|
}
|
||||||
|
if (!"published".equals(contest.getContestState())) {
|
||||||
|
throw BusinessException.of(ErrorCode.BAD_REQUEST, "赛事未发布,无法报名");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
SysUser user = sysUserMapper.selectById(dto.getUserId());
|
||||||
|
String accountNo = user != null ? user.getUsername() : null;
|
||||||
|
String accountName = user != null ? user.getNickname() : null;
|
||||||
|
|
||||||
|
BizContestRegistration registration = new BizContestRegistration();
|
||||||
|
registration.setContestId(dto.getContestId());
|
||||||
|
registration.setTenantId(tenantId);
|
||||||
|
registration.setRegistrationType(dto.getRegistrationType());
|
||||||
|
registration.setTeamId(dto.getTeamId());
|
||||||
|
registration.setUserId(dto.getUserId());
|
||||||
|
registration.setAccountNo(accountNo);
|
||||||
|
registration.setAccountName(accountName);
|
||||||
|
registration.setRegistrationState("pending");
|
||||||
|
registration.setParticipantType("self");
|
||||||
|
registration.setRegistrationTime(LocalDateTime.now());
|
||||||
|
registration.setRegistrant(creatorId != null ? creatorId.intValue() : null);
|
||||||
|
registration.setCreator(creatorId != null ? creatorId.intValue() : null);
|
||||||
|
|
||||||
|
save(registration);
|
||||||
|
log.info("报名创建成功,ID:{}", registration.getId());
|
||||||
|
|
||||||
|
// 如果创建者是教师角色,自动添加为默认指导教师
|
||||||
|
// 简化实现:此处由调用方根据角色判断后调用 addTeacher
|
||||||
|
// TODO: 检查创建者角色,自动关联教师
|
||||||
|
|
||||||
|
Map<String, Object> result = registrationToMap(registration);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<Map<String, Object>> findAll(QueryRegistrationDto dto, Long tenantId, boolean isSuperTenant) {
|
||||||
|
log.info("查询报名列表,赛事ID:{},页码:{}", dto.getContestId(), dto.getPage());
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestRegistration> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
|
||||||
|
if (dto.getContestId() != null) {
|
||||||
|
wrapper.eq(BizContestRegistration::getContestId, dto.getContestId());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getRegistrationState())) {
|
||||||
|
wrapper.eq(BizContestRegistration::getRegistrationState, dto.getRegistrationState());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getRegistrationType())) {
|
||||||
|
wrapper.eq(BizContestRegistration::getRegistrationType, dto.getRegistrationType());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getParticipantType())) {
|
||||||
|
wrapper.eq(BizContestRegistration::getParticipantType, dto.getParticipantType());
|
||||||
|
}
|
||||||
|
if (dto.getUserId() != null) {
|
||||||
|
wrapper.eq(BizContestRegistration::getUserId, dto.getUserId());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getKeyword())) {
|
||||||
|
wrapper.and(w -> w.like(BizContestRegistration::getAccountNo, dto.getKeyword())
|
||||||
|
.or().like(BizContestRegistration::getAccountName, dto.getKeyword()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSuperTenant && tenantId != null) {
|
||||||
|
wrapper.eq(BizContestRegistration::getTenantId, tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.orderByDesc(BizContestRegistration::getRegistrationTime);
|
||||||
|
|
||||||
|
Page<BizContestRegistration> page = new Page<>(dto.getPage(), dto.getPageSize());
|
||||||
|
Page<BizContestRegistration> result = contestRegistrationMapper.selectPage(page, wrapper);
|
||||||
|
|
||||||
|
List<Map<String, Object>> voList = result.getRecords().stream()
|
||||||
|
.map(this::registrationToMap)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return PageResult.from(result, voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getStats(Long contestId, Long tenantId) {
|
||||||
|
log.info("获取报名统计,赛事ID:{}", contestId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestRegistration> baseWrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (contestId != null) {
|
||||||
|
baseWrapper.eq(BizContestRegistration::getContestId, contestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
long total = count(baseWrapper);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestRegistration> pendingWrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (contestId != null) {
|
||||||
|
pendingWrapper.eq(BizContestRegistration::getContestId, contestId);
|
||||||
|
}
|
||||||
|
pendingWrapper.eq(BizContestRegistration::getRegistrationState, "pending");
|
||||||
|
long pending = count(pendingWrapper);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestRegistration> passedWrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (contestId != null) {
|
||||||
|
passedWrapper.eq(BizContestRegistration::getContestId, contestId);
|
||||||
|
}
|
||||||
|
passedWrapper.eq(BizContestRegistration::getRegistrationState, "passed");
|
||||||
|
long passed = count(passedWrapper);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestRegistration> rejectedWrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (contestId != null) {
|
||||||
|
rejectedWrapper.eq(BizContestRegistration::getContestId, contestId);
|
||||||
|
}
|
||||||
|
rejectedWrapper.eq(BizContestRegistration::getRegistrationState, "rejected");
|
||||||
|
long rejected = count(rejectedWrapper);
|
||||||
|
|
||||||
|
Map<String, Object> stats = new LinkedHashMap<>();
|
||||||
|
stats.put("total", total);
|
||||||
|
stats.put("pending", pending);
|
||||||
|
stats.put("passed", passed);
|
||||||
|
stats.put("rejected", rejected);
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> findDetail(Long id, Long tenantId) {
|
||||||
|
log.info("查询报名详情,ID:{}", id);
|
||||||
|
|
||||||
|
BizContestRegistration registration = getById(id);
|
||||||
|
if (registration == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "报名记录不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = registrationToMap(registration);
|
||||||
|
|
||||||
|
// 查询用户详情
|
||||||
|
if (registration.getUserId() != null) {
|
||||||
|
SysUser user = sysUserMapper.selectById(registration.getUserId());
|
||||||
|
if (user != null) {
|
||||||
|
Map<String, Object> userInfo = new LinkedHashMap<>();
|
||||||
|
userInfo.put("id", user.getId());
|
||||||
|
userInfo.put("username", user.getUsername());
|
||||||
|
userInfo.put("nickname", user.getNickname());
|
||||||
|
userInfo.put("phone", user.getPhone());
|
||||||
|
userInfo.put("email", user.getEmail());
|
||||||
|
userInfo.put("avatar", user.getAvatar());
|
||||||
|
result.put("userInfo", userInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询指导教师列表
|
||||||
|
LambdaQueryWrapper<BizContestRegistrationTeacher> teacherWrapper = new LambdaQueryWrapper<>();
|
||||||
|
teacherWrapper.eq(BizContestRegistrationTeacher::getRegistrationId, id);
|
||||||
|
List<BizContestRegistrationTeacher> teachers = contestRegistrationTeacherMapper.selectList(teacherWrapper);
|
||||||
|
result.put("teachers", teachers);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getMyRegistration(Long contestId, Long userId, Long tenantId) {
|
||||||
|
log.info("查询我的报名,赛事ID:{},用户ID:{}", contestId, userId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestRegistration> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizContestRegistration::getContestId, contestId);
|
||||||
|
wrapper.eq(BizContestRegistration::getUserId, userId);
|
||||||
|
wrapper.last("LIMIT 1");
|
||||||
|
|
||||||
|
BizContestRegistration registration = getOne(wrapper, false);
|
||||||
|
if (registration == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return registrationToMap(registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reviewRegistration(Long id, String state, String reason, Long operatorId, Long tenantId) {
|
||||||
|
log.info("审核报名,ID:{},状态:{}", id, state);
|
||||||
|
|
||||||
|
BizContestRegistration registration = getById(id);
|
||||||
|
if (registration == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "报名记录不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
registration.setRegistrationState(state);
|
||||||
|
registration.setReason(reason);
|
||||||
|
registration.setOperator(operatorId != null ? operatorId.intValue() : null);
|
||||||
|
registration.setOperationDate(LocalDateTime.now());
|
||||||
|
updateById(registration);
|
||||||
|
|
||||||
|
log.info("报名审核完成,ID:{},结果:{}", id, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void revokeReview(Long id, Long tenantId) {
|
||||||
|
log.info("撤回审核,ID:{}", id);
|
||||||
|
|
||||||
|
BizContestRegistration registration = getById(id);
|
||||||
|
if (registration == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "报名记录不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
registration.setRegistrationState("pending");
|
||||||
|
registration.setReason(null);
|
||||||
|
registration.setOperator(null);
|
||||||
|
registration.setOperationDate(null);
|
||||||
|
updateById(registration);
|
||||||
|
|
||||||
|
log.info("审核已撤回,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void batchReview(List<Long> ids, String state, String reason, Long operatorId, Long tenantId) {
|
||||||
|
log.info("批量审核报名,数量:{},状态:{}", ids.size(), state);
|
||||||
|
|
||||||
|
for (Long id : ids) {
|
||||||
|
reviewRegistration(id, state, reason, operatorId, tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("批量审核完成,共处理 {} 条", ids.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTeacher(Long registrationId, Long teacherUserId, Long tenantId, Long creatorId) {
|
||||||
|
log.info("添加指导教师,报名ID:{},教师用户ID:{}", registrationId, teacherUserId);
|
||||||
|
|
||||||
|
// 检查重复
|
||||||
|
LambdaQueryWrapper<BizContestRegistrationTeacher> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizContestRegistrationTeacher::getRegistrationId, registrationId);
|
||||||
|
wrapper.eq(BizContestRegistrationTeacher::getUserId, teacherUserId);
|
||||||
|
Long existCount = contestRegistrationTeacherMapper.selectCount(wrapper);
|
||||||
|
if (existCount > 0) {
|
||||||
|
throw BusinessException.of(ErrorCode.CONFLICT, "该教师已关联此报名");
|
||||||
|
}
|
||||||
|
|
||||||
|
BizContestRegistrationTeacher teacher = new BizContestRegistrationTeacher();
|
||||||
|
teacher.setRegistrationId(registrationId);
|
||||||
|
teacher.setTenantId(tenantId);
|
||||||
|
teacher.setUserId(teacherUserId);
|
||||||
|
teacher.setIsDefault(false);
|
||||||
|
teacher.setCreator(creatorId != null ? creatorId.intValue() : null);
|
||||||
|
teacher.setCreateTime(LocalDateTime.now());
|
||||||
|
contestRegistrationTeacherMapper.insert(teacher);
|
||||||
|
|
||||||
|
log.info("指导教师添加成功,报名ID:{},教师用户ID:{}", registrationId, teacherUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeTeacher(Long registrationId, Long teacherUserId, Long tenantId) {
|
||||||
|
log.info("移除指导教师,报名ID:{},教师用户ID:{}", registrationId, teacherUserId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestRegistrationTeacher> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizContestRegistrationTeacher::getRegistrationId, registrationId);
|
||||||
|
wrapper.eq(BizContestRegistrationTeacher::getUserId, teacherUserId);
|
||||||
|
contestRegistrationTeacherMapper.delete(wrapper);
|
||||||
|
|
||||||
|
log.info("指导教师移除成功,报名ID:{},教师用户ID:{}", registrationId, teacherUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeRegistration(Long id, Long tenantId) {
|
||||||
|
log.info("删除报名,ID:{}", id);
|
||||||
|
removeById(id);
|
||||||
|
log.info("报名删除成功,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 私有辅助方法 ======
|
||||||
|
|
||||||
|
private Map<String, Object> registrationToMap(BizContestRegistration entity) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", entity.getId());
|
||||||
|
map.put("contestId", entity.getContestId());
|
||||||
|
map.put("tenantId", entity.getTenantId());
|
||||||
|
map.put("registrationType", entity.getRegistrationType());
|
||||||
|
map.put("teamId", entity.getTeamId());
|
||||||
|
map.put("teamName", entity.getTeamName());
|
||||||
|
map.put("userId", entity.getUserId());
|
||||||
|
map.put("accountNo", entity.getAccountNo());
|
||||||
|
map.put("accountName", entity.getAccountName());
|
||||||
|
map.put("role", entity.getRole());
|
||||||
|
map.put("registrationState", entity.getRegistrationState());
|
||||||
|
map.put("participantType", entity.getParticipantType());
|
||||||
|
map.put("childId", entity.getChildId());
|
||||||
|
map.put("registrant", entity.getRegistrant());
|
||||||
|
map.put("registrationTime", entity.getRegistrationTime());
|
||||||
|
map.put("reason", entity.getReason());
|
||||||
|
map.put("operator", entity.getOperator());
|
||||||
|
map.put("operationDate", entity.getOperationDate());
|
||||||
|
map.put("createTime", entity.getCreateTime());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,463 @@
|
|||||||
|
package com.competition.modules.biz.contest.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import com.competition.common.exception.BusinessException;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.contest.dto.CreateContestDto;
|
||||||
|
import com.competition.modules.biz.contest.dto.QueryContestDto;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContest;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestAttachment;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestRegistration;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestWork;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestAttachmentMapper;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestMapper;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestWorkMapper;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestServiceImpl extends ServiceImpl<ContestMapper, BizContest> implements IContestService {
|
||||||
|
|
||||||
|
private final ContestMapper contestMapper;
|
||||||
|
private final ContestAttachmentMapper contestAttachmentMapper;
|
||||||
|
private final ContestRegistrationMapper contestRegistrationMapper;
|
||||||
|
private final ContestWorkMapper contestWorkMapper;
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DT_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BizContest createContest(CreateContestDto dto, Long creatorId) {
|
||||||
|
log.info("开始创建赛事,名称:{}", dto.getContestName());
|
||||||
|
|
||||||
|
BizContest entity = new BizContest();
|
||||||
|
mapDtoToEntity(dto, entity);
|
||||||
|
|
||||||
|
// 默认状态
|
||||||
|
entity.setContestState("unpublished");
|
||||||
|
entity.setStatus("ongoing");
|
||||||
|
entity.setResultState("unpublished");
|
||||||
|
if (!StringUtils.hasText(entity.getSubmitRule())) {
|
||||||
|
entity.setSubmitRule("once");
|
||||||
|
}
|
||||||
|
entity.setCreator(creatorId != null ? creatorId.intValue() : null);
|
||||||
|
|
||||||
|
save(entity);
|
||||||
|
log.info("赛事创建成功,ID:{}", entity.getId());
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<Map<String, Object>> findAll(QueryContestDto dto, Long tenantId, boolean isSuperTenant) {
|
||||||
|
log.info("查询赛事列表,页码:{},每页:{}", dto.getPage(), dto.getPageSize());
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContest> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizContest::getValidState, 1);
|
||||||
|
|
||||||
|
if (StringUtils.hasText(dto.getContestName())) {
|
||||||
|
wrapper.like(BizContest::getContestName, dto.getContestName());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getContestState())) {
|
||||||
|
wrapper.eq(BizContest::getContestState, dto.getContestState());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getStatus())) {
|
||||||
|
wrapper.eq(BizContest::getStatus, dto.getStatus());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getVisibility())) {
|
||||||
|
wrapper.eq(BizContest::getVisibility, dto.getVisibility());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getContestType())) {
|
||||||
|
wrapper.eq(BizContest::getContestType, dto.getContestType());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阶段筛选
|
||||||
|
if (StringUtils.hasText(dto.getStage())) {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
switch (dto.getStage()) {
|
||||||
|
case "registering":
|
||||||
|
wrapper.le(BizContest::getRegisterStartTime, now)
|
||||||
|
.ge(BizContest::getRegisterEndTime, now);
|
||||||
|
break;
|
||||||
|
case "submitting":
|
||||||
|
wrapper.le(BizContest::getSubmitStartTime, now)
|
||||||
|
.ge(BizContest::getSubmitEndTime, now);
|
||||||
|
break;
|
||||||
|
case "reviewing":
|
||||||
|
wrapper.le(BizContest::getReviewStartTime, now)
|
||||||
|
.ge(BizContest::getReviewEndTime, now);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非超级租户按授权租户过滤
|
||||||
|
if (!isSuperTenant && tenantId != null) {
|
||||||
|
wrapper.apply("JSON_CONTAINS(contest_tenants, CAST({0} AS JSON))", tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.orderByDesc(BizContest::getCreateTime);
|
||||||
|
|
||||||
|
Page<BizContest> page = new Page<>(dto.getPage(), dto.getPageSize());
|
||||||
|
Page<BizContest> result = contestMapper.selectPage(page, wrapper);
|
||||||
|
|
||||||
|
List<Map<String, Object>> voList = result.getRecords().stream()
|
||||||
|
.map(this::entityToMap)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return PageResult.from(result, voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<Map<String, Object>> getMyContests(QueryContestDto dto, Long userId, Long tenantId) {
|
||||||
|
log.info("查询我的赛事,用户ID:{},租户ID:{}", userId, tenantId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContest> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizContest::getValidState, 1);
|
||||||
|
wrapper.eq(BizContest::getContestState, "published");
|
||||||
|
|
||||||
|
if (StringUtils.hasText(dto.getContestName())) {
|
||||||
|
wrapper.like(BizContest::getContestName, dto.getContestName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按租户过滤
|
||||||
|
if (tenantId != null) {
|
||||||
|
wrapper.apply("JSON_CONTAINS(contest_tenants, CAST({0} AS JSON))", tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.orderByDesc(BizContest::getCreateTime);
|
||||||
|
|
||||||
|
Page<BizContest> page = new Page<>(dto.getPage(), dto.getPageSize());
|
||||||
|
Page<BizContest> result = contestMapper.selectPage(page, wrapper);
|
||||||
|
|
||||||
|
List<Map<String, Object>> voList = result.getRecords().stream()
|
||||||
|
.map(this::entityToMap)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return PageResult.from(result, voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> findDetail(Long id) {
|
||||||
|
log.info("查询赛事详情,ID:{}", id);
|
||||||
|
|
||||||
|
BizContest contest = getById(id);
|
||||||
|
if (contest == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "赛事不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = entityToMap(contest);
|
||||||
|
|
||||||
|
// 查询附件列表
|
||||||
|
LambdaQueryWrapper<BizContestAttachment> attWrapper = new LambdaQueryWrapper<>();
|
||||||
|
attWrapper.eq(BizContestAttachment::getContestId, id);
|
||||||
|
List<BizContestAttachment> attachments = contestAttachmentMapper.selectList(attWrapper);
|
||||||
|
result.put("attachments", attachments);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BizContest updateContest(Long id, CreateContestDto dto) {
|
||||||
|
log.info("更新赛事,ID:{}", id);
|
||||||
|
|
||||||
|
BizContest entity = getById(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "赛事不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
mapDtoToEntity(dto, entity);
|
||||||
|
updateById(entity);
|
||||||
|
|
||||||
|
log.info("赛事更新成功,ID:{}", id);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishContest(Long id, String contestState) {
|
||||||
|
log.info("发布/撤回赛事,ID:{},状态:{}", id, contestState);
|
||||||
|
|
||||||
|
BizContest entity = getById(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "赛事不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setContestState(contestState);
|
||||||
|
updateById(entity);
|
||||||
|
log.info("赛事状态更新成功,ID:{},新状态:{}", id, contestState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishContest(Long id) {
|
||||||
|
log.info("结束赛事,ID:{}", id);
|
||||||
|
|
||||||
|
BizContest entity = getById(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "赛事不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setStatus("finished");
|
||||||
|
updateById(entity);
|
||||||
|
log.info("赛事已结束,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reopenContest(Long id) {
|
||||||
|
log.info("重新开启赛事,ID:{}", id);
|
||||||
|
|
||||||
|
BizContest entity = getById(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "赛事不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setStatus("ongoing");
|
||||||
|
updateById(entity);
|
||||||
|
log.info("赛事已重新开启,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeContest(Long id) {
|
||||||
|
log.info("删除赛事,ID:{}", id);
|
||||||
|
removeById(id);
|
||||||
|
log.info("赛事删除成功,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getStats(Long tenantId, boolean isSuperTenant) {
|
||||||
|
log.info("获取赛事统计,租户ID:{}", tenantId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContest> baseWrapper = new LambdaQueryWrapper<>();
|
||||||
|
baseWrapper.eq(BizContest::getValidState, 1);
|
||||||
|
if (!isSuperTenant && tenantId != null) {
|
||||||
|
baseWrapper.apply("JSON_CONTAINS(contest_tenants, CAST({0} AS JSON))", tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
long total = count(baseWrapper);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContest> publishedWrapper = new LambdaQueryWrapper<>();
|
||||||
|
publishedWrapper.eq(BizContest::getValidState, 1);
|
||||||
|
publishedWrapper.eq(BizContest::getContestState, "published");
|
||||||
|
if (!isSuperTenant && tenantId != null) {
|
||||||
|
publishedWrapper.apply("JSON_CONTAINS(contest_tenants, CAST({0} AS JSON))", tenantId);
|
||||||
|
}
|
||||||
|
long published = count(publishedWrapper);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContest> ongoingWrapper = new LambdaQueryWrapper<>();
|
||||||
|
ongoingWrapper.eq(BizContest::getValidState, 1);
|
||||||
|
ongoingWrapper.eq(BizContest::getStatus, "ongoing");
|
||||||
|
if (!isSuperTenant && tenantId != null) {
|
||||||
|
ongoingWrapper.apply("JSON_CONTAINS(contest_tenants, CAST({0} AS JSON))", tenantId);
|
||||||
|
}
|
||||||
|
long ongoing = count(ongoingWrapper);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContest> finishedWrapper = new LambdaQueryWrapper<>();
|
||||||
|
finishedWrapper.eq(BizContest::getValidState, 1);
|
||||||
|
finishedWrapper.eq(BizContest::getStatus, "finished");
|
||||||
|
if (!isSuperTenant && tenantId != null) {
|
||||||
|
finishedWrapper.apply("JSON_CONTAINS(contest_tenants, CAST({0} AS JSON))", tenantId);
|
||||||
|
}
|
||||||
|
long finished = count(finishedWrapper);
|
||||||
|
|
||||||
|
Map<String, Object> stats = new LinkedHashMap<>();
|
||||||
|
stats.put("total", total);
|
||||||
|
stats.put("published", published);
|
||||||
|
stats.put("ongoing", ongoing);
|
||||||
|
stats.put("finished", finished);
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getDashboard(Long tenantId) {
|
||||||
|
log.info("获取仪表盘数据,租户ID:{}", tenantId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContest> contestWrapper = new LambdaQueryWrapper<>();
|
||||||
|
contestWrapper.eq(BizContest::getValidState, 1);
|
||||||
|
if (tenantId != null) {
|
||||||
|
contestWrapper.apply("JSON_CONTAINS(contest_tenants, CAST({0} AS JSON))", tenantId);
|
||||||
|
}
|
||||||
|
long totalContests = count(contestWrapper);
|
||||||
|
|
||||||
|
long totalRegistrations = contestRegistrationMapper.selectCount(new LambdaQueryWrapper<BizContestRegistration>());
|
||||||
|
|
||||||
|
long totalWorks = contestWorkMapper.selectCount(new LambdaQueryWrapper<BizContestWork>());
|
||||||
|
|
||||||
|
Map<String, Object> dashboard = new LinkedHashMap<>();
|
||||||
|
dashboard.put("totalContests", totalContests);
|
||||||
|
dashboard.put("totalRegistrations", totalRegistrations);
|
||||||
|
dashboard.put("totalWorks", totalWorks);
|
||||||
|
return dashboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 私有辅助方法 ======
|
||||||
|
|
||||||
|
private void mapDtoToEntity(CreateContestDto dto, BizContest entity) {
|
||||||
|
if (StringUtils.hasText(dto.getContestName())) {
|
||||||
|
entity.setContestName(dto.getContestName());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getContestType())) {
|
||||||
|
entity.setContestType(dto.getContestType());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getVisibility())) {
|
||||||
|
entity.setVisibility(dto.getVisibility());
|
||||||
|
}
|
||||||
|
if (dto.getTargetCities() != null) {
|
||||||
|
entity.setTargetCities(dto.getTargetCities());
|
||||||
|
}
|
||||||
|
if (dto.getAgeMin() != null) {
|
||||||
|
entity.setAgeMin(dto.getAgeMin());
|
||||||
|
}
|
||||||
|
if (dto.getAgeMax() != null) {
|
||||||
|
entity.setAgeMax(dto.getAgeMax());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getStartTime())) {
|
||||||
|
entity.setStartTime(LocalDateTime.parse(dto.getStartTime(), DT_FORMATTER));
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getEndTime())) {
|
||||||
|
entity.setEndTime(LocalDateTime.parse(dto.getEndTime(), DT_FORMATTER));
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getAddress())) {
|
||||||
|
entity.setAddress(dto.getAddress());
|
||||||
|
}
|
||||||
|
if (dto.getContent() != null) {
|
||||||
|
entity.setContent(dto.getContent());
|
||||||
|
}
|
||||||
|
if (dto.getContestTenants() != null) {
|
||||||
|
entity.setContestTenants(dto.getContestTenants());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getCoverUrl())) {
|
||||||
|
entity.setCoverUrl(dto.getCoverUrl());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getPosterUrl())) {
|
||||||
|
entity.setPosterUrl(dto.getPosterUrl());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getContactName())) {
|
||||||
|
entity.setContactName(dto.getContactName());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getContactPhone())) {
|
||||||
|
entity.setContactPhone(dto.getContactPhone());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getContactQrcode())) {
|
||||||
|
entity.setContactQrcode(dto.getContactQrcode());
|
||||||
|
}
|
||||||
|
if (dto.getOrganizers() != null) {
|
||||||
|
entity.setOrganizers(dto.getOrganizers());
|
||||||
|
}
|
||||||
|
if (dto.getCoOrganizers() != null) {
|
||||||
|
entity.setCoOrganizers(dto.getCoOrganizers());
|
||||||
|
}
|
||||||
|
if (dto.getSponsors() != null) {
|
||||||
|
entity.setSponsors(dto.getSponsors());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getRegisterStartTime())) {
|
||||||
|
entity.setRegisterStartTime(LocalDateTime.parse(dto.getRegisterStartTime(), DT_FORMATTER));
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getRegisterEndTime())) {
|
||||||
|
entity.setRegisterEndTime(LocalDateTime.parse(dto.getRegisterEndTime(), DT_FORMATTER));
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getRegisterState())) {
|
||||||
|
entity.setRegisterState(dto.getRegisterState());
|
||||||
|
}
|
||||||
|
if (dto.getRequireAudit() != null) {
|
||||||
|
entity.setRequireAudit(dto.getRequireAudit());
|
||||||
|
}
|
||||||
|
if (dto.getAllowedGrades() != null) {
|
||||||
|
entity.setAllowedGrades(dto.getAllowedGrades());
|
||||||
|
}
|
||||||
|
if (dto.getAllowedClasses() != null) {
|
||||||
|
entity.setAllowedClasses(dto.getAllowedClasses());
|
||||||
|
}
|
||||||
|
if (dto.getTeamMinMembers() != null) {
|
||||||
|
entity.setTeamMinMembers(dto.getTeamMinMembers());
|
||||||
|
}
|
||||||
|
if (dto.getTeamMaxMembers() != null) {
|
||||||
|
entity.setTeamMaxMembers(dto.getTeamMaxMembers());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getSubmitRule())) {
|
||||||
|
entity.setSubmitRule(dto.getSubmitRule());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getSubmitStartTime())) {
|
||||||
|
entity.setSubmitStartTime(LocalDateTime.parse(dto.getSubmitStartTime(), DT_FORMATTER));
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getSubmitEndTime())) {
|
||||||
|
entity.setSubmitEndTime(LocalDateTime.parse(dto.getSubmitEndTime(), DT_FORMATTER));
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getWorkType())) {
|
||||||
|
entity.setWorkType(dto.getWorkType());
|
||||||
|
}
|
||||||
|
if (dto.getWorkRequirement() != null) {
|
||||||
|
entity.setWorkRequirement(dto.getWorkRequirement());
|
||||||
|
}
|
||||||
|
if (dto.getReviewRuleId() != null) {
|
||||||
|
entity.setReviewRuleId(dto.getReviewRuleId());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getReviewStartTime())) {
|
||||||
|
entity.setReviewStartTime(LocalDateTime.parse(dto.getReviewStartTime(), DT_FORMATTER));
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getReviewEndTime())) {
|
||||||
|
entity.setReviewEndTime(LocalDateTime.parse(dto.getReviewEndTime(), DT_FORMATTER));
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getResultPublishTime())) {
|
||||||
|
entity.setResultPublishTime(LocalDateTime.parse(dto.getResultPublishTime(), DT_FORMATTER));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> entityToMap(BizContest entity) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", entity.getId());
|
||||||
|
map.put("contestName", entity.getContestName());
|
||||||
|
map.put("contestType", entity.getContestType());
|
||||||
|
map.put("contestState", entity.getContestState());
|
||||||
|
map.put("status", entity.getStatus());
|
||||||
|
map.put("visibility", entity.getVisibility());
|
||||||
|
map.put("startTime", entity.getStartTime());
|
||||||
|
map.put("endTime", entity.getEndTime());
|
||||||
|
map.put("address", entity.getAddress());
|
||||||
|
map.put("content", entity.getContent());
|
||||||
|
map.put("contestTenants", entity.getContestTenants());
|
||||||
|
map.put("coverUrl", entity.getCoverUrl());
|
||||||
|
map.put("posterUrl", entity.getPosterUrl());
|
||||||
|
map.put("contactName", entity.getContactName());
|
||||||
|
map.put("contactPhone", entity.getContactPhone());
|
||||||
|
map.put("contactQrcode", entity.getContactQrcode());
|
||||||
|
map.put("organizers", entity.getOrganizers());
|
||||||
|
map.put("coOrganizers", entity.getCoOrganizers());
|
||||||
|
map.put("sponsors", entity.getSponsors());
|
||||||
|
map.put("registerStartTime", entity.getRegisterStartTime());
|
||||||
|
map.put("registerEndTime", entity.getRegisterEndTime());
|
||||||
|
map.put("registerState", entity.getRegisterState());
|
||||||
|
map.put("requireAudit", entity.getRequireAudit());
|
||||||
|
map.put("allowedGrades", entity.getAllowedGrades());
|
||||||
|
map.put("allowedClasses", entity.getAllowedClasses());
|
||||||
|
map.put("teamMinMembers", entity.getTeamMinMembers());
|
||||||
|
map.put("teamMaxMembers", entity.getTeamMaxMembers());
|
||||||
|
map.put("targetCities", entity.getTargetCities());
|
||||||
|
map.put("ageMin", entity.getAgeMin());
|
||||||
|
map.put("ageMax", entity.getAgeMax());
|
||||||
|
map.put("submitRule", entity.getSubmitRule());
|
||||||
|
map.put("submitStartTime", entity.getSubmitStartTime());
|
||||||
|
map.put("submitEndTime", entity.getSubmitEndTime());
|
||||||
|
map.put("workType", entity.getWorkType());
|
||||||
|
map.put("workRequirement", entity.getWorkRequirement());
|
||||||
|
map.put("reviewRuleId", entity.getReviewRuleId());
|
||||||
|
map.put("reviewStartTime", entity.getReviewStartTime());
|
||||||
|
map.put("reviewEndTime", entity.getReviewEndTime());
|
||||||
|
map.put("resultState", entity.getResultState());
|
||||||
|
map.put("resultPublishTime", entity.getResultPublishTime());
|
||||||
|
map.put("createTime", entity.getCreateTime());
|
||||||
|
map.put("modifyTime", entity.getModifyTime());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,215 @@
|
|||||||
|
package com.competition.modules.biz.contest.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import com.competition.common.exception.BusinessException;
|
||||||
|
import com.competition.modules.biz.contest.dto.CreateTeamDto;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestTeam;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestTeamMember;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestTeamMapper;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestTeamMemberMapper;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestTeamService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestTeamServiceImpl extends ServiceImpl<ContestTeamMapper, BizContestTeam>
|
||||||
|
implements IContestTeamService {
|
||||||
|
|
||||||
|
private final ContestTeamMapper contestTeamMapper;
|
||||||
|
private final ContestTeamMemberMapper contestTeamMemberMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Map<String, Object> createTeam(CreateTeamDto dto, Long tenantId, Long creatorId) {
|
||||||
|
log.info("开始创建团队,赛事ID:{},团队名称:{}", dto.getContestId(), dto.getTeamName());
|
||||||
|
|
||||||
|
BizContestTeam team = new BizContestTeam();
|
||||||
|
team.setTenantId(tenantId);
|
||||||
|
team.setContestId(dto.getContestId());
|
||||||
|
team.setTeamName(dto.getTeamName());
|
||||||
|
team.setLeaderUserId(dto.getLeaderId());
|
||||||
|
team.setMaxMembers(dto.getMaxMembers());
|
||||||
|
team.setCreator(creatorId != null ? creatorId.intValue() : null);
|
||||||
|
|
||||||
|
save(team);
|
||||||
|
log.info("团队创建成功,ID:{}", team.getId());
|
||||||
|
|
||||||
|
// 添加队长为成员
|
||||||
|
addMemberInternal(team.getId(), dto.getLeaderId(), "leader", tenantId, creatorId);
|
||||||
|
|
||||||
|
// 添加其他成员
|
||||||
|
if (dto.getMemberIds() != null) {
|
||||||
|
for (Long memberId : dto.getMemberIds()) {
|
||||||
|
if (!memberId.equals(dto.getLeaderId())) {
|
||||||
|
addMemberInternal(team.getId(), memberId, "member", tenantId, creatorId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加指导老师
|
||||||
|
if (dto.getTeacherIds() != null) {
|
||||||
|
for (Long teacherId : dto.getTeacherIds()) {
|
||||||
|
addMemberInternal(team.getId(), teacherId, "mentor", tenantId, creatorId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("团队成员添加完成,团队ID:{}", team.getId());
|
||||||
|
return teamToMap(team, getMemberList(team.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> findByContest(Long contestId, Long tenantId) {
|
||||||
|
log.info("查询赛事团队列表,赛事ID:{}", contestId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestTeam> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizContestTeam::getContestId, contestId);
|
||||||
|
if (tenantId != null) {
|
||||||
|
wrapper.eq(BizContestTeam::getTenantId, tenantId);
|
||||||
|
}
|
||||||
|
wrapper.orderByDesc(BizContestTeam::getCreateTime);
|
||||||
|
|
||||||
|
List<BizContestTeam> teams = list(wrapper);
|
||||||
|
|
||||||
|
return teams.stream()
|
||||||
|
.map(team -> teamToMap(team, getMemberList(team.getId())))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> findDetail(Long id) {
|
||||||
|
log.info("查询团队详情,ID:{}", id);
|
||||||
|
|
||||||
|
BizContestTeam team = getById(id);
|
||||||
|
if (team == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "团队不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
return teamToMap(team, getMemberList(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BizContestTeam updateTeam(Long id, CreateTeamDto dto) {
|
||||||
|
log.info("更新团队,ID:{}", id);
|
||||||
|
|
||||||
|
BizContestTeam team = getById(id);
|
||||||
|
if (team == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "团队不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(dto.getTeamName())) {
|
||||||
|
team.setTeamName(dto.getTeamName());
|
||||||
|
}
|
||||||
|
if (dto.getLeaderId() != null) {
|
||||||
|
team.setLeaderUserId(dto.getLeaderId());
|
||||||
|
}
|
||||||
|
if (dto.getMaxMembers() != null) {
|
||||||
|
team.setMaxMembers(dto.getMaxMembers());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateById(team);
|
||||||
|
log.info("团队更新成功,ID:{}", id);
|
||||||
|
return team;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addMember(Long teamId, Long userId, String role, Long tenantId) {
|
||||||
|
log.info("添加团队成员,团队ID:{},用户ID:{},角色:{}", teamId, userId, role);
|
||||||
|
|
||||||
|
// 检查重复
|
||||||
|
LambdaQueryWrapper<BizContestTeamMember> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizContestTeamMember::getTeamId, teamId);
|
||||||
|
wrapper.eq(BizContestTeamMember::getUserId, userId);
|
||||||
|
Long existCount = contestTeamMemberMapper.selectCount(wrapper);
|
||||||
|
if (existCount > 0) {
|
||||||
|
throw BusinessException.of(ErrorCode.CONFLICT, "该用户已在团队中");
|
||||||
|
}
|
||||||
|
|
||||||
|
addMemberInternal(teamId, userId, role, tenantId, null);
|
||||||
|
log.info("团队成员添加成功,团队ID:{},用户ID:{}", teamId, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeMember(Long teamId, Long userId) {
|
||||||
|
log.info("移除团队成员,团队ID:{},用户ID:{}", teamId, userId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestTeamMember> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizContestTeamMember::getTeamId, teamId);
|
||||||
|
wrapper.eq(BizContestTeamMember::getUserId, userId);
|
||||||
|
contestTeamMemberMapper.delete(wrapper);
|
||||||
|
|
||||||
|
log.info("团队成员移除成功,团队ID:{},用户ID:{}", teamId, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeTeam(Long id) {
|
||||||
|
log.info("删除团队,ID:{}", id);
|
||||||
|
|
||||||
|
// 删除团队成员
|
||||||
|
LambdaQueryWrapper<BizContestTeamMember> memberWrapper = new LambdaQueryWrapper<>();
|
||||||
|
memberWrapper.eq(BizContestTeamMember::getTeamId, id);
|
||||||
|
contestTeamMemberMapper.delete(memberWrapper);
|
||||||
|
|
||||||
|
removeById(id);
|
||||||
|
log.info("团队删除成功,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 私有辅助方法 ======
|
||||||
|
|
||||||
|
private void addMemberInternal(Long teamId, Long userId, String role, Long tenantId, Long creatorId) {
|
||||||
|
BizContestTeamMember member = new BizContestTeamMember();
|
||||||
|
member.setTeamId(teamId);
|
||||||
|
member.setTenantId(tenantId);
|
||||||
|
member.setUserId(userId);
|
||||||
|
member.setRole(role);
|
||||||
|
member.setCreator(creatorId != null ? creatorId.intValue() : null);
|
||||||
|
member.setCreateTime(LocalDateTime.now());
|
||||||
|
contestTeamMemberMapper.insert(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<BizContestTeamMember> getMemberList(Long teamId) {
|
||||||
|
LambdaQueryWrapper<BizContestTeamMember> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizContestTeamMember::getTeamId, teamId);
|
||||||
|
wrapper.orderByAsc(BizContestTeamMember::getCreateTime);
|
||||||
|
return contestTeamMemberMapper.selectList(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> teamToMap(BizContestTeam entity, List<BizContestTeamMember> members) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", entity.getId());
|
||||||
|
map.put("tenantId", entity.getTenantId());
|
||||||
|
map.put("contestId", entity.getContestId());
|
||||||
|
map.put("teamName", entity.getTeamName());
|
||||||
|
map.put("leaderUserId", entity.getLeaderUserId());
|
||||||
|
map.put("maxMembers", entity.getMaxMembers());
|
||||||
|
map.put("createTime", entity.getCreateTime());
|
||||||
|
|
||||||
|
if (members != null) {
|
||||||
|
List<Map<String, Object>> memberList = members.stream()
|
||||||
|
.map(m -> {
|
||||||
|
Map<String, Object> mMap = new LinkedHashMap<>();
|
||||||
|
mMap.put("id", m.getId());
|
||||||
|
mMap.put("teamId", m.getTeamId());
|
||||||
|
mMap.put("userId", m.getUserId());
|
||||||
|
mMap.put("role", m.getRole());
|
||||||
|
mMap.put("createTime", m.getCreateTime());
|
||||||
|
return mMap;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
map.put("members", memberList);
|
||||||
|
map.put("memberCount", memberList.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,388 @@
|
|||||||
|
package com.competition.modules.biz.contest.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import com.competition.common.exception.BusinessException;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.contest.dto.QueryWorkDto;
|
||||||
|
import com.competition.modules.biz.contest.dto.SubmitWorkDto;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContest;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestRegistration;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestWork;
|
||||||
|
import com.competition.modules.biz.contest.entity.BizContestWorkAttachment;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestMapper;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestRegistrationMapper;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestWorkAttachmentMapper;
|
||||||
|
import com.competition.modules.biz.contest.mapper.ContestWorkMapper;
|
||||||
|
import com.competition.modules.biz.contest.service.IContestWorkService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestWorkServiceImpl extends ServiceImpl<ContestWorkMapper, BizContestWork>
|
||||||
|
implements IContestWorkService {
|
||||||
|
|
||||||
|
private final ContestWorkMapper contestWorkMapper;
|
||||||
|
private final ContestWorkAttachmentMapper contestWorkAttachmentMapper;
|
||||||
|
private final ContestRegistrationMapper contestRegistrationMapper;
|
||||||
|
private final ContestMapper contestMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Map<String, Object> submitWork(SubmitWorkDto dto, Long tenantId, Long submitterId) {
|
||||||
|
log.info("开始提交作品,报名ID:{},提交者:{}", dto.getRegistrationId(), submitterId);
|
||||||
|
|
||||||
|
// 验证报名存在且已通过
|
||||||
|
BizContestRegistration registration = contestRegistrationMapper.selectById(dto.getRegistrationId());
|
||||||
|
if (registration == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "报名记录不存在");
|
||||||
|
}
|
||||||
|
if (!"passed".equals(registration.getRegistrationState())) {
|
||||||
|
throw BusinessException.of(ErrorCode.BAD_REQUEST, "报名未通过审核,无法提交作品");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long contestId = registration.getContestId();
|
||||||
|
|
||||||
|
// 查询赛事提交规则
|
||||||
|
BizContest contest = contestMapper.selectById(contestId);
|
||||||
|
String submitRule = contest != null ? contest.getSubmitRule() : "once";
|
||||||
|
|
||||||
|
// 计算版本号
|
||||||
|
int version = 1;
|
||||||
|
if ("resubmit".equals(submitRule)) {
|
||||||
|
// 将旧版本标记为非最新
|
||||||
|
LambdaUpdateWrapper<BizContestWork> updateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
updateWrapper.eq(BizContestWork::getRegistrationId, dto.getRegistrationId())
|
||||||
|
.eq(BizContestWork::getIsLatest, true)
|
||||||
|
.set(BizContestWork::getIsLatest, false);
|
||||||
|
update(updateWrapper);
|
||||||
|
|
||||||
|
// 查询当前最大版本号
|
||||||
|
LambdaQueryWrapper<BizContestWork> versionWrapper = new LambdaQueryWrapper<>();
|
||||||
|
versionWrapper.eq(BizContestWork::getRegistrationId, dto.getRegistrationId());
|
||||||
|
versionWrapper.orderByDesc(BizContestWork::getVersion);
|
||||||
|
versionWrapper.last("LIMIT 1");
|
||||||
|
BizContestWork latestWork = getOne(versionWrapper, false);
|
||||||
|
if (latestWork != null) {
|
||||||
|
version = latestWork.getVersion() + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// once 规则:检查是否已有作品
|
||||||
|
LambdaQueryWrapper<BizContestWork> existWrapper = new LambdaQueryWrapper<>();
|
||||||
|
existWrapper.eq(BizContestWork::getRegistrationId, dto.getRegistrationId());
|
||||||
|
long existCount = count(existWrapper);
|
||||||
|
if (existCount > 0) {
|
||||||
|
throw BusinessException.of(ErrorCode.CONFLICT, "该报名已提交作品,不允许重复提交");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成作品编号
|
||||||
|
String workNo = generateWorkNo(contestId);
|
||||||
|
|
||||||
|
// 创建作品
|
||||||
|
BizContestWork work = new BizContestWork();
|
||||||
|
work.setTenantId(tenantId);
|
||||||
|
work.setContestId(contestId);
|
||||||
|
work.setRegistrationId(dto.getRegistrationId());
|
||||||
|
work.setWorkNo(workNo);
|
||||||
|
work.setTitle(dto.getTitle());
|
||||||
|
work.setDescription(dto.getDescription());
|
||||||
|
work.setFiles(dto.getFiles());
|
||||||
|
work.setVersion(version);
|
||||||
|
work.setIsLatest(true);
|
||||||
|
work.setStatus("submitted");
|
||||||
|
work.setSubmitTime(LocalDateTime.now());
|
||||||
|
work.setSubmitterUserId(submitterId);
|
||||||
|
work.setPreviewUrl(dto.getPreviewUrl());
|
||||||
|
work.setPreviewUrls(dto.getPreviewUrls());
|
||||||
|
work.setAiModelMeta(dto.getAiModelMeta());
|
||||||
|
|
||||||
|
save(work);
|
||||||
|
log.info("作品提交成功,ID:{},编号:{}", work.getId(), workNo);
|
||||||
|
|
||||||
|
// 保存附件
|
||||||
|
if (dto.getAttachments() != null && !dto.getAttachments().isEmpty()) {
|
||||||
|
for (SubmitWorkDto.AttachmentItem item : dto.getAttachments()) {
|
||||||
|
BizContestWorkAttachment attachment = new BizContestWorkAttachment();
|
||||||
|
attachment.setTenantId(tenantId);
|
||||||
|
attachment.setContestId(contestId);
|
||||||
|
attachment.setWorkId(work.getId());
|
||||||
|
attachment.setFileName(item.getFileName());
|
||||||
|
attachment.setFileUrl(item.getFileUrl());
|
||||||
|
attachment.setFileType(item.getFileType());
|
||||||
|
attachment.setSize(item.getSize());
|
||||||
|
attachment.setCreator(submitterId != null ? submitterId.intValue() : null);
|
||||||
|
attachment.setCreateTime(LocalDateTime.now());
|
||||||
|
contestWorkAttachmentMapper.insert(attachment);
|
||||||
|
}
|
||||||
|
log.info("作品附件保存成功,数量:{}", dto.getAttachments().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
return workToMap(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<Map<String, Object>> findAll(QueryWorkDto dto, Long tenantId, boolean isSuperTenant) {
|
||||||
|
log.info("查询作品列表,赛事ID:{},页码:{}", dto.getContestId(), dto.getPage());
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestWork> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
|
||||||
|
if (dto.getContestId() != null) {
|
||||||
|
wrapper.eq(BizContestWork::getContestId, dto.getContestId());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getStatus())) {
|
||||||
|
wrapper.eq(BizContestWork::getStatus, dto.getStatus());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getTitle())) {
|
||||||
|
wrapper.like(BizContestWork::getTitle, dto.getTitle());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getWorkNo())) {
|
||||||
|
wrapper.eq(BizContestWork::getWorkNo, dto.getWorkNo());
|
||||||
|
}
|
||||||
|
if (dto.getRegistrationId() != null) {
|
||||||
|
wrapper.eq(BizContestWork::getRegistrationId, dto.getRegistrationId());
|
||||||
|
}
|
||||||
|
if (dto.getTenantId() != null) {
|
||||||
|
wrapper.eq(BizContestWork::getTenantId, dto.getTenantId());
|
||||||
|
} else if (!isSuperTenant && tenantId != null) {
|
||||||
|
wrapper.eq(BizContestWork::getTenantId, tenantId);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getKeyword())) {
|
||||||
|
wrapper.and(w -> w.like(BizContestWork::getTitle, dto.getKeyword())
|
||||||
|
.or().like(BizContestWork::getWorkNo, dto.getKeyword()));
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getSubmitStartTime())) {
|
||||||
|
wrapper.ge(BizContestWork::getSubmitTime,
|
||||||
|
LocalDateTime.parse(dto.getSubmitStartTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getSubmitEndTime())) {
|
||||||
|
wrapper.le(BizContestWork::getSubmitTime,
|
||||||
|
LocalDateTime.parse(dto.getSubmitEndTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认只查最新版本
|
||||||
|
wrapper.eq(BizContestWork::getIsLatest, true);
|
||||||
|
wrapper.orderByDesc(BizContestWork::getSubmitTime);
|
||||||
|
|
||||||
|
Page<BizContestWork> page = new Page<>(dto.getPage(), dto.getPageSize());
|
||||||
|
Page<BizContestWork> result = contestWorkMapper.selectPage(page, wrapper);
|
||||||
|
|
||||||
|
// 批量查询报名信息
|
||||||
|
Set<Long> registrationIds = result.getRecords().stream()
|
||||||
|
.map(BizContestWork::getRegistrationId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Map<Long, BizContestRegistration> registrationMap = new HashMap<>();
|
||||||
|
if (!registrationIds.isEmpty()) {
|
||||||
|
List<BizContestRegistration> registrations = contestRegistrationMapper.selectBatchIds(registrationIds);
|
||||||
|
registrationMap = registrations.stream()
|
||||||
|
.collect(Collectors.toMap(BizContestRegistration::getId, r -> r));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Long, BizContestRegistration> finalRegistrationMap = registrationMap;
|
||||||
|
List<Map<String, Object>> voList = result.getRecords().stream()
|
||||||
|
.map(work -> {
|
||||||
|
Map<String, Object> map = workToMap(work);
|
||||||
|
BizContestRegistration reg = finalRegistrationMap.get(work.getRegistrationId());
|
||||||
|
if (reg != null) {
|
||||||
|
map.put("accountNo", reg.getAccountNo());
|
||||||
|
map.put("accountName", reg.getAccountName());
|
||||||
|
map.put("userId", reg.getUserId());
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return PageResult.from(result, voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getStats(Long contestId, Long tenantId) {
|
||||||
|
log.info("获取作品统计,赛事ID:{}", contestId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestWork> baseWrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (contestId != null) {
|
||||||
|
baseWrapper.eq(BizContestWork::getContestId, contestId);
|
||||||
|
}
|
||||||
|
baseWrapper.eq(BizContestWork::getIsLatest, true);
|
||||||
|
long total = count(baseWrapper);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestWork> submittedWrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (contestId != null) {
|
||||||
|
submittedWrapper.eq(BizContestWork::getContestId, contestId);
|
||||||
|
}
|
||||||
|
submittedWrapper.eq(BizContestWork::getIsLatest, true);
|
||||||
|
submittedWrapper.eq(BizContestWork::getStatus, "submitted");
|
||||||
|
long submitted = count(submittedWrapper);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestWork> reviewingWrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (contestId != null) {
|
||||||
|
reviewingWrapper.eq(BizContestWork::getContestId, contestId);
|
||||||
|
}
|
||||||
|
reviewingWrapper.eq(BizContestWork::getIsLatest, true);
|
||||||
|
reviewingWrapper.eq(BizContestWork::getStatus, "reviewing");
|
||||||
|
long reviewing = count(reviewingWrapper);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestWork> acceptedWrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (contestId != null) {
|
||||||
|
acceptedWrapper.eq(BizContestWork::getContestId, contestId);
|
||||||
|
}
|
||||||
|
acceptedWrapper.eq(BizContestWork::getIsLatest, true);
|
||||||
|
acceptedWrapper.eq(BizContestWork::getStatus, "accepted");
|
||||||
|
long accepted = count(acceptedWrapper);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestWork> rejectedWrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (contestId != null) {
|
||||||
|
rejectedWrapper.eq(BizContestWork::getContestId, contestId);
|
||||||
|
}
|
||||||
|
rejectedWrapper.eq(BizContestWork::getIsLatest, true);
|
||||||
|
rejectedWrapper.eq(BizContestWork::getStatus, "rejected");
|
||||||
|
long rejected = count(rejectedWrapper);
|
||||||
|
|
||||||
|
Map<String, Object> stats = new LinkedHashMap<>();
|
||||||
|
stats.put("total", total);
|
||||||
|
stats.put("submitted", submitted);
|
||||||
|
stats.put("reviewing", reviewing);
|
||||||
|
stats.put("accepted", accepted);
|
||||||
|
stats.put("rejected", rejected);
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> findDetail(Long id) {
|
||||||
|
log.info("查询作品详情,ID:{}", id);
|
||||||
|
|
||||||
|
BizContestWork work = getById(id);
|
||||||
|
if (work == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作品不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = workToMap(work);
|
||||||
|
|
||||||
|
// 查询报名信息
|
||||||
|
if (work.getRegistrationId() != null) {
|
||||||
|
BizContestRegistration registration = contestRegistrationMapper.selectById(work.getRegistrationId());
|
||||||
|
if (registration != null) {
|
||||||
|
Map<String, Object> regInfo = new LinkedHashMap<>();
|
||||||
|
regInfo.put("id", registration.getId());
|
||||||
|
regInfo.put("userId", registration.getUserId());
|
||||||
|
regInfo.put("accountNo", registration.getAccountNo());
|
||||||
|
regInfo.put("accountName", registration.getAccountName());
|
||||||
|
regInfo.put("registrationType", registration.getRegistrationType());
|
||||||
|
regInfo.put("participantType", registration.getParticipantType());
|
||||||
|
result.put("registration", regInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询附件
|
||||||
|
LambdaQueryWrapper<BizContestWorkAttachment> attWrapper = new LambdaQueryWrapper<>();
|
||||||
|
attWrapper.eq(BizContestWorkAttachment::getWorkId, id);
|
||||||
|
List<BizContestWorkAttachment> attachments = contestWorkAttachmentMapper.selectList(attWrapper);
|
||||||
|
result.put("attachments", attachments);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> getWorkVersions(Long registrationId) {
|
||||||
|
log.info("查询作品版本历史,报名ID:{}", registrationId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizContestWork> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizContestWork::getRegistrationId, registrationId);
|
||||||
|
wrapper.orderByDesc(BizContestWork::getVersion);
|
||||||
|
|
||||||
|
List<BizContestWork> works = list(wrapper);
|
||||||
|
return works.stream()
|
||||||
|
.map(this::workToMap)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<Map<String, Object>> getGuidedWorks(Long contestId, String workNo, String playerName,
|
||||||
|
String accountNo, Long page, Long pageSize, Long userId) {
|
||||||
|
log.info("查询指导作品,赛事ID:{},教师用户ID:{}", contestId, userId);
|
||||||
|
|
||||||
|
// 简化实现:查询当前教师指导的报名ID列表,再查对应作品
|
||||||
|
// 完整实现需要关联 t_biz_contest_registration_teacher 表
|
||||||
|
LambdaQueryWrapper<BizContestWork> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (contestId != null) {
|
||||||
|
wrapper.eq(BizContestWork::getContestId, contestId);
|
||||||
|
}
|
||||||
|
wrapper.eq(BizContestWork::getIsLatest, true);
|
||||||
|
|
||||||
|
if (StringUtils.hasText(workNo)) {
|
||||||
|
wrapper.eq(BizContestWork::getWorkNo, workNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.orderByDesc(BizContestWork::getSubmitTime);
|
||||||
|
|
||||||
|
Page<BizContestWork> pageObj = new Page<>(page != null ? page : 1L, pageSize != null ? pageSize : 10L);
|
||||||
|
Page<BizContestWork> result = contestWorkMapper.selectPage(pageObj, wrapper);
|
||||||
|
|
||||||
|
List<Map<String, Object>> voList = result.getRecords().stream()
|
||||||
|
.map(this::workToMap)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return PageResult.from(result, voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeWork(Long id) {
|
||||||
|
log.info("删除作品,ID:{}", id);
|
||||||
|
removeById(id);
|
||||||
|
log.info("作品删除成功,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 私有辅助方法 ======
|
||||||
|
|
||||||
|
private String generateWorkNo(Long contestId) {
|
||||||
|
LambdaQueryWrapper<BizContestWork> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizContestWork::getContestId, contestId);
|
||||||
|
long count = count(wrapper);
|
||||||
|
return "W" + contestId + "-" + (count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> workToMap(BizContestWork entity) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", entity.getId());
|
||||||
|
map.put("tenantId", entity.getTenantId());
|
||||||
|
map.put("contestId", entity.getContestId());
|
||||||
|
map.put("registrationId", entity.getRegistrationId());
|
||||||
|
map.put("workNo", entity.getWorkNo());
|
||||||
|
map.put("title", entity.getTitle());
|
||||||
|
map.put("description", entity.getDescription());
|
||||||
|
map.put("files", entity.getFiles());
|
||||||
|
map.put("version", entity.getVersion());
|
||||||
|
map.put("isLatest", entity.getIsLatest());
|
||||||
|
map.put("status", entity.getStatus());
|
||||||
|
map.put("submitTime", entity.getSubmitTime());
|
||||||
|
map.put("submitterUserId", entity.getSubmitterUserId());
|
||||||
|
map.put("submitterAccountNo", entity.getSubmitterAccountNo());
|
||||||
|
map.put("submitSource", entity.getSubmitSource());
|
||||||
|
map.put("previewUrl", entity.getPreviewUrl());
|
||||||
|
map.put("previewUrls", entity.getPreviewUrls());
|
||||||
|
map.put("aiModelMeta", entity.getAiModelMeta());
|
||||||
|
map.put("userWorkId", entity.getUserWorkId());
|
||||||
|
map.put("finalScore", entity.getFinalScore());
|
||||||
|
map.put("rank", entity.getRank());
|
||||||
|
map.put("awardLevel", entity.getAwardLevel());
|
||||||
|
map.put("awardName", entity.getAwardName());
|
||||||
|
map.put("certificateUrl", entity.getCertificateUrl());
|
||||||
|
map.put("createTime", entity.getCreateTime());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
package com.competition.modules.biz.homework.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateHomeworkDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomework;
|
||||||
|
import com.competition.modules.biz.homework.service.IHomeworkService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "作业管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/homework/homeworks")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class HomeworkController {
|
||||||
|
|
||||||
|
private final IHomeworkService homeworkService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission("homework:create")
|
||||||
|
@Operation(summary = "创建作业")
|
||||||
|
public Result<BizHomework> create(@Valid @RequestBody CreateHomeworkDto dto) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(homeworkService.create(dto, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission("homework:read")
|
||||||
|
@Operation(summary = "查询作业列表")
|
||||||
|
public Result<PageResult<Map<String, Object>>> findAll(
|
||||||
|
@RequestParam(defaultValue = "1") Long page,
|
||||||
|
@RequestParam(defaultValue = "10") Long pageSize,
|
||||||
|
@RequestParam(required = false) String name,
|
||||||
|
@RequestParam(required = false) String status) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(homeworkService.findAll(page, pageSize, tenantId, name, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/my")
|
||||||
|
@RequirePermission({"homework:read", "homework:student:read"})
|
||||||
|
@Operation(summary = "查询我的作业列表")
|
||||||
|
public Result<PageResult<Map<String, Object>>> findMyHomeworks(
|
||||||
|
@RequestParam(defaultValue = "1") Long page,
|
||||||
|
@RequestParam(defaultValue = "10") Long pageSize,
|
||||||
|
@RequestParam(required = false) String name) {
|
||||||
|
Long userId = SecurityUtil.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(homeworkService.findMyHomeworks(page, pageSize, userId, tenantId, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission({"homework:read", "homework:student:read"})
|
||||||
|
@Operation(summary = "查询作业详情")
|
||||||
|
public Result<Map<String, Object>> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(homeworkService.findDetail(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}")
|
||||||
|
@RequirePermission("homework:update")
|
||||||
|
@Operation(summary = "更新作业")
|
||||||
|
public Result<BizHomework> update(@PathVariable Long id, @RequestBody CreateHomeworkDto dto) {
|
||||||
|
return Result.success(homeworkService.update(id, dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/publish")
|
||||||
|
@RequirePermission("homework:update")
|
||||||
|
@Operation(summary = "发布作业")
|
||||||
|
public Result<Void> publish(@PathVariable Long id, @RequestBody(required = false) List<Integer> publishScope) {
|
||||||
|
homeworkService.publish(id, publishScope);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/unpublish")
|
||||||
|
@RequirePermission("homework:update")
|
||||||
|
@Operation(summary = "取消发布作业")
|
||||||
|
public Result<Void> unpublish(@PathVariable Long id) {
|
||||||
|
homeworkService.unpublish(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@RequirePermission("homework:delete")
|
||||||
|
@Operation(summary = "删除作业")
|
||||||
|
public Result<Void> remove(@PathVariable Long id) {
|
||||||
|
homeworkService.remove(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
package com.competition.modules.biz.homework.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateHomeworkReviewRuleDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkReviewRule;
|
||||||
|
import com.competition.modules.biz.homework.service.IHomeworkReviewRuleService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "作业评审规则")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/homework/review-rules")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class HomeworkReviewRuleController {
|
||||||
|
|
||||||
|
private final IHomeworkReviewRuleService homeworkReviewRuleService;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission("homework:read")
|
||||||
|
@Operation(summary = "查询作业评审规则列表")
|
||||||
|
public Result<PageResult<Map<String, Object>>> findAll(
|
||||||
|
@RequestParam(defaultValue = "1") Long page,
|
||||||
|
@RequestParam(defaultValue = "10") Long pageSize,
|
||||||
|
@RequestParam(required = false) String name) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(homeworkReviewRuleService.findAll(page, pageSize, tenantId, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/select")
|
||||||
|
@RequirePermission("homework:read")
|
||||||
|
@Operation(summary = "查询作业评审规则选项列表")
|
||||||
|
public Result<List<Map<String, Object>>> findAllForSelect() {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(homeworkReviewRuleService.findAllForSelect(tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission("homework:update")
|
||||||
|
@Operation(summary = "创建作业评审规则")
|
||||||
|
public Result<BizHomeworkReviewRule> create(@Valid @RequestBody CreateHomeworkReviewRuleDto dto) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(homeworkReviewRuleService.create(dto, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission("homework:read")
|
||||||
|
@Operation(summary = "查询作业评审规则详情")
|
||||||
|
public Result<Map<String, Object>> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(homeworkReviewRuleService.findDetail(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}")
|
||||||
|
@RequirePermission("homework:update")
|
||||||
|
@Operation(summary = "更新作业评审规则")
|
||||||
|
public Result<BizHomeworkReviewRule> update(@PathVariable Long id, @RequestBody CreateHomeworkReviewRuleDto dto) {
|
||||||
|
return Result.success(homeworkReviewRuleService.update(id, dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@RequirePermission("homework:update")
|
||||||
|
@Operation(summary = "删除作业评审规则")
|
||||||
|
public Result<Void> remove(@PathVariable Long id) {
|
||||||
|
homeworkReviewRuleService.remove(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
package com.competition.modules.biz.homework.controller;
|
||||||
|
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import com.competition.common.exception.BusinessException;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateHomeworkScoreDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkScore;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkSubmission;
|
||||||
|
import com.competition.modules.biz.homework.service.IHomeworkScoreService;
|
||||||
|
import com.competition.modules.biz.homework.service.IHomeworkSubmissionService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Tag(name = "作业评分")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/homework/scores")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class HomeworkScoreController {
|
||||||
|
|
||||||
|
private final IHomeworkScoreService homeworkScoreService;
|
||||||
|
private final IHomeworkSubmissionService homeworkSubmissionService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission("homework:update")
|
||||||
|
@Operation(summary = "创建评分")
|
||||||
|
public Result<BizHomeworkScore> create(@Valid @RequestBody CreateHomeworkScoreDto dto) {
|
||||||
|
Long reviewerId = SecurityUtil.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(homeworkScoreService.create(dto, reviewerId, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{submissionId}/violation")
|
||||||
|
@RequirePermission("homework:update")
|
||||||
|
@Operation(summary = "标记违规")
|
||||||
|
public Result<Void> markViolation(@PathVariable Long submissionId) {
|
||||||
|
log.info("标记作业提交违规,提交ID:{}", submissionId);
|
||||||
|
|
||||||
|
BizHomeworkSubmission submission = homeworkSubmissionService.getById(submissionId);
|
||||||
|
if (submission == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作业提交不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
submission.setStatus("violation");
|
||||||
|
homeworkSubmissionService.updateById(submission);
|
||||||
|
log.info("作业提交已标记为违规,提交ID:{}", submissionId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{submissionId}/reset")
|
||||||
|
@RequirePermission("homework:update")
|
||||||
|
@Operation(summary = "重置评分状态")
|
||||||
|
public Result<Void> resetStatus(@PathVariable Long submissionId) {
|
||||||
|
log.info("重置作业提交评分状态,提交ID:{}", submissionId);
|
||||||
|
|
||||||
|
BizHomeworkSubmission submission = homeworkSubmissionService.getById(submissionId);
|
||||||
|
if (submission == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作业提交不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
submission.setStatus("pending");
|
||||||
|
submission.setTotalScore(null);
|
||||||
|
homeworkSubmissionService.updateById(submission);
|
||||||
|
log.info("作业提交评分状态已重置,提交ID:{}", submissionId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
package com.competition.modules.biz.homework.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateSubmissionDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkSubmission;
|
||||||
|
import com.competition.modules.biz.homework.service.IHomeworkSubmissionService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "作业提交")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/homework/submissions")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class HomeworkSubmissionController {
|
||||||
|
|
||||||
|
private final IHomeworkSubmissionService homeworkSubmissionService;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission("homework:read")
|
||||||
|
@Operation(summary = "查询作业提交列表")
|
||||||
|
public Result<PageResult<Map<String, Object>>> findAll(
|
||||||
|
@RequestParam(defaultValue = "1") Long page,
|
||||||
|
@RequestParam(defaultValue = "10") Long pageSize,
|
||||||
|
@RequestParam(required = false) Long homeworkId,
|
||||||
|
@RequestParam(required = false) String workNo,
|
||||||
|
@RequestParam(required = false) String workName,
|
||||||
|
@RequestParam(required = false) String studentAccount,
|
||||||
|
@RequestParam(required = false) String studentName,
|
||||||
|
@RequestParam(required = false) String status) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(homeworkSubmissionService.findAll(page, pageSize, tenantId, homeworkId,
|
||||||
|
workNo, workName, studentAccount, studentName, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/class-tree")
|
||||||
|
@RequirePermission("homework:read")
|
||||||
|
@Operation(summary = "获取班级树")
|
||||||
|
public Result<List<Map<String, Object>>> getClassTree() {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(homeworkSubmissionService.getClassTree(tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/my/{homeworkId}")
|
||||||
|
@RequirePermission({"homework:read", "homework:student:read"})
|
||||||
|
@Operation(summary = "查询我的作业提交")
|
||||||
|
public Result<Map<String, Object>> findMySubmission(@PathVariable Long homeworkId) {
|
||||||
|
Long userId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(homeworkSubmissionService.findMySubmission(homeworkId, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission({"homework:read", "homework:student:read"})
|
||||||
|
@Operation(summary = "提交作业")
|
||||||
|
public Result<BizHomeworkSubmission> create(@Valid @RequestBody CreateSubmissionDto dto) {
|
||||||
|
Long studentId = SecurityUtil.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(homeworkSubmissionService.create(dto, studentId, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission("homework:read")
|
||||||
|
@Operation(summary = "查询作业提交详情")
|
||||||
|
public Result<Map<String, Object>> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(homeworkSubmissionService.findDetail(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.competition.modules.biz.homework.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建作业DTO")
|
||||||
|
public class CreateHomeworkDto {
|
||||||
|
|
||||||
|
@NotBlank(message = "作业名称不能为空")
|
||||||
|
@Schema(description = "作业名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "作业内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@NotBlank(message = "提交开始时间不能为空")
|
||||||
|
@Schema(description = "提交开始时间")
|
||||||
|
private String submitStartTime;
|
||||||
|
|
||||||
|
@NotBlank(message = "提交截止时间不能为空")
|
||||||
|
@Schema(description = "提交截止时间")
|
||||||
|
private String submitEndTime;
|
||||||
|
|
||||||
|
@Schema(description = "附件")
|
||||||
|
private Object attachments;
|
||||||
|
|
||||||
|
@Schema(description = "发布范围")
|
||||||
|
private List<Integer> publishScope;
|
||||||
|
|
||||||
|
@Schema(description = "评审规则ID")
|
||||||
|
private Long reviewRuleId;
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package com.competition.modules.biz.homework.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建作业评审规则DTO")
|
||||||
|
public class CreateHomeworkReviewRuleDto {
|
||||||
|
|
||||||
|
@NotBlank(message = "规则名称不能为空")
|
||||||
|
@Schema(description = "规则名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "规则描述")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@NotNull(message = "评分标准不能为空")
|
||||||
|
@Schema(description = "评分标准(JSON数组)")
|
||||||
|
private Object criteria;
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.competition.modules.biz.homework.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建作业评分DTO")
|
||||||
|
public class CreateHomeworkScoreDto {
|
||||||
|
|
||||||
|
@NotNull(message = "提交ID不能为空")
|
||||||
|
@Schema(description = "提交ID")
|
||||||
|
private Long submissionId;
|
||||||
|
|
||||||
|
@Schema(description = "维度评分(JSON)")
|
||||||
|
private Object dimensionScores;
|
||||||
|
|
||||||
|
@NotNull(message = "总分不能为空")
|
||||||
|
@Schema(description = "总分")
|
||||||
|
private BigDecimal totalScore;
|
||||||
|
|
||||||
|
@Schema(description = "评语")
|
||||||
|
private String comments;
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.competition.modules.biz.homework.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建作业提交DTO")
|
||||||
|
public class CreateSubmissionDto {
|
||||||
|
|
||||||
|
@NotNull(message = "作业ID不能为空")
|
||||||
|
@Schema(description = "作业ID")
|
||||||
|
private Long homeworkId;
|
||||||
|
|
||||||
|
@NotBlank(message = "作品名称不能为空")
|
||||||
|
@Schema(description = "作品名称")
|
||||||
|
private String workName;
|
||||||
|
|
||||||
|
@Schema(description = "作品描述")
|
||||||
|
private String workDescription;
|
||||||
|
|
||||||
|
@Schema(description = "作品文件")
|
||||||
|
private Object files;
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
package com.competition.modules.biz.homework.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
|
import com.competition.common.entity.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName(value = "t_homework", autoResultMap = true)
|
||||||
|
public class BizHomework extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("tenant_id")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@TableField("submit_start_time")
|
||||||
|
private LocalDateTime submitStartTime;
|
||||||
|
|
||||||
|
@TableField("submit_end_time")
|
||||||
|
private LocalDateTime submitEndTime;
|
||||||
|
|
||||||
|
@TableField(value = "attachments", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private Object attachments;
|
||||||
|
|
||||||
|
@TableField(value = "publish_scope", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private Object publishScope;
|
||||||
|
|
||||||
|
@TableField("review_rule_id")
|
||||||
|
private Long reviewRuleId;
|
||||||
|
|
||||||
|
/** unpublished / published */
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@TableField("publish_time")
|
||||||
|
private LocalDateTime publishTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.competition.modules.biz.homework.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
|
import com.competition.common.entity.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName(value = "t_homework_review_rule", autoResultMap = true)
|
||||||
|
public class BizHomeworkReviewRule extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("tenant_id")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/** JSON array of {name, maxScore, description} */
|
||||||
|
@TableField(value = "criteria", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private Object criteria;
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.competition.modules.biz.homework.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
|
import com.competition.common.entity.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName(value = "t_homework_score", autoResultMap = true)
|
||||||
|
public class BizHomeworkScore extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("tenant_id")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
@TableField("submission_id")
|
||||||
|
private Long submissionId;
|
||||||
|
|
||||||
|
@TableField("reviewer_id")
|
||||||
|
private Long reviewerId;
|
||||||
|
|
||||||
|
@TableField(value = "dimension_scores", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private Object dimensionScores;
|
||||||
|
|
||||||
|
@TableField("total_score")
|
||||||
|
private BigDecimal totalScore;
|
||||||
|
|
||||||
|
private String comments;
|
||||||
|
|
||||||
|
@TableField("score_time")
|
||||||
|
private LocalDateTime scoreTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
package com.competition.modules.biz.homework.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
|
import com.competition.common.entity.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName(value = "t_homework_submission", autoResultMap = true)
|
||||||
|
public class BizHomeworkSubmission extends BaseEntity {
|
||||||
|
|
||||||
|
@TableField("tenant_id")
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
@TableField("homework_id")
|
||||||
|
private Long homeworkId;
|
||||||
|
|
||||||
|
@TableField("student_id")
|
||||||
|
private Long studentId;
|
||||||
|
|
||||||
|
@TableField("work_no")
|
||||||
|
private String workNo;
|
||||||
|
|
||||||
|
@TableField("work_name")
|
||||||
|
private String workName;
|
||||||
|
|
||||||
|
@TableField("work_description")
|
||||||
|
private String workDescription;
|
||||||
|
|
||||||
|
@TableField(value = "files", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private Object files;
|
||||||
|
|
||||||
|
@TableField(value = "attachments", typeHandler = JacksonTypeHandler.class)
|
||||||
|
private Object attachments;
|
||||||
|
|
||||||
|
@TableField("submit_time")
|
||||||
|
private LocalDateTime submitTime;
|
||||||
|
|
||||||
|
/** pending / reviewed */
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@TableField("total_score")
|
||||||
|
private BigDecimal totalScore;
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.homework.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomework;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface HomeworkMapper extends BaseMapper<BizHomework> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.homework.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkReviewRule;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface HomeworkReviewRuleMapper extends BaseMapper<BizHomeworkReviewRule> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.homework.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkScore;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface HomeworkScoreMapper extends BaseMapper<BizHomeworkScore> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.competition.modules.biz.homework.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkSubmission;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface HomeworkSubmissionMapper extends BaseMapper<BizHomeworkSubmission> {
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.competition.modules.biz.homework.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateHomeworkReviewRuleDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkReviewRule;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IHomeworkReviewRuleService extends IService<BizHomeworkReviewRule> {
|
||||||
|
|
||||||
|
BizHomeworkReviewRule create(CreateHomeworkReviewRuleDto dto, Long tenantId);
|
||||||
|
|
||||||
|
PageResult<Map<String, Object>> findAll(Long page, Long pageSize, Long tenantId, String name);
|
||||||
|
|
||||||
|
List<Map<String, Object>> findAllForSelect(Long tenantId);
|
||||||
|
|
||||||
|
Map<String, Object> findDetail(Long id);
|
||||||
|
|
||||||
|
BizHomeworkReviewRule update(Long id, CreateHomeworkReviewRuleDto dto);
|
||||||
|
|
||||||
|
void remove(Long id);
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package com.competition.modules.biz.homework.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateHomeworkScoreDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkScore;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IHomeworkScoreService extends IService<BizHomeworkScore> {
|
||||||
|
|
||||||
|
BizHomeworkScore create(CreateHomeworkScoreDto dto, Long reviewerId, Long tenantId);
|
||||||
|
|
||||||
|
List<Map<String, Object>> findBySubmission(Long submissionId);
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.competition.modules.biz.homework.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateHomeworkDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomework;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IHomeworkService extends IService<BizHomework> {
|
||||||
|
|
||||||
|
BizHomework create(CreateHomeworkDto dto, Long tenantId);
|
||||||
|
|
||||||
|
PageResult<Map<String, Object>> findAll(Long page, Long pageSize, Long tenantId, String name, String status);
|
||||||
|
|
||||||
|
PageResult<Map<String, Object>> findMyHomeworks(Long page, Long pageSize, Long userId, Long tenantId, String name);
|
||||||
|
|
||||||
|
Map<String, Object> findDetail(Long id);
|
||||||
|
|
||||||
|
BizHomework update(Long id, CreateHomeworkDto dto);
|
||||||
|
|
||||||
|
void publish(Long id, List<Integer> publishScope);
|
||||||
|
|
||||||
|
void unpublish(Long id);
|
||||||
|
|
||||||
|
void remove(Long id);
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.competition.modules.biz.homework.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateSubmissionDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkSubmission;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IHomeworkSubmissionService extends IService<BizHomeworkSubmission> {
|
||||||
|
|
||||||
|
BizHomeworkSubmission create(CreateSubmissionDto dto, Long studentId, Long tenantId);
|
||||||
|
|
||||||
|
PageResult<Map<String, Object>> findAll(Long page, Long pageSize, Long tenantId, Long homeworkId,
|
||||||
|
String workNo, String workName, String studentAccount,
|
||||||
|
String studentName, String status);
|
||||||
|
|
||||||
|
Map<String, Object> findDetail(Long id);
|
||||||
|
|
||||||
|
Map<String, Object> findMySubmission(Long homeworkId, Long userId);
|
||||||
|
|
||||||
|
List<Map<String, Object>> getClassTree(Long tenantId);
|
||||||
|
}
|
||||||
@ -0,0 +1,139 @@
|
|||||||
|
package com.competition.modules.biz.homework.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import com.competition.common.exception.BusinessException;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateHomeworkReviewRuleDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkReviewRule;
|
||||||
|
import com.competition.modules.biz.homework.mapper.HomeworkReviewRuleMapper;
|
||||||
|
import com.competition.modules.biz.homework.service.IHomeworkReviewRuleService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class HomeworkReviewRuleServiceImpl extends ServiceImpl<HomeworkReviewRuleMapper, BizHomeworkReviewRule> implements IHomeworkReviewRuleService {
|
||||||
|
|
||||||
|
private final HomeworkReviewRuleMapper homeworkReviewRuleMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BizHomeworkReviewRule create(CreateHomeworkReviewRuleDto dto, Long tenantId) {
|
||||||
|
log.info("创建作业评审规则,名称:{},租户:{}", dto.getName(), tenantId);
|
||||||
|
|
||||||
|
BizHomeworkReviewRule entity = new BizHomeworkReviewRule();
|
||||||
|
entity.setTenantId(tenantId);
|
||||||
|
entity.setName(dto.getName());
|
||||||
|
entity.setDescription(dto.getDescription());
|
||||||
|
entity.setCriteria(dto.getCriteria());
|
||||||
|
|
||||||
|
save(entity);
|
||||||
|
log.info("作业评审规则创建成功,ID:{}", entity.getId());
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<Map<String, Object>> findAll(Long page, Long pageSize, Long tenantId, String name) {
|
||||||
|
log.info("查询作业评审规则列表,页码:{},每页:{}", page, pageSize);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizHomeworkReviewRule> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizHomeworkReviewRule::getValidState, 1);
|
||||||
|
|
||||||
|
if (tenantId != null) {
|
||||||
|
wrapper.eq(BizHomeworkReviewRule::getTenantId, tenantId);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(name)) {
|
||||||
|
wrapper.like(BizHomeworkReviewRule::getName, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.orderByDesc(BizHomeworkReviewRule::getCreateTime);
|
||||||
|
|
||||||
|
Page<BizHomeworkReviewRule> pageObj = new Page<>(page, pageSize);
|
||||||
|
Page<BizHomeworkReviewRule> result = homeworkReviewRuleMapper.selectPage(pageObj, wrapper);
|
||||||
|
|
||||||
|
List<Map<String, Object>> voList = result.getRecords().stream()
|
||||||
|
.map(this::entityToMap)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return PageResult.from(result, voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> findAllForSelect(Long tenantId) {
|
||||||
|
log.info("查询作业评审规则下拉列表,租户:{}", tenantId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizHomeworkReviewRule> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizHomeworkReviewRule::getValidState, 1);
|
||||||
|
if (tenantId != null) {
|
||||||
|
wrapper.eq(BizHomeworkReviewRule::getTenantId, tenantId);
|
||||||
|
}
|
||||||
|
wrapper.orderByDesc(BizHomeworkReviewRule::getCreateTime);
|
||||||
|
|
||||||
|
List<BizHomeworkReviewRule> list = homeworkReviewRuleMapper.selectList(wrapper);
|
||||||
|
return list.stream().map(this::entityToMap).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> findDetail(Long id) {
|
||||||
|
log.info("查询作业评审规则详情,ID:{}", id);
|
||||||
|
|
||||||
|
BizHomeworkReviewRule entity = getById(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作业评审规则不存在");
|
||||||
|
}
|
||||||
|
return entityToMap(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BizHomeworkReviewRule update(Long id, CreateHomeworkReviewRuleDto dto) {
|
||||||
|
log.info("更新作业评审规则,ID:{}", id);
|
||||||
|
|
||||||
|
BizHomeworkReviewRule entity = getById(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作业评审规则不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(dto.getName())) {
|
||||||
|
entity.setName(dto.getName());
|
||||||
|
}
|
||||||
|
if (dto.getDescription() != null) {
|
||||||
|
entity.setDescription(dto.getDescription());
|
||||||
|
}
|
||||||
|
if (dto.getCriteria() != null) {
|
||||||
|
entity.setCriteria(dto.getCriteria());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateById(entity);
|
||||||
|
log.info("作业评审规则更新成功,ID:{}", id);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Long id) {
|
||||||
|
log.info("删除作业评审规则,ID:{}", id);
|
||||||
|
removeById(id);
|
||||||
|
log.info("作业评审规则删除成功,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 私有辅助方法 ======
|
||||||
|
|
||||||
|
private Map<String, Object> entityToMap(BizHomeworkReviewRule entity) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", entity.getId());
|
||||||
|
map.put("tenantId", entity.getTenantId());
|
||||||
|
map.put("name", entity.getName());
|
||||||
|
map.put("description", entity.getDescription());
|
||||||
|
map.put("criteria", entity.getCriteria());
|
||||||
|
map.put("createTime", entity.getCreateTime());
|
||||||
|
map.put("modifyTime", entity.getModifyTime());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
package com.competition.modules.biz.homework.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import com.competition.common.exception.BusinessException;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateHomeworkScoreDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkScore;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkSubmission;
|
||||||
|
import com.competition.modules.biz.homework.mapper.HomeworkScoreMapper;
|
||||||
|
import com.competition.modules.biz.homework.service.IHomeworkScoreService;
|
||||||
|
import com.competition.modules.biz.homework.service.IHomeworkSubmissionService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class HomeworkScoreServiceImpl extends ServiceImpl<HomeworkScoreMapper, BizHomeworkScore> implements IHomeworkScoreService {
|
||||||
|
|
||||||
|
private final HomeworkScoreMapper homeworkScoreMapper;
|
||||||
|
@Lazy
|
||||||
|
private final IHomeworkSubmissionService homeworkSubmissionService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public BizHomeworkScore create(CreateHomeworkScoreDto dto, Long reviewerId, Long tenantId) {
|
||||||
|
log.info("创建作业评分,提交ID:{},评审人:{}", dto.getSubmissionId(), reviewerId);
|
||||||
|
|
||||||
|
// 验证提交存在
|
||||||
|
BizHomeworkSubmission submission = homeworkSubmissionService.getById(dto.getSubmissionId());
|
||||||
|
if (submission == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作业提交不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
BizHomeworkScore entity = new BizHomeworkScore();
|
||||||
|
entity.setTenantId(tenantId);
|
||||||
|
entity.setSubmissionId(dto.getSubmissionId());
|
||||||
|
entity.setReviewerId(reviewerId);
|
||||||
|
entity.setDimensionScores(dto.getDimensionScores());
|
||||||
|
entity.setTotalScore(dto.getTotalScore());
|
||||||
|
entity.setComments(dto.getComments());
|
||||||
|
entity.setScoreTime(LocalDateTime.now());
|
||||||
|
|
||||||
|
save(entity);
|
||||||
|
log.info("作业评分创建成功,ID:{}", entity.getId());
|
||||||
|
|
||||||
|
// 更新提交的总分和状态
|
||||||
|
submission.setTotalScore(dto.getTotalScore());
|
||||||
|
submission.setStatus("reviewed");
|
||||||
|
homeworkSubmissionService.updateById(submission);
|
||||||
|
log.info("作业提交状态已更新为reviewed,提交ID:{}", submission.getId());
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> findBySubmission(Long submissionId) {
|
||||||
|
log.info("查询作业提交的评分记录,提交ID:{}", submissionId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizHomeworkScore> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizHomeworkScore::getSubmissionId, submissionId);
|
||||||
|
wrapper.eq(BizHomeworkScore::getValidState, 1);
|
||||||
|
wrapper.orderByDesc(BizHomeworkScore::getScoreTime);
|
||||||
|
|
||||||
|
List<BizHomeworkScore> list = homeworkScoreMapper.selectList(wrapper);
|
||||||
|
return list.stream().map(this::entityToMap).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 私有辅助方法 ======
|
||||||
|
|
||||||
|
private Map<String, Object> entityToMap(BizHomeworkScore entity) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", entity.getId());
|
||||||
|
map.put("tenantId", entity.getTenantId());
|
||||||
|
map.put("submissionId", entity.getSubmissionId());
|
||||||
|
map.put("reviewerId", entity.getReviewerId());
|
||||||
|
map.put("dimensionScores", entity.getDimensionScores());
|
||||||
|
map.put("totalScore", entity.getTotalScore());
|
||||||
|
map.put("comments", entity.getComments());
|
||||||
|
map.put("scoreTime", entity.getScoreTime());
|
||||||
|
map.put("createTime", entity.getCreateTime());
|
||||||
|
map.put("modifyTime", entity.getModifyTime());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,216 @@
|
|||||||
|
package com.competition.modules.biz.homework.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import com.competition.common.exception.BusinessException;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateHomeworkDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomework;
|
||||||
|
import com.competition.modules.biz.homework.mapper.HomeworkMapper;
|
||||||
|
import com.competition.modules.biz.homework.service.IHomeworkService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class HomeworkServiceImpl extends ServiceImpl<HomeworkMapper, BizHomework> implements IHomeworkService {
|
||||||
|
|
||||||
|
private final HomeworkMapper homeworkMapper;
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BizHomework create(CreateHomeworkDto dto, Long tenantId) {
|
||||||
|
log.info("创建作业,名称:{},租户:{}", dto.getName(), tenantId);
|
||||||
|
|
||||||
|
BizHomework entity = new BizHomework();
|
||||||
|
entity.setTenantId(tenantId);
|
||||||
|
entity.setName(dto.getName());
|
||||||
|
entity.setContent(dto.getContent());
|
||||||
|
entity.setSubmitStartTime(LocalDateTime.parse(dto.getSubmitStartTime(), DATE_TIME_FORMATTER));
|
||||||
|
entity.setSubmitEndTime(LocalDateTime.parse(dto.getSubmitEndTime(), DATE_TIME_FORMATTER));
|
||||||
|
entity.setAttachments(dto.getAttachments());
|
||||||
|
entity.setPublishScope(dto.getPublishScope());
|
||||||
|
entity.setReviewRuleId(dto.getReviewRuleId());
|
||||||
|
entity.setStatus("unpublished");
|
||||||
|
|
||||||
|
save(entity);
|
||||||
|
log.info("作业创建成功,ID:{}", entity.getId());
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<Map<String, Object>> findAll(Long page, Long pageSize, Long tenantId, String name, String status) {
|
||||||
|
log.info("查询作业列表,页码:{},每页:{}", page, pageSize);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizHomework> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizHomework::getValidState, 1);
|
||||||
|
|
||||||
|
if (tenantId != null) {
|
||||||
|
wrapper.eq(BizHomework::getTenantId, tenantId);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(name)) {
|
||||||
|
wrapper.like(BizHomework::getName, name);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(status)) {
|
||||||
|
wrapper.eq(BizHomework::getStatus, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.orderByDesc(BizHomework::getCreateTime);
|
||||||
|
|
||||||
|
Page<BizHomework> pageObj = new Page<>(page, pageSize);
|
||||||
|
Page<BizHomework> result = homeworkMapper.selectPage(pageObj, wrapper);
|
||||||
|
|
||||||
|
List<Map<String, Object>> voList = result.getRecords().stream()
|
||||||
|
.map(this::entityToMap)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return PageResult.from(result, voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<Map<String, Object>> findMyHomeworks(Long page, Long pageSize, Long userId, Long tenantId, String name) {
|
||||||
|
log.info("查询我的作业列表,用户:{},租户:{}", userId, tenantId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizHomework> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizHomework::getValidState, 1);
|
||||||
|
wrapper.eq(BizHomework::getStatus, "published");
|
||||||
|
|
||||||
|
if (tenantId != null) {
|
||||||
|
wrapper.eq(BizHomework::getTenantId, tenantId);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(name)) {
|
||||||
|
wrapper.like(BizHomework::getName, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.orderByDesc(BizHomework::getPublishTime);
|
||||||
|
|
||||||
|
Page<BizHomework> pageObj = new Page<>(page, pageSize);
|
||||||
|
Page<BizHomework> result = homeworkMapper.selectPage(pageObj, wrapper);
|
||||||
|
|
||||||
|
List<Map<String, Object>> voList = result.getRecords().stream()
|
||||||
|
.map(this::entityToMap)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return PageResult.from(result, voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> findDetail(Long id) {
|
||||||
|
log.info("查询作业详情,ID:{}", id);
|
||||||
|
|
||||||
|
BizHomework entity = getById(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作业不存在");
|
||||||
|
}
|
||||||
|
return entityToMap(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BizHomework update(Long id, CreateHomeworkDto dto) {
|
||||||
|
log.info("更新作业,ID:{}", id);
|
||||||
|
|
||||||
|
BizHomework entity = getById(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作业不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(dto.getName())) {
|
||||||
|
entity.setName(dto.getName());
|
||||||
|
}
|
||||||
|
if (dto.getContent() != null) {
|
||||||
|
entity.setContent(dto.getContent());
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getSubmitStartTime())) {
|
||||||
|
entity.setSubmitStartTime(LocalDateTime.parse(dto.getSubmitStartTime(), DATE_TIME_FORMATTER));
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(dto.getSubmitEndTime())) {
|
||||||
|
entity.setSubmitEndTime(LocalDateTime.parse(dto.getSubmitEndTime(), DATE_TIME_FORMATTER));
|
||||||
|
}
|
||||||
|
if (dto.getAttachments() != null) {
|
||||||
|
entity.setAttachments(dto.getAttachments());
|
||||||
|
}
|
||||||
|
if (dto.getPublishScope() != null) {
|
||||||
|
entity.setPublishScope(dto.getPublishScope());
|
||||||
|
}
|
||||||
|
if (dto.getReviewRuleId() != null) {
|
||||||
|
entity.setReviewRuleId(dto.getReviewRuleId());
|
||||||
|
}
|
||||||
|
|
||||||
|
updateById(entity);
|
||||||
|
log.info("作业更新成功,ID:{}", id);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publish(Long id, List<Integer> publishScope) {
|
||||||
|
log.info("发布作业,ID:{}", id);
|
||||||
|
|
||||||
|
BizHomework entity = getById(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作业不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setStatus("published");
|
||||||
|
entity.setPublishTime(LocalDateTime.now());
|
||||||
|
if (publishScope != null) {
|
||||||
|
entity.setPublishScope(publishScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateById(entity);
|
||||||
|
log.info("作业发布成功,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unpublish(Long id) {
|
||||||
|
log.info("取消发布作业,ID:{}", id);
|
||||||
|
|
||||||
|
BizHomework entity = getById(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作业不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setStatus("unpublished");
|
||||||
|
entity.setPublishTime(null);
|
||||||
|
|
||||||
|
updateById(entity);
|
||||||
|
log.info("作业取消发布成功,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Long id) {
|
||||||
|
log.info("删除作业,ID:{}", id);
|
||||||
|
removeById(id);
|
||||||
|
log.info("作业删除成功,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 私有辅助方法 ======
|
||||||
|
|
||||||
|
private Map<String, Object> entityToMap(BizHomework entity) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", entity.getId());
|
||||||
|
map.put("tenantId", entity.getTenantId());
|
||||||
|
map.put("name", entity.getName());
|
||||||
|
map.put("content", entity.getContent());
|
||||||
|
map.put("submitStartTime", entity.getSubmitStartTime());
|
||||||
|
map.put("submitEndTime", entity.getSubmitEndTime());
|
||||||
|
map.put("attachments", entity.getAttachments());
|
||||||
|
map.put("publishScope", entity.getPublishScope());
|
||||||
|
map.put("reviewRuleId", entity.getReviewRuleId());
|
||||||
|
map.put("status", entity.getStatus());
|
||||||
|
map.put("publishTime", entity.getPublishTime());
|
||||||
|
map.put("createTime", entity.getCreateTime());
|
||||||
|
map.put("modifyTime", entity.getModifyTime());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,162 @@
|
|||||||
|
package com.competition.modules.biz.homework.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import com.competition.common.exception.BusinessException;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.homework.dto.CreateSubmissionDto;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomework;
|
||||||
|
import com.competition.modules.biz.homework.entity.BizHomeworkSubmission;
|
||||||
|
import com.competition.modules.biz.homework.mapper.HomeworkSubmissionMapper;
|
||||||
|
import com.competition.modules.biz.homework.service.IHomeworkService;
|
||||||
|
import com.competition.modules.biz.homework.service.IHomeworkSubmissionService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class HomeworkSubmissionServiceImpl extends ServiceImpl<HomeworkSubmissionMapper, BizHomeworkSubmission> implements IHomeworkSubmissionService {
|
||||||
|
|
||||||
|
private final HomeworkSubmissionMapper homeworkSubmissionMapper;
|
||||||
|
@Lazy
|
||||||
|
private final IHomeworkService homeworkService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BizHomeworkSubmission create(CreateSubmissionDto dto, Long studentId, Long tenantId) {
|
||||||
|
log.info("提交作业,作业ID:{},学生ID:{}", dto.getHomeworkId(), studentId);
|
||||||
|
|
||||||
|
// 验证作业已发布
|
||||||
|
BizHomework homework = homeworkService.getById(dto.getHomeworkId());
|
||||||
|
if (homework == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作业不存在");
|
||||||
|
}
|
||||||
|
if (!"published".equals(homework.getStatus())) {
|
||||||
|
throw BusinessException.of(ErrorCode.BAD_REQUEST, "作业未发布,无法提交");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已提交过
|
||||||
|
LambdaQueryWrapper<BizHomeworkSubmission> checkWrapper = new LambdaQueryWrapper<>();
|
||||||
|
checkWrapper.eq(BizHomeworkSubmission::getHomeworkId, dto.getHomeworkId());
|
||||||
|
checkWrapper.eq(BizHomeworkSubmission::getStudentId, studentId);
|
||||||
|
checkWrapper.eq(BizHomeworkSubmission::getValidState, 1);
|
||||||
|
long existCount = count(checkWrapper);
|
||||||
|
if (existCount > 0) {
|
||||||
|
throw BusinessException.of(ErrorCode.CONFLICT, "您已提交过该作业");
|
||||||
|
}
|
||||||
|
|
||||||
|
BizHomeworkSubmission entity = new BizHomeworkSubmission();
|
||||||
|
entity.setTenantId(tenantId);
|
||||||
|
entity.setHomeworkId(dto.getHomeworkId());
|
||||||
|
entity.setStudentId(studentId);
|
||||||
|
entity.setWorkName(dto.getWorkName());
|
||||||
|
entity.setWorkDescription(dto.getWorkDescription());
|
||||||
|
entity.setFiles(dto.getFiles());
|
||||||
|
entity.setSubmitTime(LocalDateTime.now());
|
||||||
|
entity.setStatus("pending");
|
||||||
|
|
||||||
|
save(entity);
|
||||||
|
log.info("作业提交成功,ID:{}", entity.getId());
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<Map<String, Object>> findAll(Long page, Long pageSize, Long tenantId, Long homeworkId,
|
||||||
|
String workNo, String workName, String studentAccount,
|
||||||
|
String studentName, String status) {
|
||||||
|
log.info("查询作业提交列表,页码:{},每页:{}", page, pageSize);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizHomeworkSubmission> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizHomeworkSubmission::getValidState, 1);
|
||||||
|
|
||||||
|
if (tenantId != null) {
|
||||||
|
wrapper.eq(BizHomeworkSubmission::getTenantId, tenantId);
|
||||||
|
}
|
||||||
|
if (homeworkId != null) {
|
||||||
|
wrapper.eq(BizHomeworkSubmission::getHomeworkId, homeworkId);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(workNo)) {
|
||||||
|
wrapper.like(BizHomeworkSubmission::getWorkNo, workNo);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(workName)) {
|
||||||
|
wrapper.like(BizHomeworkSubmission::getWorkName, workName);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(status)) {
|
||||||
|
wrapper.eq(BizHomeworkSubmission::getStatus, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.orderByDesc(BizHomeworkSubmission::getSubmitTime);
|
||||||
|
|
||||||
|
Page<BizHomeworkSubmission> pageObj = new Page<>(page, pageSize);
|
||||||
|
Page<BizHomeworkSubmission> result = homeworkSubmissionMapper.selectPage(pageObj, wrapper);
|
||||||
|
|
||||||
|
List<Map<String, Object>> voList = result.getRecords().stream()
|
||||||
|
.map(this::entityToMap)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return PageResult.from(result, voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> findDetail(Long id) {
|
||||||
|
log.info("查询作业提交详情,ID:{}", id);
|
||||||
|
|
||||||
|
BizHomeworkSubmission entity = getById(id);
|
||||||
|
if (entity == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "作业提交不存在");
|
||||||
|
}
|
||||||
|
return entityToMap(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> findMySubmission(Long homeworkId, Long userId) {
|
||||||
|
log.info("查询我的作业提交,作业ID:{},用户ID:{}", homeworkId, userId);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<BizHomeworkSubmission> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(BizHomeworkSubmission::getHomeworkId, homeworkId);
|
||||||
|
wrapper.eq(BizHomeworkSubmission::getStudentId, userId);
|
||||||
|
wrapper.eq(BizHomeworkSubmission::getValidState, 1);
|
||||||
|
|
||||||
|
BizHomeworkSubmission entity = getOne(wrapper);
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return entityToMap(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> getClassTree(Long tenantId) {
|
||||||
|
log.info("获取班级树,租户:{}(学校模块已剥离,返回空列表)", tenantId);
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 私有辅助方法 ======
|
||||||
|
|
||||||
|
private Map<String, Object> entityToMap(BizHomeworkSubmission entity) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", entity.getId());
|
||||||
|
map.put("tenantId", entity.getTenantId());
|
||||||
|
map.put("homeworkId", entity.getHomeworkId());
|
||||||
|
map.put("studentId", entity.getStudentId());
|
||||||
|
map.put("workNo", entity.getWorkNo());
|
||||||
|
map.put("workName", entity.getWorkName());
|
||||||
|
map.put("workDescription", entity.getWorkDescription());
|
||||||
|
map.put("files", entity.getFiles());
|
||||||
|
map.put("attachments", entity.getAttachments());
|
||||||
|
map.put("submitTime", entity.getSubmitTime());
|
||||||
|
map.put("status", entity.getStatus());
|
||||||
|
map.put("totalScore", entity.getTotalScore());
|
||||||
|
map.put("createTime", entity.getCreateTime());
|
||||||
|
map.put("modifyTime", entity.getModifyTime());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
package com.competition.modules.biz.judge.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.judge.service.IJudgesManagementService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "评委库管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/judges-management")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JudgesManagementController {
|
||||||
|
|
||||||
|
private final IJudgesManagementService judgesManagementService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission("judge:create")
|
||||||
|
@Operation(summary = "创建评委账号")
|
||||||
|
public Result<Map<String, Object>> create(@RequestBody Map<String, Object> body) {
|
||||||
|
return Result.success(judgesManagementService.createJudge(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission("judge:read")
|
||||||
|
@Operation(summary = "查询评委列表")
|
||||||
|
public Result<PageResult<Map<String, Object>>> findAll(
|
||||||
|
@RequestParam(defaultValue = "1") Long page,
|
||||||
|
@RequestParam(defaultValue = "10") Long pageSize,
|
||||||
|
@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) String status) {
|
||||||
|
return Result.success(judgesManagementService.findAll(page, pageSize, keyword, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission("judge:read")
|
||||||
|
@Operation(summary = "查询评委详情")
|
||||||
|
public Result<Map<String, Object>> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(judgesManagementService.findDetail(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}")
|
||||||
|
@RequirePermission("judge:update")
|
||||||
|
@Operation(summary = "更新评委信息")
|
||||||
|
public Result<Map<String, Object>> update(@PathVariable Long id, @RequestBody Map<String, Object> body) {
|
||||||
|
return Result.success(judgesManagementService.updateJudge(id, body));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}/freeze")
|
||||||
|
@RequirePermission("judge:update")
|
||||||
|
@Operation(summary = "冻结评委账号")
|
||||||
|
public Result<Void> freeze(@PathVariable Long id) {
|
||||||
|
judgesManagementService.freeze(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}/unfreeze")
|
||||||
|
@RequirePermission("judge:update")
|
||||||
|
@Operation(summary = "解冻评委账号")
|
||||||
|
public Result<Void> unfreeze(@PathVariable Long id) {
|
||||||
|
judgesManagementService.unfreeze(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@RequirePermission("judge:delete")
|
||||||
|
@Operation(summary = "删除评委")
|
||||||
|
public Result<Void> remove(@PathVariable Long id) {
|
||||||
|
judgesManagementService.remove(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/batch-delete")
|
||||||
|
@RequirePermission("judge:delete")
|
||||||
|
@Operation(summary = "批量删除评委")
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Result<Void> batchDelete(@RequestBody Map<String, Object> body) {
|
||||||
|
List<Long> ids = ((List<Number>) body.get("ids"))
|
||||||
|
.stream().map(Number::longValue).toList();
|
||||||
|
judgesManagementService.batchDelete(ids);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
package com.competition.modules.biz.judge.service;
|
||||||
|
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface IJudgesManagementService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建评委账号
|
||||||
|
*/
|
||||||
|
Map<String, Object> createJudge(Map<String, Object> params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询评委列表
|
||||||
|
*/
|
||||||
|
PageResult<Map<String, Object>> findAll(Long page, Long pageSize, String keyword, String status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询评委详情
|
||||||
|
*/
|
||||||
|
Map<String, Object> findDetail(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新评委信息
|
||||||
|
*/
|
||||||
|
Map<String, Object> updateJudge(Long id, Map<String, Object> params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 冻结评委
|
||||||
|
*/
|
||||||
|
void freeze(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解冻评委
|
||||||
|
*/
|
||||||
|
void unfreeze(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除评委
|
||||||
|
*/
|
||||||
|
void remove(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除评委
|
||||||
|
*/
|
||||||
|
void batchDelete(List<Long> ids);
|
||||||
|
}
|
||||||
@ -0,0 +1,281 @@
|
|||||||
|
package com.competition.modules.biz.judge.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.competition.common.enums.ErrorCode;
|
||||||
|
import com.competition.common.exception.BusinessException;
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.modules.biz.judge.service.IJudgesManagementService;
|
||||||
|
import com.competition.modules.sys.entity.SysRole;
|
||||||
|
import com.competition.modules.sys.entity.SysTenant;
|
||||||
|
import com.competition.modules.sys.entity.SysUser;
|
||||||
|
import com.competition.modules.sys.entity.SysUserRole;
|
||||||
|
import com.competition.modules.sys.mapper.SysRoleMapper;
|
||||||
|
import com.competition.modules.sys.mapper.SysTenantMapper;
|
||||||
|
import com.competition.modules.sys.mapper.SysUserMapper;
|
||||||
|
import com.competition.modules.sys.mapper.SysUserRoleMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JudgesManagementServiceImpl implements IJudgesManagementService {
|
||||||
|
|
||||||
|
private final SysUserMapper sysUserMapper;
|
||||||
|
private final SysUserRoleMapper sysUserRoleMapper;
|
||||||
|
private final SysRoleMapper sysRoleMapper;
|
||||||
|
private final SysTenantMapper sysTenantMapper;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评委专属租户 ID
|
||||||
|
*/
|
||||||
|
private Long getJudgeTenantId() {
|
||||||
|
LambdaQueryWrapper<SysTenant> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SysTenant::getCode, "judge");
|
||||||
|
SysTenant tenant = sysTenantMapper.selectOne(wrapper);
|
||||||
|
if (tenant == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.BAD_REQUEST, "评委租户不存在,请先创建 code='judge' 的租户");
|
||||||
|
}
|
||||||
|
return tenant.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评委角色 ID
|
||||||
|
*/
|
||||||
|
private Long getJudgeRoleId(Long tenantId) {
|
||||||
|
LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SysRole::getCode, "judge");
|
||||||
|
wrapper.eq(SysRole::getTenantId, tenantId);
|
||||||
|
SysRole role = sysRoleMapper.selectOne(wrapper);
|
||||||
|
if (role == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.BAD_REQUEST, "评委角色不存在,请先在评委租户下创建 code='judge' 的角色");
|
||||||
|
}
|
||||||
|
return role.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 SysUser 转为前端需要的 Map
|
||||||
|
*/
|
||||||
|
private Map<String, Object> toMap(SysUser user) {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("id", user.getId());
|
||||||
|
map.put("username", user.getUsername());
|
||||||
|
map.put("nickname", user.getNickname());
|
||||||
|
map.put("email", user.getEmail());
|
||||||
|
map.put("phone", user.getPhone());
|
||||||
|
map.put("organization", user.getOrganization());
|
||||||
|
map.put("avatar", user.getAvatar());
|
||||||
|
map.put("status", user.getStatus());
|
||||||
|
map.put("userSource", user.getUserSource());
|
||||||
|
map.put("createTime", user.getCreateTime());
|
||||||
|
map.put("modifyTime", user.getModifyTime());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Map<String, Object> createJudge(Map<String, Object> params) {
|
||||||
|
String username = (String) params.get("username");
|
||||||
|
String password = (String) params.get("password");
|
||||||
|
String nickname = (String) params.get("nickname");
|
||||||
|
String email = (String) params.get("email");
|
||||||
|
String phone = (String) params.get("phone");
|
||||||
|
String organization = (String) params.get("organization");
|
||||||
|
|
||||||
|
if (username == null || username.isBlank()) {
|
||||||
|
throw BusinessException.of(ErrorCode.BAD_REQUEST, "用户名不能为空");
|
||||||
|
}
|
||||||
|
if (password == null || password.isBlank()) {
|
||||||
|
throw BusinessException.of(ErrorCode.BAD_REQUEST, "密码不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long judgeTenantId = getJudgeTenantId();
|
||||||
|
|
||||||
|
// 检查用户名在评委租户内唯一
|
||||||
|
LambdaQueryWrapper<SysUser> dupWrapper = new LambdaQueryWrapper<>();
|
||||||
|
dupWrapper.eq(SysUser::getTenantId, judgeTenantId);
|
||||||
|
dupWrapper.eq(SysUser::getUsername, username);
|
||||||
|
if (sysUserMapper.selectCount(dupWrapper) > 0) {
|
||||||
|
throw BusinessException.of(ErrorCode.BAD_REQUEST, "该用户名已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建用户
|
||||||
|
SysUser user = new SysUser();
|
||||||
|
user.setTenantId(judgeTenantId);
|
||||||
|
user.setUsername(username);
|
||||||
|
user.setPassword(passwordEncoder.encode(password));
|
||||||
|
user.setNickname(nickname);
|
||||||
|
user.setEmail(email);
|
||||||
|
user.setPhone(phone);
|
||||||
|
user.setOrganization(organization);
|
||||||
|
user.setUserSource("admin_created");
|
||||||
|
user.setUserType("adult");
|
||||||
|
user.setStatus("enabled");
|
||||||
|
sysUserMapper.insert(user);
|
||||||
|
|
||||||
|
// 分配评委角色
|
||||||
|
Long judgeRoleId = getJudgeRoleId(judgeTenantId);
|
||||||
|
SysUserRole userRole = new SysUserRole();
|
||||||
|
userRole.setUserId(user.getId());
|
||||||
|
userRole.setRoleId(judgeRoleId);
|
||||||
|
sysUserRoleMapper.insert(userRole);
|
||||||
|
|
||||||
|
log.info("评委账号创建成功,ID:{},用户名:{}", user.getId(), username);
|
||||||
|
return toMap(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<Map<String, Object>> findAll(Long page, Long pageSize, String keyword, String status) {
|
||||||
|
Long judgeTenantId = getJudgeTenantId();
|
||||||
|
|
||||||
|
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SysUser::getTenantId, judgeTenantId);
|
||||||
|
|
||||||
|
if (keyword != null && !keyword.isBlank()) {
|
||||||
|
wrapper.and(w -> w
|
||||||
|
.like(SysUser::getUsername, keyword)
|
||||||
|
.or().like(SysUser::getNickname, keyword)
|
||||||
|
.or().like(SysUser::getOrganization, keyword));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != null && !status.isBlank()) {
|
||||||
|
wrapper.eq(SysUser::getStatus, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.orderByDesc(SysUser::getCreateTime);
|
||||||
|
|
||||||
|
Page<SysUser> pageParam = new Page<>(page, pageSize);
|
||||||
|
Page<SysUser> result = sysUserMapper.selectPage(pageParam, wrapper);
|
||||||
|
|
||||||
|
List<Map<String, Object>> list = result.getRecords().stream()
|
||||||
|
.map(this::toMap)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return new PageResult<>(list, result.getTotal(), result.getCurrent(), result.getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> findDetail(Long id) {
|
||||||
|
SysUser user = sysUserMapper.selectById(id);
|
||||||
|
if (user == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long judgeTenantId = getJudgeTenantId();
|
||||||
|
if (!judgeTenantId.equals(user.getTenantId())) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
return toMap(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Map<String, Object> updateJudge(Long id, Map<String, Object> params) {
|
||||||
|
SysUser user = sysUserMapper.selectById(id);
|
||||||
|
if (user == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long judgeTenantId = getJudgeTenantId();
|
||||||
|
if (!judgeTenantId.equals(user.getTenantId())) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.containsKey("nickname")) {
|
||||||
|
user.setNickname((String) params.get("nickname"));
|
||||||
|
}
|
||||||
|
if (params.containsKey("email")) {
|
||||||
|
user.setEmail((String) params.get("email"));
|
||||||
|
}
|
||||||
|
if (params.containsKey("phone")) {
|
||||||
|
user.setPhone((String) params.get("phone"));
|
||||||
|
}
|
||||||
|
if (params.containsKey("organization")) {
|
||||||
|
user.setOrganization((String) params.get("organization"));
|
||||||
|
}
|
||||||
|
if (params.containsKey("password")) {
|
||||||
|
String newPassword = (String) params.get("password");
|
||||||
|
if (newPassword != null && !newPassword.isBlank()) {
|
||||||
|
user.setPassword(passwordEncoder.encode(newPassword));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sysUserMapper.updateById(user);
|
||||||
|
log.info("评委信息更新成功,ID:{}", id);
|
||||||
|
return toMap(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void freeze(Long id) {
|
||||||
|
updateJudgeStatus(id, "disabled");
|
||||||
|
log.info("评委账号已冻结,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unfreeze(Long id) {
|
||||||
|
updateJudgeStatus(id, "enabled");
|
||||||
|
log.info("评委账号已解冻,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateJudgeStatus(Long id, String status) {
|
||||||
|
SysUser user = sysUserMapper.selectById(id);
|
||||||
|
if (user == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long judgeTenantId = getJudgeTenantId();
|
||||||
|
if (!judgeTenantId.equals(user.getTenantId())) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
user.setStatus(status);
|
||||||
|
sysUserMapper.updateById(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Long id) {
|
||||||
|
SysUser user = sysUserMapper.selectById(id);
|
||||||
|
if (user == null) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long judgeTenantId = getJudgeTenantId();
|
||||||
|
if (!judgeTenantId.equals(user.getTenantId())) {
|
||||||
|
throw BusinessException.of(ErrorCode.NOT_FOUND, "评委不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
sysUserMapper.deleteById(id);
|
||||||
|
log.info("评委已删除,ID:{}", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void batchDelete(List<Long> ids) {
|
||||||
|
if (ids == null || ids.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Long judgeTenantId = getJudgeTenantId();
|
||||||
|
|
||||||
|
// 校验所有 ID 都属于评委租户
|
||||||
|
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.in(SysUser::getId, ids);
|
||||||
|
wrapper.eq(SysUser::getTenantId, judgeTenantId);
|
||||||
|
Long count = sysUserMapper.selectCount(wrapper);
|
||||||
|
if (count != ids.size()) {
|
||||||
|
throw BusinessException.of(ErrorCode.BAD_REQUEST, "部分评委不存在或不属于评委库");
|
||||||
|
}
|
||||||
|
|
||||||
|
sysUserMapper.deleteBatchIds(ids);
|
||||||
|
log.info("批量删除评委成功,数量:{}", ids.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package com.competition.modules.biz.review.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.review.service.AnalyticsService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "数据分析")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/analytics")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AnalyticsController {
|
||||||
|
|
||||||
|
private final AnalyticsService analyticsService;
|
||||||
|
|
||||||
|
@GetMapping("/overview")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "数据概览")
|
||||||
|
public Result<Map<String, Object>> getOverview(
|
||||||
|
@RequestParam(required = false) String timeRange,
|
||||||
|
@RequestParam(required = false) Long contestId) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(analyticsService.getOverview(tenantId, contestId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/review")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "评审分析")
|
||||||
|
public Result<Map<String, Object>> getReviewAnalysis(
|
||||||
|
@RequestParam(required = false) Long contestId) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(analyticsService.getReviewAnalysis(tenantId, contestId));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
package com.competition.modules.biz.review.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.modules.biz.review.entity.BizContestJudge;
|
||||||
|
import com.competition.modules.biz.review.service.IContestJudgeService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "赛事评委")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/contests/judges")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestJudgeController {
|
||||||
|
|
||||||
|
private final IContestJudgeService contestJudgeService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "添加赛事评委")
|
||||||
|
public Result<BizContestJudge> createJudge(@RequestBody Map<String, Object> body) {
|
||||||
|
Long contestId = Long.valueOf(body.get("contestId").toString());
|
||||||
|
Long judgeId = Long.valueOf(body.get("judgeId").toString());
|
||||||
|
String specialty = (String) body.get("specialty");
|
||||||
|
BigDecimal weight = body.get("weight") != null ? new BigDecimal(body.get("weight").toString()) : null;
|
||||||
|
String description = (String) body.get("description");
|
||||||
|
return Result.success(contestJudgeService.createJudge(contestId, judgeId, specialty, weight, description));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/contest/{contestId}")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询赛事评委列表")
|
||||||
|
public Result<List<Map<String, Object>>> findByContest(@PathVariable Long contestId) {
|
||||||
|
return Result.success(contestJudgeService.findByContest(contestId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询评委详情")
|
||||||
|
public Result<Map<String, Object>> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(contestJudgeService.findDetail(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "更新评委信息")
|
||||||
|
public Result<BizContestJudge> updateJudge(@PathVariable Long id, @RequestBody Map<String, Object> body) {
|
||||||
|
String specialty = (String) body.get("specialty");
|
||||||
|
BigDecimal weight = body.get("weight") != null ? new BigDecimal(body.get("weight").toString()) : null;
|
||||||
|
String description = (String) body.get("description");
|
||||||
|
return Result.success(contestJudgeService.updateJudge(id, specialty, weight, description));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "移除评委")
|
||||||
|
public Result<Void> removeJudge(@PathVariable Long id) {
|
||||||
|
contestJudgeService.removeJudge(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
package com.competition.modules.biz.review.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.modules.biz.review.dto.AutoSetAwardsDto;
|
||||||
|
import com.competition.modules.biz.review.dto.BatchSetAwardsDto;
|
||||||
|
import com.competition.modules.biz.review.dto.SetAwardDto;
|
||||||
|
import com.competition.modules.biz.review.service.IContestResultService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "成果发布")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/contests/results")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestResultController {
|
||||||
|
|
||||||
|
private final IContestResultService contestResultService;
|
||||||
|
|
||||||
|
@PostMapping("/{contestId}/calculate-scores")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "计算所有最终得分")
|
||||||
|
public Result<Map<String, Object>> calculateAllFinalScores(@PathVariable Long contestId) {
|
||||||
|
return Result.success(contestResultService.calculateAllFinalScores(contestId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{contestId}/calculate-rankings")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "计算排名")
|
||||||
|
public Result<Map<String, Object>> calculateRankings(@PathVariable Long contestId) {
|
||||||
|
return Result.success(contestResultService.calculateRankings(contestId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/work/{workId}/award")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "设置奖项")
|
||||||
|
public Result<Void> setAward(@PathVariable Long workId, @Valid @RequestBody SetAwardDto dto) {
|
||||||
|
contestResultService.setAward(workId, dto);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{contestId}/batch-set-awards")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "批量设置奖项")
|
||||||
|
public Result<Map<String, Object>> batchSetAwards(
|
||||||
|
@PathVariable Long contestId,
|
||||||
|
@Valid @RequestBody BatchSetAwardsDto dto) {
|
||||||
|
return Result.success(contestResultService.batchSetAwards(contestId, dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{contestId}/auto-set-awards")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "自动设置奖项")
|
||||||
|
public Result<Map<String, Object>> autoSetAwards(
|
||||||
|
@PathVariable Long contestId,
|
||||||
|
@Valid @RequestBody AutoSetAwardsDto dto) {
|
||||||
|
return Result.success(contestResultService.autoSetAwards(contestId, dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{contestId}/publish")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "发布成果")
|
||||||
|
public Result<Void> publishResults(@PathVariable Long contestId) {
|
||||||
|
contestResultService.publishResults(contestId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{contestId}/unpublish")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "撤回成果发布")
|
||||||
|
public Result<Void> unpublishResults(@PathVariable Long contestId) {
|
||||||
|
contestResultService.unpublishResults(contestId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{contestId}")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询成果列表")
|
||||||
|
public Result<PageResult<Map<String, Object>>> getResults(
|
||||||
|
@PathVariable Long contestId,
|
||||||
|
@RequestParam(defaultValue = "1") Long page,
|
||||||
|
@RequestParam(defaultValue = "10") Long pageSize,
|
||||||
|
@RequestParam(required = false) String workNo,
|
||||||
|
@RequestParam(required = false) String accountNo) {
|
||||||
|
return Result.success(contestResultService.getResults(contestId, page, pageSize, workNo, accountNo));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{contestId}/summary")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询成果统计摘要")
|
||||||
|
public Result<Map<String, Object>> getResultsSummary(@PathVariable Long contestId) {
|
||||||
|
return Result.success(contestResultService.getResultsSummary(contestId));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
package com.competition.modules.biz.review.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.review.dto.AssignWorkDto;
|
||||||
|
import com.competition.modules.biz.review.dto.BatchAssignDto;
|
||||||
|
import com.competition.modules.biz.review.dto.CreateScoreDto;
|
||||||
|
import com.competition.modules.biz.review.service.IContestReviewService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "评审管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/contests/reviews")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestReviewController {
|
||||||
|
|
||||||
|
private final IContestReviewService contestReviewService;
|
||||||
|
|
||||||
|
@PostMapping("/assign")
|
||||||
|
@RequirePermission("review:assign")
|
||||||
|
@Operation(summary = "分配作品给评委")
|
||||||
|
public Result<Map<String, Object>> assignWork(
|
||||||
|
@RequestParam Long contestId,
|
||||||
|
@Valid @RequestBody AssignWorkDto dto) {
|
||||||
|
Long creatorId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(contestReviewService.assignWork(contestId, dto.getWorkId(), dto.getJudgeIds(), creatorId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/batch-assign")
|
||||||
|
@RequirePermission("review:assign")
|
||||||
|
@Operation(summary = "批量分配作品给评委")
|
||||||
|
public Result<Map<String, Object>> batchAssignWorks(
|
||||||
|
@RequestParam Long contestId,
|
||||||
|
@Valid @RequestBody BatchAssignDto dto) {
|
||||||
|
Long creatorId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(contestReviewService.batchAssignWorks(contestId, dto.getWorkIds(), dto.getJudgeIds(), creatorId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/auto-assign")
|
||||||
|
@RequirePermission("review:assign")
|
||||||
|
@Operation(summary = "自动分配作品")
|
||||||
|
public Result<Map<String, Object>> autoAssignWorks(@RequestParam Long contestId) {
|
||||||
|
Long creatorId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(contestReviewService.autoAssignWorks(contestId, creatorId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/score")
|
||||||
|
@RequirePermission("review:score")
|
||||||
|
@Operation(summary = "评分")
|
||||||
|
public Result<Map<String, Object>> score(@Valid @RequestBody CreateScoreDto dto) {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(contestReviewService.score(dto, judgeId, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/score/{id}")
|
||||||
|
@RequirePermission("review:score")
|
||||||
|
@Operation(summary = "修改评分")
|
||||||
|
public Result<Map<String, Object>> updateScore(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@Valid @RequestBody CreateScoreDto dto) {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(contestReviewService.updateScore(id, dto, judgeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/assigned")
|
||||||
|
@RequirePermission("review:read")
|
||||||
|
@Operation(summary = "查询已分配作品")
|
||||||
|
public Result<List<Map<String, Object>>> getAssignedWorks(@RequestParam Long contestId) {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(contestReviewService.getAssignedWorks(judgeId, contestId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/judge/contests")
|
||||||
|
@RequirePermission("review:score")
|
||||||
|
@Operation(summary = "获取评委参与的赛事列表")
|
||||||
|
public Result<List<Map<String, Object>>> getJudgeContests() {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(contestReviewService.getJudgeContests(judgeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/judge/contests/{contestId}/works")
|
||||||
|
@RequirePermission("review:score")
|
||||||
|
@Operation(summary = "获取评委赛事作品列表")
|
||||||
|
public Result<PageResult<Map<String, Object>>> getJudgeContestWorks(
|
||||||
|
@PathVariable Long contestId,
|
||||||
|
@RequestParam(defaultValue = "1") Long page,
|
||||||
|
@RequestParam(defaultValue = "10") Long pageSize,
|
||||||
|
@RequestParam(required = false) String workNo,
|
||||||
|
@RequestParam(required = false) String accountNo,
|
||||||
|
@RequestParam(required = false) String reviewStatus) {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(contestReviewService.getJudgeContestWorks(judgeId, contestId, page, pageSize, workNo, accountNo, reviewStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/progress/{contestId}")
|
||||||
|
@RequirePermission("review:read")
|
||||||
|
@Operation(summary = "获取评审进度")
|
||||||
|
public Result<Map<String, Object>> getReviewProgress(@PathVariable Long contestId) {
|
||||||
|
return Result.success(contestReviewService.getReviewProgress(contestId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/work-status/{contestId}")
|
||||||
|
@RequirePermission("review:read")
|
||||||
|
@Operation(summary = "获取作品评审状态统计")
|
||||||
|
public Result<Map<String, Object>> getWorkStatusStats(@PathVariable Long contestId) {
|
||||||
|
return Result.success(contestReviewService.getWorkStatusStats(contestId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/work/{workId}/scores")
|
||||||
|
@RequirePermission("review:read")
|
||||||
|
@Operation(summary = "查询作品评分记录")
|
||||||
|
public Result<List<Map<String, Object>>> getWorkScores(@PathVariable Long workId) {
|
||||||
|
return Result.success(contestReviewService.getWorkScores(workId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/work/{workId}/final-score")
|
||||||
|
@RequirePermission("review:read")
|
||||||
|
@Operation(summary = "计算作品最终得分")
|
||||||
|
public Result<Map<String, Object>> calculateFinalScore(@PathVariable Long workId) {
|
||||||
|
return Result.success(contestReviewService.calculateFinalScore(workId));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
package com.competition.modules.biz.review.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.PageResult;
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.review.dto.CreateReviewRuleDto;
|
||||||
|
import com.competition.modules.biz.review.entity.BizContestReviewRule;
|
||||||
|
import com.competition.modules.biz.review.service.IContestReviewRuleService;
|
||||||
|
import com.competition.security.annotation.RequirePermission;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "评审规则")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/contests/review-rules")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestReviewRuleController {
|
||||||
|
|
||||||
|
private final IContestReviewRuleService contestReviewRuleService;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询评审规则列表")
|
||||||
|
public Result<PageResult<Map<String, Object>>> findAll(
|
||||||
|
@RequestParam(defaultValue = "1") Long page,
|
||||||
|
@RequestParam(defaultValue = "10") Long pageSize,
|
||||||
|
@RequestParam(required = false) String ruleName) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(contestReviewRuleService.findAll(page, pageSize, ruleName, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/select")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询评审规则选项列表")
|
||||||
|
public Result<List<Map<String, Object>>> findAllForSelect() {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(contestReviewRuleService.findAllForSelect(tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "创建评审规则")
|
||||||
|
public Result<BizContestReviewRule> createRule(@Valid @RequestBody CreateReviewRuleDto dto) {
|
||||||
|
Long tenantId = SecurityUtil.getCurrentTenantId();
|
||||||
|
return Result.success(contestReviewRuleService.createRule(dto, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@RequirePermission("contest:read")
|
||||||
|
@Operation(summary = "查询评审规则详情")
|
||||||
|
public Result<Map<String, Object>> findDetail(@PathVariable Long id) {
|
||||||
|
return Result.success(contestReviewRuleService.findDetail(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "更新评审规则")
|
||||||
|
public Result<BizContestReviewRule> updateRule(@PathVariable Long id, @RequestBody CreateReviewRuleDto dto) {
|
||||||
|
return Result.success(contestReviewRuleService.updateRule(id, dto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@RequirePermission("contest:update")
|
||||||
|
@Operation(summary = "删除评审规则")
|
||||||
|
public Result<Void> removeRule(@PathVariable Long id) {
|
||||||
|
contestReviewRuleService.removeRule(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
package com.competition.modules.biz.review.controller;
|
||||||
|
|
||||||
|
import com.competition.common.result.Result;
|
||||||
|
import com.competition.common.util.SecurityUtil;
|
||||||
|
import com.competition.modules.biz.review.dto.CreatePresetCommentDto;
|
||||||
|
import com.competition.modules.biz.review.entity.BizPresetComment;
|
||||||
|
import com.competition.modules.biz.review.service.IPresetCommentService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "预设评语")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/contests/preset-comments")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PresetCommentController {
|
||||||
|
|
||||||
|
private final IPresetCommentService presetCommentService;
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@Operation(summary = "创建预设评语")
|
||||||
|
public Result<BizPresetComment> createComment(@Valid @RequestBody CreatePresetCommentDto dto) {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(presetCommentService.createComment(dto, judgeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@Operation(summary = "查询预设评语列表")
|
||||||
|
public Result<List<Map<String, Object>>> findAll(@RequestParam(required = false) Long contestId) {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(presetCommentService.findAll(contestId, judgeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/judge/contests")
|
||||||
|
@Operation(summary = "获取评委参与的赛事列表")
|
||||||
|
public Result<List<Map<String, Object>>> getJudgeContests() {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(presetCommentService.getJudgeContests(judgeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@Operation(summary = "查询预设评语详情")
|
||||||
|
public Result<Map<String, Object>> findDetail(@PathVariable Long id) {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(presetCommentService.findDetail(id, judgeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{id}")
|
||||||
|
@Operation(summary = "更新预设评语")
|
||||||
|
public Result<BizPresetComment> updateComment(@PathVariable Long id, @RequestBody CreatePresetCommentDto dto) {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(presetCommentService.updateComment(id, dto, judgeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@Operation(summary = "删除预设评语")
|
||||||
|
public Result<Void> removeComment(@PathVariable Long id) {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
presetCommentService.removeComment(id, judgeId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@PostMapping("/batch-delete")
|
||||||
|
@Operation(summary = "批量删除预设评语")
|
||||||
|
public Result<Void> batchDelete(@RequestBody Map<String, Object> body) {
|
||||||
|
List<Long> ids = (List<Long>) body.get("ids");
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
presetCommentService.batchDelete(ids, judgeId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/sync")
|
||||||
|
@Operation(summary = "同步评语到其他赛事")
|
||||||
|
public Result<Map<String, Object>> syncComments(@RequestBody Map<String, Object> body) {
|
||||||
|
Long sourceContestId = Long.valueOf(body.get("sourceContestId").toString());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Long> targetContestIds = (List<Long>) body.get("targetContestIds");
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
return Result.success(presetCommentService.syncComments(sourceContestId, targetContestIds, judgeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/use")
|
||||||
|
@Operation(summary = "增加评语使用次数")
|
||||||
|
public Result<Void> incrementUseCount(@PathVariable Long id) {
|
||||||
|
Long judgeId = SecurityUtil.getCurrentUserId();
|
||||||
|
presetCommentService.incrementUseCount(id, judgeId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.competition.modules.biz.review.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "分配作品给评委DTO")
|
||||||
|
public class AssignWorkDto {
|
||||||
|
|
||||||
|
@NotNull(message = "作品ID不能为空")
|
||||||
|
@Schema(description = "作品ID")
|
||||||
|
private Long workId;
|
||||||
|
|
||||||
|
@NotNull(message = "评委ID列表不能为空")
|
||||||
|
@Schema(description = "评委ID列表")
|
||||||
|
private List<Long> judgeIds;
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.competition.modules.biz.review.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "自动设置奖项DTO")
|
||||||
|
public class AutoSetAwardsDto {
|
||||||
|
|
||||||
|
@Schema(description = "奖项层级列表")
|
||||||
|
private List<AwardTier> awards;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "奖项层级")
|
||||||
|
public static class AwardTier {
|
||||||
|
|
||||||
|
@Schema(description = "奖项名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "数量")
|
||||||
|
private Integer count;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package com.competition.modules.biz.review.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "批量分配作品给评委DTO")
|
||||||
|
public class BatchAssignDto {
|
||||||
|
|
||||||
|
@NotNull(message = "作品ID列表不能为空")
|
||||||
|
@Schema(description = "作品ID列表")
|
||||||
|
private List<Long> workIds;
|
||||||
|
|
||||||
|
@NotNull(message = "评委ID列表不能为空")
|
||||||
|
@Schema(description = "评委ID列表")
|
||||||
|
private List<Long> judgeIds;
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.competition.modules.biz.review.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "批量设置奖项DTO")
|
||||||
|
public class BatchSetAwardsDto {
|
||||||
|
|
||||||
|
@Schema(description = "奖项列表")
|
||||||
|
private List<AwardItem> awards;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "奖项条目")
|
||||||
|
public static class AwardItem {
|
||||||
|
|
||||||
|
@Schema(description = "作品ID")
|
||||||
|
private Long workId;
|
||||||
|
|
||||||
|
@Schema(description = "奖项等级")
|
||||||
|
private String awardLevel;
|
||||||
|
|
||||||
|
@Schema(description = "奖项名称")
|
||||||
|
private String awardName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.competition.modules.biz.review.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建预设评语DTO")
|
||||||
|
public class CreatePresetCommentDto {
|
||||||
|
|
||||||
|
@NotNull(message = "赛事ID不能为空")
|
||||||
|
@Schema(description = "赛事ID")
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
@NotBlank(message = "评语内容不能为空")
|
||||||
|
@Schema(description = "评语内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "关联分数")
|
||||||
|
private BigDecimal score;
|
||||||
|
|
||||||
|
@Schema(description = "排序")
|
||||||
|
private Integer sortOrder;
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.competition.modules.biz.review.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建评审规则DTO")
|
||||||
|
public class CreateReviewRuleDto {
|
||||||
|
|
||||||
|
@NotBlank(message = "规则名称不能为空")
|
||||||
|
@Schema(description = "规则名称")
|
||||||
|
private String ruleName;
|
||||||
|
|
||||||
|
@Schema(description = "规则描述")
|
||||||
|
private String ruleDescription;
|
||||||
|
|
||||||
|
@NotNull(message = "评委人数不能为空")
|
||||||
|
@Schema(description = "评委人数")
|
||||||
|
private Integer judgeCount;
|
||||||
|
|
||||||
|
@NotNull(message = "评分维度不能为空")
|
||||||
|
@Schema(description = "评分维度(JSON数组)")
|
||||||
|
private Object dimensions;
|
||||||
|
|
||||||
|
@Schema(description = "计算规则:average/remove_max_min/remove_min/max/weighted")
|
||||||
|
private String calculationRule;
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.competition.modules.biz.review.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "创建评分DTO")
|
||||||
|
public class CreateScoreDto {
|
||||||
|
|
||||||
|
@NotNull(message = "作品ID不能为空")
|
||||||
|
@Schema(description = "作品ID")
|
||||||
|
private Long workId;
|
||||||
|
|
||||||
|
@NotNull(message = "分配记录ID不能为空")
|
||||||
|
@Schema(description = "分配记录ID")
|
||||||
|
private Long assignmentId;
|
||||||
|
|
||||||
|
@Schema(description = "维度评分(JSON对象)")
|
||||||
|
private Object dimensionScores;
|
||||||
|
|
||||||
|
@NotNull(message = "总分不能为空")
|
||||||
|
@Schema(description = "总分")
|
||||||
|
private BigDecimal totalScore;
|
||||||
|
|
||||||
|
@Schema(description = "评语")
|
||||||
|
private String comments;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user