98 changed files with 2805 additions and 434 deletions
@ -0,0 +1,171 @@
@@ -0,0 +1,171 @@
|
||||
package apelet.association.controller; |
||||
|
||||
import apelet.association.model.CourseCourseware; |
||||
import apelet.association.model.CourseVideo; |
||||
import apelet.association.service.CourseCoursewareService; |
||||
import apelet.association.service.CourseService; |
||||
import apelet.association.service.CourseVideoService; |
||||
import apelet.common.aliyun.oss.config.AliyunOssProperties; |
||||
import apelet.common.aliyun.oss.wrapper.AliyunOssTemplate; |
||||
|
||||
import apelet.common.core.exception.MyRuntimeException; |
||||
import apelet.common.core.object.*; |
||||
import apelet.common.generator.utils.OrmGenDataSourceUtil; |
||||
import apelet.common.orm.impl.Filter; |
||||
import apelet.common.orm.impl.FilterItem; |
||||
import apelet.common.orm.impl.Selector; |
||||
import cn.hutool.core.date.DateUtil; |
||||
import cn.hutool.core.text.CharSequenceUtil; |
||||
import cn.hutool.core.util.IdUtil; |
||||
import com.alibaba.fastjson.JSON; |
||||
import com.alibaba.fastjson.JSONArray; |
||||
import com.alibaba.fastjson.JSONObject; |
||||
import com.aliyun.oss.OSS; |
||||
import com.aliyun.oss.OSSClient; |
||||
import com.aliyun.oss.OSSClientBuilder; |
||||
import com.aliyun.oss.model.GetObjectRequest; |
||||
import com.aliyun.oss.model.OSSObject; |
||||
import com.aliyun.oss.model.ObjectMetadata; |
||||
import org.redisson.api.RedissonClient; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.web.bind.annotation.*; |
||||
|
||||
import javax.annotation.PostConstruct; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import java.io.*; |
||||
import java.net.URLConnection; |
||||
import java.nio.file.Files; |
||||
import java.nio.file.Path; |
||||
import java.nio.file.Paths; |
||||
import java.util.*; |
||||
|
||||
|
||||
/** |
||||
* 课程管理 |
||||
*/ |
||||
@RestController |
||||
@RequestMapping("/tenantadmin/Course") |
||||
public class CourseController { |
||||
|
||||
|
||||
@Autowired |
||||
private CourseCoursewareService courseCoursewareService; |
||||
@Autowired |
||||
private CourseVideoService courseVideoService; |
||||
@Autowired |
||||
private AliyunOssTemplate aliyunOssTemplate; |
||||
@Autowired |
||||
private OSS ossClient; |
||||
|
||||
|
||||
private static final String VIDEO_MIME = "video/mp4"; |
||||
|
||||
/** |
||||
* 视频断点续播 —— 根据学习进度自动从对应字节偏移量开始流式输出 |
||||
*/ |
||||
@GetMapping("/queryVideoProgress") |
||||
public void queryVideoProgress( |
||||
@RequestParam("videoId") Long videoId, |
||||
@RequestParam("userId") Long userId, |
||||
HttpServletRequest request, |
||||
HttpServletResponse response) throws IOException { |
||||
|
||||
CourseVideo courseVideo = courseVideoService.getById(videoId); |
||||
if (courseVideo == null || courseVideo.getVideoUrl() == null) { |
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "视频不存在"); |
||||
return; |
||||
} |
||||
|
||||
String videoUrl = courseVideo.getVideoUrl(); |
||||
JSONArray jsonArray = JSON.parseArray(videoUrl); |
||||
JSONObject urlObject = jsonArray.getJSONObject(0); |
||||
String filePath = urlObject.getString("uploadPath") + "/" + urlObject.getString("filename"); |
||||
AliyunOssProperties properties = aliyunOssTemplate.getProperties(); |
||||
String bucketName = properties.getBucketName(); |
||||
ObjectMetadata meta = ossClient.getObjectMetadata(bucketName, filePath); |
||||
long totalSize = meta.getContentLength(); |
||||
long start = courseVideoService.getByteOffsetByTime(videoId, userId); |
||||
long end = totalSize - 1; |
||||
String rangeHeader = request.getHeader(HttpHeaders.RANGE); |
||||
if (rangeHeader != null && rangeHeader.startsWith("bytes=")) { |
||||
String rangeVal = rangeHeader.substring(6); |
||||
String[] arr = rangeVal.split("-"); |
||||
if (!CharSequenceUtil.isBlank(arr[0])) { |
||||
start = Long.parseLong(arr[0]); |
||||
} |
||||
if (arr.length > 1 && !CharSequenceUtil.isBlank(arr[1])) { |
||||
end = Long.parseLong(arr[1]); |
||||
} |
||||
} |
||||
start = Math.max(start, 0); |
||||
end = Math.min(end, totalSize - 1); |
||||
if (start >= totalSize) { |
||||
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes */" + totalSize); |
||||
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); |
||||
return; |
||||
} |
||||
long sliceLength = end - start + 1; |
||||
response.setContentType(VIDEO_MIME); |
||||
response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); |
||||
response.setHeader(HttpHeaders.CONTENT_RANGE, |
||||
String.format("bytes %d-%d/%d", start, end, totalSize)); |
||||
response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(sliceLength)); |
||||
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); |
||||
GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, filePath); |
||||
getObjectRequest.setRange(start, end); |
||||
try (OSSObject ossObject = ossClient.getObject(getObjectRequest); |
||||
InputStream is = ossObject.getObjectContent()) { |
||||
byte[] buffer = new byte[8192]; |
||||
int read; |
||||
while ((read = is.read(buffer)) != -1) { |
||||
response.getOutputStream().write(buffer, 0, read); |
||||
} |
||||
response.getOutputStream().flush(); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 保存课程视频学习记录 |
||||
* |
||||
* @return |
||||
*/ |
||||
@PostMapping("/videoLearning") |
||||
public ResponseResult<?> videoLearning(@RequestBody CourseVideo courseVideo) { |
||||
courseVideoService.saveVideoLearning(courseVideo); |
||||
return ResponseResult.success(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 查询课件学习进度 |
||||
* |
||||
* @param coursewareId |
||||
* @param userId |
||||
* @return |
||||
*/ |
||||
@GetMapping("/queryCoursewareProgress") |
||||
public ResponseResult<?> queryCoursewareProgress( |
||||
@RequestParam("coursewareId") Long coursewareId, @RequestParam("userId") Long userId) { |
||||
Object result = courseCoursewareService.getCourseLearningProgress(coursewareId, userId); |
||||
return ResponseResult.success(result); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 课程课件学习记录 |
||||
* |
||||
* @return |
||||
*/ |
||||
@PostMapping("/coursewareLearning") |
||||
public ResponseResult<?> coursewareLearning(@RequestBody CourseCourseware courseCourseware) { |
||||
courseCoursewareService.saveCoursewareLearning(courseCourseware); |
||||
return ResponseResult.success(); |
||||
} |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,183 @@
@@ -0,0 +1,183 @@
|
||||
package apelet.association.controller; |
||||
|
||||
import apelet.association.dto.UserExerciseDto; |
||||
import apelet.association.model.*; |
||||
import apelet.association.service.*; |
||||
import apelet.common.core.exception.MyRuntimeException; |
||||
import apelet.common.core.object.*; |
||||
import apelet.common.generator.utils.OrmGenDataSourceUtil; |
||||
import apelet.common.orm.impl.Filter; |
||||
import apelet.common.orm.impl.FilterItem; |
||||
import apelet.common.orm.impl.Selector; |
||||
import cn.hutool.core.date.DateUtil; |
||||
import cn.hutool.core.text.CharSequenceUtil; |
||||
import cn.hutool.core.util.IdUtil; |
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
import org.redisson.api.RedissonClient; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.web.bind.annotation.*; |
||||
|
||||
import javax.annotation.PostConstruct; |
||||
import java.io.IOException; |
||||
import java.util.*; |
||||
import java.util.stream.Collectors; |
||||
|
||||
@RestController |
||||
@RequestMapping("/tenantadmin/questionBank") |
||||
public class DoPracticeProblemsController { |
||||
|
||||
@Autowired |
||||
private OrmGenDataSourceUtil ormGenDataSourceUtil; |
||||
|
||||
@Autowired |
||||
private ExamService examService; |
||||
|
||||
@Autowired |
||||
private QuestionBankService questionBankService; |
||||
@Autowired |
||||
private QuestionBankEntryService questionBankEntryService; |
||||
|
||||
@Autowired |
||||
private ExamQuestionsService examQuestionsService; |
||||
@Autowired |
||||
private ExamQuestionsEntryService examQuestionsEntryService; |
||||
|
||||
@Autowired |
||||
private RedissonClient redissonClient; |
||||
|
||||
@PostConstruct |
||||
public void checkRedisConfig() throws IOException { |
||||
System.out.println("Redisson config: {}" + redissonClient.getConfig().toYAML()); |
||||
System.out.println("Redisson config: {}" + redissonClient.getConfig().toYAML()); |
||||
System.out.println("Redisson config: {}" + redissonClient.getConfig().toYAML()); |
||||
} |
||||
|
||||
/** |
||||
* 刷题 |
||||
* |
||||
* @param id 题库id |
||||
* @return |
||||
*/ |
||||
@GetMapping("/doPracticeProblems") |
||||
public ResponseResult<?> doPracticeProblems(@RequestParam(name = "id") String id) { |
||||
Map<String, Object> result = new HashMap<>(); |
||||
ObjectValue questionBank = ormGenDataSourceUtil.queryOne("question_bank", id); |
||||
ObjectCollection collection = questionBank.getObjectCollection("question_bank_entry"); |
||||
if (collection == null || collection.isEmpty()) { |
||||
throw new MyRuntimeException("题库下没有试题, 请先添加试题!!!"); |
||||
} |
||||
result.put("questionBankId", id); |
||||
result.put("questionBankName", questionBank.getString("name")); |
||||
List<String> list = new ArrayList<>(); |
||||
|
||||
//保存用户刷题记录
|
||||
long batchNumber = IdUtil.getSnowflakeNextId(); |
||||
result.put("batchNumber", batchNumber); |
||||
|
||||
collection.forEach(f -> { |
||||
ObjectValue examQuestions = f.getObjectValue("exam_questions_id"); |
||||
list.add(examQuestions.getString("id")); |
||||
|
||||
ObjectValue userExercise = new ObjectValue("user_exercise"); |
||||
ObjectValue xySysUser = new ObjectValue("xy_sys_user"); |
||||
xySysUser.put("id", TokenData.takeFromRequest().getUserId()); |
||||
userExercise.put("question_bank_id", questionBank); |
||||
userExercise.put("exam_questions_id", examQuestions); |
||||
userExercise.put("membership_apply_id", xySysUser); |
||||
userExercise.put("batch", batchNumber); |
||||
userExercise.put("is_correct", 0); |
||||
userExercise.put("create_time", DateUtil.now()); |
||||
try { |
||||
ormGenDataSourceUtil.addNew(userExercise.getTableName(), userExercise); |
||||
} catch (Exception e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
}); |
||||
List<Map<String, Object>> examQuestionsList = new ArrayList<>(); |
||||
result.put("examQuestionsList", examQuestionsList); |
||||
|
||||
Filter filter = new Filter(); |
||||
filter.add(new FilterItem("id", FilterItem.INNER, CharSequenceUtil.join(",", list))); |
||||
filter.add(new FilterItem("type", FilterItem.not_equals, 4)); |
||||
ObjectCollection examQuestions = ormGenDataSourceUtil.query("exam_questions", filter, new Selector()); |
||||
int no = 1; |
||||
for (ObjectValue examQuestion : examQuestions) { |
||||
Map<String, Object> hashMap = new HashMap<>(); |
||||
hashMap.put("id", examQuestion.getString("id")); |
||||
hashMap.put("no", no); |
||||
hashMap.put("name", examQuestion.getString("name")); |
||||
hashMap.put("type", examQuestion.getString("type")); |
||||
hashMap.put("title", examQuestion.getString("content")); |
||||
hashMap.put("exam_picture", examQuestion.getString("exam_picture")); |
||||
|
||||
String answer = examQuestion.getString("answer"); |
||||
hashMap.put("standardAnswer", answer.chars().mapToObj(c -> String.valueOf((char) c)).toArray(String[]::new)); |
||||
hashMap.put("standardAnswerText", answer); |
||||
hashMap.put("analysis", examQuestion.getString("analysis")); |
||||
hashMap.put("score", examQuestion.getString("score")); |
||||
hashMap.put("difficulty", examQuestion.getString("difficulty")); |
||||
ObjectCollection examQuestionsEntry = examQuestion.getObjectCollection("exam_questions_entry"); |
||||
List<Map<String, Object>> option = new ArrayList<>(); |
||||
examQuestionsEntry.forEach(f -> option.add(f.getValues())); |
||||
hashMap.put("options", option); |
||||
examQuestionsList.add(hashMap); |
||||
no++; |
||||
} |
||||
return ResponseResult.success(result); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 刷题 提交答案 |
||||
* |
||||
* @return |
||||
*/ |
||||
@PostMapping("/submitPractice") |
||||
public ResponseResult<?> submitPractice(@RequestBody UserExerciseDto userExerciseDto) { |
||||
|
||||
return ResponseResult.success(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 考试 |
||||
* |
||||
* @param id 考试 id |
||||
* @return |
||||
*/ |
||||
@GetMapping("/doExam") |
||||
public ResponseResult<?> doExam(@RequestParam(name = "id") String id) { |
||||
Map<String, Object> result = new HashMap<>(); |
||||
Exam exam = examService.getById(id); |
||||
result.put("exam", exam); |
||||
Long questionBankId = exam.getQuestionBankId(); |
||||
// 题库
|
||||
List<QuestionBankEntry> questionBankEntryList = questionBankEntryService.list(new LambdaQueryWrapper<QuestionBankEntry>().eq( |
||||
QuestionBankEntry::getParentId, questionBankId)); |
||||
List<Long> collect = questionBankEntryList.stream().map(QuestionBankEntry::getExamQuestionsId).collect(Collectors.toList()); |
||||
// 题库试题
|
||||
List<ExamQuestions> examQuestionsList = examQuestionsService.list(new LambdaQueryWrapper<ExamQuestions>().in(ExamQuestions::getId, collect)); |
||||
// 试题选项
|
||||
List<ExamQuestionsEntry> examQuestionsEntries = examQuestionsEntryService.list(new LambdaQueryWrapper<ExamQuestionsEntry>().in(ExamQuestionsEntry::getParentId, collect)); |
||||
|
||||
examQuestionsList.forEach(examQuestions -> { |
||||
examQuestions.setExamQuestionsEntryList(examQuestionsEntries.stream().filter(f -> f.getParentId().equals(examQuestions.getId())).collect(Collectors.toList())); |
||||
}); |
||||
result.put("examQuestionsList", examQuestionsList); |
||||
return ResponseResult.success(result); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 考试 提交答案 |
||||
* |
||||
* @return |
||||
*/ |
||||
@PostMapping("/submitExam") |
||||
public ResponseResult<?> submitExam(@RequestBody UserExerciseDto userExerciseDto) { |
||||
|
||||
return ResponseResult.success(); |
||||
} |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
package apelet.association.dao; |
||||
|
||||
import apelet.association.model.Course; |
||||
import apelet.association.model.CourseCourseware; |
||||
import apelet.common.core.base.dao.BaseDaoMapper; |
||||
|
||||
public interface CourseCoursewareMapper extends BaseDaoMapper<CourseCourseware> { |
||||
} |
||||
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
package apelet.association.dao; |
||||
|
||||
import apelet.association.model.Course; |
||||
import apelet.common.core.base.dao.BaseDaoMapper; |
||||
|
||||
public interface CourseMapper extends BaseDaoMapper<Course> { |
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
package apelet.association.dao; |
||||
|
||||
import apelet.association.model.Course; |
||||
import apelet.association.model.CourseVideo; |
||||
import apelet.common.core.base.dao.BaseDaoMapper; |
||||
|
||||
public interface CourseVideoMapper extends BaseDaoMapper<CourseVideo> { |
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
package apelet.association.dao; |
||||
|
||||
import apelet.association.model.Course; |
||||
import apelet.association.model.Exam; |
||||
import apelet.common.core.base.dao.BaseDaoMapper; |
||||
|
||||
public interface ExamMapper extends BaseDaoMapper<Exam> { |
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
package apelet.association.dao; |
||||
|
||||
import apelet.association.model.ExamQuestionsEntry; |
||||
import apelet.common.core.base.dao.BaseDaoMapper; |
||||
|
||||
public interface ExamQuestionsEntryMapper extends BaseDaoMapper<ExamQuestionsEntry> { |
||||
|
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
package apelet.association.dao; |
||||
|
||||
import apelet.association.model.ExamQuestions; |
||||
import apelet.common.core.base.dao.BaseDaoMapper; |
||||
|
||||
public interface ExamQuestionsMapper extends BaseDaoMapper<ExamQuestions> { |
||||
|
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
package apelet.association.dao; |
||||
|
||||
import apelet.association.model.QuestionBankEntry; |
||||
import apelet.common.core.base.dao.BaseDaoMapper; |
||||
|
||||
public interface QuestionBankEntryMapper extends BaseDaoMapper<QuestionBankEntry> { |
||||
|
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
package apelet.association.dao; |
||||
|
||||
import apelet.association.model.QuestionBank; |
||||
import apelet.common.core.base.dao.BaseDaoMapper; |
||||
|
||||
public interface QuestionBankMapper extends BaseDaoMapper<QuestionBank> { |
||||
|
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
package apelet.association.dao; |
||||
|
||||
import apelet.association.model.UserCourseProgress; |
||||
import apelet.common.core.base.dao.BaseDaoMapper; |
||||
|
||||
public interface UserCourseProgressMapper extends BaseDaoMapper<UserCourseProgress> { |
||||
|
||||
} |
||||
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
package apelet.association.dao; |
||||
|
||||
import apelet.association.model.UserExercise; |
||||
import apelet.common.core.base.dao.BaseDaoMapper; |
||||
|
||||
public interface UserExerciseMapper extends BaseDaoMapper<UserExercise> { |
||||
|
||||
} |
||||
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
package apelet.association.dto; |
||||
|
||||
import apelet.association.model.QuestionBank; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
public class QuestionBankDto extends QuestionBank implements Serializable { |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
package apelet.association.dto; |
||||
|
||||
import apelet.association.model.UserExercise; |
||||
import lombok.Data; |
||||
import org.flowable.spring.security.UserDto; |
||||
|
||||
import java.util.List; |
||||
|
||||
@Data |
||||
public class UserExerciseDto { |
||||
|
||||
// 考试id
|
||||
Long examId; |
||||
|
||||
// 题库id
|
||||
Long questionBankId; |
||||
|
||||
// 答题用户id
|
||||
Long userId; |
||||
|
||||
//答题记录
|
||||
List<UserExercise> userExerciseList; |
||||
|
||||
} |
||||
@ -0,0 +1,224 @@
@@ -0,0 +1,224 @@
|
||||
package apelet.association.model; // 请根据你的实际包名修改
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import com.baomidou.mybatisplus.annotation.IdType; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
|
||||
/** |
||||
* 课程表 |
||||
*/ |
||||
@Data |
||||
@TableName("course") |
||||
public class Course implements Serializable { |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
|
||||
/** |
||||
* 主键ID |
||||
*/ |
||||
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
private Long id; |
||||
|
||||
/** |
||||
* 创建人登录名称 |
||||
*/ |
||||
@TableField("create_user_id") |
||||
private String createUserId; |
||||
|
||||
/** |
||||
* 创建日期 |
||||
*/ |
||||
@TableField("create_time") |
||||
private Date createTime; |
||||
|
||||
/** |
||||
* 更新人登录名称 |
||||
*/ |
||||
@TableField("update_user_id") |
||||
private String updateUserId; |
||||
|
||||
/** |
||||
* 更新日期 |
||||
*/ |
||||
@TableField("update_time") |
||||
private Date updateTime; |
||||
|
||||
/** |
||||
* 逻辑删除标记(1: 正常 -1: 已删除) |
||||
*/ |
||||
@TableField("deleted_flag") |
||||
private Integer deletedFlag; |
||||
|
||||
/** |
||||
* 编码 |
||||
*/ |
||||
@TableField("number") |
||||
private String number; |
||||
|
||||
/** |
||||
* 名称 |
||||
*/ |
||||
@TableField("name") |
||||
private String name; |
||||
|
||||
/** |
||||
* 流程状态 |
||||
*/ |
||||
@TableField("flow_status") |
||||
private Integer flowStatus; |
||||
|
||||
/** |
||||
* 流程最新状态 |
||||
*/ |
||||
@TableField("flow_approval_status") |
||||
private Integer flowApprovalStatus; |
||||
|
||||
/** |
||||
* 流程实例id |
||||
*/ |
||||
@TableField("pro_ins_id") |
||||
private String proInsId; |
||||
|
||||
/** |
||||
* 状态 |
||||
*/ |
||||
@TableField("billstatus") |
||||
private String billstatus; |
||||
|
||||
/** |
||||
* 公司 |
||||
*/ |
||||
@TableField("org") |
||||
private Integer org; |
||||
|
||||
/** |
||||
* 来源单据ID |
||||
*/ |
||||
@TableField("srcbillid") |
||||
private Integer srcbillid; |
||||
|
||||
/** |
||||
* 来源单据编码 |
||||
*/ |
||||
@TableField("srcbillnumber") |
||||
private Integer srcbillnumber; |
||||
|
||||
/** |
||||
* 来源分录Id |
||||
*/ |
||||
@TableField("srcentryid") |
||||
private Integer srcentryid; |
||||
|
||||
/** |
||||
* 课程详细描述 |
||||
*/ |
||||
@TableField("course_desc") |
||||
private String courseDesc; |
||||
|
||||
/** |
||||
* 课程封面图 |
||||
*/ |
||||
@TableField("course_cover") |
||||
private String courseCover; |
||||
|
||||
/** |
||||
* 一级专业分类 |
||||
*/ |
||||
@TableField("first_profession") |
||||
private String firstProfession; |
||||
|
||||
/** |
||||
* 二级专业细分分类 |
||||
*/ |
||||
@TableField("second_profession") |
||||
private String secondProfession; |
||||
|
||||
/** |
||||
* 授课讲师ID集合 |
||||
*/ |
||||
@TableField("teacher_ids") |
||||
private String teacherIds; |
||||
|
||||
/** |
||||
* 课程搜索关键字 |
||||
*/ |
||||
@TableField("keywords") |
||||
private String keywords; |
||||
|
||||
/** |
||||
* 课程启用状态 |
||||
*/ |
||||
@TableField("is_enabled") |
||||
private Integer isEnabled; |
||||
|
||||
/** |
||||
* 学习人脸抓拍核验开关 |
||||
*/ |
||||
@TableField("face_capture") |
||||
private Integer faceCapture; |
||||
|
||||
/** |
||||
* 人脸抓拍时间间隔 |
||||
*/ |
||||
@TableField("capture_interval") |
||||
private Integer captureInterval; |
||||
|
||||
/** |
||||
* 人脸核验连续失败次数上限 |
||||
*/ |
||||
@TableField("capture_times") |
||||
private Integer captureTimes; |
||||
|
||||
/** |
||||
* 课程课件权限范围 |
||||
*/ |
||||
@TableField("visible_scope") |
||||
private Integer visibleScope; |
||||
|
||||
/** |
||||
* 指定可见部门ID/人员ID集合(逗号分隔) |
||||
*/ |
||||
@TableField("scope_ids") |
||||
private String scopeIds; |
||||
|
||||
/** |
||||
* 课程学习有效期类型 |
||||
*/ |
||||
@TableField("validity_type") |
||||
private Integer validityType; |
||||
|
||||
/** |
||||
* 课程自定义学习开始时间 |
||||
*/ |
||||
@TableField("start_time") |
||||
private Date startTime; |
||||
|
||||
/** |
||||
* 课程自定义学习结束时间 |
||||
*/ |
||||
@TableField("end_time") |
||||
private Date endTime; |
||||
|
||||
/** |
||||
* 课程结业完成规则 |
||||
*/ |
||||
@TableField("completion_rule") |
||||
private String completionRule; |
||||
|
||||
/** |
||||
* 课程发布状态 |
||||
*/ |
||||
@TableField("status") |
||||
private Integer status; |
||||
|
||||
/** |
||||
* 课程全程 |
||||
*/ |
||||
@TableField("course_name") |
||||
private String courseName; |
||||
} |
||||
@ -0,0 +1,131 @@
@@ -0,0 +1,131 @@
|
||||
package apelet.association.model; |
||||
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*; |
||||
import lombok.Data; |
||||
|
||||
import java.util.Date; |
||||
|
||||
/** |
||||
* 课程课件表 |
||||
*/ |
||||
@Data |
||||
@TableName("course_courseware") |
||||
public class CourseCourseware { |
||||
|
||||
/** |
||||
* 主键ID |
||||
*/ |
||||
@TableId(type = IdType.ASSIGN_ID) |
||||
private Long id; |
||||
|
||||
/** |
||||
* 创建人登录名称 |
||||
*/ |
||||
@TableField("create_user_id") |
||||
private String createUserId; |
||||
|
||||
/** |
||||
* 创建日期 |
||||
*/ |
||||
@TableField("create_time") |
||||
private Date createTime; |
||||
|
||||
/** |
||||
* 更新人登录名称 |
||||
*/ |
||||
@TableField("update_user_id") |
||||
private String updateUserId; |
||||
|
||||
/** |
||||
* 更新日期 |
||||
*/ |
||||
@TableField("update_time") |
||||
private Date updateTime; |
||||
|
||||
/** |
||||
* 逻辑删除标记(1: 正常 -1: 已删除) |
||||
*/ |
||||
@TableField("deleted_flag") |
||||
private Integer deletedFlag; |
||||
|
||||
/** |
||||
* 关联所属课程ID外键 |
||||
*/ |
||||
@TableField("course_id") |
||||
private Long courseId; |
||||
|
||||
/** |
||||
* 编码 |
||||
*/ |
||||
@TableField("number") |
||||
private String number; |
||||
|
||||
/** |
||||
* 名称 |
||||
*/ |
||||
@TableField("name") |
||||
private String name; |
||||
|
||||
/** |
||||
* 父表id |
||||
*/ |
||||
@TableField("parent_id") |
||||
private Long parentId; |
||||
|
||||
/** |
||||
* 课件名称 |
||||
*/ |
||||
@TableField("courseware_name") |
||||
private String coursewareName; |
||||
|
||||
/** |
||||
* 课件文件类型 |
||||
*/ |
||||
@TableField("file_type") |
||||
private Integer fileType; |
||||
|
||||
/** |
||||
* 课件阿里云OSS存储预览下载地址 |
||||
*/ |
||||
@TableField("file_url") |
||||
private String fileUrl; |
||||
|
||||
/** |
||||
* 课件总页数 |
||||
*/ |
||||
@TableField("page_count") |
||||
private Integer pageCount; |
||||
|
||||
/** |
||||
* 课件排序序号 |
||||
*/ |
||||
@TableField("sort") |
||||
private Integer sort; |
||||
|
||||
/** |
||||
* PPT顺序学习锁定开关 |
||||
*/ |
||||
@TableField("is_seq_lock") |
||||
private Integer isSeqLock; |
||||
|
||||
/** |
||||
* 课件每页最低停留学习时长 |
||||
*/ |
||||
@TableField("min_stay_time") |
||||
private Integer minStayTime; |
||||
|
||||
|
||||
/** |
||||
* 用户ID |
||||
*/ |
||||
@TableField(exist = false) |
||||
private Long userId; |
||||
|
||||
/** |
||||
* 当前进度 |
||||
*/ |
||||
@TableField(exist = false) |
||||
private String progress; |
||||
|
||||
} |
||||
@ -0,0 +1,180 @@
@@ -0,0 +1,180 @@
|
||||
package apelet.association.model; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import lombok.Data; |
||||
import java.time.LocalDateTime; |
||||
|
||||
/** |
||||
* 课程视频表 |
||||
*/ |
||||
@Data |
||||
@TableName("course_video") |
||||
public class CourseVideo { |
||||
|
||||
/** |
||||
* 主键ID |
||||
*/ |
||||
@TableId(type = IdType.ASSIGN_ID) |
||||
private Long id; |
||||
|
||||
/** |
||||
* 创建人登录名称 |
||||
*/ |
||||
@TableField("create_user_id") |
||||
private String createUserId; |
||||
|
||||
/** |
||||
* 创建日期 |
||||
*/ |
||||
@TableField("create_time") |
||||
private LocalDateTime createTime; |
||||
|
||||
/** |
||||
* 更新人登录名称 |
||||
*/ |
||||
@TableField("update_user_id") |
||||
private String updateUserId; |
||||
|
||||
/** |
||||
* 更新日期 |
||||
*/ |
||||
@TableField("update_time") |
||||
private LocalDateTime updateTime; |
||||
|
||||
/** |
||||
* 逻辑删除标记(1: 正常 -1: 已删除) |
||||
*/ |
||||
@TableField("deleted_flag") |
||||
private Integer deletedFlag; |
||||
|
||||
/** |
||||
* 编码 |
||||
*/ |
||||
@TableField("number") |
||||
private String number; |
||||
|
||||
/** |
||||
* 名称 |
||||
*/ |
||||
@TableField("name") |
||||
private String name; |
||||
|
||||
/** |
||||
* 父表id |
||||
*/ |
||||
@TableField("parent_id") |
||||
private Long parentId; |
||||
|
||||
/** |
||||
* 课程表id |
||||
*/ |
||||
@TableField("course_id") |
||||
private Long courseId; |
||||
|
||||
/** |
||||
* 视频章节名 |
||||
*/ |
||||
@TableField("video_name") |
||||
private String videoName; |
||||
|
||||
/** |
||||
* 视频内容关键字 |
||||
*/ |
||||
@TableField("video_keywords") |
||||
private String videoKeywords; |
||||
|
||||
/** |
||||
* 视频上传管理员用户ID |
||||
*/ |
||||
@TableField("author_id") |
||||
private Long authorId; |
||||
|
||||
/** |
||||
* 视频阿里云OSS存储播放地址 |
||||
*/ |
||||
@TableField("video_url") |
||||
private String videoUrl; |
||||
|
||||
/** |
||||
* 视频章节封面图OSS地址 |
||||
*/ |
||||
@TableField("video_cover") |
||||
private String videoCover; |
||||
|
||||
/** |
||||
* 视频总长 |
||||
*/ |
||||
@TableField("video_duration") |
||||
private Integer videoDuration; |
||||
|
||||
/** |
||||
* 视频章节排序序号 |
||||
*/ |
||||
@TableField("sort") |
||||
private Integer sort; |
||||
|
||||
/** |
||||
* 流程状态 |
||||
*/ |
||||
@TableField("flow_status") |
||||
private Integer flowStatus; |
||||
|
||||
/** |
||||
* 流程最新状态 |
||||
*/ |
||||
@TableField("flow_approval_status") |
||||
private Integer flowApprovalStatus; |
||||
|
||||
/** |
||||
* 流程实例id |
||||
*/ |
||||
@TableField("pro_ins_id") |
||||
private String proInsId; |
||||
|
||||
/** |
||||
* 状态 |
||||
*/ |
||||
@TableField("billstatus") |
||||
private String billstatus; |
||||
|
||||
/** |
||||
* 公司 |
||||
*/ |
||||
@TableField("org") |
||||
private Integer org; |
||||
|
||||
/** |
||||
* 来源单据ID |
||||
*/ |
||||
@TableField("srcbillid") |
||||
private Integer srcbillid; |
||||
|
||||
/** |
||||
* 来源单据编码 |
||||
*/ |
||||
@TableField("srcbillnumber") |
||||
private Integer srcbillnumber; |
||||
|
||||
/** |
||||
* 来源分录Id |
||||
*/ |
||||
@TableField("srcentryid") |
||||
private Integer srcentryid; |
||||
|
||||
|
||||
/** |
||||
* 用户ID |
||||
*/ |
||||
@TableField(exist = false) |
||||
private Long userId; |
||||
|
||||
/** |
||||
* 当前进度 |
||||
*/ |
||||
@TableField(exist = false) |
||||
private String progress; |
||||
|
||||
} |
||||
@ -0,0 +1,175 @@
@@ -0,0 +1,175 @@
|
||||
package apelet.association.model; |
||||
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import lombok.Data; |
||||
import java.time.LocalDateTime; |
||||
|
||||
/** |
||||
* 考试实体 |
||||
*/ |
||||
@Data |
||||
@TableName("exam") |
||||
public class Exam { |
||||
|
||||
/** |
||||
* 主键id |
||||
*/ |
||||
@TableId(type = IdType.ASSIGN_ID) |
||||
@TableField("id") |
||||
private Long id; |
||||
|
||||
/** |
||||
* 创建人登录名称 |
||||
*/ |
||||
@TableField("create_user_id") |
||||
private String createUserId; |
||||
|
||||
/** |
||||
* 创建日期 |
||||
*/ |
||||
@TableField("create_time") |
||||
private LocalDateTime createTime; |
||||
|
||||
/** |
||||
* 更新人登录名称 |
||||
*/ |
||||
@TableField("update_user_id") |
||||
private String updateUserId; |
||||
|
||||
/** |
||||
* 更新日期 |
||||
*/ |
||||
@TableField("update_time") |
||||
private LocalDateTime updateTime; |
||||
|
||||
/** |
||||
* 逻辑删除标记(1: 正常 -1: 已删除) |
||||
*/ |
||||
@TableField("deleted_flag") |
||||
private Integer deletedFlag; |
||||
|
||||
/** |
||||
* 编码 |
||||
*/ |
||||
@TableField("number") |
||||
private String number; |
||||
|
||||
/** |
||||
* 名称 |
||||
*/ |
||||
@TableField("name") |
||||
private String name; |
||||
|
||||
/** |
||||
* 流程状态 |
||||
*/ |
||||
@TableField("flow_status") |
||||
private Integer flowStatus; |
||||
|
||||
/** |
||||
* 流程最新状态 |
||||
*/ |
||||
@TableField("flow_approval_status") |
||||
private Integer flowApprovalStatus; |
||||
|
||||
/** |
||||
* 流程实例id |
||||
*/ |
||||
@TableField("pro_ins_id") |
||||
private String proInsId; |
||||
|
||||
/** |
||||
* 状态 |
||||
*/ |
||||
@TableField("billstatus") |
||||
private String billstatus; |
||||
|
||||
/** |
||||
* 公司 |
||||
*/ |
||||
@TableField("org") |
||||
private Integer org; |
||||
|
||||
/** |
||||
* 来源单据ID |
||||
*/ |
||||
@TableField("srcbillid") |
||||
private Integer srcbillid; |
||||
|
||||
/** |
||||
* 来源单据编码 |
||||
* 注意:数据库字段是int类型,不适合存储长编码,业务注意! |
||||
*/ |
||||
@TableField("srcbillnumber") |
||||
private Integer srcbillnumber; |
||||
|
||||
/** |
||||
* 来源分录Id |
||||
*/ |
||||
@TableField("srcentryid") |
||||
private Integer srcentryid; |
||||
|
||||
/** |
||||
* 考试分类 |
||||
*/ |
||||
@TableField("type") |
||||
private Integer type; |
||||
|
||||
/** |
||||
* 满分 |
||||
*/ |
||||
@TableField("full_marks") |
||||
private Integer fullMarks; |
||||
|
||||
/** |
||||
* 及格分 |
||||
*/ |
||||
@TableField("passing_score") |
||||
private Integer passingScore; |
||||
|
||||
/** |
||||
* 开始时间 |
||||
*/ |
||||
@TableField("start_time") |
||||
private LocalDateTime startTime; |
||||
|
||||
/** |
||||
* 结束时间 |
||||
*/ |
||||
@TableField("end_time") |
||||
private LocalDateTime endTime; |
||||
|
||||
/** |
||||
* 时长 |
||||
*/ |
||||
@TableField("duration") |
||||
private Integer duration; |
||||
|
||||
/** |
||||
* 公布考试结果 |
||||
*/ |
||||
@TableField("is_announce_result") |
||||
private Integer isAnnounceResult; |
||||
|
||||
/** |
||||
* 公布考试答案 |
||||
*/ |
||||
@TableField("is_announce_answer") |
||||
private Integer isAnnounceAnswer; |
||||
|
||||
/** |
||||
* 题库 |
||||
*/ |
||||
@TableField("question_bank_id") |
||||
private Long questionBankId; |
||||
|
||||
/** |
||||
* 状态 |
||||
*/ |
||||
@TableField("status") |
||||
private Integer status; |
||||
} |
||||
@ -0,0 +1,81 @@
@@ -0,0 +1,81 @@
|
||||
package apelet.association.model; |
||||
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*; |
||||
import lombok.Data; |
||||
|
||||
import java.util.Date; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 试题 |
||||
*/ |
||||
@Data |
||||
@TableName("exam_questions") |
||||
public class ExamQuestions { |
||||
|
||||
/** 主键ID */ |
||||
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
private Long id; |
||||
|
||||
/** 创建人ID */ |
||||
@TableField(value = "create_user_id") |
||||
private String createUserId; |
||||
|
||||
/** 创建时间 */ |
||||
@TableField(value = "create_time") |
||||
private Date createTime; |
||||
|
||||
/** 更新人ID */ |
||||
@TableField(value = "update_user_id") |
||||
private String updateUserId; |
||||
|
||||
/** 更新时间 */ |
||||
@TableField(value = "update_time") |
||||
private Date updateTime; |
||||
|
||||
/** 逻辑删除标记(0未删除 1已删除) */ |
||||
@TableField(value = "deleted_flag") |
||||
private Integer deletedFlag; |
||||
|
||||
/** 编号 */ |
||||
@TableField(value = "number") |
||||
private String number; |
||||
|
||||
/** 试题名称 */ |
||||
@TableField(value = "name") |
||||
private String name; |
||||
|
||||
/** 试题类型(1单选题 2多选题 3判断题 4填空题 5简答题) */ |
||||
@TableField(value = "type") |
||||
private Integer type; |
||||
|
||||
/** 试题图片 */ |
||||
@TableField(value = "exam_picture") |
||||
private String examPicture; |
||||
|
||||
/** 答案 */ |
||||
@TableField(value = "answer") |
||||
private String answer; |
||||
|
||||
/** 试题解析 */ |
||||
@TableField(value = "analysis") |
||||
private String analysis; |
||||
|
||||
/** 分数 */ |
||||
@TableField(value = "score") |
||||
private Double score; |
||||
|
||||
/** 难度(1简单 2中等 3困难) */ |
||||
@TableField(value = "difficulty") |
||||
private Integer difficulty; |
||||
|
||||
/** 启用状态(0禁用 1启用) */ |
||||
@TableField(value = "enabled") |
||||
private Integer enabled; |
||||
|
||||
|
||||
@TableField(exist = false) |
||||
private List<ExamQuestionsEntry> examQuestionsEntryList; |
||||
|
||||
} |
||||
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
package apelet.association.model; |
||||
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*; |
||||
import lombok.Data; |
||||
|
||||
import java.util.Date; |
||||
|
||||
/** |
||||
* 试题选项 |
||||
*/ |
||||
@Data |
||||
@TableName("exam_questions_entry") |
||||
public class ExamQuestionsEntry { |
||||
|
||||
/** 主键ID */ |
||||
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
private Long id; |
||||
|
||||
/** 创建人ID */ |
||||
@TableField(value = "create_user_id") |
||||
private String createUserId; |
||||
|
||||
/** 创建时间 */ |
||||
@TableField(value = "create_time") |
||||
private Date createTime; |
||||
|
||||
/** 更新人ID */ |
||||
@TableField(value = "update_user_id") |
||||
private String updateUserId; |
||||
|
||||
/** 更新时间 */ |
||||
@TableField(value = "update_time") |
||||
private Date updateTime; |
||||
|
||||
/** 逻辑删除标记(0未删除 1已删除) */ |
||||
@TableField(value = "deleted_flag") |
||||
private Integer deletedFlag; |
||||
|
||||
/** 选项编号(如A、B、C、D) */ |
||||
@TableField(value = "number") |
||||
private String number; |
||||
|
||||
/** 选项内容 */ |
||||
@TableField(value = "name") |
||||
private String name; |
||||
|
||||
/** 所属试题ID */ |
||||
@TableField(value = "parent_id") |
||||
private Long parentId; |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
package apelet.association.model; |
||||
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*; |
||||
import lombok.Data; |
||||
|
||||
import java.util.Date; |
||||
|
||||
/** |
||||
* 题库 |
||||
*/ |
||||
@Data |
||||
@TableName("question_bank") |
||||
public class QuestionBank { |
||||
|
||||
/** 主键ID */ |
||||
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
private Long id; |
||||
|
||||
/** 创建人ID */ |
||||
@TableField(value = "create_user_id") |
||||
private String createUserId; |
||||
|
||||
/** 创建时间 */ |
||||
@TableField(value = "create_time") |
||||
private Date createTime; |
||||
|
||||
/** 更新人ID */ |
||||
@TableField(value = "update_user_id") |
||||
private String updateUserId; |
||||
|
||||
/** 更新时间 */ |
||||
@TableField(value = "update_time") |
||||
private Date updateTime; |
||||
|
||||
/** 逻辑删除标记(0未删除 1已删除) */ |
||||
@TableField(value = "deleted_flag") |
||||
private Integer deletedFlag; |
||||
|
||||
/** 编号 */ |
||||
@TableField(value = "number") |
||||
private String number; |
||||
|
||||
/** 题库名称 */ |
||||
@TableField(value = "name") |
||||
private String name; |
||||
|
||||
/** 一级专业/行业 */ |
||||
@TableField(value = "first_profession") |
||||
private String firstProfession; |
||||
|
||||
/** 二级专业/行业 */ |
||||
@TableField(value = "second_profession") |
||||
private String secondProfession; |
||||
|
||||
/** 题库说明 */ |
||||
@TableField(value = "description") |
||||
private String description; |
||||
|
||||
/** 启用状态(0禁用 1启用) */ |
||||
@TableField(value = "enabled") |
||||
private Integer enabled; |
||||
|
||||
/** 可见范围(1全部可见 2指定可见) */ |
||||
@TableField(value = "scope") |
||||
private String scope; |
||||
|
||||
/** 试题数量 */ |
||||
@TableField(value = "count") |
||||
private Integer count; |
||||
|
||||
/** 可见范围ID列表(逗号分隔的部门/用户ID) */ |
||||
@TableField(value = "scope_ids") |
||||
private String scopeIds; |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
package apelet.association.model; |
||||
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*; |
||||
import lombok.Data; |
||||
|
||||
import java.util.Date; |
||||
|
||||
/** |
||||
* 题库试题分录 |
||||
*/ |
||||
@Data |
||||
@TableName("question_bank_entry") |
||||
public class QuestionBankEntry { |
||||
|
||||
/** 主键ID */ |
||||
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
private Long id; |
||||
|
||||
/** 创建人ID */ |
||||
@TableField(value = "create_user_id") |
||||
private String createUserId; |
||||
|
||||
/** 创建时间 */ |
||||
@TableField(value = "create_time") |
||||
private Date createTime; |
||||
|
||||
/** 更新人ID */ |
||||
@TableField(value = "update_user_id") |
||||
private String updateUserId; |
||||
|
||||
/** 更新时间 */ |
||||
@TableField(value = "update_time") |
||||
private Date updateTime; |
||||
|
||||
/** 逻辑删除标记(0未删除 1已删除) */ |
||||
@TableField(value = "deleted_flag") |
||||
private Integer deletedFlag; |
||||
|
||||
/** 编号 */ |
||||
@TableField(value = "number") |
||||
private String number; |
||||
|
||||
/** 名称 */ |
||||
@TableField(value = "name") |
||||
private String name; |
||||
|
||||
/** 所属题库ID */ |
||||
@TableField(value = "parent_id") |
||||
private Long parentId; |
||||
|
||||
/** 试题ID */ |
||||
@TableField(value = "exam_questions_id") |
||||
private Long examQuestionsId; |
||||
|
||||
} |
||||
@ -0,0 +1,109 @@
@@ -0,0 +1,109 @@
|
||||
package apelet.association.model; // 请根据你的实际包名修改
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
|
||||
/** |
||||
* 课程进度表(新)实体类 |
||||
*/ |
||||
@Data |
||||
@TableName("user_course_progressn") |
||||
public class UserCourseProgress implements Serializable { |
||||
|
||||
|
||||
/** |
||||
* 主键ID |
||||
*/ |
||||
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
private Long id; |
||||
|
||||
/** |
||||
* 创建人登录名称 |
||||
*/ |
||||
@TableField("create_user_id") |
||||
private String createUserId; |
||||
|
||||
/** |
||||
* 创建日期 |
||||
*/ |
||||
@TableField("create_time") |
||||
private Date createTime; |
||||
|
||||
/** |
||||
* 更新人登录名称 |
||||
*/ |
||||
@TableField("update_user_id") |
||||
private String updateUserId; |
||||
|
||||
/** |
||||
* 更新日期 |
||||
*/ |
||||
@TableField("update_time") |
||||
private Date updateTime; |
||||
|
||||
/** |
||||
* 逻辑删除标记(1: 正常 -1: 已删除) |
||||
*/ |
||||
@TableField("deleted_flag") |
||||
private Integer deletedFlag; |
||||
|
||||
/** |
||||
* 编码 |
||||
*/ |
||||
@TableField("number") |
||||
private String number; |
||||
|
||||
/** |
||||
* 名称 |
||||
*/ |
||||
@TableField("name") |
||||
private String name; |
||||
|
||||
/** |
||||
* 状态 |
||||
*/ |
||||
@TableField("billstatus") |
||||
private String billstatus; |
||||
|
||||
/** |
||||
* 公司 |
||||
*/ |
||||
@TableField("org") |
||||
private Integer org; |
||||
|
||||
/** |
||||
* 用户进度(百分比) |
||||
*/ |
||||
@TableField("progress") |
||||
private Integer progress; |
||||
|
||||
/** |
||||
* 当前进度 |
||||
*/ |
||||
@TableField("current_progress") |
||||
private Integer currentProgress; |
||||
|
||||
/** |
||||
* 用户id |
||||
*/ |
||||
@TableField("user_id") |
||||
private Long userId; |
||||
|
||||
/** |
||||
* 视频ID |
||||
*/ |
||||
@TableField("video_id") |
||||
private Long videoId; |
||||
|
||||
/** |
||||
* 课件ID |
||||
*/ |
||||
@TableField("courseware_id") |
||||
private Long coursewareId; |
||||
} |
||||
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
package apelet.association.model; |
||||
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*; |
||||
import lombok.Data; |
||||
|
||||
import java.util.Date; |
||||
|
||||
/** |
||||
* 用户答题记录 |
||||
*/ |
||||
@Data |
||||
@TableName("user_exercise") |
||||
public class UserExercise { |
||||
|
||||
/** 主键ID */ |
||||
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
private Long id; |
||||
|
||||
/** 创建人ID(答题用户ID) */ |
||||
@TableField(value = "create_user_id") |
||||
private String createUserId; |
||||
|
||||
/** 创建时间 */ |
||||
@TableField(value = "create_time") |
||||
private Date createTime; |
||||
|
||||
/** 更新人ID */ |
||||
@TableField(value = "update_user_id") |
||||
private String updateUserId; |
||||
|
||||
/** 更新时间 */ |
||||
@TableField(value = "update_time") |
||||
private Date updateTime; |
||||
|
||||
/** 逻辑删除标记(0未删除 1已删除) */ |
||||
@TableField(value = "deleted_flag") |
||||
private Integer deletedFlag; |
||||
|
||||
/** 编号 */ |
||||
@TableField(value = "number") |
||||
private String number; |
||||
|
||||
/** 名称 */ |
||||
@TableField(value = "name") |
||||
private String name; |
||||
|
||||
/** 题库ID */ |
||||
@TableField(value = "question_bank_id") |
||||
private Long questionBankId; |
||||
|
||||
/** 试题ID */ |
||||
@TableField(value = "exam_questions_id") |
||||
private Long examQuestionsId; |
||||
|
||||
/** 入会申请ID */ |
||||
@TableField(value = "membership_apply_id") |
||||
private Long membershipApplyId; |
||||
|
||||
/** 用户答案 */ |
||||
@TableField(value = "answer") |
||||
private String answer; |
||||
|
||||
/** 是否正确(0错误 1正确) */ |
||||
@TableField(value = "is_correct") |
||||
private Integer isCorrect; |
||||
|
||||
/** 得分 */ |
||||
@TableField(value = "score") |
||||
private Double score; |
||||
|
||||
/** 答题耗时(秒) */ |
||||
@TableField(value = "exercise_time") |
||||
private String exerciseTime; |
||||
|
||||
/** 课程ID */ |
||||
@TableField(value = "course_id") |
||||
private Long courseId; |
||||
|
||||
/** 批次号 */ |
||||
@TableField(value = "batch") |
||||
private String batch; |
||||
|
||||
/** 考试ID */ |
||||
@TableField(value = "exam_id") |
||||
private Long examId; |
||||
|
||||
/** 类型 (1: 刷题, 2: 考试)*/ |
||||
@TableField(value = "type") |
||||
private Integer type; |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
package apelet.association.plugin.active; |
||||
|
||||
import apelet.common.core.object.ObjectCollection; |
||||
import apelet.common.core.object.ObjectValue; |
||||
import apelet.common.core.util.ApplicationContextHolder; |
||||
import apelet.common.generator.utils.OrmGenDataSourceUtil; |
||||
import apelet.common.online.abstractplugin.ListPlugin; |
||||
import apelet.common.online.model.constant.AttributeEnum; |
||||
import apelet.common.orm.impl.Filter; |
||||
import apelet.common.orm.impl.FilterItem; |
||||
import apelet.common.orm.impl.Selector; |
||||
import com.alibaba.fastjson.JSONArray; |
||||
|
||||
/** |
||||
* 大赛活动详情插件 |
||||
*/ |
||||
public class CompetitionDetailsPlugin extends ListPlugin { |
||||
|
||||
private final OrmGenDataSourceUtil ormGenDataSourceUtil; |
||||
|
||||
public CompetitionDetailsPlugin() { |
||||
ormGenDataSourceUtil = ApplicationContextHolder.getBean(OrmGenDataSourceUtil.class); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void formCreated(String widgetVariableName, ObjectValue objectValue) { |
||||
setWidgetAttribute("id", AttributeEnum.SHOW, false); |
||||
Filter filter = new Filter(); |
||||
filter.add(new FilterItem("association_activity_id",FilterItem.equals, objectValue.get("id"))); |
||||
ObjectCollection collection = ormGenDataSourceUtil.query("competition_entries", filter, new Selector()); |
||||
JSONArray jsonArray = new JSONArray(); |
||||
collection.forEach(f -> { |
||||
jsonArray.add(f.getValues()); |
||||
}); |
||||
setWidgetAttribute("table1783059589608", AttributeEnum.ADD_ROWS, jsonArray); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
package apelet.association.plugin.question; |
||||
|
||||
import apelet.common.core.object.ObjectCollection; |
||||
import apelet.common.core.object.ObjectValue; |
||||
import apelet.common.online.abstractplugin.ListPlugin; |
||||
import apelet.common.online.dto.OnlineEventPluginExecuteDto; |
||||
import apelet.common.online.model.constant.AttributeEnum; |
||||
import apelet.common.orm.impl.Filter; |
||||
import apelet.common.orm.impl.FilterItem; |
||||
import apelet.common.orm.impl.Selector; |
||||
import org.apache.commons.lang3.StringUtils; |
||||
|
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* 刷题 插件 |
||||
*/ |
||||
public class DoPracticeProblemsSavePlugin extends ListPlugin { |
||||
|
||||
@Override |
||||
public void formCreated(String widgetVariableName, ObjectValue objectValue) { |
||||
|
||||
this.setWidgetAttribute("answer", AttributeEnum.SHOW, false); |
||||
|
||||
this.setWidgetAttribute("analysis", AttributeEnum.SHOW, false); |
||||
|
||||
if(objectValue.getString("type").equals("4")){ |
||||
this.setWidgetAttribute("block1782895105921", AttributeEnum.SHOW, false); |
||||
this.setWidgetAttribute("table1782871009238", AttributeEnum.SHOW, false); |
||||
|
||||
this.setWidgetAttribute("flowStatus", AttributeEnum.REQUIRED, true); |
||||
}else{ |
||||
this.setWidgetAttribute("flowStatus", AttributeEnum.SHOW, false); |
||||
} |
||||
if(StringUtils.isEmpty(objectValue.getString("exam_picture"))){ |
||||
this.setWidgetAttribute("examPicture", AttributeEnum.SHOW, false); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void buttonTriggered(String widgetVariableName, ObjectValue objectValue) { |
||||
if(widgetVariableName.equals("确认")){ |
||||
this.cancelOperate(); |
||||
return; |
||||
} |
||||
if(widgetVariableName.equals("下一题")){ |
||||
Map eventParams = getDto().getEventParams(); |
||||
Filter filter = new Filter(); |
||||
filter.add(new FilterItem("batchNumber", FilterItem.equals, eventParams.get("batch"))); |
||||
filter.add(new FilterItem("exam_questions_id", FilterItem.equals, objectValue.get("id"))); |
||||
filter.add(new FilterItem("question_bank_id", FilterItem.equals, eventParams.get("question_bank_id"))); |
||||
ObjectCollection collection = ormGenDataSourceUtil().query("user_exercise", filter, new Selector()); |
||||
if (collection.isEmpty()) { |
||||
|
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void rowClick(String widgetVariableName, ObjectValue objectValue) { |
||||
super.rowClick(widgetVariableName, objectValue); |
||||
} |
||||
} |
||||
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
package apelet.association.plugin.question; |
||||
|
||||
|
||||
import apelet.common.core.object.ObjectCollection; |
||||
import apelet.common.core.object.ObjectValue; |
||||
import apelet.common.core.object.TokenData; |
||||
import apelet.common.online.abstractplugin.ListPlugin; |
||||
import apelet.common.online.model.ShowParameter; |
||||
import apelet.common.online.model.constant.ShowTypeEnum; |
||||
import apelet.common.online.model.constant.ViewStatus; |
||||
import cn.hutool.core.util.IdUtil; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
public class QuestionBankListPlugin extends ListPlugin { |
||||
|
||||
public QuestionBankListPlugin() { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void buttonTriggered(String widgetVariableName, ObjectValue objectValue) { |
||||
if ("刷题".equals(widgetVariableName)) { |
||||
List<Map> rowDatas = getDto().getModel().getRowDatas(); |
||||
if (rowDatas == null || rowDatas.size() != 1) { |
||||
showWarningMessage("请先在列表中选中一条单据"); |
||||
this.cancelOperate(); |
||||
return; |
||||
} |
||||
Map data = rowDatas.get(0); |
||||
|
||||
ObjectValue questionBank = ormGenDataSourceUtil().queryOne("question_bank", data.get("id")); |
||||
ObjectCollection collection = questionBank.getObjectCollection("question_bank_entry"); |
||||
if (collection == null || collection.isEmpty()) { |
||||
this.showWarningMessage("题库下没有试题, 请先添加试题!!!"); |
||||
this.cancelOperate(); |
||||
return; |
||||
} |
||||
long batchNumber = IdUtil.getSnowflakeNextId(); |
||||
collection.forEach(f -> { |
||||
ObjectValue userExercise = new ObjectValue("user_exercise"); |
||||
userExercise.put("question_bank_id", questionBank); |
||||
userExercise.put("exam_questions_id", f.getObjectValue("exam_questions_id")); |
||||
userExercise.put("membership_apply_id", TokenData.takeFromRequest().getUserId()); |
||||
userExercise.put("batch", batchNumber); |
||||
userExercise.put("is_correct", 0); |
||||
try { |
||||
ormGenDataSourceUtil().addNew(userExercise.getTableName(), userExercise); |
||||
} catch (Exception e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
}); |
||||
ObjectValue object = collection.getObject(0); |
||||
ObjectValue examQuestions = object.getObjectValue("exam_questions_id"); |
||||
ShowParameter showParameter = new ShowParameter(); |
||||
showParameter.setFormId("2072135972052537344"); |
||||
showParameter.setHowType(ShowTypeEnum.OPEN_ONLINE_PAGE); |
||||
showParameter.setStatus(ViewStatus.EDIT); |
||||
showParameter.setPkId(examQuestions.getString("id")); |
||||
// 设置自定义参数
|
||||
Map<String, Object> customParam = new HashMap<>(); |
||||
customParam.put("question_bank_id", questionBank.getString("id")); |
||||
customParam.put("exam_questions_id", examQuestions.getString("id")); |
||||
customParam.put("membership_apply_id", TokenData.takeFromRequest().getUserId()); |
||||
customParam.put("batchNumber", batchNumber); |
||||
showParameter.setCustomParam(customParam); |
||||
super.showForm(showParameter); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
package apelet.association.service; |
||||
|
||||
import apelet.association.model.CourseCourseware; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
public interface CourseCoursewareService extends IService<CourseCourseware> { |
||||
|
||||
/** |
||||
* 保存课件学习记录 |
||||
* |
||||
* @param courseCourseware |
||||
*/ |
||||
void saveCoursewareLearning(CourseCourseware courseCourseware); |
||||
|
||||
/** |
||||
* 获取课件学习进度 |
||||
* @param courseId 课程ID |
||||
* @param userId 用户ID |
||||
* @return |
||||
*/ |
||||
Object getCourseLearningProgress(Long courseId, Long userId); |
||||
|
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
package apelet.association.service; |
||||
|
||||
import apelet.association.model.Course; |
||||
import apelet.association.model.CourseCourseware; |
||||
import apelet.association.model.CourseVideo; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
public interface CourseService extends IService<Course> { |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
package apelet.association.service; |
||||
|
||||
import apelet.association.model.CourseVideo; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
public interface CourseVideoService extends IService<CourseVideo> { |
||||
|
||||
/** |
||||
* 保存视频学习记录 |
||||
* |
||||
* @param courseVideo |
||||
*/ |
||||
void saveVideoLearning(CourseVideo courseVideo); |
||||
|
||||
|
||||
/** |
||||
* 获取课程视频学习进度 |
||||
* |
||||
* @param videoId |
||||
* @param userId |
||||
* @return |
||||
*/ |
||||
Object getVideoLearningProgress(Long videoId, Long userId); |
||||
|
||||
long getByteOffsetByTime(Long videoId, Long userId); |
||||
|
||||
} |
||||
|
||||
|
||||
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
package apelet.association.service; |
||||
|
||||
|
||||
import apelet.association.model.ExamQuestionsEntry; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
public interface ExamQuestionsEntryService extends IService<ExamQuestionsEntry> { |
||||
|
||||
} |
||||
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
package apelet.association.service; |
||||
|
||||
|
||||
import apelet.association.model.ExamQuestions; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
public interface ExamQuestionsService extends IService<ExamQuestions> { |
||||
|
||||
} |
||||
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
package apelet.association.service; |
||||
|
||||
import apelet.association.model.Course; |
||||
import apelet.association.model.Exam; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
public interface ExamService extends IService<Exam> { |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
package apelet.association.service; |
||||
|
||||
|
||||
import apelet.association.model.QuestionBankEntry; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
public interface QuestionBankEntryService extends IService<QuestionBankEntry> { |
||||
|
||||
} |
||||
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
package apelet.association.service; |
||||
|
||||
|
||||
import apelet.association.model.QuestionBank; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
public interface QuestionBankService extends IService<QuestionBank> { |
||||
|
||||
} |
||||
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
package apelet.association.service; |
||||
|
||||
import apelet.association.model.UserCourseProgress; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
|
||||
public interface UserCourseProgressService extends IService<UserCourseProgress> { |
||||
|
||||
} |
||||
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
package apelet.association.service; |
||||
|
||||
|
||||
import apelet.association.model.UserExercise; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
public interface UserExerciseService extends IService<UserExercise> { |
||||
|
||||
} |
||||
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
package apelet.association.service.impl; |
||||
|
||||
import apelet.association.dao.CourseCoursewareMapper; |
||||
import apelet.association.dao.UserExerciseMapper; |
||||
import apelet.association.model.CourseCourseware; |
||||
import apelet.association.model.UserCourseProgress; |
||||
import apelet.association.model.UserExercise; |
||||
import apelet.association.service.CourseCoursewareService; |
||||
import apelet.association.service.UserCourseProgressService; |
||||
import apelet.association.service.UserExerciseService; |
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.util.Date; |
||||
|
||||
@Service |
||||
public class CourseCoursewareServiceImpl extends ServiceImpl<CourseCoursewareMapper, CourseCourseware> |
||||
implements CourseCoursewareService { |
||||
|
||||
@Autowired |
||||
private UserCourseProgressService userCourseProgressService; |
||||
|
||||
@Override |
||||
public void saveCoursewareLearning(CourseCourseware courseCourseware) { |
||||
UserCourseProgress userCourseProgress = userCourseProgressService.getOne(new LambdaQueryWrapper<UserCourseProgress>() |
||||
.eq(UserCourseProgress::getUserId, courseCourseware.getUserId()) |
||||
.eq(UserCourseProgress::getCoursewareId, courseCourseware.getId())); |
||||
String progress = courseCourseware.getProgress(); |
||||
int pageCount = courseCourseware.getPageCount(); |
||||
int progressPercentage = Integer.parseInt(progress) / pageCount; |
||||
if(userCourseProgress == null){ |
||||
userCourseProgress = new UserCourseProgress(); |
||||
} |
||||
userCourseProgress.setCreateTime(new Date()); |
||||
userCourseProgress.setUserId(courseCourseware.getUserId()); |
||||
userCourseProgress.setCoursewareId(courseCourseware.getId()); |
||||
userCourseProgress.setProgress(progressPercentage); |
||||
userCourseProgress.setCurrentProgress(Integer.valueOf(progress)); |
||||
userCourseProgressService.saveOrUpdate(userCourseProgress); |
||||
} |
||||
|
||||
|
||||
|
||||
@Override |
||||
public Object getCourseLearningProgress(Long courseId, Long userId) { |
||||
UserCourseProgress userCourseProgress = userCourseProgressService.getOne(new LambdaQueryWrapper<UserCourseProgress>() |
||||
.eq(UserCourseProgress::getUserId, userId) |
||||
.eq(UserCourseProgress::getCoursewareId, courseId)); |
||||
if(userCourseProgress != null){ |
||||
return userCourseProgress.getCurrentProgress(); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
package apelet.association.service.impl; |
||||
|
||||
import apelet.association.dao.CourseCoursewareMapper; |
||||
import apelet.association.dao.CourseMapper; |
||||
import apelet.association.model.Course; |
||||
import apelet.association.model.CourseCourseware; |
||||
import apelet.association.model.CourseVideo; |
||||
import apelet.association.service.CourseCoursewareService; |
||||
import apelet.association.service.CourseService; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
@Service |
||||
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements CourseService { |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
package apelet.association.service.impl; |
||||
|
||||
import apelet.association.dao.CourseMapper; |
||||
import apelet.association.dao.CourseVideoMapper; |
||||
import apelet.association.model.Course; |
||||
import apelet.association.model.CourseVideo; |
||||
import apelet.association.model.UserCourseProgress; |
||||
import apelet.association.service.CourseService; |
||||
import apelet.association.service.CourseVideoService; |
||||
import apelet.association.service.UserCourseProgressService; |
||||
import apelet.common.core.exception.MyRuntimeException; |
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import org.bytedeco.javacv.FFmpegFrameGrabber; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.io.File; |
||||
import java.util.Date; |
||||
|
||||
@Service |
||||
public class CourseVideoServiceImpl extends ServiceImpl<CourseVideoMapper, CourseVideo> |
||||
implements CourseVideoService { |
||||
|
||||
@Autowired |
||||
private UserCourseProgressService userCourseProgressService; |
||||
|
||||
@Override |
||||
public void saveVideoLearning(CourseVideo courseVideo) { |
||||
UserCourseProgress userCourseProgress = userCourseProgressService.getOne(new LambdaQueryWrapper<UserCourseProgress>() |
||||
.eq(UserCourseProgress::getUserId, courseVideo.getUserId()) |
||||
.eq(UserCourseProgress::getVideoId, courseVideo.getId())); |
||||
if (userCourseProgress == null) { |
||||
userCourseProgress = new UserCourseProgress(); |
||||
} |
||||
String progress = courseVideo.getProgress(); |
||||
int videoDuration = courseVideo.getVideoDuration(); |
||||
int progressPercentage = Integer.parseInt(progress) / videoDuration; |
||||
userCourseProgress.setCreateTime(new Date()); |
||||
userCourseProgress.setVideoId(courseVideo.getId()); |
||||
userCourseProgress.setUserId(courseVideo.getUserId()); |
||||
userCourseProgress.setProgress(progressPercentage); |
||||
userCourseProgress.setCurrentProgress(Integer.valueOf(progress)); |
||||
userCourseProgressService.saveOrUpdate(userCourseProgress); |
||||
} |
||||
|
||||
@Override |
||||
public Object getVideoLearningProgress(Long videoId, Long userId) { |
||||
UserCourseProgress userCourseProgress = userCourseProgressService.getOne(new LambdaQueryWrapper<UserCourseProgress>() |
||||
.eq(UserCourseProgress::getUserId, userId) |
||||
.eq(UserCourseProgress::getVideoId, videoId)); |
||||
if (userCourseProgress != null) { |
||||
return userCourseProgress.getCurrentProgress(); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public long getByteOffsetByTime(Long videoId, Long userId) { |
||||
UserCourseProgress userCourseProgress = userCourseProgressService.getOne(new LambdaQueryWrapper<UserCourseProgress>() |
||||
.eq(UserCourseProgress::getUserId, userId) |
||||
.eq(UserCourseProgress::getVideoId, videoId)); |
||||
if (userCourseProgress == null || userCourseProgress.getCurrentProgress() == null |
||||
|| userCourseProgress.getCurrentProgress() <= 0) { |
||||
return 0L; |
||||
} |
||||
return userCourseProgress.getCurrentProgress(); |
||||
} |
||||
} |
||||
|
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
package apelet.association.service.impl; |
||||
|
||||
import apelet.association.dao.ExamQuestionsEntryMapper; |
||||
import apelet.association.model.ExamQuestionsEntry; |
||||
import apelet.association.service.ExamQuestionsEntryService; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
@Service |
||||
public class ExamQuestionsEntryServiceImpl extends ServiceImpl<ExamQuestionsEntryMapper, ExamQuestionsEntry> |
||||
implements ExamQuestionsEntryService { |
||||
|
||||
} |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
package apelet.association.service.impl; |
||||
|
||||
import apelet.association.dao.ExamQuestionsMapper; |
||||
import apelet.association.model.ExamQuestions; |
||||
import apelet.association.service.ExamQuestionsService; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
@Service |
||||
public class ExamQuestionsServiceImpl extends ServiceImpl<ExamQuestionsMapper, ExamQuestions> |
||||
implements ExamQuestionsService { |
||||
|
||||
} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
package apelet.association.service.impl; |
||||
|
||||
import apelet.association.dao.CourseMapper; |
||||
import apelet.association.dao.ExamMapper; |
||||
import apelet.association.model.Course; |
||||
import apelet.association.model.Exam; |
||||
import apelet.association.service.CourseService; |
||||
import apelet.association.service.ExamService; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
@Service |
||||
public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements ExamService { |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
package apelet.association.service.impl; |
||||
|
||||
import apelet.association.dao.QuestionBankEntryMapper; |
||||
import apelet.association.model.QuestionBankEntry; |
||||
import apelet.association.service.QuestionBankEntryService; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
@Service |
||||
public class QuestionBankEntryServiceImpl extends ServiceImpl<QuestionBankEntryMapper, QuestionBankEntry> |
||||
implements QuestionBankEntryService { |
||||
|
||||
} |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
package apelet.association.service.impl; |
||||
|
||||
import apelet.association.dao.QuestionBankMapper; |
||||
import apelet.association.model.QuestionBank; |
||||
import apelet.association.service.QuestionBankService; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
@Service |
||||
public class QuestionBankServiceImpl extends ServiceImpl<QuestionBankMapper, QuestionBank> |
||||
implements QuestionBankService { |
||||
|
||||
} |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
package apelet.association.service.impl; |
||||
|
||||
import apelet.association.dao.UserCourseProgressMapper; |
||||
import apelet.association.model.UserCourseProgress; |
||||
import apelet.association.service.UserCourseProgressService; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
@Service |
||||
public class UserCourseProgressServiceImpl extends ServiceImpl<UserCourseProgressMapper, UserCourseProgress> |
||||
implements UserCourseProgressService { |
||||
|
||||
} |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
package apelet.association.service.impl; |
||||
|
||||
import apelet.association.dao.UserExerciseMapper; |
||||
import apelet.association.model.UserExercise; |
||||
import apelet.association.service.UserExerciseService; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
@Service |
||||
public class UserExerciseServiceImpl extends ServiceImpl<UserExerciseMapper, UserExercise> |
||||
implements UserExerciseService { |
||||
|
||||
} |
||||
@ -0,0 +1,126 @@
@@ -0,0 +1,126 @@
|
||||
package apelet.association.utils; |
||||
|
||||
import apelet.association.vo.VideoMeta; |
||||
import org.bytedeco.javacv.FFmpegFrameGrabber; |
||||
import org.bytedeco.javacv.Frame; |
||||
import org.bytedeco.javacv.Java2DFrameConverter; |
||||
|
||||
import javax.imageio.ImageIO; |
||||
import java.awt.image.BufferedImage; |
||||
import java.io.File; |
||||
|
||||
public class VideoCvUtil { |
||||
|
||||
|
||||
/** |
||||
* 获取视频元信息 |
||||
* |
||||
* @param videoPath 本地视频绝对路径 |
||||
*/ |
||||
public static VideoMeta getVideoMeta(String videoPath) throws Exception { |
||||
// Create a File object from the provided video path
|
||||
File videoFile = new File(videoPath); |
||||
if (!videoFile.exists()) { |
||||
throw new IllegalArgumentException("视频文件不存在"); |
||||
} |
||||
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoFile); |
||||
try { |
||||
grabber.start(); |
||||
VideoMeta meta = new VideoMeta(); |
||||
meta.setWidth(grabber.getImageWidth()); |
||||
meta.setHeight(grabber.getImageHeight()); |
||||
// 总时长:微秒 → 秒
|
||||
meta.setDuration(grabber.getLengthInTime() / 1000000.0); |
||||
meta.setFrameRate(grabber.getFrameRate()); |
||||
// 旋转角度
|
||||
meta.setRotate((int) grabber.getDisplayRotation()); |
||||
return meta; |
||||
} finally { |
||||
closeGrabber(grabber); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 视频截图 |
||||
* |
||||
* @param videoPath 视频路径 |
||||
* @param outputImagePath 输出图片路径 |
||||
* @param seekSecond 在第几秒截取 |
||||
* @return 是否截图成功 |
||||
*/ |
||||
public static boolean captureImage(String videoPath, String outputImagePath, double seekSecond) { |
||||
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoPath); |
||||
Java2DFrameConverter converter = new Java2DFrameConverter(); |
||||
try { |
||||
grabber.start(); |
||||
// 定位到指定时间(微秒)
|
||||
grabber.setTimestamp((long) (seekSecond * 1000000)); |
||||
Frame frame = grabber.grabFrame(); |
||||
if (frame == null || frame.image == null) { |
||||
return false; |
||||
} |
||||
BufferedImage bufferedImage = converter.convert(frame); |
||||
// 处理视频旋转(手机竖拍视频画面颠倒)
|
||||
bufferedImage = rotateImage(bufferedImage, (int) grabber.getDisplayRotation()); |
||||
ImageIO.write(bufferedImage, "jpg", new File(outputImagePath)); |
||||
return true; |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
return false; |
||||
} finally { |
||||
closeGrabber(grabber); |
||||
} |
||||
// converter 无需手动关闭
|
||||
} |
||||
|
||||
/** |
||||
* 关闭资源,防止文件句柄泄露 |
||||
*/ |
||||
private static void closeGrabber(FFmpegFrameGrabber grabber) { |
||||
if (grabber != null) { |
||||
try { |
||||
grabber.stop(); |
||||
grabber.close(); |
||||
} catch (Exception ignored) { |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据旋转角度修正图片 |
||||
*/ |
||||
private static BufferedImage rotateImage(BufferedImage src, int rotate) { |
||||
if (rotate == 0) { |
||||
return src; |
||||
} |
||||
int w = src.getWidth(); |
||||
int h = src.getHeight(); |
||||
java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform(); |
||||
switch (rotate) { |
||||
case 90: |
||||
transform.translate(h, 0); |
||||
transform.rotate(Math.PI / 2); |
||||
return createRotatedImage(src, transform, h, w); |
||||
case 180: |
||||
transform.translate(w, h); |
||||
transform.rotate(Math.PI); |
||||
return createRotatedImage(src, transform, w, h); |
||||
case 270: |
||||
transform.translate(0, w); |
||||
transform.rotate(-Math.PI / 2); |
||||
return createRotatedImage(src, transform, h, w); |
||||
default: |
||||
return src; |
||||
} |
||||
} |
||||
|
||||
private static BufferedImage createRotatedImage(BufferedImage src, java.awt.geom.AffineTransform transform, int width, int height) { |
||||
BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); |
||||
java.awt.Graphics2D g2d = dest.createGraphics(); |
||||
g2d.drawImage(src, transform, null); |
||||
g2d.dispose(); |
||||
return dest; |
||||
} |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
package apelet.association.vo; |
||||
|
||||
import lombok.Data; |
||||
|
||||
@Data |
||||
public class VideoMeta { |
||||
|
||||
/** 视频宽度 */ |
||||
private Integer width; |
||||
/** 视频高度 */ |
||||
private Integer height; |
||||
/** 总时长(秒,保留小数) */ |
||||
private Double duration; |
||||
/** 帧率 */ |
||||
private Double frameRate; |
||||
/** 视频旋转角度(部分手机拍摄视频存在旋转) */ |
||||
private Integer rotate; |
||||
|
||||
} |
||||
Loading…
Reference in new issue