feat(backend): 补全所有未提交的 Java 源码文件
新增 44 个此前仅在本地存在、从未提交到 git 的源码文件: - Controllers: FileUpload, AdminStats, AdminTheme, AdminCoursePackage, AdminCourseLesson, AdminResource, AdminOperationLog, School(Course/Schedule/Settings/Stats/Export/OperationLog/CoursePackage), Teacher(Dashboard/Schedule/SchoolCourse/CourseLesson) - Entities: CourseLesson, CoursePackage, SchoolCourse, Theme - Mappers: CourseLesson, CoursePackage, SchoolCourse, Theme - Services: All 13 service classes - Config: WebMvcConfig Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3f3ed084f7
commit
e9dff31242
13
reading-platform-java/Dockerfile
Normal file
13
reading-platform-java/Dockerfile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
FROM maven:3.9-eclipse-temurin-21 AS build
|
||||||
|
WORKDIR /app
|
||||||
|
COPY pom.xml .
|
||||||
|
RUN mvn dependency:go-offline -q
|
||||||
|
COPY src ./src
|
||||||
|
RUN mvn package -DskipTests -q
|
||||||
|
|
||||||
|
FROM eclipse-temurin:21-jre-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/target/*.jar app.jar
|
||||||
|
RUN mkdir -p /app/uploads
|
||||||
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "app.jar"]
|
||||||
@ -24,6 +24,8 @@
|
|||||||
<jjwt.version>0.12.5</jjwt.version>
|
<jjwt.version>0.12.5</jjwt.version>
|
||||||
<knife4j.version>4.4.0</knife4j.version>
|
<knife4j.version>4.4.0</knife4j.version>
|
||||||
<hutool.version>5.8.26</hutool.version>
|
<hutool.version>5.8.26</hutool.version>
|
||||||
|
<poi.version>5.2.5</poi.version>
|
||||||
|
<flyway.version>10.8.1</flyway.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -32,6 +34,10 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-validation</artifactId>
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
@ -95,6 +101,25 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache POI (Excel export) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml</artifactId>
|
||||||
|
<version>${poi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Flyway Database Migration -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-core</artifactId>
|
||||||
|
<version>${flyway.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-mysql</artifactId>
|
||||||
|
<version>${flyway.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Test -->
|
<!-- Test -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|||||||
@ -40,11 +40,11 @@ public class SecurityConfig {
|
|||||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
// Public endpoints
|
// Public endpoints
|
||||||
.requestMatchers("/api/auth/**").permitAll()
|
.requestMatchers("/api/v1/auth/**").permitAll()
|
||||||
// Swagger/Knife4j endpoints
|
// Swagger/Knife4j endpoints
|
||||||
.requestMatchers("/doc.html", "/swagger-ui/**", "/v3/api-docs/**", "/webjars/**").permitAll()
|
.requestMatchers("/doc.html", "/swagger-ui/**", "/v3/api-docs/**", "/webjars/**").permitAll()
|
||||||
// Static resources
|
// Static resources - uploads
|
||||||
.requestMatchers("/static/**", "/favicon.ico").permitAll()
|
.requestMatchers("/uploads/**", "/static/**", "/favicon.ico").permitAll()
|
||||||
// OPTIONS requests
|
// OPTIONS requests
|
||||||
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
|
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
|
||||||
// All other requests require authentication
|
// All other requests require authentication
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
package com.reading.platform.common.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class WebMvcConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Value("${file.upload.path:/app/uploads/}")
|
||||||
|
private String uploadPath;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
|
registry.addResourceHandler("/uploads/**")
|
||||||
|
.addResourceLocations("file:" + uploadPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.reading.platform.common.response;
|
package com.reading.platform.common.response;
|
||||||
|
|
||||||
|
import com.reading.platform.common.enums.ErrorCode;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@ -47,4 +48,8 @@ public class Result<T> implements Serializable {
|
|||||||
return error(500, message);
|
return error(500, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error(ErrorCode errorCode) {
|
||||||
|
return error(errorCode.getCode(), errorCode.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.reading.platform.controller;
|
||||||
|
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.service.FileUploadService;
|
||||||
|
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 org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Tag(name = "File Upload", description = "File Upload APIs")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/files")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class FileUploadController {
|
||||||
|
|
||||||
|
private final FileUploadService fileUploadService;
|
||||||
|
|
||||||
|
@Operation(summary = "Upload file")
|
||||||
|
@PostMapping("/upload")
|
||||||
|
public Result<Map<String, String>> uploadFile(@RequestParam("file") MultipartFile file) {
|
||||||
|
String url = fileUploadService.uploadFile(file);
|
||||||
|
Map<String, String> result = new HashMap<>();
|
||||||
|
result.put("url", url);
|
||||||
|
result.put("filename", file.getOriginalFilename());
|
||||||
|
return Result.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Delete file")
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
public Result<Void> deleteFile(@RequestParam("filePath") String filePath) {
|
||||||
|
fileUploadService.deleteFile(filePath);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package com.reading.platform.controller.admin;
|
||||||
|
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.entity.CourseLesson;
|
||||||
|
import com.reading.platform.service.CourseLessonService;
|
||||||
|
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 = "Admin - Course Lessons", description = "Course Lesson Management for Admin")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/admin/courses/{courseId}/lessons")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.ADMIN)
|
||||||
|
public class AdminCourseLessonController {
|
||||||
|
|
||||||
|
private final CourseLessonService courseLessonService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get lessons for a course")
|
||||||
|
@GetMapping
|
||||||
|
public Result<List<CourseLesson>> getLessons(@PathVariable Long courseId) {
|
||||||
|
return Result.success(courseLessonService.getLessonsByCourse(courseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get lesson by ID")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<CourseLesson> getLesson(@PathVariable Long courseId, @PathVariable Long id) {
|
||||||
|
return Result.success(courseLessonService.getLessonById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Create course lesson")
|
||||||
|
@PostMapping
|
||||||
|
public Result<CourseLesson> createLesson(@PathVariable Long courseId, @RequestBody CourseLesson lesson) {
|
||||||
|
lesson.setCourseId(courseId);
|
||||||
|
return Result.success(courseLessonService.createLesson(lesson));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Update course lesson")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<CourseLesson> updateLesson(@PathVariable Long courseId, @PathVariable Long id, @RequestBody CourseLesson lesson) {
|
||||||
|
return Result.success(courseLessonService.updateLesson(id, lesson));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Delete course lesson")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deleteLesson(@PathVariable Long courseId, @PathVariable Long id) {
|
||||||
|
courseLessonService.deleteLesson(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
package com.reading.platform.controller.admin;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.PageResult;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.entity.CoursePackage;
|
||||||
|
import com.reading.platform.service.CoursePackageService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Tag(name = "Admin - Course Packages", description = "Course Package Management for Admin")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/admin/course-packages")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.ADMIN)
|
||||||
|
public class AdminCoursePackageController {
|
||||||
|
|
||||||
|
private final CoursePackageService coursePackageService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get course packages")
|
||||||
|
@GetMapping
|
||||||
|
public Result<PageResult<CoursePackage>> getPackages(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "20") int pageSize,
|
||||||
|
@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) String status) {
|
||||||
|
Page<CoursePackage> page = coursePackageService.getPackages(pageNum, pageSize, keyword, status);
|
||||||
|
return Result.success(PageResult.of(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get course package by ID")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<CoursePackage> getPackage(@PathVariable Long id) {
|
||||||
|
return Result.success(coursePackageService.getPackageById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Create course package")
|
||||||
|
@PostMapping
|
||||||
|
public Result<CoursePackage> createPackage(@RequestBody CoursePackage pkg) {
|
||||||
|
return Result.success(coursePackageService.createPackage(pkg));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Update course package")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<CoursePackage> updatePackage(@PathVariable Long id, @RequestBody CoursePackage pkg) {
|
||||||
|
return Result.success(coursePackageService.updatePackage(id, pkg));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Delete course package")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deletePackage(@PathVariable Long id) {
|
||||||
|
coursePackageService.deletePackage(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package com.reading.platform.controller.admin;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.PageResult;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.entity.OperationLog;
|
||||||
|
import com.reading.platform.service.OperationLogService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Tag(name = "Admin - Operation Logs", description = "Operation Log Management for Admin")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/admin/operation-logs")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.ADMIN)
|
||||||
|
public class AdminOperationLogController {
|
||||||
|
|
||||||
|
private final OperationLogService operationLogService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get operation logs")
|
||||||
|
@GetMapping
|
||||||
|
public Result<PageResult<OperationLog>> getLogs(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "20") int pageSize,
|
||||||
|
@RequestParam(required = false) Long tenantId,
|
||||||
|
@RequestParam(required = false) String module) {
|
||||||
|
Page<OperationLog> page = operationLogService.getLogs(pageNum, pageSize, tenantId, module);
|
||||||
|
return Result.success(PageResult.of(page));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
package com.reading.platform.controller.admin;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.PageResult;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.entity.ResourceItem;
|
||||||
|
import com.reading.platform.entity.ResourceLibrary;
|
||||||
|
import com.reading.platform.service.ResourceService;
|
||||||
|
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 = "Admin - Resources", description = "Resource Library Management for Admin")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/admin/resources")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.ADMIN)
|
||||||
|
public class AdminResourceController {
|
||||||
|
|
||||||
|
private final ResourceService resourceService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get all resource libraries")
|
||||||
|
@GetMapping("/libraries")
|
||||||
|
public Result<List<ResourceLibrary>> getLibraries(@RequestParam(required = false) Long tenantId) {
|
||||||
|
return Result.success(resourceService.getLibraries(tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Create resource library")
|
||||||
|
@PostMapping("/libraries")
|
||||||
|
public Result<ResourceLibrary> createLibrary(@RequestBody ResourceLibrary library) {
|
||||||
|
return Result.success(resourceService.createLibrary(library));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Update resource library")
|
||||||
|
@PutMapping("/libraries/{id}")
|
||||||
|
public Result<ResourceLibrary> updateLibrary(@PathVariable Long id, @RequestBody ResourceLibrary library) {
|
||||||
|
return Result.success(resourceService.updateLibrary(id, library));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Delete resource library")
|
||||||
|
@DeleteMapping("/libraries/{id}")
|
||||||
|
public Result<Void> deleteLibrary(@PathVariable Long id) {
|
||||||
|
resourceService.deleteLibrary(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get resource items")
|
||||||
|
@GetMapping("/items")
|
||||||
|
public Result<PageResult<ResourceItem>> getItems(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "20") int pageSize,
|
||||||
|
@RequestParam(required = false) Long libraryId,
|
||||||
|
@RequestParam(required = false) String keyword) {
|
||||||
|
Page<ResourceItem> page = resourceService.getItems(pageNum, pageSize, libraryId, keyword);
|
||||||
|
return Result.success(PageResult.of(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Create resource item")
|
||||||
|
@PostMapping("/items")
|
||||||
|
public Result<ResourceItem> createItem(@RequestBody ResourceItem item) {
|
||||||
|
return Result.success(resourceService.createItem(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Update resource item")
|
||||||
|
@PutMapping("/items/{id}")
|
||||||
|
public Result<ResourceItem> updateItem(@PathVariable Long id, @RequestBody ResourceItem item) {
|
||||||
|
return Result.success(resourceService.updateItem(id, item));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Delete resource item")
|
||||||
|
@DeleteMapping("/items/{id}")
|
||||||
|
public Result<Void> deleteItem(@PathVariable Long id) {
|
||||||
|
resourceService.deleteItem(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
package com.reading.platform.controller.admin;
|
||||||
|
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.service.AdminStatsService;
|
||||||
|
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 = "Admin - Stats", description = "Admin Statistics Dashboard")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/admin/stats")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.ADMIN)
|
||||||
|
public class AdminStatsController {
|
||||||
|
|
||||||
|
private final AdminStatsService adminStatsService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get overall statistics")
|
||||||
|
@GetMapping
|
||||||
|
public Result<Map<String, Object>> getStats() {
|
||||||
|
return Result.success(adminStatsService.getStats());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get trend data (last 6 months)")
|
||||||
|
@GetMapping("/trend")
|
||||||
|
public Result<List<Map<String, Object>>> getTrendData() {
|
||||||
|
return Result.success(adminStatsService.getTrendData());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get active tenants")
|
||||||
|
@GetMapping("/tenants/active")
|
||||||
|
public Result<List<Map<String, Object>>> getActiveTenants(
|
||||||
|
@RequestParam(defaultValue = "10") int limit) {
|
||||||
|
return Result.success(adminStatsService.getActiveTenants(limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get popular courses")
|
||||||
|
@GetMapping("/courses/popular")
|
||||||
|
public Result<List<Map<String, Object>>> getPopularCourses(
|
||||||
|
@RequestParam(defaultValue = "10") int limit) {
|
||||||
|
return Result.success(adminStatsService.getPopularCourses(limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get recent activities")
|
||||||
|
@GetMapping("/activities")
|
||||||
|
public Result<List<Map<String, Object>>> getActivities(
|
||||||
|
@RequestParam(defaultValue = "10") int limit) {
|
||||||
|
return Result.success(adminStatsService.getRecentActivities(limit));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package com.reading.platform.controller.admin;
|
||||||
|
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.entity.Theme;
|
||||||
|
import com.reading.platform.service.ThemeService;
|
||||||
|
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 = "Admin - Themes", description = "Theme Management for Admin")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/admin/themes")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.ADMIN)
|
||||||
|
public class AdminThemeController {
|
||||||
|
|
||||||
|
private final ThemeService themeService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get all themes")
|
||||||
|
@GetMapping
|
||||||
|
public Result<List<Theme>> getThemes(@RequestParam(required = false) Boolean enabledOnly) {
|
||||||
|
return Result.success(themeService.getAllThemes(enabledOnly));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get theme by ID")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<Theme> getTheme(@PathVariable Long id) {
|
||||||
|
return Result.success(themeService.getThemeById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Create theme")
|
||||||
|
@PostMapping
|
||||||
|
public Result<Theme> createTheme(@RequestBody Theme theme) {
|
||||||
|
return Result.success(themeService.createTheme(theme));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Update theme")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<Theme> updateTheme(@PathVariable Long id, @RequestBody Theme theme) {
|
||||||
|
return Result.success(themeService.updateTheme(id, theme));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Delete theme")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deleteTheme(@PathVariable Long id) {
|
||||||
|
themeService.deleteTheme(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,7 +13,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@Tag(name = "Parent - Child", description = "Child Information APIs for Parent")
|
@Tag(name = "Parent - Child", description = "Child Information APIs for Parent")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/parent/children")
|
@RequestMapping("/api/v1/parent/children")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ParentChildController {
|
public class ParentChildController {
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,62 @@
|
|||||||
|
package com.reading.platform.controller.school;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.PageResult;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.entity.SchoolCourse;
|
||||||
|
import com.reading.platform.service.SchoolCourseService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Tag(name = "School - School Courses", description = "School Custom Course Management")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/school/school-courses")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.SCHOOL)
|
||||||
|
public class SchoolCourseController {
|
||||||
|
|
||||||
|
private final SchoolCourseService schoolCourseService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get school courses")
|
||||||
|
@GetMapping
|
||||||
|
public Result<PageResult<SchoolCourse>> getCourses(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "20") int pageSize,
|
||||||
|
@RequestParam(required = false) String keyword) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
Page<SchoolCourse> page = schoolCourseService.getCourses(pageNum, pageSize, tenantId, keyword);
|
||||||
|
return Result.success(PageResult.of(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get school course by ID")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<SchoolCourse> getCourse(@PathVariable Long id) {
|
||||||
|
return Result.success(schoolCourseService.getCourseById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Create school course")
|
||||||
|
@PostMapping
|
||||||
|
public Result<SchoolCourse> createCourse(@RequestBody SchoolCourse course) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
Long userId = SecurityUtils.getCurrentUserId();
|
||||||
|
return Result.success(schoolCourseService.createCourse(tenantId, userId, course));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Update school course")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<SchoolCourse> updateCourse(@PathVariable Long id, @RequestBody SchoolCourse course) {
|
||||||
|
return Result.success(schoolCourseService.updateCourse(id, course));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Delete school course")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deleteCourse(@PathVariable Long id) {
|
||||||
|
schoolCourseService.deleteCourse(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package com.reading.platform.controller.school;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.PageResult;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.entity.CoursePackage;
|
||||||
|
import com.reading.platform.service.CoursePackageService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Tag(name = "School - Course Packages", description = "Course Packages for School")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/school/course-packages")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.SCHOOL)
|
||||||
|
public class SchoolCoursePackageController {
|
||||||
|
|
||||||
|
private final CoursePackageService coursePackageService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get available course packages")
|
||||||
|
@GetMapping
|
||||||
|
public Result<PageResult<CoursePackage>> getPackages(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "20") int pageSize,
|
||||||
|
@RequestParam(required = false) String keyword) {
|
||||||
|
Page<CoursePackage> page = coursePackageService.getPackages(pageNum, pageSize, keyword, "published");
|
||||||
|
return Result.success(PageResult.of(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get course package by ID")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<CoursePackage> getPackage(@PathVariable Long id) {
|
||||||
|
return Result.success(coursePackageService.getPackageById(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
package com.reading.platform.controller.school;
|
||||||
|
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.service.ExportService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
@Tag(name = "School - Export", description = "School Data Export")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/school/export")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.SCHOOL)
|
||||||
|
public class SchoolExportController {
|
||||||
|
|
||||||
|
private final ExportService exportService;
|
||||||
|
|
||||||
|
@Operation(summary = "Export teachers to Excel")
|
||||||
|
@GetMapping("/teachers")
|
||||||
|
public ResponseEntity<byte[]> exportTeachers() throws IOException {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
byte[] data = exportService.exportTeachers(tenantId);
|
||||||
|
return buildResponse(data, "teachers_" + LocalDate.now() + ".xlsx");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Export students to Excel")
|
||||||
|
@GetMapping("/students")
|
||||||
|
public ResponseEntity<byte[]> exportStudents() throws IOException {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
byte[] data = exportService.exportStudents(tenantId);
|
||||||
|
return buildResponse(data, "students_" + LocalDate.now() + ".xlsx");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Export lessons to Excel")
|
||||||
|
@GetMapping("/lessons")
|
||||||
|
public ResponseEntity<byte[]> exportLessons() throws IOException {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
byte[] data = exportService.exportLessons(tenantId);
|
||||||
|
return buildResponse(data, "lessons_" + LocalDate.now() + ".xlsx");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Export growth records to Excel")
|
||||||
|
@GetMapping("/growth-records")
|
||||||
|
public ResponseEntity<byte[]> exportGrowthRecords() throws IOException {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
byte[] data = exportService.exportGrowthRecords(tenantId);
|
||||||
|
return buildResponse(data, "growth_records_" + LocalDate.now() + ".xlsx");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<byte[]> buildResponse(byte[] data, String filename) {
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
|
||||||
|
.contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
|
||||||
|
.body(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.reading.platform.controller.school;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.PageResult;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.entity.OperationLog;
|
||||||
|
import com.reading.platform.service.OperationLogService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Tag(name = "School - Operation Logs", description = "Operation Log for School")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/school/operation-logs")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.SCHOOL)
|
||||||
|
public class SchoolOperationLogController {
|
||||||
|
|
||||||
|
private final OperationLogService operationLogService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get school operation logs")
|
||||||
|
@GetMapping
|
||||||
|
public Result<PageResult<OperationLog>> getLogs(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "20") int pageSize,
|
||||||
|
@RequestParam(required = false) String module) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
Page<OperationLog> page = operationLogService.getLogs(pageNum, pageSize, tenantId, module);
|
||||||
|
return Result.success(PageResult.of(page));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package com.reading.platform.controller.school;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.PageResult;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.entity.SchedulePlan;
|
||||||
|
import com.reading.platform.entity.ScheduleTemplate;
|
||||||
|
import com.reading.platform.service.ScheduleService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Tag(name = "School - Schedule", description = "School Schedule Management")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/school/schedules")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.SCHOOL)
|
||||||
|
public class SchoolScheduleController {
|
||||||
|
|
||||||
|
private final ScheduleService scheduleService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get schedule plans")
|
||||||
|
@GetMapping
|
||||||
|
public Result<PageResult<SchedulePlan>> getSchedulePlans(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "20") int pageSize,
|
||||||
|
@RequestParam(required = false) Long classId) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
Page<SchedulePlan> page = scheduleService.getSchedulePlans(pageNum, pageSize, tenantId, classId);
|
||||||
|
return Result.success(PageResult.of(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get schedule plan by ID")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<SchedulePlan> getSchedulePlan(@PathVariable Long id) {
|
||||||
|
return Result.success(scheduleService.getSchedulePlanById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Create schedule plan")
|
||||||
|
@PostMapping
|
||||||
|
public Result<SchedulePlan> createSchedulePlan(@RequestBody SchedulePlan plan) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
return Result.success(scheduleService.createSchedulePlan(tenantId, plan));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Update schedule plan")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<SchedulePlan> updateSchedulePlan(@PathVariable Long id, @RequestBody SchedulePlan plan) {
|
||||||
|
return Result.success(scheduleService.updateSchedulePlan(id, plan));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Delete schedule plan")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deleteSchedulePlan(@PathVariable Long id) {
|
||||||
|
scheduleService.deleteSchedulePlan(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get schedule templates")
|
||||||
|
@GetMapping("/templates")
|
||||||
|
public Result<PageResult<ScheduleTemplate>> getScheduleTemplates(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "20") int pageSize) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
Page<ScheduleTemplate> page = scheduleService.getScheduleTemplates(pageNum, pageSize, tenantId);
|
||||||
|
return Result.success(PageResult.of(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Create schedule template")
|
||||||
|
@PostMapping("/templates")
|
||||||
|
public Result<ScheduleTemplate> createScheduleTemplate(@RequestBody ScheduleTemplate template) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
return Result.success(scheduleService.createScheduleTemplate(tenantId, template));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Delete schedule template")
|
||||||
|
@DeleteMapping("/templates/{id}")
|
||||||
|
public Result<Void> deleteScheduleTemplate(@PathVariable Long id) {
|
||||||
|
scheduleService.deleteScheduleTemplate(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.reading.platform.controller.school;
|
||||||
|
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.service.SystemSettingService;
|
||||||
|
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 = "School - Settings", description = "School Settings Management")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/school/settings")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.SCHOOL)
|
||||||
|
public class SchoolSettingsController {
|
||||||
|
|
||||||
|
private final SystemSettingService systemSettingService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get school settings")
|
||||||
|
@GetMapping
|
||||||
|
public Result<Map<String, String>> getSettings() {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
return Result.success(systemSettingService.getSettings(tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Update school settings")
|
||||||
|
@PutMapping
|
||||||
|
public Result<Void> updateSettings(@RequestBody Map<String, String> settings) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
systemSettingService.updateSettings(tenantId, settings);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.reading.platform.controller.school;
|
||||||
|
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.service.SchoolStatsService;
|
||||||
|
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 = "School - Stats", description = "School Statistics Dashboard")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/school/stats")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.SCHOOL)
|
||||||
|
public class SchoolStatsController {
|
||||||
|
|
||||||
|
private final SchoolStatsService schoolStatsService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get school statistics")
|
||||||
|
@GetMapping
|
||||||
|
public Result<Map<String, Object>> getStats() {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
return Result.success(schoolStatsService.getStats(tenantId));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.reading.platform.controller.teacher;
|
||||||
|
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.entity.CourseLesson;
|
||||||
|
import com.reading.platform.service.CourseLessonService;
|
||||||
|
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 = "Teacher - Course Lessons", description = "Course Lessons for Teacher")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/teacher/courses/{courseId}/lessons")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.TEACHER)
|
||||||
|
public class TeacherCourseLessonController {
|
||||||
|
|
||||||
|
private final CourseLessonService courseLessonService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get lessons for a course")
|
||||||
|
@GetMapping
|
||||||
|
public Result<List<CourseLesson>> getLessons(@PathVariable Long courseId) {
|
||||||
|
return Result.success(courseLessonService.getLessonsByCourse(courseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get lesson by ID")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<CourseLesson> getLesson(@PathVariable Long courseId, @PathVariable Long id) {
|
||||||
|
return Result.success(courseLessonService.getLessonById(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package com.reading.platform.controller.teacher;
|
||||||
|
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.service.TeacherDashboardService;
|
||||||
|
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 = "Teacher - Dashboard", description = "Teacher Dashboard")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/teacher/dashboard")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.TEACHER)
|
||||||
|
public class TeacherDashboardController {
|
||||||
|
|
||||||
|
private final TeacherDashboardService teacherDashboardService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get teacher dashboard overview")
|
||||||
|
@GetMapping
|
||||||
|
public Result<Map<String, Object>> getDashboard() {
|
||||||
|
Long teacherId = SecurityUtils.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
return Result.success(teacherDashboardService.getDashboard(teacherId, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get today's lessons")
|
||||||
|
@GetMapping("/today")
|
||||||
|
public Result<List<Map<String, Object>>> getTodayLessons() {
|
||||||
|
Long teacherId = SecurityUtils.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
return Result.success(teacherDashboardService.getTodayLessons(teacherId, tenantId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get weekly lessons")
|
||||||
|
@GetMapping("/weekly")
|
||||||
|
public Result<List<Map<String, Object>>> getWeeklyLessons() {
|
||||||
|
Long teacherId = SecurityUtils.getCurrentUserId();
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
return Result.success(teacherDashboardService.getWeeklyLessons(teacherId, tenantId));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package com.reading.platform.controller.teacher;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.PageResult;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.entity.SchedulePlan;
|
||||||
|
import com.reading.platform.service.ScheduleService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Tag(name = "Teacher - Schedule", description = "Teacher Schedule View")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/teacher/schedules")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.TEACHER)
|
||||||
|
public class TeacherScheduleController {
|
||||||
|
|
||||||
|
private final ScheduleService scheduleService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get teacher schedule plans")
|
||||||
|
@GetMapping
|
||||||
|
public Result<PageResult<SchedulePlan>> getSchedulePlans(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "20") int pageSize) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
Page<SchedulePlan> page = scheduleService.getSchedulePlans(pageNum, pageSize, tenantId, null);
|
||||||
|
return Result.success(PageResult.of(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get schedule plan by ID")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<SchedulePlan> getSchedulePlan(@PathVariable Long id) {
|
||||||
|
return Result.success(scheduleService.getSchedulePlanById(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package com.reading.platform.controller.teacher;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.annotation.RequireRole;
|
||||||
|
import com.reading.platform.common.enums.UserRole;
|
||||||
|
import com.reading.platform.common.response.PageResult;
|
||||||
|
import com.reading.platform.common.response.Result;
|
||||||
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.entity.SchoolCourse;
|
||||||
|
import com.reading.platform.service.SchoolCourseService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Tag(name = "Teacher - School Courses", description = "School Courses for Teacher")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/teacher/school-courses")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequireRole(UserRole.TEACHER)
|
||||||
|
public class TeacherSchoolCourseController {
|
||||||
|
|
||||||
|
private final SchoolCourseService schoolCourseService;
|
||||||
|
|
||||||
|
@Operation(summary = "Get school courses")
|
||||||
|
@GetMapping
|
||||||
|
public Result<PageResult<SchoolCourse>> getCourses(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "20") int pageSize,
|
||||||
|
@RequestParam(required = false) String keyword) {
|
||||||
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
|
Page<SchoolCourse> page = schoolCourseService.getCourses(pageNum, pageSize, tenantId, keyword);
|
||||||
|
return Result.success(PageResult.of(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "Get school course by ID")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<SchoolCourse> getCourse(@PathVariable Long id) {
|
||||||
|
return Result.success(schoolCourseService.getCourseById(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package com.reading.platform.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Course Lesson Entity (chapters/sections within a course)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("course_lessons")
|
||||||
|
public class CourseLesson {
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private Long courseId;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
private Integer durationMinutes;
|
||||||
|
|
||||||
|
private String videoUrl;
|
||||||
|
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package com.reading.platform.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Course Package Entity
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("course_packages")
|
||||||
|
public class CoursePackage {
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private String coverUrl;
|
||||||
|
|
||||||
|
private Integer courseCount;
|
||||||
|
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
private Integer isSystem;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package com.reading.platform.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* School Course Entity (tenant-customized courses)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("school_courses")
|
||||||
|
public class SchoolCourse {
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private Long tenantId;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private String coverUrl;
|
||||||
|
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
private String ageRange;
|
||||||
|
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
private Long createdBy;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.reading.platform.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Theme Entity
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("themes")
|
||||||
|
public class Theme {
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String displayName;
|
||||||
|
|
||||||
|
private String color;
|
||||||
|
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
private Integer isEnabled;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.reading.platform.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.reading.platform.entity.CourseLesson;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface CourseLessonMapper extends BaseMapper<CourseLesson> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.reading.platform.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.reading.platform.entity.CoursePackage;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface CoursePackageMapper extends BaseMapper<CoursePackage> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.reading.platform.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.reading.platform.entity.SchoolCourse;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface SchoolCourseMapper extends BaseMapper<SchoolCourse> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.reading.platform.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.reading.platform.entity.Theme;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface ThemeMapper extends BaseMapper<Theme> {
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.reading.platform.common.exception.BusinessException;
|
||||||
|
import com.reading.platform.common.enums.ErrorCode;
|
||||||
|
import com.reading.platform.entity.CourseLesson;
|
||||||
|
import com.reading.platform.mapper.CourseLessonMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CourseLessonService {
|
||||||
|
|
||||||
|
private final CourseLessonMapper courseLessonMapper;
|
||||||
|
|
||||||
|
public List<CourseLesson> getLessonsByCourse(Long courseId) {
|
||||||
|
LambdaQueryWrapper<CourseLesson> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(CourseLesson::getCourseId, courseId)
|
||||||
|
.orderByAsc(CourseLesson::getSortOrder);
|
||||||
|
return courseLessonMapper.selectList(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CourseLesson getLessonById(Long id) {
|
||||||
|
CourseLesson lesson = courseLessonMapper.selectById(id);
|
||||||
|
if (lesson == null) {
|
||||||
|
throw new BusinessException(ErrorCode.DATA_NOT_FOUND, "Course lesson not found");
|
||||||
|
}
|
||||||
|
return lesson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CourseLesson createLesson(CourseLesson lesson) {
|
||||||
|
courseLessonMapper.insert(lesson);
|
||||||
|
return lesson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CourseLesson updateLesson(Long id, CourseLesson lesson) {
|
||||||
|
getLessonById(id);
|
||||||
|
lesson.setId(id);
|
||||||
|
courseLessonMapper.updateById(lesson);
|
||||||
|
return courseLessonMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteLesson(Long id) {
|
||||||
|
courseLessonMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.exception.BusinessException;
|
||||||
|
import com.reading.platform.common.enums.ErrorCode;
|
||||||
|
import com.reading.platform.entity.CoursePackage;
|
||||||
|
import com.reading.platform.mapper.CoursePackageMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CoursePackageService {
|
||||||
|
|
||||||
|
private final CoursePackageMapper coursePackageMapper;
|
||||||
|
|
||||||
|
public Page<CoursePackage> getPackages(int pageNum, int pageSize, String keyword, String status) {
|
||||||
|
Page<CoursePackage> page = new Page<>(pageNum, pageSize);
|
||||||
|
LambdaQueryWrapper<CoursePackage> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (keyword != null && !keyword.isEmpty()) {
|
||||||
|
wrapper.like(CoursePackage::getName, keyword);
|
||||||
|
}
|
||||||
|
if (status != null && !status.isEmpty()) {
|
||||||
|
wrapper.eq(CoursePackage::getStatus, status);
|
||||||
|
}
|
||||||
|
wrapper.orderByDesc(CoursePackage::getCreatedAt);
|
||||||
|
return coursePackageMapper.selectPage(page, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CoursePackage getPackageById(Long id) {
|
||||||
|
CoursePackage pkg = coursePackageMapper.selectById(id);
|
||||||
|
if (pkg == null) {
|
||||||
|
throw new BusinessException(ErrorCode.DATA_NOT_FOUND, "Course package not found");
|
||||||
|
}
|
||||||
|
return pkg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CoursePackage createPackage(CoursePackage pkg) {
|
||||||
|
coursePackageMapper.insert(pkg);
|
||||||
|
return pkg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CoursePackage updatePackage(Long id, CoursePackage pkg) {
|
||||||
|
getPackageById(id);
|
||||||
|
pkg.setId(id);
|
||||||
|
coursePackageMapper.updateById(pkg);
|
||||||
|
return coursePackageMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deletePackage(Long id) {
|
||||||
|
coursePackageMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,141 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.reading.platform.entity.*;
|
||||||
|
import com.reading.platform.mapper.*;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.poi.ss.usermodel.*;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ExportService {
|
||||||
|
|
||||||
|
private final TeacherMapper teacherMapper;
|
||||||
|
private final StudentMapper studentMapper;
|
||||||
|
private final LessonMapper lessonMapper;
|
||||||
|
private final GrowthRecordMapper growthRecordMapper;
|
||||||
|
|
||||||
|
public byte[] exportTeachers(Long tenantId) throws IOException {
|
||||||
|
LambdaQueryWrapper<Teacher> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(Teacher::getTenantId, tenantId);
|
||||||
|
List<Teacher> teachers = teacherMapper.selectList(wrapper);
|
||||||
|
|
||||||
|
try (Workbook workbook = new XSSFWorkbook()) {
|
||||||
|
Sheet sheet = workbook.createSheet("Teachers");
|
||||||
|
String[] headers = {"ID", "Name", "Username", "Phone", "Email", "Gender", "Status"};
|
||||||
|
createHeaderRow(sheet, headers);
|
||||||
|
|
||||||
|
int rowNum = 1;
|
||||||
|
for (Teacher t : teachers) {
|
||||||
|
Row row = sheet.createRow(rowNum++);
|
||||||
|
row.createCell(0).setCellValue(t.getId());
|
||||||
|
row.createCell(1).setCellValue(t.getName() != null ? t.getName() : "");
|
||||||
|
row.createCell(2).setCellValue(t.getUsername() != null ? t.getUsername() : "");
|
||||||
|
row.createCell(3).setCellValue(t.getPhone() != null ? t.getPhone() : "");
|
||||||
|
row.createCell(4).setCellValue(t.getEmail() != null ? t.getEmail() : "");
|
||||||
|
row.createCell(5).setCellValue(t.getGender() != null ? t.getGender() : "");
|
||||||
|
row.createCell(6).setCellValue(t.getStatus() != null ? t.getStatus() : "");
|
||||||
|
}
|
||||||
|
return toBytes(workbook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] exportStudents(Long tenantId) throws IOException {
|
||||||
|
LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(Student::getTenantId, tenantId);
|
||||||
|
List<Student> students = studentMapper.selectList(wrapper);
|
||||||
|
|
||||||
|
try (Workbook workbook = new XSSFWorkbook()) {
|
||||||
|
Sheet sheet = workbook.createSheet("Students");
|
||||||
|
String[] headers = {"ID", "Name", "Gender", "Birth Date", "Grade", "Student No", "Status"};
|
||||||
|
createHeaderRow(sheet, headers);
|
||||||
|
|
||||||
|
int rowNum = 1;
|
||||||
|
for (Student s : students) {
|
||||||
|
Row row = sheet.createRow(rowNum++);
|
||||||
|
row.createCell(0).setCellValue(s.getId());
|
||||||
|
row.createCell(1).setCellValue(s.getName() != null ? s.getName() : "");
|
||||||
|
row.createCell(2).setCellValue(s.getGender() != null ? s.getGender() : "");
|
||||||
|
row.createCell(3).setCellValue(s.getBirthDate() != null ? s.getBirthDate().toString() : "");
|
||||||
|
row.createCell(4).setCellValue(s.getGrade() != null ? s.getGrade() : "");
|
||||||
|
row.createCell(5).setCellValue(s.getStudentNo() != null ? s.getStudentNo() : "");
|
||||||
|
row.createCell(6).setCellValue(s.getStatus() != null ? s.getStatus() : "");
|
||||||
|
}
|
||||||
|
return toBytes(workbook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] exportLessons(Long tenantId) throws IOException {
|
||||||
|
LambdaQueryWrapper<Lesson> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(Lesson::getTenantId, tenantId);
|
||||||
|
List<Lesson> lessons = lessonMapper.selectList(wrapper);
|
||||||
|
|
||||||
|
try (Workbook workbook = new XSSFWorkbook()) {
|
||||||
|
Sheet sheet = workbook.createSheet("Lessons");
|
||||||
|
String[] headers = {"ID", "Title", "Lesson Date", "Start Time", "End Time", "Location", "Status"};
|
||||||
|
createHeaderRow(sheet, headers);
|
||||||
|
|
||||||
|
int rowNum = 1;
|
||||||
|
for (Lesson l : lessons) {
|
||||||
|
Row row = sheet.createRow(rowNum++);
|
||||||
|
row.createCell(0).setCellValue(l.getId());
|
||||||
|
row.createCell(1).setCellValue(l.getTitle() != null ? l.getTitle() : "");
|
||||||
|
row.createCell(2).setCellValue(l.getLessonDate() != null ? l.getLessonDate().toString() : "");
|
||||||
|
row.createCell(3).setCellValue(l.getStartTime() != null ? l.getStartTime().toString() : "");
|
||||||
|
row.createCell(4).setCellValue(l.getEndTime() != null ? l.getEndTime().toString() : "");
|
||||||
|
row.createCell(5).setCellValue(l.getLocation() != null ? l.getLocation() : "");
|
||||||
|
row.createCell(6).setCellValue(l.getStatus() != null ? l.getStatus() : "");
|
||||||
|
}
|
||||||
|
return toBytes(workbook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] exportGrowthRecords(Long tenantId) throws IOException {
|
||||||
|
LambdaQueryWrapper<GrowthRecord> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(GrowthRecord::getTenantId, tenantId);
|
||||||
|
List<GrowthRecord> records = growthRecordMapper.selectList(wrapper);
|
||||||
|
|
||||||
|
try (Workbook workbook = new XSSFWorkbook()) {
|
||||||
|
Sheet sheet = workbook.createSheet("Growth Records");
|
||||||
|
String[] headers = {"ID", "Student ID", "Type", "Title", "Content", "Record Date"};
|
||||||
|
createHeaderRow(sheet, headers);
|
||||||
|
|
||||||
|
int rowNum = 1;
|
||||||
|
for (GrowthRecord g : records) {
|
||||||
|
Row row = sheet.createRow(rowNum++);
|
||||||
|
row.createCell(0).setCellValue(g.getId());
|
||||||
|
row.createCell(1).setCellValue(g.getStudentId());
|
||||||
|
row.createCell(2).setCellValue(g.getType() != null ? g.getType() : "");
|
||||||
|
row.createCell(3).setCellValue(g.getTitle() != null ? g.getTitle() : "");
|
||||||
|
row.createCell(4).setCellValue(g.getContent() != null ? g.getContent() : "");
|
||||||
|
row.createCell(5).setCellValue(g.getRecordDate() != null ? g.getRecordDate().toString() : "");
|
||||||
|
}
|
||||||
|
return toBytes(workbook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createHeaderRow(Sheet sheet, String[] headers) {
|
||||||
|
Row headerRow = sheet.createRow(0);
|
||||||
|
CellStyle style = sheet.getWorkbook().createCellStyle();
|
||||||
|
Font font = sheet.getWorkbook().createFont();
|
||||||
|
font.setBold(true);
|
||||||
|
style.setFont(font);
|
||||||
|
for (int i = 0; i < headers.length; i++) {
|
||||||
|
Cell cell = headerRow.createCell(i);
|
||||||
|
cell.setCellValue(headers[i]);
|
||||||
|
cell.setCellStyle(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] toBytes(Workbook workbook) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
workbook.write(out);
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.reading.platform.common.exception.BusinessException;
|
||||||
|
import com.reading.platform.common.enums.ErrorCode;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class FileUploadService {
|
||||||
|
|
||||||
|
@Value("${file.upload.path:/app/uploads/}")
|
||||||
|
private String uploadPath;
|
||||||
|
|
||||||
|
@Value("${file.upload.base-url:/uploads/}")
|
||||||
|
private String baseUrl;
|
||||||
|
|
||||||
|
public String uploadFile(MultipartFile file) {
|
||||||
|
if (file == null || file.isEmpty()) {
|
||||||
|
throw new BusinessException(ErrorCode.INVALID_PARAMETER, "File cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
String originalFilename = file.getOriginalFilename();
|
||||||
|
String extension = "";
|
||||||
|
if (originalFilename != null && originalFilename.contains(".")) {
|
||||||
|
extension = originalFilename.substring(originalFilename.lastIndexOf("."));
|
||||||
|
}
|
||||||
|
|
||||||
|
String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
||||||
|
String newFilename = UUID.randomUUID().toString().replace("-", "") + extension;
|
||||||
|
String relativePath = datePath + "/" + newFilename;
|
||||||
|
String fullPath = uploadPath + relativePath;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Path targetPath = Paths.get(fullPath);
|
||||||
|
Files.createDirectories(targetPath.getParent());
|
||||||
|
file.transferTo(targetPath.toFile());
|
||||||
|
log.info("File uploaded: {}", fullPath);
|
||||||
|
return baseUrl + relativePath;
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("File upload failed", e);
|
||||||
|
throw new BusinessException(ErrorCode.INTERNAL_ERROR, "File upload failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteFile(String filePath) {
|
||||||
|
if (filePath == null || filePath.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String relativePath = filePath.startsWith(baseUrl) ? filePath.substring(baseUrl.length()) : filePath;
|
||||||
|
String fullPath = uploadPath + relativePath;
|
||||||
|
File file = new File(fullPath);
|
||||||
|
if (file.exists()) {
|
||||||
|
boolean deleted = file.delete();
|
||||||
|
if (!deleted) {
|
||||||
|
log.warn("Failed to delete file: {}", fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.security.SecurityUtils;
|
||||||
|
import com.reading.platform.entity.OperationLog;
|
||||||
|
import com.reading.platform.mapper.OperationLogMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class OperationLogService {
|
||||||
|
|
||||||
|
private final OperationLogMapper operationLogMapper;
|
||||||
|
|
||||||
|
public Page<OperationLog> getLogs(int pageNum, int pageSize, Long tenantId, String module) {
|
||||||
|
Page<OperationLog> page = new Page<>(pageNum, pageSize);
|
||||||
|
LambdaQueryWrapper<OperationLog> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (tenantId != null) {
|
||||||
|
wrapper.eq(OperationLog::getTenantId, tenantId);
|
||||||
|
}
|
||||||
|
if (module != null && !module.isEmpty()) {
|
||||||
|
wrapper.eq(OperationLog::getModule, module);
|
||||||
|
}
|
||||||
|
wrapper.orderByDesc(OperationLog::getCreatedAt);
|
||||||
|
return operationLogMapper.selectPage(page, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(String action, String module, String targetType, Long targetId, String details) {
|
||||||
|
try {
|
||||||
|
OperationLog log = new OperationLog();
|
||||||
|
log.setAction(action);
|
||||||
|
log.setModule(module);
|
||||||
|
log.setTargetType(targetType);
|
||||||
|
log.setTargetId(targetId);
|
||||||
|
log.setDetails(details);
|
||||||
|
try {
|
||||||
|
log.setUserId(SecurityUtils.getCurrentUserId());
|
||||||
|
log.setUserRole(SecurityUtils.getCurrentRole());
|
||||||
|
log.setTenantId(SecurityUtils.getCurrentTenantId());
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
operationLogMapper.insert(log);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Log silently - don't fail main operations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.exception.BusinessException;
|
||||||
|
import com.reading.platform.common.enums.ErrorCode;
|
||||||
|
import com.reading.platform.entity.ResourceItem;
|
||||||
|
import com.reading.platform.entity.ResourceLibrary;
|
||||||
|
import com.reading.platform.mapper.ResourceItemMapper;
|
||||||
|
import com.reading.platform.mapper.ResourceLibraryMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ResourceService {
|
||||||
|
|
||||||
|
private final ResourceLibraryMapper resourceLibraryMapper;
|
||||||
|
private final ResourceItemMapper resourceItemMapper;
|
||||||
|
|
||||||
|
public List<ResourceLibrary> getLibraries(Long tenantId) {
|
||||||
|
LambdaQueryWrapper<ResourceLibrary> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (tenantId != null) {
|
||||||
|
wrapper.eq(ResourceLibrary::getTenantId, tenantId);
|
||||||
|
}
|
||||||
|
wrapper.orderByDesc(ResourceLibrary::getCreatedAt);
|
||||||
|
return resourceLibraryMapper.selectList(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceLibrary getLibraryById(Long id) {
|
||||||
|
ResourceLibrary lib = resourceLibraryMapper.selectById(id);
|
||||||
|
if (lib == null) {
|
||||||
|
throw new BusinessException(ErrorCode.DATA_NOT_FOUND, "Resource library not found");
|
||||||
|
}
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceLibrary createLibrary(ResourceLibrary library) {
|
||||||
|
resourceLibraryMapper.insert(library);
|
||||||
|
return library;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceLibrary updateLibrary(Long id, ResourceLibrary library) {
|
||||||
|
getLibraryById(id);
|
||||||
|
library.setId(id);
|
||||||
|
resourceLibraryMapper.updateById(library);
|
||||||
|
return resourceLibraryMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteLibrary(Long id) {
|
||||||
|
resourceLibraryMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<ResourceItem> getItems(int pageNum, int pageSize, Long libraryId, String keyword) {
|
||||||
|
Page<ResourceItem> page = new Page<>(pageNum, pageSize);
|
||||||
|
LambdaQueryWrapper<ResourceItem> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (libraryId != null) {
|
||||||
|
wrapper.eq(ResourceItem::getLibraryId, libraryId);
|
||||||
|
}
|
||||||
|
if (keyword != null && !keyword.isEmpty()) {
|
||||||
|
wrapper.like(ResourceItem::getName, keyword);
|
||||||
|
}
|
||||||
|
wrapper.orderByDesc(ResourceItem::getCreatedAt);
|
||||||
|
return resourceItemMapper.selectPage(page, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceItem getItemById(Long id) {
|
||||||
|
ResourceItem item = resourceItemMapper.selectById(id);
|
||||||
|
if (item == null) {
|
||||||
|
throw new BusinessException(ErrorCode.DATA_NOT_FOUND, "Resource item not found");
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceItem createItem(ResourceItem item) {
|
||||||
|
resourceItemMapper.insert(item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceItem updateItem(Long id, ResourceItem item) {
|
||||||
|
getItemById(id);
|
||||||
|
item.setId(id);
|
||||||
|
resourceItemMapper.updateById(item);
|
||||||
|
return resourceItemMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteItem(Long id) {
|
||||||
|
resourceItemMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.exception.BusinessException;
|
||||||
|
import com.reading.platform.common.enums.ErrorCode;
|
||||||
|
import com.reading.platform.entity.SchedulePlan;
|
||||||
|
import com.reading.platform.entity.ScheduleTemplate;
|
||||||
|
import com.reading.platform.mapper.SchedulePlanMapper;
|
||||||
|
import com.reading.platform.mapper.ScheduleTemplateMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ScheduleService {
|
||||||
|
|
||||||
|
private final SchedulePlanMapper schedulePlanMapper;
|
||||||
|
private final ScheduleTemplateMapper scheduleTemplateMapper;
|
||||||
|
|
||||||
|
public Page<SchedulePlan> getSchedulePlans(int pageNum, int pageSize, Long tenantId, Long classId) {
|
||||||
|
Page<SchedulePlan> page = new Page<>(pageNum, pageSize);
|
||||||
|
LambdaQueryWrapper<SchedulePlan> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SchedulePlan::getTenantId, tenantId);
|
||||||
|
if (classId != null) {
|
||||||
|
wrapper.eq(SchedulePlan::getClassId, classId);
|
||||||
|
}
|
||||||
|
wrapper.orderByDesc(SchedulePlan::getCreatedAt);
|
||||||
|
return schedulePlanMapper.selectPage(page, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SchedulePlan getSchedulePlanById(Long id) {
|
||||||
|
SchedulePlan plan = schedulePlanMapper.selectById(id);
|
||||||
|
if (plan == null) {
|
||||||
|
throw new BusinessException(ErrorCode.DATA_NOT_FOUND, "Schedule plan not found");
|
||||||
|
}
|
||||||
|
return plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SchedulePlan createSchedulePlan(Long tenantId, SchedulePlan plan) {
|
||||||
|
plan.setTenantId(tenantId);
|
||||||
|
schedulePlanMapper.insert(plan);
|
||||||
|
return plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SchedulePlan updateSchedulePlan(Long id, SchedulePlan plan) {
|
||||||
|
SchedulePlan existing = getSchedulePlanById(id);
|
||||||
|
plan.setId(id);
|
||||||
|
plan.setTenantId(existing.getTenantId());
|
||||||
|
schedulePlanMapper.updateById(plan);
|
||||||
|
return schedulePlanMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteSchedulePlan(Long id) {
|
||||||
|
schedulePlanMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<ScheduleTemplate> getScheduleTemplates(int pageNum, int pageSize, Long tenantId) {
|
||||||
|
Page<ScheduleTemplate> page = new Page<>(pageNum, pageSize);
|
||||||
|
LambdaQueryWrapper<ScheduleTemplate> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(ScheduleTemplate::getTenantId, tenantId)
|
||||||
|
.orderByDesc(ScheduleTemplate::getCreatedAt);
|
||||||
|
return scheduleTemplateMapper.selectPage(page, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScheduleTemplate createScheduleTemplate(Long tenantId, ScheduleTemplate template) {
|
||||||
|
template.setTenantId(tenantId);
|
||||||
|
scheduleTemplateMapper.insert(template);
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteScheduleTemplate(Long id) {
|
||||||
|
scheduleTemplateMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.reading.platform.common.exception.BusinessException;
|
||||||
|
import com.reading.platform.common.enums.ErrorCode;
|
||||||
|
import com.reading.platform.entity.SchoolCourse;
|
||||||
|
import com.reading.platform.mapper.SchoolCourseMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SchoolCourseService {
|
||||||
|
|
||||||
|
private final SchoolCourseMapper schoolCourseMapper;
|
||||||
|
|
||||||
|
public Page<SchoolCourse> getCourses(int pageNum, int pageSize, Long tenantId, String keyword) {
|
||||||
|
Page<SchoolCourse> page = new Page<>(pageNum, pageSize);
|
||||||
|
LambdaQueryWrapper<SchoolCourse> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SchoolCourse::getTenantId, tenantId);
|
||||||
|
if (keyword != null && !keyword.isEmpty()) {
|
||||||
|
wrapper.like(SchoolCourse::getName, keyword);
|
||||||
|
}
|
||||||
|
wrapper.orderByDesc(SchoolCourse::getCreatedAt);
|
||||||
|
return schoolCourseMapper.selectPage(page, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SchoolCourse getCourseById(Long id) {
|
||||||
|
SchoolCourse course = schoolCourseMapper.selectById(id);
|
||||||
|
if (course == null) {
|
||||||
|
throw new BusinessException(ErrorCode.DATA_NOT_FOUND, "School course not found");
|
||||||
|
}
|
||||||
|
return course;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SchoolCourse createCourse(Long tenantId, Long userId, SchoolCourse course) {
|
||||||
|
course.setTenantId(tenantId);
|
||||||
|
course.setCreatedBy(userId);
|
||||||
|
schoolCourseMapper.insert(course);
|
||||||
|
return course;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SchoolCourse updateCourse(Long id, SchoolCourse course) {
|
||||||
|
SchoolCourse existing = getCourseById(id);
|
||||||
|
course.setId(id);
|
||||||
|
course.setTenantId(existing.getTenantId());
|
||||||
|
schoolCourseMapper.updateById(course);
|
||||||
|
return schoolCourseMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteCourse(Long id) {
|
||||||
|
schoolCourseMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.reading.platform.entity.*;
|
||||||
|
import com.reading.platform.mapper.*;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SchoolStatsService {
|
||||||
|
|
||||||
|
private final TeacherMapper teacherMapper;
|
||||||
|
private final StudentMapper studentMapper;
|
||||||
|
private final ClazzMapper clazzMapper;
|
||||||
|
private final LessonMapper lessonMapper;
|
||||||
|
|
||||||
|
public Map<String, Object> getStats(Long tenantId) {
|
||||||
|
Map<String, Object> stats = new HashMap<>();
|
||||||
|
stats.put("teacherCount", teacherMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Teacher>().eq(Teacher::getTenantId, tenantId)));
|
||||||
|
stats.put("studentCount", studentMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Student>().eq(Student::getTenantId, tenantId)));
|
||||||
|
stats.put("classCount", clazzMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Clazz>().eq(Clazz::getTenantId, tenantId)));
|
||||||
|
stats.put("lessonCount", lessonMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Lesson>().eq(Lesson::getTenantId, tenantId)));
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
|
import com.reading.platform.entity.SystemSetting;
|
||||||
|
import com.reading.platform.mapper.SystemSettingMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SystemSettingService {
|
||||||
|
|
||||||
|
private final SystemSettingMapper systemSettingMapper;
|
||||||
|
|
||||||
|
public Map<String, String> getSettings(Long tenantId) {
|
||||||
|
LambdaQueryWrapper<SystemSetting> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SystemSetting::getTenantId, tenantId);
|
||||||
|
List<SystemSetting> settings = systemSettingMapper.selectList(wrapper);
|
||||||
|
Map<String, String> result = new HashMap<>();
|
||||||
|
for (SystemSetting s : settings) {
|
||||||
|
result.put(s.getSettingKey(), s.getSettingValue());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateSettings(Long tenantId, Map<String, String> settings) {
|
||||||
|
for (Map.Entry<String, String> entry : settings.entrySet()) {
|
||||||
|
LambdaQueryWrapper<SystemSetting> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SystemSetting::getTenantId, tenantId)
|
||||||
|
.eq(SystemSetting::getSettingKey, entry.getKey());
|
||||||
|
SystemSetting existing = systemSettingMapper.selectOne(wrapper);
|
||||||
|
if (existing != null) {
|
||||||
|
LambdaUpdateWrapper<SystemSetting> updateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
updateWrapper.eq(SystemSetting::getTenantId, tenantId)
|
||||||
|
.eq(SystemSetting::getSettingKey, entry.getKey());
|
||||||
|
SystemSetting update = new SystemSetting();
|
||||||
|
update.setSettingValue(entry.getValue());
|
||||||
|
systemSettingMapper.update(update, updateWrapper);
|
||||||
|
} else {
|
||||||
|
SystemSetting newSetting = new SystemSetting();
|
||||||
|
newSetting.setTenantId(tenantId);
|
||||||
|
newSetting.setSettingKey(entry.getKey());
|
||||||
|
newSetting.setSettingValue(entry.getValue());
|
||||||
|
systemSettingMapper.insert(newSetting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSetting(Long tenantId, String key) {
|
||||||
|
LambdaQueryWrapper<SystemSetting> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(SystemSetting::getTenantId, tenantId)
|
||||||
|
.eq(SystemSetting::getSettingKey, key);
|
||||||
|
SystemSetting setting = systemSettingMapper.selectOne(wrapper);
|
||||||
|
return setting != null ? setting.getSettingValue() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.reading.platform.entity.*;
|
||||||
|
import com.reading.platform.mapper.*;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class TeacherDashboardService {
|
||||||
|
|
||||||
|
private final LessonMapper lessonMapper;
|
||||||
|
private final TaskMapper taskMapper;
|
||||||
|
private final GrowthRecordMapper growthRecordMapper;
|
||||||
|
private final NotificationMapper notificationMapper;
|
||||||
|
|
||||||
|
public Map<String, Object> getDashboard(Long teacherId, Long tenantId) {
|
||||||
|
Map<String, Object> dashboard = new HashMap<>();
|
||||||
|
dashboard.put("lessonCount", lessonMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Lesson>()
|
||||||
|
.eq(Lesson::getTeacherId, teacherId)
|
||||||
|
.eq(Lesson::getTenantId, tenantId)));
|
||||||
|
dashboard.put("taskCount", taskMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Task>()
|
||||||
|
.eq(Task::getCreatorId, teacherId)
|
||||||
|
.eq(Task::getTenantId, tenantId)));
|
||||||
|
dashboard.put("growthRecordCount", growthRecordMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<GrowthRecord>()
|
||||||
|
.eq(GrowthRecord::getRecordedBy, teacherId)
|
||||||
|
.eq(GrowthRecord::getTenantId, tenantId)));
|
||||||
|
dashboard.put("unreadNotifications", notificationMapper.selectCount(
|
||||||
|
new LambdaQueryWrapper<Notification>()
|
||||||
|
.eq(Notification::getTenantId, tenantId)
|
||||||
|
.eq(Notification::getIsRead, 0)));
|
||||||
|
return dashboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Map<String, Object>> getTodayLessons(Long teacherId, Long tenantId) {
|
||||||
|
LocalDate today = LocalDate.now();
|
||||||
|
LambdaQueryWrapper<Lesson> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(Lesson::getTeacherId, teacherId)
|
||||||
|
.eq(Lesson::getTenantId, tenantId)
|
||||||
|
.eq(Lesson::getLessonDate, today)
|
||||||
|
.orderByAsc(Lesson::getStartTime);
|
||||||
|
List<Lesson> lessons = lessonMapper.selectList(wrapper);
|
||||||
|
return lessons.stream().map(l -> {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("id", l.getId());
|
||||||
|
map.put("title", l.getTitle());
|
||||||
|
map.put("startTime", l.getStartTime());
|
||||||
|
map.put("endTime", l.getEndTime());
|
||||||
|
map.put("location", l.getLocation());
|
||||||
|
map.put("status", l.getStatus());
|
||||||
|
return map;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Map<String, Object>> getWeeklyLessons(Long teacherId, Long tenantId) {
|
||||||
|
LocalDate today = LocalDate.now();
|
||||||
|
LocalDate weekStart = today.minusDays(today.getDayOfWeek().getValue() - 1);
|
||||||
|
LocalDate weekEnd = weekStart.plusDays(6);
|
||||||
|
LambdaQueryWrapper<Lesson> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(Lesson::getTeacherId, teacherId)
|
||||||
|
.eq(Lesson::getTenantId, tenantId)
|
||||||
|
.between(Lesson::getLessonDate, weekStart, weekEnd)
|
||||||
|
.orderByAsc(Lesson::getLessonDate, Lesson::getStartTime);
|
||||||
|
List<Lesson> lessons = lessonMapper.selectList(wrapper);
|
||||||
|
return lessons.stream().map(l -> {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("id", l.getId());
|
||||||
|
map.put("title", l.getTitle());
|
||||||
|
map.put("lessonDate", l.getLessonDate());
|
||||||
|
map.put("startTime", l.getStartTime());
|
||||||
|
map.put("endTime", l.getEndTime());
|
||||||
|
map.put("status", l.getStatus());
|
||||||
|
return map;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
package com.reading.platform.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.reading.platform.common.exception.BusinessException;
|
||||||
|
import com.reading.platform.common.enums.ErrorCode;
|
||||||
|
import com.reading.platform.entity.Theme;
|
||||||
|
import com.reading.platform.mapper.ThemeMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ThemeService {
|
||||||
|
|
||||||
|
private final ThemeMapper themeMapper;
|
||||||
|
|
||||||
|
public List<Theme> getAllThemes(Boolean enabledOnly) {
|
||||||
|
LambdaQueryWrapper<Theme> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (Boolean.TRUE.equals(enabledOnly)) {
|
||||||
|
wrapper.eq(Theme::getIsEnabled, 1);
|
||||||
|
}
|
||||||
|
wrapper.orderByAsc(Theme::getSortOrder);
|
||||||
|
return themeMapper.selectList(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Theme getThemeById(Long id) {
|
||||||
|
Theme theme = themeMapper.selectById(id);
|
||||||
|
if (theme == null) {
|
||||||
|
throw new BusinessException(ErrorCode.DATA_NOT_FOUND, "Theme not found");
|
||||||
|
}
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Theme createTheme(Theme theme) {
|
||||||
|
themeMapper.insert(theme);
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Theme updateTheme(Long id, Theme theme) {
|
||||||
|
getThemeById(id);
|
||||||
|
theme.setId(id);
|
||||||
|
themeMapper.updateById(theme);
|
||||||
|
return themeMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteTheme(Long id) {
|
||||||
|
themeMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,6 +13,16 @@ spring:
|
|||||||
serialization:
|
serialization:
|
||||||
write-dates-as-timestamps: false
|
write-dates-as-timestamps: false
|
||||||
|
|
||||||
|
flyway:
|
||||||
|
enabled: true
|
||||||
|
locations: classpath:db/migration
|
||||||
|
baseline-on-migrate: true
|
||||||
|
|
||||||
|
file:
|
||||||
|
upload:
|
||||||
|
path: ./uploads/
|
||||||
|
base-url: /uploads/
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
com.reading.platform: debug
|
com.reading.platform: debug
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://mysql:3306/reading_platform?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
|
username: root
|
||||||
|
password: reading_platform_pwd
|
||||||
|
jackson:
|
||||||
|
date-format: yyyy-MM-dd HH:mm:ss
|
||||||
|
time-zone: Asia/Shanghai
|
||||||
|
serialization:
|
||||||
|
write-dates-as-timestamps: false
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 300MB
|
||||||
|
max-request-size: 1500MB
|
||||||
|
web:
|
||||||
|
resources:
|
||||||
|
static-locations: file:/app/uploads/,classpath:/static/
|
||||||
|
|
||||||
|
flyway:
|
||||||
|
enabled: true
|
||||||
|
locations: classpath:db/migration
|
||||||
|
baseline-on-migrate: true
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
secret: ${JWT_SECRET:reading-platform-jwt-secret-key-must-be-at-least-256-bits-long}
|
||||||
|
expiration: 604800000
|
||||||
|
|
||||||
|
file:
|
||||||
|
upload:
|
||||||
|
path: /app/uploads/
|
||||||
|
base-url: /uploads/
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.reading.platform: info
|
||||||
@ -6,6 +6,10 @@ spring:
|
|||||||
name: reading-platform
|
name: reading-platform
|
||||||
profiles:
|
profiles:
|
||||||
active: dev
|
active: dev
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 300MB
|
||||||
|
max-request-size: 1500MB
|
||||||
|
|
||||||
# MyBatis-Plus Configuration
|
# MyBatis-Plus Configuration
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
|
|||||||
@ -519,4 +519,4 @@ CREATE INDEX idx_notifications_tenant ON notifications(tenant_id);
|
|||||||
|
|
||||||
-- Insert default admin user (password: admin123)
|
-- Insert default admin user (password: admin123)
|
||||||
INSERT INTO admin_users (username, password, name, status) VALUES
|
INSERT INTO admin_users (username, password, name, status) VALUES
|
||||||
('admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', 'Super Admin', 'active');
|
('admin', '$2b$10$VSes.57X35WZov4LdyJ.eu1pll7qnt7tGuq/u3iXOrYawHTebe.Eu', 'Super Admin', 'active');
|
||||||
|
|||||||
@ -4,75 +4,75 @@
|
|||||||
-- Add new fields to courses table for course package refactoring
|
-- Add new fields to courses table for course package refactoring
|
||||||
|
|
||||||
-- Core content
|
-- Core content
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS core_content TEXT COMMENT 'Core content summary';
|
ALTER TABLE courses ADD COLUMN core_content TEXT COMMENT 'Core content summary';
|
||||||
|
|
||||||
-- Course introduction fields (8 fields)
|
-- Course introduction fields (8 fields)
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS intro_summary TEXT COMMENT 'Course summary';
|
ALTER TABLE courses ADD COLUMN intro_summary TEXT COMMENT 'Course summary';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS intro_highlights TEXT COMMENT 'Course highlights';
|
ALTER TABLE courses ADD COLUMN intro_highlights TEXT COMMENT 'Course highlights';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS intro_goals TEXT COMMENT 'Course goals';
|
ALTER TABLE courses ADD COLUMN intro_goals TEXT COMMENT 'Course goals';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS intro_schedule TEXT COMMENT 'Content schedule';
|
ALTER TABLE courses ADD COLUMN intro_schedule TEXT COMMENT 'Content schedule';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS intro_key_points TEXT COMMENT 'Key points and difficulties';
|
ALTER TABLE courses ADD COLUMN intro_key_points TEXT COMMENT 'Key points and difficulties';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS intro_methods TEXT COMMENT 'Teaching methods';
|
ALTER TABLE courses ADD COLUMN intro_methods TEXT COMMENT 'Teaching methods';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS intro_evaluation TEXT COMMENT 'Evaluation methods';
|
ALTER TABLE courses ADD COLUMN intro_evaluation TEXT COMMENT 'Evaluation methods';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS intro_notes TEXT COMMENT 'Notes and precautions';
|
ALTER TABLE courses ADD COLUMN intro_notes TEXT COMMENT 'Notes and precautions';
|
||||||
|
|
||||||
-- Schedule reference data
|
-- Schedule reference data
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS schedule_ref_data TEXT COMMENT 'Schedule reference data (JSON)';
|
ALTER TABLE courses ADD COLUMN schedule_ref_data TEXT COMMENT 'Schedule reference data (JSON)';
|
||||||
|
|
||||||
-- Environment construction (Step 7)
|
-- Environment construction (Step 7)
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS environment_construction TEXT COMMENT 'Environment construction content';
|
ALTER TABLE courses ADD COLUMN environment_construction TEXT COMMENT 'Environment construction content';
|
||||||
|
|
||||||
-- Theme and picture book relation
|
-- Theme and picture book relation
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS theme_id BIGINT COMMENT 'Theme ID';
|
ALTER TABLE courses ADD COLUMN theme_id BIGINT COMMENT 'Theme ID';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS picture_book_name VARCHAR(200) COMMENT 'Picture book name';
|
ALTER TABLE courses ADD COLUMN picture_book_name VARCHAR(200) COMMENT 'Picture book name';
|
||||||
|
|
||||||
-- Cover image path
|
-- Cover image path
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS cover_image_path VARCHAR(500) COMMENT 'Cover image file path';
|
ALTER TABLE courses ADD COLUMN cover_image_path VARCHAR(500) COMMENT 'Cover image file path';
|
||||||
|
|
||||||
-- Digital resources (JSON arrays)
|
-- Digital resources (JSON arrays)
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS ebook_paths TEXT COMMENT 'Ebook paths (JSON array)';
|
ALTER TABLE courses ADD COLUMN ebook_paths TEXT COMMENT 'Ebook paths (JSON array)';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS audio_paths TEXT COMMENT 'Audio paths (JSON array)';
|
ALTER TABLE courses ADD COLUMN audio_paths TEXT COMMENT 'Audio paths (JSON array)';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS video_paths TEXT COMMENT 'Video paths (JSON array)';
|
ALTER TABLE courses ADD COLUMN video_paths TEXT COMMENT 'Video paths (JSON array)';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS other_resources TEXT COMMENT 'Other resources (JSON array)';
|
ALTER TABLE courses ADD COLUMN other_resources TEXT COMMENT 'Other resources (JSON array)';
|
||||||
|
|
||||||
-- Teaching materials
|
-- Teaching materials
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS ppt_path VARCHAR(500) COMMENT 'PPT file path';
|
ALTER TABLE courses ADD COLUMN ppt_path VARCHAR(500) COMMENT 'PPT file path';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS ppt_name VARCHAR(200) COMMENT 'PPT file name';
|
ALTER TABLE courses ADD COLUMN ppt_name VARCHAR(200) COMMENT 'PPT file name';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS poster_paths TEXT COMMENT 'Poster paths (JSON array)';
|
ALTER TABLE courses ADD COLUMN poster_paths TEXT COMMENT 'Poster paths (JSON array)';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS tools TEXT COMMENT 'Teaching tools (JSON array)';
|
ALTER TABLE courses ADD COLUMN tools TEXT COMMENT 'Teaching tools (JSON array)';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS student_materials TEXT COMMENT 'Student materials';
|
ALTER TABLE courses ADD COLUMN student_materials TEXT COMMENT 'Student materials';
|
||||||
|
|
||||||
-- Lesson plan and activities
|
-- Lesson plan and activities
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS lesson_plan_data TEXT COMMENT 'Lesson plan data (JSON)';
|
ALTER TABLE courses ADD COLUMN lesson_plan_data TEXT COMMENT 'Lesson plan data (JSON)';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS activities_data TEXT COMMENT 'Activities data (JSON)';
|
ALTER TABLE courses ADD COLUMN activities_data TEXT COMMENT 'Activities data (JSON)';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS assessment_data TEXT COMMENT 'Assessment data (JSON)';
|
ALTER TABLE courses ADD COLUMN assessment_data TEXT COMMENT 'Assessment data (JSON)';
|
||||||
|
|
||||||
-- Grade and domain tags
|
-- Grade and domain tags
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS grade_tags TEXT COMMENT 'Grade tags (JSON array)';
|
ALTER TABLE courses ADD COLUMN grade_tags TEXT COMMENT 'Grade tags (JSON array)';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS domain_tags TEXT COMMENT 'Domain tags (JSON array)';
|
ALTER TABLE courses ADD COLUMN domain_tags TEXT COMMENT 'Domain tags (JSON array)';
|
||||||
|
|
||||||
-- Collective lesson flag
|
-- Collective lesson flag
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS has_collective_lesson TINYINT DEFAULT 0 COMMENT 'Has collective lesson';
|
ALTER TABLE courses ADD COLUMN has_collective_lesson TINYINT DEFAULT 0 COMMENT 'Has collective lesson';
|
||||||
|
|
||||||
-- Version and review fields
|
-- Version and review fields
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS version VARCHAR(20) DEFAULT '1.0' COMMENT 'Version number';
|
ALTER TABLE courses ADD COLUMN version VARCHAR(20) DEFAULT '1.0' COMMENT 'Version number';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS parent_id BIGINT COMMENT 'Parent course ID for versions';
|
ALTER TABLE courses ADD COLUMN parent_id BIGINT COMMENT 'Parent course ID for versions';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS is_latest TINYINT DEFAULT 1 COMMENT 'Is latest version';
|
ALTER TABLE courses ADD COLUMN is_latest TINYINT DEFAULT 1 COMMENT 'Is latest version';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS submitted_at DATETIME COMMENT 'Submitted for review at';
|
ALTER TABLE courses ADD COLUMN submitted_at DATETIME COMMENT 'Submitted for review at';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS submitted_by BIGINT COMMENT 'Submitted by user ID';
|
ALTER TABLE courses ADD COLUMN submitted_by BIGINT COMMENT 'Submitted by user ID';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS reviewed_at DATETIME COMMENT 'Reviewed at';
|
ALTER TABLE courses ADD COLUMN reviewed_at DATETIME COMMENT 'Reviewed at';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS reviewed_by BIGINT COMMENT 'Reviewed by user ID';
|
ALTER TABLE courses ADD COLUMN reviewed_by BIGINT COMMENT 'Reviewed by user ID';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS review_comment TEXT COMMENT 'Review comment';
|
ALTER TABLE courses ADD COLUMN review_comment TEXT COMMENT 'Review comment';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS review_checklist TEXT COMMENT 'Review checklist (JSON)';
|
ALTER TABLE courses ADD COLUMN review_checklist TEXT COMMENT 'Review checklist (JSON)';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS published_at DATETIME COMMENT 'Published at';
|
ALTER TABLE courses ADD COLUMN published_at DATETIME COMMENT 'Published at';
|
||||||
|
|
||||||
-- Usage statistics
|
-- Usage statistics
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS usage_count INT DEFAULT 0 COMMENT 'Usage count';
|
ALTER TABLE courses ADD COLUMN usage_count INT DEFAULT 0 COMMENT 'Usage count';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS teacher_count INT DEFAULT 0 COMMENT 'Teacher count';
|
ALTER TABLE courses ADD COLUMN teacher_count INT DEFAULT 0 COMMENT 'Teacher count';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS avg_rating DECIMAL(3,2) DEFAULT 0 COMMENT 'Average rating';
|
ALTER TABLE courses ADD COLUMN avg_rating DECIMAL(3,2) DEFAULT 0 COMMENT 'Average rating';
|
||||||
ALTER TABLE courses ADD COLUMN IF NOT EXISTS created_by BIGINT COMMENT 'Created by user ID';
|
ALTER TABLE courses ADD COLUMN created_by BIGINT COMMENT 'Created by user ID';
|
||||||
|
|
||||||
-- Create indexes for better query performance
|
-- Create indexes for better query performance
|
||||||
CREATE INDEX IF NOT EXISTS idx_courses_theme ON courses(theme_id);
|
CREATE INDEX idx_courses_theme ON courses(theme_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_courses_status ON courses(status);
|
CREATE INDEX idx_courses_status ON courses(status);
|
||||||
CREATE INDEX IF NOT EXISTS idx_courses_parent ON courses(parent_id);
|
CREATE INDEX idx_courses_parent ON courses(parent_id);
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
-- Add themes table
|
||||||
|
CREATE TABLE IF NOT EXISTS themes (
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
name VARCHAR(50) NOT NULL UNIQUE COMMENT 'Theme technical name',
|
||||||
|
display_name VARCHAR(100) NOT NULL COMMENT 'Display name',
|
||||||
|
color VARCHAR(20) COMMENT 'Theme color (hex)',
|
||||||
|
icon VARCHAR(100) COMMENT 'Theme icon',
|
||||||
|
sort_order INT DEFAULT 0 COMMENT 'Sort order',
|
||||||
|
is_enabled TINYINT DEFAULT 1 COMMENT 'Is enabled',
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
deleted TINYINT DEFAULT 0
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Themes';
|
||||||
|
|
||||||
|
-- Insert default themes
|
||||||
|
INSERT INTO themes (name, display_name, color, sort_order, is_enabled) VALUES
|
||||||
|
('nature', '自然探索', '#4CAF50', 1, 1),
|
||||||
|
('ocean', '海洋世界', '#2196F3', 2, 1),
|
||||||
|
('space', '太空冒险', '#9C27B0', 3, 1),
|
||||||
|
('animals', '动物王国', '#FF9800', 4, 1),
|
||||||
|
('fairytale', '童话故事', '#E91E63', 5, 1);
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
-- Add course packages table
|
||||||
|
CREATE TABLE IF NOT EXISTS course_packages (
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
name VARCHAR(200) NOT NULL COMMENT 'Package name',
|
||||||
|
description TEXT COMMENT 'Package description',
|
||||||
|
cover_url VARCHAR(500) COMMENT 'Cover image URL',
|
||||||
|
course_count INT DEFAULT 0 COMMENT 'Number of courses',
|
||||||
|
price DECIMAL(10, 2) DEFAULT 0 COMMENT 'Price',
|
||||||
|
status VARCHAR(20) DEFAULT 'draft' COMMENT 'Status: draft, published, archived',
|
||||||
|
is_system TINYINT DEFAULT 1 COMMENT 'Is system package',
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
deleted TINYINT DEFAULT 0
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Course Packages';
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
-- Add school courses table (tenant-customized courses)
|
||||||
|
CREATE TABLE IF NOT EXISTS school_courses (
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
tenant_id BIGINT NOT NULL COMMENT 'Tenant ID',
|
||||||
|
name VARCHAR(200) NOT NULL COMMENT 'Course name',
|
||||||
|
description TEXT COMMENT 'Course description',
|
||||||
|
cover_url VARCHAR(500) COMMENT 'Cover URL',
|
||||||
|
category VARCHAR(50) COMMENT 'Course category',
|
||||||
|
age_range VARCHAR(50) COMMENT 'Age range',
|
||||||
|
status VARCHAR(20) DEFAULT 'draft' COMMENT 'Status: draft, published, archived',
|
||||||
|
created_by BIGINT COMMENT 'Created by user ID',
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
deleted TINYINT DEFAULT 0,
|
||||||
|
INDEX idx_tenant (tenant_id),
|
||||||
|
INDEX idx_status (status)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='School Custom Courses';
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
-- Add course lessons table (chapters/sections within a course)
|
||||||
|
CREATE TABLE IF NOT EXISTS course_lessons (
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
course_id BIGINT NOT NULL COMMENT 'Course ID',
|
||||||
|
title VARCHAR(200) NOT NULL COMMENT 'Lesson title',
|
||||||
|
description TEXT COMMENT 'Lesson description',
|
||||||
|
content TEXT COMMENT 'Lesson content',
|
||||||
|
sort_order INT DEFAULT 0 COMMENT 'Sort order',
|
||||||
|
duration_minutes INT COMMENT 'Duration in minutes',
|
||||||
|
video_url VARCHAR(1000) COMMENT 'Video URL',
|
||||||
|
status VARCHAR(20) DEFAULT 'draft' COMMENT 'Status: draft, published',
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
deleted TINYINT DEFAULT 0,
|
||||||
|
INDEX idx_course (course_id),
|
||||||
|
INDEX idx_sort (sort_order)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Course Lessons (chapters/sections)';
|
||||||
Loading…
Reference in New Issue
Block a user