fix: [1+66]

This commit is contained in:
ovo 2025-05-08 20:16:09 +08:00
parent 676cf30e44
commit 46adeb5622
81 changed files with 2372 additions and 294 deletions

View File

@ -0,0 +1,100 @@
package com.guwan.backend.Handler;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.guwan.backend.service.CourseService;
import com.guwan.backend.service.WebSocketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class CourseWebSocketHandler extends TextWebSocketHandler {
@Autowired
private WebSocketService webSocketService;
@Autowired
private CourseService courseService;
// 存储每个课程的在线用户
private static final Map<String, Set<WebSocketSession>> courseSessions = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) {
log.info("Course detail WebSocket connection established: {}", session.getId());
String courseId = getCourseIdFromSession(session);
courseSessions.computeIfAbsent(courseId, k -> ConcurrentHashMap.newKeySet()).add(session);
broadcastStudentCount(courseId);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
log.info("Course detail received message from {}: {}", session.getId(), message.getPayload());
try {
JsonNode jsonNode = new ObjectMapper().readTree(message.getPayload());
String type = jsonNode.get("type").asText();
String courseId = jsonNode.get("courseId").asText();
if ("JOIN_COURSE".equals(type)) {
courseSessions.computeIfAbsent(courseId, k -> ConcurrentHashMap.newKeySet()).add(session);
broadcastStudentCount(courseId);
} else if ("LEAVE_COURSE".equals(type)) {
courseSessions.getOrDefault(courseId, Collections.emptySet()).remove(session);
broadcastStudentCount(courseId);
}
} catch (Exception e) {
log.error("Error handling course detail message", e);
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
log.info("Course detail WebSocket connection closed: {}, status: {}", session.getId(), status);
String courseId = getCourseIdFromSession(session);
courseSessions.getOrDefault(courseId, Collections.emptySet()).remove(session);
broadcastStudentCount(courseId);
}
private String getCourseIdFromSession(WebSocketSession session) {
String path = session.getUri().getPath();
return path.substring(path.lastIndexOf('/') + 1);
}
private void broadcastStudentCount(String courseId) {
int count = courseSessions.getOrDefault(courseId, Collections.emptySet()).size();
// 更新持久化的学习人数
courseService.updateStudentCount(courseId, count);
// 广播给课程详情页
String message = String.format(
"{\"type\":\"STUDENT_COUNT_UPDATE\",\"courseId\":\"%s\",\"count\":%d}",
courseId, count
);
courseSessions.getOrDefault(courseId, Collections.emptySet())
.forEach(session -> {
try {
if (session.isOpen()) {
session.sendMessage(new TextMessage(message));
}
} catch (IOException e) {
log.error("Error broadcasting to course detail", e);
}
});
// 通过WebSocketService广播给首页
webSocketService.broadcastStudentCount(courseId, count);
}
}

View File

@ -0,0 +1,93 @@
package com.guwan.backend.Handler;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.guwan.backend.service.CourseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class CoursesWebSocketHandler extends TextWebSocketHandler {
@Autowired
private CourseService courseService;
private static final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet();
@Override
public void afterConnectionEstablished(WebSocketSession session) {
log.info("Home page WebSocket connection established: {}", session.getId());
sessions.add(session);
// 连接建立时发送所有课程的当前学习人数
sendAllCourseCounts(session);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
log.info("Home page received message from {}: {}", session.getId(), message.getPayload());
try {
JsonNode jsonNode = new ObjectMapper().readTree(message.getPayload());
String type = jsonNode.get("type").asText();
if ("STUDENT_COUNT_UPDATE".equals(type)) {
// 转发消息给所有首页连接的客户端
broadcastMessage(message.getPayload());
}
} catch (Exception e) {
log.error("Error handling home page message", e);
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
log.info("Home page WebSocket connection closed: {}, status: {}", session.getId(), status);
sessions.remove(session);
}
public void broadcastMessage(String message) {
sessions.forEach(session -> {
try {
if (session.isOpen()) {
session.sendMessage(new TextMessage(message));
}
} catch (IOException e) {
log.error("Error broadcasting to home page", e);
}
});
}
private void sendAllCourseCounts(WebSocketSession session) {
try {
// 获取所有课程的学习人数
Map<String, Integer> allCounts = courseService.getAllCourseCounts();
// 发送每个课程的学习人数
allCounts.forEach((courseId, count) -> {
String message = String.format(
"{\"type\":\"STUDENT_COUNT_UPDATE\",\"courseId\":\"%s\",\"count\":%d}",
courseId, count
);
try {
if (session.isOpen()) {
session.sendMessage(new TextMessage(message));
}
} catch (IOException e) {
log.error("Error sending course count", e);
}
});
} catch (Exception e) {
log.error("Error sending all course counts", e);
}
}
}

View File

@ -0,0 +1,57 @@
package com.guwan.backend;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.lang.reflect.Method;
import java.util.*;
public class JudgeEngine {
// 假设用户代码已编译好类名为 Solution
public static void main(String[] args) throws Exception {
// 1. 取出测试用例
String inputJson = "{\"nums\": [2,7,11,15], \"target\": 9}";
String outputJson = "[0,1]";
List<String> paramList = Arrays.asList("nums", "target");
// 2. 反序列化参数
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> paramMap = mapper.readValue(inputJson, Map.class);
// 3. 构造参数数组 paramList 顺序
Object[] params = new Object[paramList.size()];
for (int i = 0; i < paramList.size(); i++) {
String key = paramList.get(i);
Object value = paramMap.get(key);
// 类型转换如果是数组需转为 int[]
if (key.equals("nums")) {
// value List<Integer>转为 int[]
List<Integer> list = (List<Integer>) value;
int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
params[i] = arr;
} else if (key.equals("target")) {
params[i] = ((Number) value).intValue();
}
// 其他类型可按需扩展
}
// 4. 反射调用用户代码
Class<?> clazz = Class.forName("Solution");
Method method = clazz.getMethod("twoSum", int[].class, int.class);
Object instance = clazz.getDeclaredConstructor().newInstance();
Object result = method.invoke(instance, params);
// 5. 反序列化期望输出
int[] expected = mapper.readValue(outputJson, int[].class);
// 6. 比对结果
if (Arrays.equals((int[]) result, expected)) {
System.out.println("通过");
} else {
System.out.println("不通过");
System.out.println("期望: " + Arrays.toString(expected));
System.out.println("实际: " + Arrays.toString((int[]) result));
}
}
}

View File

@ -1,7 +1,12 @@
package com.guwan.backend.config;
import com.guwan.backend.Handler.CourseWebSocketHandler;
import com.guwan.backend.Handler.CoursesWebSocketHandler;
import com.guwan.backend.websocket.ChatWebSocketHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@ -10,9 +15,17 @@ import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
private CourseWebSocketHandler courseWebSocketHandler;
@Autowired
private CoursesWebSocketHandler coursesWebSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ChatWebSocketHandler(), "/ws/chat")
.addHandler(coursesWebSocketHandler, "/ws/courses") // 首页
.addHandler(courseWebSocketHandler, "/ws/course/{courseId}")
.setAllowedOrigins("*"); // 允许所有来源
}
}

View File

@ -6,54 +6,49 @@ import java.util.List;
* 安全相关常量配置
*/
public class SecurityConstants {
/**
* API接口白名单
* 这些路径可以直接访问不需要认证
*/
public static final List<String> WHITE_LIST = List.of(
"/faceTest","/compareFaces",
"/challenge",
"/ws/**",
"/faceTest", "/compareFaces",
"/minio/**",
"/bs/user/getEmailCode",
"/exam/api/paper/**",
"/bs/user/login",
"/bs/user/register",
"/bs/courses/**",
"/api/common/**", //公共接口
"/demo/**", // 测试接口
"/demo/**", // 测试接口
"/api/products",
"/api/user/register", // 用户注册
"/api/user/login", // 用户登录
"/api/user/getEmailCode", // 获取邮箱验证码
"/api/user/getPhoneCode", // 获取手机验证码
"/chat.html",
"/api/user/register", // 用户注册
"/api/user/login", // 用户登录
"/api/user/getEmailCode", // 获取邮箱验证码
"/api/user/getPhoneCode", // 获取手机验证码
"/chat.html",
"/daxz.html/**",
"/polling-chat.html",
"/log-viewer.html",
"/ws/chat/**",
"/api/polling-chat/**",
"/v3/api-docs/**", // Swagger API文档
"/swagger-ui/**", // Swagger UI
"/swagger-ui.html", // Swagger UI HTML
"/swagger-resources/**", // Swagger 资源
"/webjars/**" // Swagger UI 相关资源
"/polling-chat.html",
"/log-viewer.html",
"/ws/chat/**",
"/api/polling-chat/**",
"/v3/api-docs/**", // Swagger API文档
"/swagger-ui/**", // Swagger UI
"/swagger-ui.html", // Swagger UI HTML
"/swagger-resources/**", // Swagger 资源
"/webjars/**" // Swagger UI 相关资源
);
/**
* 静态资源白名单
* 这些路径用于访问静态资源不需要认证
*/
public static final List<String> STATIC_RESOURCES = List.of(
"/static/**", // 静态资源目录
"/public/**", // 公共资源目录
"/error" // 错误页面
"/static/**", // 静态资源目录
"/public/**", // 公共资源目录
"/error" // 错误页面
);
}

View File

@ -0,0 +1,47 @@
package com.guwan.backend.controller;
import com.guwan.backend.common.Result;
import com.guwan.backend.pojo.bo.CodeRunResult;
import com.guwan.backend.pojo.bo.TestCaseBO;
import com.guwan.backend.pojo.dto.SubmitCodeDTO;
import com.guwan.backend.pojo.response.ChallengesVO;
import com.guwan.backend.service.ChallengesService;
import com.guwan.backend.service.TestCaseAssemblyService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/challenge")
@RequiredArgsConstructor
@Validated
public class ChallengeController {
private final ChallengesService challengesService;
private final TestCaseAssemblyService testCaseAssemblyService;
@GetMapping
public Result getChallenge() {
List<ChallengesVO> challenge = challengesService.getChallenge();
return Result.success(challenge);
}
@PostMapping
public Result submitCode(@RequestBody SubmitCodeDTO submitCodeDTO) {
CodeRunResult codeRunResult = challengesService.submitCode(submitCodeDTO);
return Result.success(codeRunResult);
}
}

View File

@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.guwan.backend.common.Result;
import com.guwan.backend.pojo.dto.BSCategory;
import com.guwan.backend.pojo.entity.Course;
import com.guwan.backend.pojo.response.courseDetail.CourseDetailVO;
import com.guwan.backend.service.BSCategoryService;
import com.guwan.backend.service.CourseService;
import lombok.RequiredArgsConstructor;
@ -63,12 +64,12 @@ public class CourseController {
}
@GetMapping("/getCourseDetail")
public Result getCourseDetail(@RequestParam("courseId") String courseId) {
@GetMapping("/getCourseDetail/{courseId}")
public Result getCourseDetail(@PathVariable("courseId") String courseId) {
courseService.getCourseDetail(courseId);
CourseDetailVO courseDetail = courseService.getCourseDetail(courseId);
return Result.success();
return Result.success(courseDetail);
}

View File

@ -7,6 +7,7 @@ import com.github.dockerjava.api.model.*;
import com.github.dockerjava.core.DockerClientBuilder;
import com.github.dockerjava.core.command.ExecStartResultCallback;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.guwan.backend.pojo.bo.TestCaseBO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
@ -167,7 +168,7 @@ public class DockerController {
@GetMapping("/judge")
public ResponseEntity<Map<String, Object>> judgeCode() {
Map<String, Object> result = new HashMap<>();
List<TestCase> testCases = new ArrayList<>();
List<TestCaseBO> testCases = new ArrayList<>();
// 真实用户提交的C++代码示例A+B问题
String cppCode =
@ -181,11 +182,11 @@ public class DockerController {
"}";
// 定义测试用例
testCases.add(new TestCase("1 2", "3")); // 基础测试
testCases.add(new TestCase("-5 8", "3")); // 负数测试
testCases.add(new TestCase("1000000 2000000", "3000000")); // 大数测试
testCases.add(new TestCase("", "")); // 错误输入测试
testCases.add(new TestCase("3", "")); // 不完整输入测试
testCases.add(new TestCaseBO("1 2", "3")); // 基础测试
testCases.add(new TestCaseBO("-5 8", "3")); // 负数测试
testCases.add(new TestCaseBO("1000000 2000000", "3000000")); // 大数测试
testCases.add(new TestCaseBO("", "")); // 错误输入测试
testCases.add(new TestCaseBO("3", "")); // 不完整输入测试
String containerId = null;
try {
@ -219,7 +220,7 @@ public class DockerController {
// 3. 执行测试用例
List<Map<String, Object>> caseResults = new ArrayList<>();
for (int i = 0; i < testCases.size(); i++) {
TestCase testCase = testCases.get(i);
TestCaseBO testCase = testCases.get(i);
Map<String, Object> caseResult = new HashMap<>();
caseResult.put("case_id", i + 1);
caseResult.put("input", testCase.input);
@ -304,16 +305,6 @@ public class DockerController {
}
// 测试用例类
static class TestCase {
String input;
String expectedOutput;
public TestCase(String input, String expectedOutput) {
this.input = input;
this.expectedOutput = expectedOutput;
}
}

View File

@ -3,10 +3,8 @@ package com.guwan.backend.controller;
import com.guwan.backend.common.Result;
import com.guwan.backend.util.MinioUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/minio")
@ -23,4 +21,14 @@ public class MinioController {
return Result.success(fileName);
}
@PostMapping("/uploadFile")
public Result uploadFile(@RequestParam String bucketName,
@RequestPart("file") MultipartFile file,
@RequestParam String folder) {
String fileName = minioUtil.uploadFile(bucketName, file, folder);
String fileUrl = minioUtil.getFileUrl(bucketName, fileName);
String url = minioUtil.getUrl(fileUrl);
return Result.success(url);
}
}

View File

@ -0,0 +1,60 @@
package com.guwan.backend.factory;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.guwan.backend.mapper.SlideMapper;
import com.guwan.backend.pojo.entity.Section;
import com.guwan.backend.pojo.entity.Slide;
import com.guwan.backend.pojo.response.courseDetail.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@Component
@RequiredArgsConstructor
public class SectionVOFactory {
private final SlideMapper sliderMapper;
public BaseSectionVO fromSection(Section section) {
switch (String.valueOf(section.getType())) {
case "video":
VideoSectionVO video = new VideoSectionVO();
BeanUtils.copyProperties(section, video);
video.setType("video");
video.setDuration(section.getDuration() + ":00");
video.setUrl(section.getUrl());
return video;
case "pdf":
PdfSectionVO pdf = new PdfSectionVO();
BeanUtils.copyProperties(section, pdf);
pdf.setType("pdf");
pdf.setUrl(section.getUrl());
pdf.setDownloadable(true);
return pdf;
case "ppt":
PptSectionVO ppt = new PptSectionVO();
BeanUtils.copyProperties(section, ppt);
ppt.setType("ppt");
String pptId = ppt.getId();
List<Slide> slideList = sliderMapper.selectList(new LambdaQueryWrapper<Slide>().eq(Slide::getSectionId, pptId));
slideList.sort(Comparator.comparingInt(Slide::getOrderNumber));
List<SlideVO> slideVOS = slideList.stream()
.map(slide -> {
SlideVO vo = new SlideVO();
BeanUtils.copyProperties(slide, vo);
return vo;
})
.collect(Collectors.toList());
ppt.setSlides(slideVOS);
return ppt;
default:
throw new IllegalArgumentException("Unsupported type: " + section.getType());
}
}
}

View File

@ -1,107 +0,0 @@
package generator.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import lombok.Data;
/**
* 课程表
* @TableName course
*/
@TableName(value ="course")
@Data
public class Course implements Serializable {
/**
* 课程ID
*/
@TableId
private String id;
/**
* 课程标题
*/
private String title;
/**
* 课程描述
*/
private String description;
/**
* 分类编码
*/
private String categoryId;
/**
* 分类名称
*/
private String categoryName;
/**
* 授课教师ID
*/
private String teacherId;
/**
* 封面图片URL
*/
private String coverImg;
/**
* 价格
*/
private BigDecimal price;
/**
* 授课教师id
*/
private String coursrTeacherId;
/**
* 评分
*/
private BigDecimal rating;
/**
* 评分人数
*/
private Integer ratingCount;
/**
* 学习人数
*/
private Integer studentCount;
/**
* 视频数量
*/
private Integer videoCount;
/**
* 文档数量
*/
private Integer documentCount;
/**
* 总时长
*/
private Integer totalDuration;
/**
* 创建时间
*/
private Date createdAt;
/**
* 更新时间
*/
private Date updatedAt;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -1,18 +0,0 @@
package generator.mapper;
import generator.domain.Course;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author 12455
* @description 针对表course(课程表)的数据库操作Mapper
* @createDate 2025-04-15 00:32:28
* @Entity generator.domain.Course
*/
public interface CourseMapper extends BaseMapper<Course> {
}

View File

@ -1,13 +0,0 @@
package generator.service;
import generator.domain.Course;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author 12455
* @description 针对表course(课程表)的数据库操作Service
* @createDate 2025-04-15 00:32:28
*/
public interface CourseService extends IService<Course> {
}

View File

@ -1,22 +0,0 @@
package generator.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import generator.domain.Course;
import generator.service.CourseService;
import generator.mapper.CourseMapper;
import org.springframework.stereotype.Service;
/**
* @author 12455
* @description 针对表course(课程表)的数据库操作Service实现
* @createDate 2025-04-15 00:32:28
*/
@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
implements CourseService{
}

View File

@ -0,0 +1,18 @@
package com.guwan.backend.mapper;
import com.guwan.backend.pojo.entity.ChallengeParams;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author 12455
* @description 针对表challenge_params的数据库操作Mapper
* @createDate 2025-05-08 18:10:37
* @Entity com.guwan.backend.pojo.entity.ChallengeParams
*/
public interface ChallengeParamsMapper extends BaseMapper<ChallengeParams> {
}

View File

@ -0,0 +1,18 @@
package com.guwan.backend.mapper;
import com.guwan.backend.pojo.entity.Challenges;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author 12455
* @description 针对表challenges的数据库操作Mapper
* @createDate 2025-05-08 17:58:59
* @Entity com.guwan.backend.pojo.entity.Challenges
*/
public interface ChallengesMapper extends BaseMapper<Challenges> {
}

View File

@ -0,0 +1,18 @@
package com.guwan.backend.mapper;
import com.guwan.backend.pojo.entity.Chapter;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author 12455
* @description 针对表chapter的数据库操作Mapper
* @createDate 2025-05-08 14:39:30
* @Entity com.guwan.backend.pojo.entity.Chapter
*/
public interface ChapterMapper extends BaseMapper<Chapter> {
}

View File

@ -0,0 +1,20 @@
package com.guwan.backend.mapper;
import com.guwan.backend.pojo.entity.RatingDistribution;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @author 12455
* @description 针对表rating_distribution(课程评分分布表)的数据库操作Mapper
* @createDate 2025-05-08 14:32:23
* @Entity com.guwan.backend.pojo.entity.RatingDistribution
*/
@Mapper
public interface RatingDistributionMapper extends BaseMapper<RatingDistribution> {
}

View File

@ -0,0 +1,18 @@
package com.guwan.backend.mapper;
import com.guwan.backend.pojo.entity.Review;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author 12455
* @description 针对表review(课程评价表)的数据库操作Mapper
* @createDate 2025-05-08 14:42:13
* @Entity com.guwan.backend.pojo.entity.Review
*/
public interface ReviewMapper extends BaseMapper<Review> {
}

View File

@ -0,0 +1,20 @@
package com.guwan.backend.mapper;
import com.guwan.backend.pojo.entity.Section;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @author 12455
* @description 针对表section的数据库操作Mapper
* @createDate 2025-05-08 14:52:51
* @Entity com.guwan.backend.pojo.entity.Section
*/
@Mapper
public interface SectionMapper extends BaseMapper<Section> {
}

View File

@ -0,0 +1,18 @@
package com.guwan.backend.mapper;
import com.guwan.backend.pojo.entity.Slide;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author 12455
* @description 针对表slide(PPT幻灯片表)的数据库操作Mapper
* @createDate 2025-05-08 15:51:55
* @Entity com.guwan.backend.pojo.entity.Slide
*/
public interface SlideMapper extends BaseMapper<Slide> {
}

View File

@ -0,0 +1,18 @@
package com.guwan.backend.mapper;
import com.guwan.backend.pojo.entity.TestCaseParams;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author 12455
* @description 针对表test_case_params的数据库操作Mapper
* @createDate 2025-05-08 18:19:18
* @Entity com.guwan.backend.pojo.entity.TestCaseParams
*/
public interface TestCaseParamsMapper extends BaseMapper<TestCaseParams> {
}

View File

@ -0,0 +1,18 @@
package com.guwan.backend.mapper;
import com.guwan.backend.pojo.entity.TestCases;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author 12455
* @description 针对表test_cases的数据库操作Mapper
* @createDate 2025-05-08 18:14:58
* @Entity com.guwan.backend.pojo.entity.TestCases
*/
public interface TestCasesMapper extends BaseMapper<TestCases> {
}

View File

@ -0,0 +1,10 @@
package com.guwan.backend.pojo.bo;
import lombok.Data;
@Data
public class CaseResult {
private Integer caseId;
private String input;
private String expected;
}

View File

@ -0,0 +1,10 @@
package com.guwan.backend.pojo.bo;
import lombok.Data;
@Data
public class CodeRunResult {
private String status;
private String message;
private Integer passCount;
}

View File

@ -0,0 +1,13 @@
package com.guwan.backend.pojo.bo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TestCaseBO {
public String input;
public String expectedOutput;
}

View File

@ -0,0 +1,10 @@
package com.guwan.backend.pojo.dto;
import lombok.Data;
@Data
public class SubmitCodeDTO {
private Integer challengeId;
private String code;
private String language;
}

View File

@ -0,0 +1,45 @@
package com.guwan.backend.pojo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import lombok.Data;
/**
*
* @TableName challenge_params
*/
@TableName(value ="challenge_params")
@Data
public class ChallengeParams implements Serializable {
/**
*
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
*
*/
private Integer challengeId;
/**
*
*/
private Integer paramOrder;
/**
*
*/
private String paramName;
/**
*
*/
private String paramType;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,61 @@
package com.guwan.backend.pojo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Data;
/**
*
* @TableName challenges
*/
@TableName(value ="challenges")
@Data
public class Challenges implements Serializable {
/**
*
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
*
*/
private String title;
/**
*
*/
private Object difficulty;
/**
*
*/
private String description;
/**
*
*/
private Integer memoryLimit;
/**
*
*/
private Integer timeLimit;
/**
*
*/
private Integer submitCount;
/**
*
*/
private BigDecimal acceptRate;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,51 @@
package com.guwan.backend.pojo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
*
* @TableName chapter
*/
@TableName(value ="chapter")
@Data
public class Chapter implements Serializable {
/**
* 章节id
*/
@TableId
private String id;
/**
* 关联课程ID
*/
private String courseId;
/**
* 章节标题
*/
private String title;
/**
* 章节排序序号
*/
private Integer orderNumber;
/**
* 创建时间
*/
private Date createdAt;
/**
* 更新时间
*/
private Date updatedAt;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,51 @@
package com.guwan.backend.pojo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
* 课程评分分布表
* @TableName rating_distribution
*/
@TableName(value ="rating_distribution")
@Data
public class RatingDistribution implements Serializable {
/**
* 分布主键
*/
@TableId
private String id;
/**
* 关联课程ID逻辑外键由代码维护关系
*/
private Integer courseId;
/**
* 星级1-5对应1星到5星
*/
private Integer ratingLevel;
/**
* 该星级评分数量
*/
private Integer count;
/**
* 创建时间
*/
private Date createdAt;
/**
* 更新时间
*/
private Date updatedAt;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,71 @@
package com.guwan.backend.pojo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
* 课程评价表
* @TableName review
*/
@TableName(value ="review")
@Data
public class Review implements Serializable {
/**
* 评价主键
*/
@TableId
private String id;
/**
* 关联课程ID逻辑外键由代码维护关系
*/
private Integer courseId;
/**
* 用户名
*/
private String userId;
/**
* 用户头像URL
*/
private String avatar;
/**
* 评分1-5
*/
private Integer rating;
/**
* 评价内容
*/
private String content;
/**
* 评价日期
*/
private Date date;
/**
* 教师回复
*/
private String reply;
/**
* 创建时间
*/
private Date createdAt;
/**
* 更新时间
*/
private Date updatedAt;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,76 @@
package com.guwan.backend.pojo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
*
* @TableName section
*/
@TableName(value ="section")
@Data
public class Section implements Serializable {
/**
* 主键
*/
@TableId
private String id;
/**
* 关联章节ID
*/
private String chapterId;
/**
* 标题
*/
private String title;
/**
* 类型
*/
private Object type;
/**
* 视频时长
*/
private Integer duration;
/**
* 资源URL
*/
private String url;
/**
* 是否免费
*/
private Integer isFree;
/**
* PDF是否允许下载
*/
private Integer downloadable;
/**
* 小节排序序号
*/
private Integer orderNumber;
/**
* 创建时间
*/
private Date createdAt;
/**
*
*/
private Date updatedAt;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,56 @@
package com.guwan.backend.pojo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
* PPT幻灯片表
* @TableName slide
*/
@TableName(value ="slide")
@Data
public class Slide implements Serializable {
/**
* 幻灯片主键
*/
@TableId
private String id;
/**
* 关联小节ID逻辑外键由代码维护关系
*/
private Integer sectionId;
/**
* 幻灯片图片URL
*/
private String url;
/**
* 幻灯片标题
*/
private String title;
/**
* 幻灯片排序
*/
private Integer orderNumber;
/**
* 创建时间
*/
private Date createdAt;
/**
* 更新时间
*/
private Date updatedAt;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,40 @@
package com.guwan.backend.pojo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import lombok.Data;
/**
*
* @TableName test_case_params
*/
@TableName(value ="test_case_params")
@Data
public class TestCaseParams implements Serializable {
/**
*
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
*
*/
private Integer testCaseId;
/**
*
*/
private String paramName;
/**
*
*/
private String paramValue;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,40 @@
package com.guwan.backend.pojo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import lombok.Data;
/**
*
* @TableName test_cases
*/
@TableName(value ="test_cases")
@Data
public class TestCases implements Serializable {
/**
*
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
*
*/
private Integer challengeId;
/**
*
*/
private String expectedOutput;
/**
*
*/
private String explanation;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,18 @@
package com.guwan.backend.pojo.response;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.guwan.backend.pojo.entity.Challenges;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List;
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class ChallengesVO extends Challenges {
@JsonProperty("testCases")
List<TestCaseVO> testCaseVOS;
}

View File

@ -0,0 +1,24 @@
package com.guwan.backend.pojo.response;
import java.math.BigDecimal;
public class CourceHomeVO {
/**
* 类别名称
*/
private String categoryName;
/**
* 类别标题
*/
private String title;
/**
* 学习人数
*/
private String studentCount;
/**
*
*/
private BigDecimal rating;
private String coverImg;
}

View File

@ -1,24 +0,0 @@
package com.guwan.backend.pojo.response;
import lombok.Data;
@Data
public class CourseDetailVo {
/**
* 课程标题
*/
private String title;
/**
* 课程描述
*/
private String description;
private String teacherAvatar;
private Double price;
private Double rating;
private Integer ratingCount;
}

View File

@ -0,0 +1,10 @@
package com.guwan.backend.pojo.response;
import lombok.Data;
@Data
public class TestCaseVO {
private String input;
private String output;
private String explanation;
}

View File

@ -0,0 +1,12 @@
package com.guwan.backend.pojo.response.courseDetail;
import lombok.Data;
@Data
public class BaseSectionVO {
private String id;
private String title;
private String type; // video / pdf / ppt
private boolean isFree;
private boolean completed;
}

View File

@ -0,0 +1,19 @@
package com.guwan.backend.pojo.response.courseDetail;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.guwan.backend.pojo.response.courseDetail.BaseSectionVO;
import lombok.Data;
import java.util.List;
@Data
public class ChapterVO {
private String id;
private String title;
@JsonProperty("sections")
List<BaseSectionVO> sectionVOS;
}

View File

@ -0,0 +1,52 @@
package com.guwan.backend.pojo.response.courseDetail;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CourseDetailVO {
/**
* 课程标题
*/
private String title;
/**
* 课程描述
*/
private String description;
private String teacher;
private String teacherAvatar;
private Double price;
private Double rating;
private Integer ratingCount;
private Integer studentCount;
private Integer videoCount;
private Integer docCount;
private Integer totalDuration;
private Boolean enrolled;
private String coverImg;
@JsonProperty("chapters")
private List<ChapterVO> chapterVOS;
@JsonProperty("reviews")
private List<ReviewVO> reviewVOS;
private List<Integer> ratingDistribution;
}

View File

@ -0,0 +1,14 @@
package com.guwan.backend.pojo.response.courseDetail;
import com.guwan.backend.pojo.response.courseDetail.BaseSectionVO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class PdfSectionVO extends BaseSectionVO {
private String url;
private boolean downloadable;
}

View File

@ -0,0 +1,15 @@
package com.guwan.backend.pojo.response.courseDetail;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List;
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class PptSectionVO extends BaseSectionVO {
private List<SlideVO> slides;
}

View File

@ -0,0 +1,44 @@
package com.guwan.backend.pojo.response.courseDetail;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.Date;
@Data
public class ReviewVO {
private String id;
@JsonProperty("username")
private String userName;
/**
* 用户头像URL
*/
private String avatar;
/**
* 评分1-5
*/
private Integer rating;
/**
* 评价内容
*/
private String content;
/**
* 评价日期
*/
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date date;
/**
* 教师回复
*/
private String reply;
}

View File

@ -0,0 +1,9 @@
package com.guwan.backend.pojo.response.courseDetail;
import lombok.Data;
@Data
public class SlideVO {
private String url;
private String title;
}

View File

@ -0,0 +1,14 @@
package com.guwan.backend.pojo.response.courseDetail;
import com.guwan.backend.pojo.response.courseDetail.BaseSectionVO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class VideoSectionVO extends BaseSectionVO {
private String duration;
private String url;
}

View File

@ -0,0 +1,13 @@
package com.guwan.backend.service;
import com.guwan.backend.pojo.entity.ChallengeParams;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author 12455
* @description 针对表challenge_params的数据库操作Service
* @createDate 2025-05-08 18:10:37
*/
public interface ChallengeParamsService extends IService<ChallengeParams> {
}

View File

@ -0,0 +1,22 @@
package com.guwan.backend.service;
import com.guwan.backend.pojo.bo.CodeRunResult;
import com.guwan.backend.pojo.dto.SubmitCodeDTO;
import com.guwan.backend.pojo.entity.Challenges;
import com.baomidou.mybatisplus.extension.service.IService;
import com.guwan.backend.pojo.response.ChallengesVO;
import java.util.List;
/**
* @author 12455
* @description 针对表challenges的数据库操作Service
* @createDate 2025-05-08 17:58:59
*/
public interface ChallengesService extends IService<Challenges> {
List<ChallengesVO> getChallenge();
CodeRunResult submitCode(SubmitCodeDTO submitCodeDTO);
}

View File

@ -0,0 +1,13 @@
package com.guwan.backend.service;
import com.guwan.backend.pojo.entity.Chapter;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author 12455
* @description 针对表chapter的数据库操作Service
* @createDate 2025-05-08 14:39:30
*/
public interface ChapterService extends IService<Chapter> {
}

View File

@ -2,7 +2,9 @@ package com.guwan.backend.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.guwan.backend.pojo.entity.Course;
import com.guwan.backend.pojo.response.CourseDetailVo;
import com.guwan.backend.pojo.response.courseDetail.CourseDetailVO;
import java.util.Map;
/**
* @author 12455
@ -11,5 +13,11 @@ import com.guwan.backend.pojo.response.CourseDetailVo;
*/
public interface CourseService extends IService<Course> {
CourseDetailVo getCourseDetail(String courseId);
CourseDetailVO getCourseDetail(String courseId);
int getStudentCount(String courseId);
void updateStudentCount(String courseId, int count);
Map<String, Integer> getAllCourseCounts();
}

View File

@ -0,0 +1,13 @@
package com.guwan.backend.service;
import com.guwan.backend.pojo.entity.RatingDistribution;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author 12455
* @description 针对表rating_distribution(课程评分分布表)的数据库操作Service
* @createDate 2025-05-08 14:32:23
*/
public interface RatingDistributionService extends IService<RatingDistribution> {
}

View File

@ -0,0 +1,13 @@
package com.guwan.backend.service;
import com.guwan.backend.pojo.entity.Review;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author 12455
* @description 针对表review(课程评价表)的数据库操作Service
* @createDate 2025-05-08 14:42:13
*/
public interface ReviewService extends IService<Review> {
}

View File

@ -0,0 +1,13 @@
package com.guwan.backend.service;
import com.guwan.backend.pojo.entity.Section;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author 12455
* @description 针对表section的数据库操作Service
* @createDate 2025-05-08 14:52:51
*/
public interface SectionService extends IService<Section> {
}

View File

@ -0,0 +1,13 @@
package com.guwan.backend.service;
import com.guwan.backend.pojo.entity.Slide;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author 12455
* @description 针对表slide(PPT幻灯片表)的数据库操作Service
* @createDate 2025-05-08 15:51:55
*/
public interface SlideService extends IService<Slide> {
}

View File

@ -0,0 +1,96 @@
package com.guwan.backend.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.guwan.backend.mapper.ChallengeParamsMapper;
import com.guwan.backend.mapper.TestCaseParamsMapper;
import com.guwan.backend.mapper.TestCasesMapper;
import com.guwan.backend.pojo.bo.TestCaseBO;
import com.guwan.backend.pojo.entity.ChallengeParams;
import com.guwan.backend.pojo.entity.TestCaseParams;
import com.guwan.backend.pojo.entity.TestCases;
import org.apache.xmlbeans.impl.xb.ltgfmt.TestCase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class TestCaseAssemblyService {
@Autowired
private ChallengeParamsMapper challengeParamsMapper;
@Autowired
private TestCasesMapper testCasesMapper;
@Autowired
private TestCaseParamsMapper testCaseParamsMapper;
@Autowired
private ObjectMapper objectMapper;
public List<TestCaseBO> buildTestCasesForChallenge(Integer challengeId) {
List<TestCaseBO> result = new ArrayList<>();
// 查询题目参数并排序
List<ChallengeParams> paramList = challengeParamsMapper.selectList(
new LambdaQueryWrapper<ChallengeParams>().eq(ChallengeParams::getChallengeId, challengeId)
);
paramList.sort(Comparator.comparingInt(ChallengeParams::getParamOrder));
// 查询所有测试用例
List<TestCases> testCases = testCasesMapper.selectList(
new LambdaQueryWrapper<TestCases>().eq(TestCases::getChallengeId, challengeId)
);
for (TestCases testCase : testCases) {
// 获取该用例的参数值
List<TestCaseParams> testCaseParams = testCaseParamsMapper.selectList(
new LambdaQueryWrapper<TestCaseParams>().eq(TestCaseParams::getTestCaseId, testCase.getId())
);
Map<String, String> valueMap = testCaseParams.stream()
.collect(Collectors.toMap(TestCaseParams::getParamName, TestCaseParams::getParamValue));
String input = buildCppInput(paramList, valueMap);
String expected = testCase.getExpectedOutput();
result.add(new TestCaseBO(input, expected));
}
return result;
}
private String buildCppInput(List<ChallengeParams> paramList, Map<String, String> valueMap) {
StringBuilder sb = new StringBuilder();
for (ChallengeParams param : paramList) {
String value = valueMap.get(param.getParamName());
String type = param.getParamType();
String cppValue = convertToCppInput(value, type);
sb.append(cppValue).append("\n");
}
return sb.toString().trim(); // 去掉最后一行的换行
}
private String convertToCppInput(String value, String type) {
try {
switch (type) {
case "int":
case "string":
return value.replaceAll("^\"|\"$", "");
case "int[]":
case "List<int>":
return value.replaceAll("\\[|\\]", "").replaceAll(",", " ");
default:
return value;
}
} catch (Exception e) {
throw new RuntimeException("参数解析失败: value=" + value + ", type=" + type, e);
}
}
}

View File

@ -0,0 +1,13 @@
package com.guwan.backend.service;
import com.guwan.backend.pojo.entity.TestCaseParams;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author 12455
* @description 针对表test_case_params的数据库操作Service
* @createDate 2025-05-08 18:19:18
*/
public interface TestCaseParamsService extends IService<TestCaseParams> {
}

View File

@ -0,0 +1,13 @@
package com.guwan.backend.service;
import com.guwan.backend.pojo.entity.TestCases;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author 12455
* @description 针对表test_cases的数据库操作Service
* @createDate 2025-05-08 18:14:58
*/
public interface TestCasesService extends IService<TestCases> {
}

View File

@ -0,0 +1,22 @@
package com.guwan.backend.service;
import com.guwan.backend.Handler.CoursesWebSocketHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class WebSocketService {
@Autowired
private CoursesWebSocketHandler coursesWebSocketHandler;
public void broadcastStudentCount(String courseId, int count) {
String message = String.format(
"{\"type\":\"STUDENT_COUNT_UPDATE\",\"courseId\":\"%s\",\"count\":%d}",
courseId, count
);
coursesWebSocketHandler.broadcastMessage(message);
}
}

View File

@ -0,0 +1,21 @@
package com.guwan.backend.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guwan.backend.mapper.ChallengeParamsMapper;
import com.guwan.backend.pojo.entity.ChallengeParams;
import com.guwan.backend.service.ChallengeParamsService;
import org.springframework.stereotype.Service;
/**
* @author 12455
* @description 针对表challenge_params的数据库操作Service实现
* @createDate 2025-05-08 18:10:37
*/
@Service
public class ChallengeParamsServiceImpl extends ServiceImpl<ChallengeParamsMapper, ChallengeParams>
implements ChallengeParamsService{
}

View File

@ -0,0 +1,253 @@
package com.guwan.backend.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.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.core.command.ExecStartResultCallback;
import com.guwan.backend.mapper.ChallengeParamsMapper;
import com.guwan.backend.mapper.ChallengesMapper;
import com.guwan.backend.mapper.TestCaseParamsMapper;
import com.guwan.backend.mapper.TestCasesMapper;
import com.guwan.backend.pojo.bo.CodeRunResult;
import com.guwan.backend.pojo.bo.TestCaseBO;
import com.guwan.backend.pojo.dto.SubmitCodeDTO;
import com.guwan.backend.pojo.entity.*;
import com.guwan.backend.pojo.response.ChallengesVO;
import com.guwan.backend.pojo.response.TestCaseVO;
import com.guwan.backend.service.ChallengesService;
import com.guwan.backend.service.TestCaseAssemblyService;
import lombok.RequiredArgsConstructor;
import org.bson.types.Code;
import org.springframework.beans.BeanUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.w3c.dom.stylesheets.LinkStyle;
import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* @author 12455
* @description 针对表challenges的数据库操作Service实现
* @createDate 2025-05-08 17:58:59
*/
@Service
@RequiredArgsConstructor
public class ChallengesServiceImpl extends ServiceImpl<ChallengesMapper, Challenges>
implements ChallengesService{
private final ChallengesMapper challengesMapper;
private final ChallengeParamsMapper challengeParamsMapper;
private final TestCasesMapper testCasesMapper;
private final TestCaseParamsMapper testCaseParamsMapper;
private final DockerClient dockerClient;
private final TestCaseAssemblyService testCaseAssemblyService;
@Override
public List<ChallengesVO> getChallenge() {
ArrayList<ChallengesVO> challengesVOS = new ArrayList<>();
Page<Challenges> page = new Page<>(1, 10);
LambdaQueryWrapper<Challenges> lambdaQueryWrapper = new LambdaQueryWrapper<>();
List<Challenges> challengesList = challengesMapper.selectList(page, lambdaQueryWrapper);
for (Challenges challenges : challengesList) {
ArrayList<TestCaseVO> testCaseVOS = new ArrayList<>();
Integer challengesId = challenges.getId();
List<ChallengeParams> challengeParams = challengeParamsMapper.selectList(new LambdaQueryWrapper<ChallengeParams>().eq(ChallengeParams::getChallengeId, challengesId));
//获取题目的参数定义并按顺序排好
challengeParams.sort(Comparator.comparingInt(ChallengeParams::getParamOrder));
List<TestCases> testCases = testCasesMapper.selectList(new LambdaQueryWrapper<TestCases>().eq(TestCases::getChallengeId, challengesId));
for (TestCases testCase : testCases) {
Integer testCaseId = testCase.getId();
List<TestCaseParams> testCaseParams = testCaseParamsMapper.selectList(new LambdaQueryWrapper<TestCaseParams>().eq(TestCaseParams::getTestCaseId, testCaseId));
Map<String, String> paramValueMap = testCaseParams.stream()
.collect(Collectors.toMap(TestCaseParams::getParamName, TestCaseParams::getParamValue));
// 4. 根据 paramDefs 顺序拼接 input 字符串
StringBuilder inputBuilder = new StringBuilder();
for (int i = 0; i < challengeParams.size(); i++) {
ChallengeParams paramDef = challengeParams.get(i);
String paramName = paramDef.getParamName();
String value = paramValueMap.get(paramName);
inputBuilder.append(paramName).append(" = ").append(value);
if (i < challengeParams.size() - 1) {
inputBuilder.append(", ");
}
}
TestCaseVO testCaseVO = new TestCaseVO();
testCaseVO.setInput(inputBuilder.toString());
testCaseVO.setOutput(testCase.getExpectedOutput());
testCaseVO.setExplanation(testCase.getExplanation() != null ? testCase.getExplanation() : "");
testCaseVOS.add(testCaseVO);
}
ChallengesVO challengesVO = new ChallengesVO();
BeanUtils.copyProperties(challenges, challengesVO);
challengesVO.setTestCaseVOS(testCaseVOS);
challengesVOS.add(challengesVO);
}
for (ChallengesVO challengesVO : challengesVOS) {
System.out.println("challengesVO = " + challengesVO);
}
return challengesVOS;
}
@Override
public CodeRunResult submitCode(SubmitCodeDTO submitCodeDTO) {
List<TestCaseBO> testCaseBOS = testCaseAssemblyService.buildTestCasesForChallenge(submitCodeDTO.getChallengeId());
for (TestCaseBO testCaseBO : testCaseBOS) {
System.out.println("testCaseBO = " + testCaseBO);
}
CodeRunResult codeRunResult = judgeCode(submitCodeDTO.getCode(), testCaseBOS);
System.out.println("codeRunResult = " + codeRunResult);
return codeRunResult;
}
public CodeRunResult judgeCode(String code, List<TestCaseBO> testCases) {
CodeRunResult codeRunResult = new CodeRunResult();
String containerId = null;
try {
// 创建容器使用官方GCC镜像
CreateContainerResponse container = dockerClient.createContainerCmd("gcc:latest")
.withTty(true)
.withWorkingDir("/app")
.withHostConfig(HostConfig.newHostConfig()
.withMemory(100 * 1024 * 1024L) // 限制内存100MB
.withCpuCount(1L)) // 限制1核CPU
.exec();
containerId = container.getId();
dockerClient.startContainerCmd(containerId).exec();
// 1. 写入代码文件
execCommand(containerId, "mkdir -p /app", 10);
execCommand(containerId,
"bash -c 'cat > /app/main.cpp <<EOF\n" +
code.replace("'", "'\"'\"'") + // 处理单引号
"\nEOF'", 10);
// 2. 编译代码带编译错误检查
String compileOutput = execCommand(containerId, "g++ main.cpp -o main -O2 -Wall", 10);
if (!compileOutput.isEmpty()) {
codeRunResult.setStatus("compile_error");
codeRunResult.setMessage(compileOutput);
return codeRunResult;
}
// 3. 执行测试用例
List<Map<String, Object>> caseResults = new ArrayList<>();
for (int i = 0; i < testCases.size(); i++) {
TestCaseBO testCase = testCases.get(i);
Map<String, Object> caseResult = new HashMap<>();
caseResult.put("case_id", i + 1);
caseResult.put("input", testCase.input);
caseResult.put("expected", testCase.expectedOutput);
try {
// 写入输入文件
execCommand(containerId,
"bash -c 'cat > /app/input.txt <<EOF\n" +
testCase.input.replace("'", "'\"'\"'") +
"\nEOF'", 10);
// 执行程序带超时控制
String actualOutput = execCommand(containerId,
"timeout 2s ./main < /app/input.txt 2>&1", // 捕获标准错误
3 // 超时3秒
);
// 标准化输出处理
actualOutput = actualOutput.trim()
.replaceAll("\r\n", "\n")
.replaceAll("[ \\t]+", " ");
// 结果对比
boolean isPassed = actualOutput.equals(testCase.expectedOutput);
caseResult.put("status", isPassed ? "passed" : "failed");
caseResult.put("actual", actualOutput);
// 特殊错误类型检测
if (actualOutput.contains("Timeout")) {
caseResult.put("status", "time_limit_exceeded");
} else if (actualOutput.contains("runtime error")) {
caseResult.put("status", "runtime_error");
} else if (actualOutput.isEmpty()) {
caseResult.put("status", "no_output");
}
} catch (Exception e) {
caseResult.put("status", "system_error");
caseResult.put("message", e.getMessage());
}
caseResults.add(caseResult);
}
codeRunResult.setStatus("completed");
//result.put("cases", caseResults);
codeRunResult.setPassCount((int) caseResults.stream()
.filter(c -> "passed".equals(c.get("status")))
.count());
} catch (Exception e) {
codeRunResult.setStatus("system_error");
codeRunResult.setMessage(e.getMessage());
} finally {
if (containerId != null) {
dockerClient.removeContainerCmd(containerId)
.withForce(true)
.exec();
}
}
return codeRunResult;
}
// 辅助方法执行容器命令
private String execCommand(String containerId, String command, int timeoutSeconds)
throws Exception {
ByteArrayOutputStream output = new ByteArrayOutputStream();
ExecCreateCmdResponse exec = dockerClient.execCreateCmd(containerId)
.withCmd("bash", "-c", command)
.withAttachStdout(true)
.withAttachStderr(true)
.exec();
dockerClient.execStartCmd(exec.getId())
.exec(new ExecStartResultCallback(output, output))
.awaitCompletion(timeoutSeconds, TimeUnit.SECONDS);
return output.toString().trim();
}
}

View File

@ -0,0 +1,22 @@
package com.guwan.backend.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guwan.backend.pojo.entity.Chapter;
import com.guwan.backend.service.ChapterService;
import com.guwan.backend.mapper.ChapterMapper;
import org.springframework.stereotype.Service;
/**
* @author 12455
* @description 针对表chapter的数据库操作Service实现
* @createDate 2025-05-08 14:39:30
*/
@Service
public class ChapterServiceImpl extends ServiceImpl<ChapterMapper, Chapter>
implements ChapterService{
}

View File

@ -2,19 +2,21 @@ package com.guwan.backend.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guwan.backend.mapper.CourseMapper;
import com.guwan.backend.mapper.TeacherMapper;
import com.guwan.backend.pojo.entity.Course;
import com.guwan.backend.pojo.entity.Teacher;
import com.guwan.backend.pojo.response.CourseDetailVo;
import com.guwan.backend.factory.SectionVOFactory;
import com.guwan.backend.mapper.*;
import com.guwan.backend.pojo.entity.*;
import com.guwan.backend.pojo.response.courseDetail.BaseSectionVO;
import com.guwan.backend.pojo.response.courseDetail.ChapterVO;
import com.guwan.backend.pojo.response.courseDetail.CourseDetailVO;
import com.guwan.backend.pojo.response.courseDetail.ReviewVO;
import com.guwan.backend.service.CourseService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* @author 12455
@ -30,31 +32,152 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
private final TeacherMapper teacherMapper;
@Override
public CourseDetailVo getCourseDetail(String courseId) {
private final RatingDistributionMapper ratingDistributionMapper;
private final ChapterMapper chapterMapper;
private final SectionMapper sectionMapper;
private final SectionVOFactory sectionVOFactory;
private final ReviewMapper reviewMapper;
private final UserMapper userMapper;
private static final Map<String, Integer> courseStudentCounts = new ConcurrentHashMap<>();
@Override
public CourseDetailVO getCourseDetail(String courseId) {
//首选查询course表
Course course = courseMapper.selectOne(new LambdaQueryWrapper<Course>().eq(Course::getId, courseId));
if (course == null){
if (course == null) {
return null;
}
System.out.println("course = " + course);
CourseDetailVO courseDetailVO = new CourseDetailVO();
courseDetailVO.setTitle(course.getTitle());
courseDetailVO.setDescription(course.getDescription());
// Course(id=1, title=黑马程序员匠心之作|C++教程从0到1入门编程, description=null, categoryId=2, categoryName=null, teacherId=1, coverImg=http://localhost:9000/photo/cover/c315f738-71aa-4329-8c7c-9092284c33c3.png, price=0.00, coursrTeacherId=null, rating=0.0, ratingCount=null, studentCount=0, videoCount=null, documentCount=null, totalDuration=null, createdAt=null, updatedAt=null)
var teacherId = course.getTeacherId();
Teacher teacher = null;
if (teacherId != null) {
teacher = teacherMapper.selectById(teacherId);
}
if (teacher != null) {
courseDetailVO.setTeacher(teacher.getName());
courseDetailVO.setTeacherAvatar(teacher.getAvatar());
}
Teacher teacher = teacherMapper.selectById(teacherId);
teacher.getName();
teacher.getAvatar();
courseDetailVO.setPrice(course.getPrice().doubleValue());
List<RatingDistribution> ratingDistributions = ratingDistributionMapper.selectList(new LambdaQueryWrapper<RatingDistribution>().eq(RatingDistribution::getCourseId, courseId));
ratingDistributions.sort(Comparator.comparingInt(RatingDistribution::getRatingLevel).reversed());
int ratingCount = 0;
int totalCount = 0;
for (RatingDistribution ratingDistribution : ratingDistributions) {
ratingCount += ratingDistribution.getCount();
totalCount += ratingDistribution.getCount() * ratingDistribution.getRatingLevel();
}
courseDetailVO.setRating((double) totalCount / ratingCount);
courseDetailVO.setRatingCount(ratingCount);
courseDetailVO.setStudentCount(0);
List<Chapter> chapters = chapterMapper.selectList(new LambdaQueryWrapper<Chapter>().eq(Chapter::getCourseId, courseId));
List<String> chapterIds = chapters.stream().map(Chapter::getId).toList();
List<Section> sections = sectionMapper.selectList(new LambdaQueryWrapper<Section>().in(Section::getChapterId, chapterIds));
int videoCount = 0;
int docCount = 0;
int totalTimeOfMinute = 0;
for (Section section : sections) {
if (Objects.equals("video", section.getType())){
videoCount ++;
totalTimeOfMinute += section.getDuration();
}else {
docCount ++;
}
}
courseDetailVO.setVideoCount(videoCount);
courseDetailVO.setDocCount(docCount);
courseDetailVO.setTotalDuration(totalTimeOfMinute / 60);
courseDetailVO.setEnrolled(false);
courseDetailVO.setCoverImg(course.getCoverImg());
List<ChapterVO> chapterVOList = new ArrayList<>();
//chapter vo
chapters.sort(Comparator.comparingInt(Chapter::getOrderNumber));
for (Chapter chapter : chapters) {
System.out.println("chapter = " + chapter);
ChapterVO chapterVO = new ChapterVO();
BeanUtils.copyProperties(chapter, chapterVO);
String chapterId = chapter.getId();
List<Section> sectionList = sectionMapper.selectList(new LambdaQueryWrapper<Section>().eq(Section::getChapterId, chapterId));
sectionList.sort(Comparator.comparingInt(Section::getOrderNumber));
List<BaseSectionVO> BaseSectionVOs = sectionList.stream()
.map(sectionVOFactory::fromSection) // 用实例方法引用
.toList();
chapterVO.setSectionVOS(BaseSectionVOs);
chapterVOList.add(chapterVO);
}
courseDetailVO.setChapterVOS(chapterVOList);
List<Review> reviews = reviewMapper.selectList(new LambdaQueryWrapper<Review>().eq(Review::getCourseId, courseId));
List<String> words = new ArrayList<>(Arrays.asList("apple", "banana", "cherry"));
Collections.sort(words, (s1, s2) -> s2.length() - s1.length()); // 按长度降序
System.out.println(words); // 输出: [banana, cherry, apple]
List<ReviewVO> reviewVOList = reviews.stream().map(review -> {
ReviewVO reviewVO = new ReviewVO();
BeanUtils.copyProperties(review, reviewVO);
String userId = review.getUserId();
User user = userMapper.selectById(userId);
reviewVO.setUserName(user.getUsername());
return reviewVO;
}).toList();
return null;
courseDetailVO.setReviewVOS(reviewVOList);
courseDetailVO.setRatingDistribution(ratingDistributions.stream().map(RatingDistribution::getCount).collect(Collectors.toList()));
System.out.println("courseDetailVO = " + courseDetailVO);
return courseDetailVO;
}
public int getStudentCount(String courseId) {
return courseStudentCounts.getOrDefault(courseId, 0);
}
public void updateStudentCount(String courseId, int count) {
courseStudentCounts.put(courseId, count);
}
public Map<String, Integer> getAllCourseCounts() {
return new HashMap<>(courseStudentCounts);
}
}

View File

@ -0,0 +1,22 @@
package com.guwan.backend.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guwan.backend.pojo.entity.RatingDistribution;
import com.guwan.backend.service.RatingDistributionService;
import com.guwan.backend.mapper.RatingDistributionMapper;
import org.springframework.stereotype.Service;
/**
* @author 12455
* @description 针对表rating_distribution(课程评分分布表)的数据库操作Service实现
* @createDate 2025-05-08 14:32:23
*/
@Service
public class RatingDistributionServiceImpl extends ServiceImpl<RatingDistributionMapper, RatingDistribution>
implements RatingDistributionService{
}

View File

@ -0,0 +1,22 @@
package com.guwan.backend.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guwan.backend.mapper.ReviewMapper;
import com.guwan.backend.pojo.entity.Review;
import com.guwan.backend.service.ReviewService;
import org.springframework.stereotype.Service;
/**
* @author 12455
* @description 针对表review(课程评价表)的数据库操作Service实现
* @createDate 2025-05-08 14:42:13
*/
@Service
public class ReviewServiceImpl extends ServiceImpl<ReviewMapper, Review>
implements ReviewService {
}

View File

@ -0,0 +1,23 @@
package com.guwan.backend.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guwan.backend.mapper.SectionMapper;
import com.guwan.backend.pojo.entity.Section;
import com.guwan.backend.service.SectionService;
import org.springframework.stereotype.Service;
/**
* @author 12455
* @description 针对表section的数据库操作Service实现
* @createDate 2025-05-08 14:52:51
*/
@Service
public class SectionServiceImpl extends ServiceImpl<SectionMapper, Section>
implements SectionService{
}

View File

@ -0,0 +1,22 @@
package com.guwan.backend.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guwan.backend.mapper.SlideMapper;
import com.guwan.backend.pojo.entity.Slide;
import com.guwan.backend.service.SlideService;
import org.springframework.stereotype.Service;
/**
* @author 12455
* @description 针对表slide(PPT幻灯片表)的数据库操作Service实现
* @createDate 2025-05-08 15:51:55
*/
@Service
public class SlideServiceImpl extends ServiceImpl<SlideMapper, Slide>
implements SlideService {
}

View File

@ -0,0 +1,22 @@
package com.guwan.backend.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guwan.backend.pojo.entity.TestCaseParams;
import com.guwan.backend.service.TestCaseParamsService;
import com.guwan.backend.mapper.TestCaseParamsMapper;
import org.springframework.stereotype.Service;
/**
* @author 12455
* @description 针对表test_case_params的数据库操作Service实现
* @createDate 2025-05-08 18:19:18
*/
@Service
public class TestCaseParamsServiceImpl extends ServiceImpl<TestCaseParamsMapper, TestCaseParams>
implements TestCaseParamsService{
}

View File

@ -0,0 +1,23 @@
package com.guwan.backend.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guwan.backend.mapper.TestCasesMapper;
import com.guwan.backend.pojo.entity.TestCases;
import com.guwan.backend.service.TestCasesService;
import org.springframework.stereotype.Service;
/**
* @author 12455
* @description 针对表test_cases的数据库操作Service实现
* @createDate 2025-05-08 18:14:58
*/
@Service
public class TestCasesServiceImpl extends ServiceImpl<TestCasesMapper, TestCases>
implements TestCasesService {
}

View File

@ -5,6 +5,9 @@ spring:
application:
name: backend
jpa:
open-in-view: false
kafka:
bootstrap-servers: localhost:9092
consumer:

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.ChallengeParamsMapper">
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.entity.ChallengeParams">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="challengeId" column="challenge_id" jdbcType="INTEGER"/>
<result property="paramOrder" column="param_order" jdbcType="INTEGER"/>
<result property="paramName" column="param_name" jdbcType="VARCHAR"/>
<result property="paramType" column="param_type" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
id,challenge_id,param_order,
param_name,param_type
</sql>
</mapper>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.ChallengesMapper">
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.entity.Challenges">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="title" column="title" jdbcType="VARCHAR"/>
<result property="difficulty" column="difficulty" jdbcType="OTHER"/>
<result property="description" column="description" jdbcType="VARCHAR"/>
<result property="memoryLimit" column="memory_limit" jdbcType="INTEGER"/>
<result property="timeLimit" column="time_limit" jdbcType="INTEGER"/>
<result property="submitCount" column="submit_count" jdbcType="INTEGER"/>
<result property="acceptRate" column="accept_rate" jdbcType="DECIMAL"/>
</resultMap>
<sql id="Base_Column_List">
id,title,difficulty,
description,memory_limit,time_limit,
submit_count,accept_rate
</sql>
</mapper>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.guwan.backend.mapper.ChapterMapper">
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.entity.Chapter">
<id property="id" column="id" jdbcType="VARCHAR"/>
<result property="courseId" column="course_id" jdbcType="VARCHAR"/>
<result property="title" column="title" jdbcType="VARCHAR"/>
<result property="orderNumber" column="order_number" jdbcType="INTEGER"/>
<result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
<result property="updatedAt" column="updated_at" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,course_id,title,
order_number,created_at,updated_at
</sql>
</mapper>

View File

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="generator.mapper.CourseMapper">
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.entity.Course">
<id property="id" column="id" jdbcType="VARCHAR"/>
<result property="title" column="title" jdbcType="VARCHAR"/>
<result property="description" column="description" jdbcType="VARCHAR"/>
<result property="categoryId" column="category_id" jdbcType="VARCHAR"/>
<result property="categoryName" column="category_name" jdbcType="VARCHAR"/>
<result property="teacherId" column="teacher_id" jdbcType="VARCHAR"/>
<result property="coverImg" column="cover_img" jdbcType="VARCHAR"/>
<result property="price" column="price" jdbcType="DECIMAL"/>
<result property="coursrTeacherId" column="coursr_teacher_id" jdbcType="VARCHAR"/>
<result property="rating" column="rating" jdbcType="DECIMAL"/>
<result property="ratingCount" column="rating_count" jdbcType="INTEGER"/>
<result property="studentCount" column="student_count" jdbcType="INTEGER"/>
<result property="videoCount" column="video_count" jdbcType="INTEGER"/>
<result property="documentCount" column="document_count" jdbcType="INTEGER"/>
<result property="totalDuration" column="total_duration" jdbcType="INTEGER"/>
<result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
<result property="updatedAt" column="updated_at" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,title,description,
category_id,category_name,teacher_id,
cover_img,price,coursr_teacher_id,
rating,rating_count,student_count,
video_count,document_count,total_duration,
created_at,updated_at
</sql>
</mapper>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="generator.mapper.RatingDistributionMapper">
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.entity.RatingDistribution">
<id property="id" column="id" jdbcType="VARCHAR"/>
<result property="courseId" column="course_id" jdbcType="INTEGER"/>
<result property="ratingLevel" column="rating_level" jdbcType="INTEGER"/>
<result property="count" column="count" jdbcType="INTEGER"/>
<result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
<result property="updatedAt" column="updated_at" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,course_id,rating_level,
count,created_at,updated_at
</sql>
</mapper>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.ReviewMapper">
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.entity.Review">
<id property="id" column="id" jdbcType="VARCHAR"/>
<result property="courseId" column="course_id" jdbcType="INTEGER"/>
<result property="userId" column="userId" jdbcType="VARCHAR"/>
<result property="avatar" column="avatar" jdbcType="VARCHAR"/>
<result property="rating" column="rating" jdbcType="INTEGER"/>
<result property="content" column="content" jdbcType="VARCHAR"/>
<result property="date" column="date" jdbcType="DATE"/>
<result property="reply" column="reply" jdbcType="VARCHAR"/>
<result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
<result property="updatedAt" column="updated_at" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,course_id,userId,
avatar,rating,content,
date,reply,created_at,
updated_at
</sql>
</mapper>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.SectionMapper">
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.entity.Section">
<id property="id" column="id" jdbcType="VARCHAR"/>
<result property="chapterId" column="chapter_id" jdbcType="VARCHAR"/>
<result property="title" column="title" jdbcType="VARCHAR"/>
<result property="type" column="type" jdbcType="OTHER"/>
<result property="duration" column="duration" jdbcType="INTEGER"/>
<result property="url" column="url" jdbcType="VARCHAR"/>
<result property="isFree" column="is_free" jdbcType="TINYINT"/>
<result property="downloadable" column="downloadable" jdbcType="TINYINT"/>
<result property="orderNumber" column="order_number" jdbcType="INTEGER"/>
<result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
<result property="updatedAt" column="updated_at" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,chapter_id,title,
type,duration,url,
is_free,downloadable,order_number,
created_at,updated_at
</sql>
</mapper>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.SlideMapper">
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.entity.Slide">
<id property="id" column="id" jdbcType="VARCHAR"/>
<result property="sectionId" column="section_id" jdbcType="INTEGER"/>
<result property="url" column="url" jdbcType="VARCHAR"/>
<result property="title" column="title" jdbcType="VARCHAR"/>
<result property="orderNumber" column="order_number" jdbcType="INTEGER"/>
<result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
<result property="updatedAt" column="updated_at" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
id,section_id,url,
title,order_number,created_at,
updated_at
</sql>
</mapper>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.guwan.backend.mapper.TestCaseParamsMapper">
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.entity.TestCaseParams">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="testCaseId" column="test_case_id" jdbcType="INTEGER"/>
<result property="paramName" column="param_name" jdbcType="VARCHAR"/>
<result property="paramValue" column="param_value" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
id,test_case_id,param_name,
param_value
</sql>
</mapper>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.TestCasesMapper">
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.entity.TestCases">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="challengeId" column="challenge_id" jdbcType="INTEGER"/>
<result property="expectedOutput" column="expected_output" jdbcType="VARCHAR"/>
<result property="explanation" column="explanation" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
id,challenge_id,expected_output,
explanation
</sql>
</mapper>