Compare commits

..

77 Commits

Author SHA1 Message Date
liguoxu
2607f3ba10 事务测试恢复 2025-09-23 15:18:04 +08:00
liguoxu
4542d67983 事务测试 2025-09-22 19:29:25 +08:00
王卓煜
ce96b76c78 组织数据和员工数据导出 2025-09-01 14:34:36 +08:00
王卓煜
1369b5295d 修复考试成绩窗口查询不支持模糊查询 2025-09-01 11:17:00 +08:00
王卓煜
507aeadccf 修复问答管理的标题查询与实际不符 2025-08-21 14:19:56 +08:00
王卓煜
238dd6b7eb 修复新建文章,上传图片报错 2025-08-11 17:17:50 +08:00
王卓煜
7b5ff86209 修复问答管理的标题查询与实际不符 2025-08-11 16:04:28 +08:00
王卓煜
4136783586 修复新建在线课程窗口中,编辑作业报错 2025-07-30 14:40:54 +08:00
joshen
5f946906be Merge remote-tracking branch 'yx/250710-bugfix-gj' into test 2025-07-10 16:51:05 +08:00
Caojr
b234c6f571 测试日志 2025-07-10 16:50:04 +08:00
joshen
ea02f4920b Merge remote-tracking branch 'yx/master-0623-lyc' into test 2025-07-04 14:19:24 +08:00
王卓煜
7ad6da6f53 修复学员端,“文章”列表页,进入文章详情。对文章发表评论后,评论数量没有即时更新;对评论进行回复后,回复的数量没有在评论的数量统计即时更新,并且修复删除评论,评论数量不对 2025-07-04 13:37:28 +08:00
王卓煜
2a1c04ccb5 修复学员端,“文章”列表页,进入文章详情。对文章发表评论后,评论数量没有即时更新;对评论进行回复后,回复的数量没有在评论的数量统计即时更新 2025-07-04 13:31:28 +08:00
670788339
bba3cde615 Merge branch 'master-0623-lyc' into test 2025-06-23 10:46:32 +08:00
670788339
171ddfb89c studyIndex添加讲师头像 2025-06-23 10:04:23 +08:00
joshen
618b0b3f62 Merge remote-tracking branch 'yx/master-20250414-lyc' into release-20250328-master 2025-05-13 15:37:05 +08:00
joshen
f227af6da6 Merge remote-tracking branch 'yx/master-20250429-lyc' into test 2025-04-29 16:00:13 +08:00
joshen
0d69078cd0 Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-21 15:27:44 +08:00
670788339
fb1d11ebab 取消lastStudyTime更新条件 2025-04-21 15:18:48 +08:00
joshen
3b26209308 Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-21 14:00:24 +08:00
670788339
add1d6abb2 取消lastStudyTime更新条件 2025-04-21 13:59:06 +08:00
joshen
1a88a7442f Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-21 13:48:13 +08:00
670788339
eafe17b99a studyindex 日志 2025-04-21 13:46:55 +08:00
joshen
65bd8d9110 Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-21 11:55:01 +08:00
670788339
d1b6573c25 studyindex调试 2025-04-21 11:40:22 +08:00
joshen
549448aa69 Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-18 11:16:15 +08:00
670788339
1039f80aec 调试 2025-04-18 11:15:36 +08:00
joshen
a91238000d Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-18 11:05:56 +08:00
670788339
efc0605115 调试 2025-04-18 11:05:18 +08:00
joshen
9dcdf31372 Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-18 10:55:27 +08:00
670788339
d938425151 日志 2025-04-18 10:54:36 +08:00
joshen
d723d9e74b Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-18 10:41:25 +08:00
670788339
00510562ca 日志 2025-04-18 10:40:50 +08:00
joshen
13ee490bff Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-18 10:22:36 +08:00
670788339
3e70e71e5a 合并影响的其他接口 2025-04-18 10:19:06 +08:00
joshen
77e2ca0b0b Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-18 10:08:43 +08:00
670788339
24011a4470 合并影响的其他接口 2025-04-18 10:06:53 +08:00
joshen
03c12f7bc9 Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-18 09:50:46 +08:00
670788339
8948165296 合并影响的其他接口 2025-04-18 09:49:59 +08:00
joshen
e28e4523ab Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-18 09:18:08 +08:00
670788339
076b1828ad 合并 2025-04-18 09:16:47 +08:00
joshen
c8512a86df Merge remote-tracking branch 'yx/master-20250414-lyc' into test 2025-04-18 09:02:52 +08:00
670788339
95312696f6 studyindex缓存获取duration 2025-04-17 13:36:30 +08:00
joshen
2e7b2ab085 Merge remote-tracking branch 'yx/master-20250414-lyc' into test
# Conflicts:
#	servers/boe-server-all/src/main/java/com/xboe/school/study/api/StudyCourseTask.java
2025-04-14 16:11:48 +08:00
670788339
1aca002b8f 合并测试 2025-04-14 16:10:18 +08:00
joshen
6fa5d8bb0d Merge remote-tracking branch 'yx/master-20250414-lyc' into test
# Conflicts:
#	servers/boe-server-all/src/main/java/com/xboe/school/study/api/StudyCourseTask.java
2025-04-14 15:49:13 +08:00
670788339
50bdc39ce8 appendtime 于 study-video-time 合并 2025-04-14 14:08:01 +08:00
joshen
14376b21b4 Merge branch '250408-bugfix-shl' into test
# Conflicts:
#	servers/boe-server-all/src/main/java/com/xboe/module/course/service/ICourseService.java
2025-04-09 09:52:14 +08:00
joshen
29221d8f25 Merge remote-tracking branch 'yx/master-20250403-lyc' into test 2025-04-03 09:42:19 +08:00
joshen
672487e29d Merge branch '250331-bugfix-shl-newmaster' into test 2025-04-01 13:53:22 +08:00
joshen
b7834eadf9 Merge branch '250331-bugfix-shl-newmaster' into test
# Conflicts:
#	servers/boe-server-all/src/main/java/com/xboe/school/study/api/StudyCourseESApi.java
2025-04-01 13:37:24 +08:00
joshen
14bd304374 Merge branch '250331-bugfix-shl' into test
# Conflicts:
#	servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTeacher.java
#	servers/boe-server-all/src/main/java/com/xboe/school/study/api/StudyCourseApi.java
2025-04-01 10:25:53 +08:00
joshen
447a65ee6f Merge branch '250331-bugfix-shl' into test
# Conflicts:
#	servers/boe-server-all/src/main/java/com/xboe/school/study/service/impl/StudyServiceImpl.java
2025-04-01 09:17:04 +08:00
joshen
d88b285729 Merge remote-tracking branch 'yx/master-20250227-lyc' into test 2025-03-29 13:00:12 +08:00
670788339
d3e84feb48 日志 2025-03-29 12:58:43 +08:00
joshen
318ba14fd3 Merge remote-tracking branch 'yx/master-20250227-lyc' into test 2025-03-29 12:23:11 +08:00
joshen
5ad578454e Merge remote-tracking branch 'yx/master-20250227-lyc' into test 2025-03-29 11:03:17 +08:00
joshen
052f673fc8 Merge remote-tracking branch 'yx/250324-casebugfix-zsh' into test 2025-03-25 08:48:55 +08:00
joshen
451156265e Merge remote-tracking branch 'yx/250324-casebugfix-zsh' into test 2025-03-25 08:41:12 +08:00
joshen
ff299b7697 Merge remote-tracking branch 'yx/250324-casebugfix-zsh' into test 2025-03-24 18:14:22 +08:00
joshen
66e4187be2 Merge remote-tracking branch 'yx/250324-casebugfix-zsh' into test 2025-03-24 17:53:08 +08:00
joshen
436f7647ce Merge remote-tracking branch 'yx/250324-casebugfix-zsh' into test 2025-03-24 17:49:26 +08:00
joshen
5c9237cb79 Merge branch '250324-bugfix-shl' into test 2025-03-24 10:14:45 +08:00
joshen
889f634c5e Merge branch '250321-bugfix-gx' into test
# Conflicts:
#	servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTeacher.java
#	servers/boe-server-all/src/main/java/com/xboe/school/study/api/StudyCourseApi.java
2025-03-21 16:08:15 +08:00
gengxin
978e1a7bf2 分析数据 img 是否为空 2025-03-21 15:26:41 +08:00
joshen
5fe9019d87 Merge branch 'master-20250227-lyc' into test 2025-03-19 13:20:33 +08:00
joshen
be8c4464dc Merge branch 'master-20250227-lyc' into test 2025-03-19 13:09:42 +08:00
joshen
c7f1a1e810 Merge branch 'master-20250227-lyc' into test 2025-03-19 11:59:14 +08:00
joshen
87350c74f9 Merge remote-tracking branch 'yx/master-20250318-lyc' into test 2025-03-18 11:51:26 +08:00
joshen
67f55c51b8 Merge branch 'master-20250227-lyc' into test 2025-03-13 10:04:24 +08:00
joshen
645c58e8c4 Merge branch 'master-20250227-lyc' into test 2025-03-10 16:05:19 +08:00
joshen
dd6e64d6ac Merge branch 'master-20250227-lyc' into test 2025-03-10 14:42:39 +08:00
joshen
754d41e087 Merge remote-tracking branch '121/test' into test 2025-03-10 14:26:08 +08:00
joshen
fedf8ec527 Merge branch 'master-20250227-lyc' of https://codeup.aliyun.com/67762337eccfc218f6110e0e/per-boe/java-servers into test 2025-03-10 14:25:19 +08:00
670788339
e2ac6a5b96 日志 2025-03-10 13:45:34 +08:00
670788339
66320dab97 Merge branch 'master-20250227-lyc' into test 2025-03-10 09:30:05 +08:00
hui
5312d9f5f4 boe_new库该boe库--生产切不可合并这个提交!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 2025-01-18 16:09:37 +08:00
13 changed files with 381 additions and 48 deletions

View File

@@ -57,4 +57,8 @@ public class CourseTeacher extends IdBaseEntity {
@Transient
private Integer teacherType;
/**讲师头像*/
@Transient
private String photo;
}

View File

@@ -105,8 +105,11 @@ public class CourseContentServiceImpl implements ICourseContentService {
if(homework!=null) {
homework.setCourseId(cc.getCourseId());
homework.setContentId(cc.getId());
homework=homeworkDao.saveOrUpdate(homework);
log.info("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
homework.setSysVersion(homeworkDao.getVersion(homework.getId()));
homeworkDao.saveOrUpdate(homework);
log.info("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
log.info("ccccccccccccccccccccccccccccccccccccccccccccccccccc");
}
}

View File

@@ -491,7 +491,7 @@ public class CourseServiceImpl implements ICourseService {
String sql = "SELECT DISTINCT\n" +
"rt.course_id\n" +
"FROM\n" +
"boe_new.student s INNER JOIN boe_new.router_task rt on s.pid=rt.router_id inner join boe_course c on c.id=rt.course_id\n" +
"boe.student s INNER JOIN boe.router_task rt on s.pid=rt.router_id inner join boe_course c on c.id=rt.course_id\n" +
"\n" +
"WHERE\n" +
"\n" +
@@ -514,7 +514,7 @@ public class CourseServiceImpl implements ICourseService {
String sql = "SELECT DISTINCT\n" +
"pt.course_id\n" +
"FROM\n" +
"boe_new.student s INNER JOIN boe_new.project_task pt on s.pid=pt.project_id inner join boe_course c on c.id=pt.course_id\n" +
"boe.student s INNER JOIN boe.project_task pt on s.pid=pt.project_id inner join boe_course c on c.id=pt.course_id\n" +
"\n" +
"WHERE\n" +
"\n" +
@@ -571,8 +571,8 @@ public class CourseServiceImpl implements ICourseService {
String sql = "SELECT DISTINCT\n" +
"\tc.id \n" +
"FROM\n" +
"\tboe_new.student s\n" +
"\tINNER JOIN boe_new.grow_task gt ON s.pid = gt.grow_id\n" +
"\tboe.student s\n" +
"\tINNER JOIN boe.grow_task gt ON s.pid = gt.grow_id\n" +
"\tINNER JOIN boe_course c ON gt.course_id = c.id \n" +
"WHERE\n" +
"\ts.type = 14 \n" +

View File

@@ -165,7 +165,7 @@ public class AloneExamServiceImpl implements IAloneExamService{
query.addFilter(FieldFilters.eq("aid",aea.getAid()));
}
if(StringUtils.isNotBlank(aea.getName())) {
query.addFilter(FieldFilters.eq("name",aea.getName()));
query.addFilter(FieldFilters.like("name",aea.getName()));
}
}
return dao.findPage(query.builder());

View File

@@ -75,6 +75,7 @@ public class CommentsServiceImpl implements ICommentsService{
c.setPraises(0);
c.setFavorites(0);
dao.save(c);
callback.increase(BoedxResourceType.toEnum(c.getObjType()),c.getObjId(), BoedxHitsField.Comments);
if(c.getParentRead()!=null && c.getParentRead()) {
dao.updateMultiFieldById(c.getParentId(),
UpdateBuilder.create("replys","replys+1",FieldUpdateType.EXPRESSION),
@@ -87,9 +88,11 @@ public class CommentsServiceImpl implements ICommentsService{
@Override
public void deleteReply(String id,String parentId) {
Comments comment=dao.get(id);
dao.deleteById(id);
dao.updateMultiFieldById(parentId, UpdateBuilder.create("replys","replys-1",FieldUpdateType.EXPRESSION));
//需要同时回调处理,修改对应的内容的评论数量
callback.reduce(BoedxResourceType.toEnum(comment.getObjType()),comment.getObjId(),BoedxHitsField.Comments);
}
@Override

View File

@@ -86,7 +86,7 @@ public class QuestionServiceImpl implements IQuestionService {
// filters.add(FieldFilters.or(FieldFilters.like("title", questionDto.getKeyword()), FieldFilters.like("content", questionDto.getKeyword())));
List<IFieldFilter> iFieldFilters = new ArrayList<>();
iFieldFilters.add(FieldFilters.like("title", questionDto.getKeyword()));
iFieldFilters.add(FieldFilters.like("content", questionDto.getKeyword()));
iFieldFilters.add(FieldFilters.like("title", questionDto.getKeyword()));
iFieldFilters.add(FieldFilters.like("sysCreateBy", questionDto.getKeyword()));
filters.add(FieldFilters.or(iFieldFilters));
}

View File

@@ -15,6 +15,7 @@ import com.xboe.constants.CacheName;
import com.xboe.module.course.vo.TeacherVo;
import com.xboe.module.usergroup.service.IUserGroupService;
import com.xboe.school.study.dao.StudyCourseDao;
import com.xboe.school.vo.StudyTimeVo;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -211,6 +212,7 @@ public class StudyCourseApi extends ApiBaseController{
ct.setRemark(t.getDescription());
ct.setSupplier(t.getSupplier());
ct.setTeacherType(t.getTeacherType());
ct.setPhoto(t.getPhoto());
}
if(redisTemplate.opsForValue().get(ct.getTeacherId())==null){
List<String>list=new ArrayList<>();
@@ -275,19 +277,6 @@ public class StudyCourseApi extends ApiBaseController{
continue;
}
// 查询redis上面的key并解析value获取到lastStudyTime
String lastActive = redisTemplate.opsForValue().get("studyContentId:" + item.getId() + ":last_active");
if (StringUtil.isNotBlank(lastActive)) {
String[] parts = lastActive.split("&");
if (parts.length == 2) {
int lastStudyTimeRedis = Integer.parseInt(parts[0]);
log.info("study-video-time-redis获取---lastStudyTimeRedis = " + lastStudyTimeRedis);
if(lastStudyTimeRedis>0){
item.setLastStudyTime(lastStudyTimeRedis);
}
}
}
BigDecimal lastStudyTime = new BigDecimal(item.getLastStudyTime());
BigDecimal duration = new BigDecimal(content.getDuration());
BigDecimal progress = lastStudyTime.divide(duration, 10, RoundingMode.HALF_UP);
@@ -415,6 +404,8 @@ public class StudyCourseApi extends ApiBaseController{
studyService.saveStudyInfo(sci,token);
//学习记录成功后处理
studyService.appendStudyDuration(sci.getStudyId(),sci.getStudyItemId(),sci.getContentId(),sci.getDuration());
log.info("在线课学习记录 sci.getStudyId() = "+ sci.getStudyId() + " , sci.getCourseId() = " + sci.getCourseId() );
List<StudyCourse> allUserList = thirdApi.getStudyCourseList(sci.getStudyId() ,sci.getCourseId(), token);
log.info("在线课学习记录"+allUserList);
//System.out.println("在线课学习记录"+allUserList);
@@ -625,7 +616,7 @@ public class StudyCourseApi extends ApiBaseController{
@Deprecated
@RequestMapping(value="/appendtime",method = {RequestMethod.GET,RequestMethod.POST})
public JsonResponse<String> appendTime(StudyTime studyTime, HttpServletRequest request){
if(StringUtils.isBlank(studyTime.getStudyId())){
return error("参数错误");
}
@@ -655,7 +646,91 @@ public class StudyCourseApi extends ApiBaseController{
return error("记录学习时长错误",e.getMessage());
}
}
/**
* appendtime 于 study-video-time 合并
* */
/*@RequestMapping(value="/updateStudyVideoTime1",method = {RequestMethod.GET,RequestMethod.POST})
public JsonResponse<String> updateStudyVideoTime1(StudyTimeVo studyTime, HttpServletRequest request){
// 0 study-video-time , 1 appendtime
if (studyTime.getType() == 0){
if(StringUtils.isBlank(studyTime.getItemId())){
return error("参数错误");
}
if(studyTime.getVideoTime()==null){
return error("无时间点");
}
//检查是否已存在
try {
studyService.updateLastTime(studyTime.getItemId(),studyTime.getVideoTime(), getCurrent().getAccountId());
if (studyTime.getContentId() != null && studyTime.getCourseId() != null && studyTime.getProgressVideo() != null){
contentService.updateProcessVideo(studyTime.getContentId(), studyTime.getCourseId(), studyTime.getProgressVideo());
}
return success("true");
}catch(Exception e) {
log.error("updateStudyVideoTime type =0 记录最后学习时间错误",e);
return error("updateStudyVideoTime type =0 记录最后学习时间失败 ",e.getMessage());
}
}else if(studyTime.getType() == 1){
if(StringUtils.isBlank(studyTime.getStudyId())){
return error("参数错误");
}
if(StringUtils.isBlank(studyTime.getCourseId())){
return error("未指定课程");
}
if(StringUtils.isBlank(studyTime.getContentId())){
return error("未指定资源内容");
}
String token = request.getHeader("Xboe-Access-Token");
if (StringUtils.isEmpty(token)) {
token = request.getHeader("token");
}
try {
studyService.updateStudyDuration(studyTime.getStudyId(),null,studyTime.getContentId(),studyTime.getVideoTime(),studyTime.getCourseId());
List<StudyCourse> allUserList = thirdApi.getStudyCourseList(studyTime.getStudyId() ,studyTime.getCourseId(), token);
log.info("updateStudyVideoTime type =1 在线课学习记录 = " + allUserList);
return success(studyTime.getId());
}catch(Exception e) {
log.error("updateStudyVideoTime type =1 记录学习时长错误",e);
return error("updateStudyVideoTime type =1 记录学习时长错误 ",e.getMessage());
}
}else{
return error("type不能为空");
}
}*/
@RequestMapping(value="/updateStudyVideoTime",method = {RequestMethod.GET,RequestMethod.POST})
public JsonResponse<String> updateStudyVideoTime(StudyTimeVo studyTime, HttpServletRequest request){
try {
if(StringUtils.isBlank(studyTime.getItemId())){
return error("参数错误");
}
if(studyTime.getVideoTime()==null){
return error("无时间点");
}
if(StringUtils.isBlank(studyTime.getStudyId())){
return error("参数错误");
}
if(StringUtils.isBlank(studyTime.getCourseId())){
return error("未指定课程");
}
if(StringUtils.isBlank(studyTime.getContentId())){
return error("未指定资源内容");
}
studyService.updateStudyDuration(studyTime.getStudyId(),studyTime.getItemId(),studyTime.getContentId(),studyTime.getVideoTime(),studyTime.getCourseId());
if (studyTime.getContentId() != null && studyTime.getCourseId() != null && studyTime.getProgressVideo() != null){
contentService.updateProcessVideo(studyTime.getContentId(), studyTime.getCourseId(), studyTime.getProgressVideo());
}
} catch (Exception e) {
log.error("updateStudyVideoTime",e);
return error("学习时长记录失败",e.getMessage(),null);
}
return success("true");
}
/**获取最后一次的学习内容*/
@GetMapping("/last-study")
public JsonResponse<Map<String,Object>> lastStudy(){

View File

@@ -1,7 +1,9 @@
package com.xboe.school.study.api;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -10,6 +12,13 @@ import cn.hutool.core.collection.CollectionUtil;
import com.xboe.module.course.entity.Course;
import com.xboe.module.course.service.ICourseService;
import org.apache.commons.compress.utils.Lists;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.xboe.core.orm.FieldFilters;
import com.xboe.core.orm.QueryBuilder;
import com.xboe.module.course.dao.CourseDao;
import com.xboe.module.course.dto.RankingDto;
import com.xboe.module.course.entity.Course;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

View File

@@ -1,5 +1,7 @@
package com.xboe.school.study.api;
import com.xboe.api.ThirdApi;
import com.xboe.school.study.entity.StudyCourse;
import com.xboe.school.study.service.IStudyService;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
@@ -10,10 +12,13 @@ import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
/**
* @author by lyc
@@ -26,13 +31,15 @@ public class StudyCourseTask {
private final IStudyService studyService;
private final StringRedisTemplate redisTemplate;
@Resource
private ThirdApi thirdApi;
/**
* 定时任务
* 获取redis 中学习结束的数据更新入库
* */
@XxlJob("saveStudyCourseItemLastTime")
public void saveStudyCourseItemLastTime() {
@XxlJob("saveStudyCourseItemLastTime2")
public void saveStudyCourseItemLastTime2() {
// 1. 定义匹配模式匹配所有目标key
final String KEY_PATTERN = "studyContentId:*:last_active";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
@@ -59,7 +66,6 @@ public class StudyCourseTask {
String[] parts = redisKey.split(":");
if (parts.length < 2) continue;
String studyContentId = parts[1];
// 7. 获取存储的时间点(示例逻辑)
String redisValue = redisTemplate.opsForValue().get(redisKey);
if (redisValue == null) continue;
@@ -69,19 +75,15 @@ public class StudyCourseTask {
if (partValues.length >= 2){
timestamp = LocalDateTime.parse(partValues[1], formatter);
}
// 8. 更新数据库(调用已有服务方法)
studyService.updateStudyCourseItemLastTime(studyContentId, lastStudyTime, timestamp);
// 9. 删除Redis键原子操作
redisTemplate.delete(redisKey);
log.info("处理成功 key: {}, lastStudyTime: {}", redisKey, lastStudyTime);
} catch (Exception e) {
log.error("处理失败 key: {}", redisKey, e);
}
}
}
cursor.close();
} catch (Exception e) {
@@ -102,5 +104,163 @@ public class StudyCourseTask {
}
@XxlJob("saveStudyCourseItemLastTime")
public void saveStudyCourseItemLastTime() {
// 1. 定义匹配模式匹配所有目标key
final String KEY_PATTERN = "studyId:*:courseId:*:courseContentId:*:studyItemId:*";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
// 2. 使用SCAN安全遍历避免阻塞
ScanOptions options = ScanOptions.scanOptions()
.match(KEY_PATTERN)
.count(100) // 分页大小
.build();
try (RedisConnection connection = Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection()) {
Cursor<byte[]> cursor = connection.scan(options);
// 3. 遍历处理符合条件的key
while (cursor.hasNext()) {
String redisKey = new String(cursor.next());
log.info("-定时任务 saveStudyCourseItemLastTime ---redisKey = " + redisKey);
// 4. 获取剩余TTL
Long ttl = redisTemplate.getExpire(redisKey, TimeUnit.SECONDS);
// 5. 过滤条件:剩余时间 >= 29天23小时30分钟转换为秒
// 总需时间 = (30天 - 30分钟) = 29天23小时30分钟 = 2590200秒
// 5分钟 300秒 || 2592000 - 300 = 2591700
if (ttl <= 2590200) {
try {
// 6. 提取studyContentId
String[] parts = redisKey.split(":");
if (parts.length < 7) continue;
String studyId = parts[1];
String courseId = parts[3];
String courseContentId = parts[5];
String studyItemId = parts[7];
// 7. 获取存储的时间点(示例逻辑)
String redisValue = redisTemplate.opsForValue().get(redisKey);
// redisValue = 60&20&2025-07-22T16:51:40
log.info("-定时任务 saveStudyCourseItemLastTime ---redisValue = " + redisValue);
if (redisValue == null) continue;
String[] partValues = redisValue.split("&");
// studyVideoTtime=60
int studyVideoTtime = Integer.parseInt(partValues[0]);
// appendtime=20
int appendtime = Integer.parseInt(partValues[1]);
LocalDateTime timestamp = null;
if (partValues.length >= 2){
timestamp = LocalDateTime.parse(partValues[2], formatter);
}
// 8. 更新数据库(调用已有服务方法)
studyService.newAppendStudyDuration(studyId,null,courseContentId,appendtime,timestamp);
log.info("-定时任务 saveStudyCourseItemLastTime ---studyItemId = " + studyItemId);
if (studyItemId != null && !studyItemId.equals("null")){
log.info("-定时任务 saveStudyCourseItemLastTime --- boolean studyItemId = " + (studyItemId != null));
// 8. 更新数据库(调用已有服务方法)
studyService.updateStudyCourseItemLastTime(studyItemId, studyVideoTtime, timestamp);
}
List<StudyCourse> allUserList = thirdApi.getStudyCourseList(studyId , courseId, null);
log.info("处理成功 allUserList: {}", allUserList);
// 9. 删除Redis键原子操作
redisTemplate.delete(redisKey);
log.info("处理成功 key: {}, lastStudyTime: {}", redisKey, appendtime);
} catch (Exception e) {
log.error("处理失败 key: {}", redisKey, e);
}
}
}
cursor.close();
} catch (Exception e) {
log.error("定时任务执行异常", e);
}
}
@XxlJob("saveStudyCourseItemLastTime1")
public void saveStudyCourseItemLastTime1() {
// 定义需要处理的键模式集合
processKeys("studyContentId:*:last_active", this::handleLastActiveKey);
processKeys("studyId:*:courseId:*:courseContentId:*", this::handleDurationKey);
}
private void processKeys(String keyPattern, BiConsumer<String, String> keyHandler) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
ScanOptions options = ScanOptions.scanOptions()
.match(keyPattern)
.count(100)
.build();
try (RedisConnection connection = Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection()) {
Cursor<byte[]> cursor = connection.scan(options);
while (cursor.hasNext()) {
String redisKey = new String(cursor.next());
Long ttl = redisTemplate.getExpire(redisKey, TimeUnit.SECONDS);
if (ttl != null && ttl <= 2590200) {
try {
String redisValue = redisTemplate.opsForValue().get(redisKey);
if (redisValue != null) {
// 调用对应的处理方法
keyHandler.accept(redisKey, redisValue);
}
redisTemplate.delete(redisKey);
log.info("Key processed: {}", redisKey);
} catch (Exception e) {
log.error("Process failed [{}]", redisKey, e);
}
}
}
cursor.close();
} catch (Exception e) {
log.error("Key processing error: {}", keyPattern, e);
}
}
// 处理 last_active 类型键
private void handleLastActiveKey(String redisKey, String redisValue) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
String[] parts = redisKey.split(":");
String studyContentId = parts[1];
String[] values = redisValue.split("&");
int lastStudyTime = Integer.parseInt(values[0]);
LocalDateTime timestamp = values.length >= 2 ?
LocalDateTime.parse(values[1], formatter) : null;
studyService.updateStudyCourseItemLastTime(studyContentId, lastStudyTime, timestamp);
}
// 处理 duration 类型键
private void handleDurationKey(String redisKey, String redisValue) {
String[] parts = redisKey.split(":");
String studyId = parts[1];
String courseId = parts[3];
String courseContentId = parts[5];
String[] values = redisValue.split("&");
int duration = Integer.parseInt(values[0]);
LocalDateTime timestamp = values.length >= 2 ?
LocalDateTime.parse(values[1], DateTimeFormatter.ISO_LOCAL_DATE_TIME) : null;
studyService.newAppendStudyDuration(studyId, null, courseContentId, duration, timestamp);
// 保留第三方调用
List<StudyCourse> allUserList = thirdApi.getStudyCourseList(studyId, courseId, null);
log.info("Study records synced: {}", allUserList.size());
}
}

View File

@@ -101,4 +101,8 @@ public interface IStudyService {
List<StudyCourseItem> getList(String courseId, String contentId, String name, Integer status);
void updateStudyCourseItemLastTime(String studyContentId, int lastStudyTime, LocalDateTime timestamp);
void updateStudyDuration(String studyId,String studyItemId, String contentId, Integer videoTime,String courseId);
void newAppendStudyDuration(String studyId, String studyItemId, String courseContentId, int duration, LocalDateTime timestamp);
}

View File

@@ -116,34 +116,74 @@ public class StudyServiceImpl implements IStudyService{
//增加内容的学习时长
if(StringUtils.isNotBlank(studyItemId)) {
//直接根据id更新
// String hql="Update StudyCourseItem set studyDuration=studyDuration+"+duration+",status=(case when status<2 then 2 else status end) where id=?1";
// scItemDao.update(hql,studyItemId);
String sql="Update boe_study_course_item set study_duration=study_duration+"+duration+",status=(case when status<2 then 2 else status end) where id=?1";
scItemDao.sqlUpdate(sql,studyItemId);
//scItemDao.updateMultiFieldById(studyItemId, UpdateBuilder.create("studyDuration", "studyDuration+"+duration,FieldUpdateType.EXPRESSION));
}else {
//根据学习id和课程内容id更新
// scItemDao.update(UpdateBuilder.from(StudyCourseItem.class)
// .addUpdateField("studyDuration", "studyDuration+"+duration,FieldUpdateType.EXPRESSION)
// .addFilter(FieldFilters.eq("studyId", studyId))
// .addFilter(FieldFilters.eq("contentId", courseContentId))
// .builder());
//
// String hql="Update StudyCourseItem set studyDuration=studyDuration+"+duration+",status=(case when status<2 then 2 else status end) where studyId=?1 and contentId=?2";
// scItemDao.update(hql,studyId,courseContentId);
String sql="Update boe_study_course_item set study_duration=study_duration+"+duration+",status=(case when status<2 then 2 else status end) where study_id=?1 and content_id=?2";
scItemDao.sqlUpdate(sql,studyId,courseContentId);
}
//追加课程的学习时长
//scDao.updateMultiFieldById(studyId, UpdateBuilder.create("totalDuration", "totalDuration+"+duration,FieldUpdateType.EXPRESSION));
String sql="Update boe_study_course set total_duration=total_duration+"+duration+",status=(case when status<2 then 2 else status end),progress=(case when progress=0 then 1 else progress end),last_time = '"+LocalDateTime.now()+"' where id=?1";
scDao.sqlUpdate(sql,studyId);
}
@Override
@Transactional
public void newAppendStudyDuration(String studyId,String studyItemId,String courseContentId, int duration,LocalDateTime timestamp) {
//增加内容的学习时长
if(StringUtils.isNotBlank(studyItemId)) {
//直接根据id更新
String sql="Update boe_study_course_item set study_duration=study_duration+"+duration+",status=(case when status<2 then 2 else status end) where id=?1";
scItemDao.sqlUpdate(sql,studyItemId);
}else {
String sql="Update boe_study_course_item set study_duration=study_duration+"+duration+",status=(case when status<2 then 2 else status end) where study_id=?1 and content_id=?2";
scItemDao.sqlUpdate(sql,studyId,courseContentId);
}
String sql="Update boe_study_course set total_duration=total_duration+"+duration+",status=(case when status<2 then 2 else status end),progress=(case when progress=0 then 1 else progress end),last_time = '"+timestamp+"' where id=?1";
scDao.sqlUpdate(sql,studyId);
}
// 更新 前端传输已学习时长
@Override
public void updateStudyDuration(String studyId,String studyItemId,String courseContentId, Integer videoTime,String courseId) {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
String key = "studyId:" + studyId + ":courseId:" + courseId + ":courseContentId:" + courseContentId + ":studyItemId:" + studyItemId;
String currentValue = redisTemplate.opsForValue().get(key);
Integer lastDuration = 0;
Integer oldVideoTime = 0;
Integer sum = 10; // 原appendtime改为固定10秒调用一次接口
if (currentValue != null) {
String[] partValues = currentValue.split("&");
oldVideoTime = Integer.parseInt(partValues[0]);
lastDuration = Integer.parseInt(partValues[1]);
sum += lastDuration;
if(oldVideoTime > videoTime){
videoTime = oldVideoTime;// 取最大值最终入库
}
};
String value = videoTime + "&" + sum + "&" + now.format(formatter); // study_video_time & appendtime & time
log.info("-study-video-time-----value = " + value);
// 20250303 优化 多次更新改一次更新
// 更新Redis中的最后活跃时间带30秒过期
redisTemplate.opsForValue().set(
key,
value,
Duration.ofSeconds(2592000)
);
log.info("- 合并 updateStudyDuration -redis保存---value = " + value);
// Duration.ofDays(30) 也就是 2592000秒
}
@Override
@Transactional
public void appendStudyDuration(StudyTime st) {
@@ -166,7 +206,21 @@ public class StudyServiceImpl implements IStudyService{
@Override
public List<StudyCourseItem> findByStudyId(String studyId) {
return scItemDao.findList(OrderCondition.desc("lastTime"),FieldFilters.eq("studyId", studyId));
List<StudyCourseItem> list = scItemDao.findList(OrderCondition.desc("lastTime"),FieldFilters.eq("studyId", studyId));
for (StudyCourseItem item : list){
log.info("-- studyIndex -查询上次学习的是什么资源。mysql查询---------------- item = " + item);
String redisKey = "studyId:" + studyId + ":courseId:" + item.getCourseId() + ":courseContentId:" + item.getContentId() + ":studyItemId:" + item.getId();
log.info("-- studyIndex -查询上次学习的是什么资源。查询用户的学习情况---------------- redisKey = " + redisKey);
String redisValue = redisTemplate.opsForValue().get(redisKey);
log.info("-- studyIndex -查询上次学习的是什么资源。查询用户的学习情况---------------- redisValue = " + redisValue);
if (redisValue != null) {
String[] values = redisValue.split("&");
int duration = Integer.parseInt(values[0]);
item.setLastStudyTime(duration);
log.info("-- studyIndex -----set 结果---------------- LastStudyTime = " + item.getLastStudyTime());
}
}
return list;
}
@Override
@@ -357,6 +411,7 @@ public class StudyServiceImpl implements IStudyService{
log.info("-study-video-time-mysql保存---studyContentId = " + studyContentId);
}
@Override
public Map<String,Object> getLast(String aid) {
//按lastTime排序第一条,只是课件内容

View File

@@ -0,0 +1,19 @@
package com.xboe.school.vo;
import com.xboe.school.study.entity.StudyTime;
import lombok.Data;
/** appendtime 于 study-video-time 合并
* appendtime 参数 StudyTime
* study-video-time 参数 是 StudyTimeVo
*/
@Data
public class StudyTimeVo extends StudyTime {
private String itemId;
private Integer videoTime;
// private String contentId; // 已继承
// private String courseId; // 已继承
private Float progressVideo;
private Integer type; // 0 study-video-time , 1 appendtime
}

View File

@@ -60,6 +60,7 @@ public class SysUploaderApi extends ApiBaseController{
fileTypeSet.add("pptx");
fileTypeSet.add("pdf");
fileTypeSet.add("zip");
fileTypeSet.add("jpeg");
}
@RequestMapping(value = "/file/upload", method = RequestMethod.POST, produces = "application/json")