diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseAuditApi.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseAuditApi.java index 44eb3716..19810333 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseAuditApi.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseAuditApi.java @@ -9,6 +9,7 @@ import javax.servlet.http.HttpServletRequest; import com.xboe.api.ThirdApi; import com.xboe.module.course.dto.CourseParam; +import com.xboe.module.course.service.*; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -28,10 +29,6 @@ import com.xboe.module.course.entity.Course; import com.xboe.module.course.entity.CourseAudit; import com.xboe.module.course.entity.CourseContent; import com.xboe.module.course.entity.CourseHRBPAudit; -import com.xboe.module.course.service.ICourseAuditService; -import com.xboe.module.course.service.ICourseContentService; -import com.xboe.module.course.service.ICourseHRBPAuditService; -import com.xboe.module.course.service.ICourseService; import com.xboe.standard.enums.BoedxContentType; import com.xboe.standard.enums.BoedxCourseType; @@ -60,7 +57,8 @@ public class CourseAuditApi extends ApiBaseController{ private ICourseContentService ccontentService; @Resource private ThirdApi thirdApi; - + @Resource + private ICourseTagService tagService; /** * 教师需要审核的课程 @@ -426,6 +424,21 @@ public class CourseAuditApi extends ApiBaseController{ dto.getCourse().setEnabled(true);//设置启用状态问题 dto.getCourse().setPublished(false);//重新提交审核设置为未发布状态 try { + log.info("------课程提审-- 标签相关开始 ------- 课程ID = {} " , dto.getCourse().getId()); + Course oldCourse = StringUtils.isBlank(dto.getCourse().getId()) ? null : courseService.get(dto.getCourse().getId()); + if(oldCourse!=null && StringUtils.isNotEmpty(oldCourse.getTags())){ + String[] tagArray = oldCourse.getTags().split(","); + // 检查每个元素是否为纯数字 + for (String tag : tagArray) { + if (!tag.matches("\\d+")) { // 使用正则表达式检查是否为纯数字 + log.info("-------- 不是纯数字 ------- tag = {} " , tag); + oldCourse.setTags(null); // 兼容 + break; + } + } + } + tagService.updateTags(oldCourse,dto.getCourse(),cuser); + log.info("-----课程提审--- 标签相关结束 -------"); courseService.submitAndPublish(dto,cuser.getAccountId(),cuser.getName()); log.info("---------------在线课开始同步到讲师管理 ------- token = " + token); diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseFullTextApi.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseFullTextApi.java index c43f2a20..7f47f851 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseFullTextApi.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseFullTextApi.java @@ -11,7 +11,12 @@ import com.boe.feign.api.serverall.entity.UserData; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException; import com.xboe.api.ThirdApi; +import com.xboe.core.orm.FieldFilters; +import com.xboe.core.orm.IFieldFilter; +import com.xboe.core.orm.QueryBuilder; import com.xboe.data.outside.IOutSideDataService; +import com.xboe.module.course.entity.CourseTag; +import com.xboe.module.course.service.*; import com.xboe.module.course.vo.TeacherVo; import com.xboe.school.study.entity.StudyCourse; import com.xboe.school.study.service.IStudyCourseService; @@ -34,11 +39,6 @@ import com.xboe.module.course.dto.CourseTeacherDto; import com.xboe.module.course.entity.Course; import com.xboe.module.course.entity.CourseCrowd; import com.xboe.module.course.entity.CourseTeacher; -import com.xboe.module.course.service.CourseToCourseFullText; -import com.xboe.module.course.service.ICourseContentService; -import com.xboe.module.course.service.ICourseFullTextSearch; -import com.xboe.module.course.service.ICourseService; -import com.xboe.module.course.service.ICourseTeacherService; import lombok.extern.slf4j.Slf4j; @@ -63,7 +63,8 @@ public class CourseFullTextApi extends ApiBaseController{ ICourseFullTextSearch fullTextSearch; @Resource IOrganizationService organizationService; - + @Autowired + ICourseTagService courseTagService; @Resource IStudyCourseService IStudyCourseService; @@ -76,6 +77,8 @@ public class CourseFullTextApi extends ApiBaseController{ @Autowired StringRedisTemplate redisTemplate; + + /** * 课程的初始化 * @return @@ -310,7 +313,34 @@ public class CourseFullTextApi extends ApiBaseController{ } paras.setDevice(dto.getDevice()); - + String tagIds = dto.getTags(); + log.info("课程查询 tagIds = " + tagIds); + if (tagIds != null && tagIds != ""){ + paras.setTags(tagIds); + }else { + String tagName = dto.getKeyword(); + log.info("课程查询 关键字 = " + tagName); + if (StringUtils.isNotEmpty(tagName)){ + //精准查询 +// CourseTag courseTag = courseTagService.getTagByName(tagName); +// log.info("课程查询 关键字对应标签 = " + courseTag); +// if (courseTag != null){ +// paras.setTags(courseTag.getId()); +// } + // 获取所有标签并进行模糊匹配 + List allTags = courseTagService.getAllTags(); + List matchedTagIds = new ArrayList<>(); + for (CourseTag tag : allTags) { + // 使用模糊匹配(不区分大小写) + if (tag.getTagName() != null && tag.getTagName().toLowerCase().contains(tagName.toLowerCase())) { + matchedTagIds.add(tag.getId()); + } + } + if (!matchedTagIds.isEmpty()) { + paras.setTags(String.join(",", matchedTagIds)); + } + } + } try { //后续会根据当前用户的资源归属查询 PageList coursePageList = fullTextSearch.search(ICourseFullTextSearch.DEFAULT_INDEX_NAME,pager.getStartRow(), pager.getPageSize(),paras); @@ -402,6 +432,12 @@ public class CourseFullTextApi extends ApiBaseController{ c.setKeywordsList(keywordsList); } } + if (StringUtils.isNotBlank(c.getTags()) && c.getTags().matches("[0-9,]+")) { + List tagList = courseTagService.getTagsByIds(c.getTags()); + List tags = tagList.stream().map(CourseTag::getTagName).collect(Collectors.toList()); + c.setTagsList(tags); + } + } diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseManageApi.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseManageApi.java index ec9e8c14..7f62af7d 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseManageApi.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseManageApi.java @@ -12,6 +12,8 @@ import com.boe.feign.api.infrastructure.entity.CommonSearchVo; import com.boe.feign.api.infrastructure.entity.Dict; import com.xboe.api.ThirdApi; import com.xboe.module.course.dto.*; +import com.xboe.module.course.entity.*; +import com.xboe.module.course.service.*; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; @@ -31,19 +33,6 @@ import com.xboe.data.dto.UserOrgIds; import com.xboe.data.outside.IOutSideDataService; import com.xboe.data.service.IDataUserSyncService; import com.xboe.module.assistance.service.IEmailService; -import com.xboe.module.course.entity.Course; -import com.xboe.module.course.entity.CourseContent; -import com.xboe.module.course.entity.CourseCrowd; -import com.xboe.module.course.entity.CourseHRBPAudit; -import com.xboe.module.course.entity.CourseSection; -import com.xboe.module.course.entity.CourseTeacher; -import com.xboe.module.course.entity.CourseUpdateLog; -import com.xboe.module.course.service.ICourseContentService; -import com.xboe.module.course.service.ICourseCrowdService; -import com.xboe.module.course.service.ICourseHRBPAuditService; -import com.xboe.module.course.service.ICourseSectionService; -import com.xboe.module.course.service.ICourseService; -import com.xboe.module.course.service.ICourseTeacherService; import com.xboe.module.excel.ExportsExcelSenderUtil; import com.xboe.standard.enums.BoedxContentType; import com.xboe.standard.enums.BoedxCourseType; @@ -94,7 +83,8 @@ public class CourseManageApi extends ApiBaseController{ @Resource private ICourseHRBPAuditService hrbpAuditService; - + @Resource + private ICourseTagService tagService; @Resource IOutSideDataService outSideDataService; @@ -184,6 +174,12 @@ public class CourseManageApi extends ApiBaseController{ rs.put("dicts",dicts); } log.error("-------是否仅内网查看 = " + isPermission); + if (StringUtils.isNotBlank(course.getTags()) && course.getTags().matches("[0-9,]+")){ + List tagList = tagService.getTagsByIds(course.getTags()); + rs.put("tagList", tagList); + } + + rs.put("course",course); rs.put("contents",cclist); rs.put("sections",sectionlist); @@ -213,7 +209,7 @@ public class CourseManageApi extends ApiBaseController{ } return success(rs); } - + /** * 管理员审核列表,教师的审核不在这里,此审核也应该移动CourseAuditApi中去 * @param pager @@ -322,6 +318,25 @@ public class CourseManageApi extends ApiBaseController{ //填充必要的信息 try { + log.info("-------- 标签相关开始 ------- 课程ID = {} " , dto.getCourse().getId()); + CurrentUser userInfo = getCurrent(); + Course oldCourse = StringUtils.isBlank(dto.getCourse().getId()) ? null : courseService.get(dto.getCourse().getId()); + log.info("-------- 标签相关 ------- oldtags = {} " , oldCourse.getTags()); + if(oldCourse!=null && StringUtils.isNotEmpty(oldCourse.getTags())){ + String[] tagArray = oldCourse.getTags().split(","); + // 检查每个元素是否为纯数字 + for (String tag : tagArray) { + if (!tag.matches("\\d+")) { // 使用正则表达式检查是否为纯数字 + log.info("-------- 不是纯数字 ------- tag = {} " , tag); + oldCourse.setTags(null); // 兼容 + break; + } + } + } + log.info("-------- 标签相关 updateTags ------- oldtags = {} " , oldCourse.getTags()); + tagService.updateTags(oldCourse,dto.getCourse(),userInfo); + log.info("-------- 标签相关结束 -------"); + if(StringUtils.isBlank(dto.getCourse().getId())) { //只有在第一次添加保存时才会这样 fillCourseData(dto.getCourse()); @@ -349,8 +364,6 @@ public class CourseManageApi extends ApiBaseController{ /*** * 仅仅是保存 - * @param dto - * @return */ @PostMapping("/save-only-course") @AutoLog(module = "课程",action = "保存课程基本信息",info = "") @@ -377,6 +390,10 @@ public class CourseManageApi extends ApiBaseController{ //修改后重置,重新提交审核,重新发布 courseService.update(course,true); } + //查询是否需要标签提示 + String aid=getCurrent().getAccountId(); + Boolean isTip = courseService.getCourseTip(aid); + course.setIsTip(isTip); return success(course); } catch (Exception e) { log.error("整体保存课程信息错误",e); @@ -727,7 +744,6 @@ public class CourseManageApi extends ApiBaseController{ /** * 审核课程,这个是管理人员的审核。老师审核不在这里处理. - * @param id * @param title * @param pass * @param remark @@ -761,7 +777,6 @@ public class CourseManageApi extends ApiBaseController{ /** * 审核并发布,未完成的处理, - * @param id * @param title * @param pass * @param remark @@ -805,10 +820,6 @@ public class CourseManageApi extends ApiBaseController{ /** * 发布课程信息,已经没有单独的发布了 - * @param id - * @param title - * @param pass - * @param remark * @return */ @Deprecated @@ -1206,5 +1217,10 @@ public class CourseManageApi extends ApiBaseController{ return success(courses); } - + @PostMapping("/saveTip") + public JsonResponse saveTip(){ + String aid=getCurrent().getAccountId(); + courseService.saveTip(aid); + return success(true); + } } diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseTagApi.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseTagApi.java new file mode 100644 index 00000000..9e1155db --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseTagApi.java @@ -0,0 +1,192 @@ +package com.xboe.module.course.api; + +import com.xboe.common.OrderCondition; +import com.xboe.common.PageList; +import com.xboe.common.Pagination; +import com.xboe.core.CurrentUser; +import com.xboe.core.JsonResponse; +import com.xboe.core.api.ApiBaseController; +import com.xboe.core.orm.FieldFilters; +import com.xboe.core.orm.IFieldFilter; +import com.xboe.module.article.entity.Article; +import com.xboe.module.article.service.IArticleService; +import com.xboe.module.course.dto.CourseTagQueryDto; +import com.xboe.module.course.dto.CourseTagRelationDto; +import com.xboe.module.course.entity.CourseTag; +import com.xboe.module.course.entity.CourseTagRelation; +import com.xboe.module.course.service.ICourseTagService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +/** + * @ClassName:CourseTagApi + * @author:zhengge@oracle.com + * @since:2025/7/2614:27 + */ +@Slf4j +@RestController +@RequestMapping(value="/xboe/m/coursetag") +public class CourseTagApi extends ApiBaseController { + + @Resource + ICourseTagService courseTagService; + + /** + * 标签列表:分页查询 + * @param pager + * @param courseTagQueryDto + * @return + */ + /* @RequestMapping(value="/page",method= {RequestMethod.GET,RequestMethod.POST}) + public JsonResponse> find(Pagination pager, CourseTagQueryDto courseTagQueryDto){ + log.info("标签列表:分页查询 pager = " + pager); + log.info("标签列表:分页查询 courseTagQueryDto = " + courseTagQueryDto); + List filters=new ArrayList(); + OrderCondition order = null; + if (courseTagQueryDto != null){ + String tagId = courseTagQueryDto.getId(); + String tagName = courseTagQueryDto.getTagName(); + Boolean isHot = courseTagQueryDto.getIsHot(); + String orderField = courseTagQueryDto.getOrderField(); + Boolean isAsc = courseTagQueryDto.getOrderAsc(); + if (StringUtils.isNotBlank(tagId)){ + filters.add(FieldFilters.eq("id",tagId)); + } + //课程标签名称:模糊查询 + if (StringUtils.isNotBlank(tagName)){ + filters.add(FieldFilters.like("tagName",tagName)); + } + // 构建排序条件:支持先按lastSetHotTime降序,再按动态字段升/降序排列‌ + if (isHot !=null ){ + filters.add(FieldFilters.eq("isHot",isHot)); + //order = OrderCondition.desc("lastSetHotTime");//固定降序 + } + // 动态排序处理 + if (StringUtils.isNotBlank(orderField)) { + if (order == null) { + order = isAsc ? OrderCondition.asc(orderField) : OrderCondition.desc(orderField); + } else { + order = isAsc ? order.asc(orderField) : order.desc(orderField); // 链式追加排序条件 + } + } + } + log.info("标签列表:分页查询 调用接口 filters = " + filters); + log.info("标签列表:分页查询 调用接口 order = " + order); + PageList list=courseTagService.query(pager.getPageIndex(),pager.getPageSize(),filters,order); + return success(list); + } +*/ + /** + * 修改指定id的课程标签的公共属性 + * @param id + * @param isPublic + * @return + */ +/* + @RequestMapping(value="/changePublicStatus",method= RequestMethod.POST) + public JsonResponse changePublicStatus(Long id,Boolean isPublic){ + courseTagService.changePublicStatus(id,isPublic); + return success(null); + } +*/ + + /** + * 修改指定id的课程标签的热点属性 + * @param id + * @param isHot + * @return + */ +/* @RequestMapping(value="/changeHotStatus",method= RequestMethod.POST) + public JsonResponse changeHotStatus(Long id,Boolean isHot){ + courseTagService.changeHotStatus(id,isHot); + return success(null); + }*/ + + /** + * 分页查询:指定id的标签关联的所有课程 + * @param courseTagQueryDto + * @return + */ +/* + @RequestMapping(value="/showCourseByTag",method= RequestMethod.POST) + public JsonResponse> showCourseByTag(Pagination pager, CourseTagQueryDto courseTagQueryDto){ + PageList list=null; + if (courseTagQueryDto != null) { + Long tagId = Long.valueOf(courseTagQueryDto.getId()); + Boolean isAsc = courseTagQueryDto.getOrderAsc()!=null?courseTagQueryDto.getOrderAsc():false; + list=courseTagService.getCourseByTag(pager.getPageIndex(),pager.getPageSize(),tagId,isAsc); + } + return success(list); + } +*/ + + /** + * 解除指定id的课程和某个标签之间的关联关系 + * @return + */ +/* + @RequestMapping(value="/unbind",method= RequestMethod.POST) + public JsonResponse unbindCourseTagRelation(CourseTagRelationDto courseTagRelationDto){ + if (courseTagRelationDto!=null){ + courseTagService.unbind(courseTagRelationDto.getId()); + return success(null); + } + return error("解绑失败!"); + } +*/ + + /** + * 模糊检索标签 + * @return 符合检索条件的所有公共标签 + */ + @RequestMapping(value="/searchTags",method= RequestMethod.POST) + public JsonResponse> searchTags(String tagName,String typeId ,HttpServletRequest request){ + + CurrentUser cuser = getCurrent(); + String aid = cuser.getAccountId(); + List courseTagList = courseTagService.searchTags(tagName,aid,typeId); + return success(courseTagList); + } + + /** + * 创建新标签,并与当前课程绑定 + * @param courseTagRelationDto + * @return + */ + @RequestMapping(value="/createTag",method= RequestMethod.POST) + public JsonResponse createTag(CourseTagRelationDto courseTagRelationDto){ + if (StringUtils.isNotBlank(courseTagRelationDto.getTagName()) && !Pattern.matches("^[\\u4e00-\\u9fa5a-zA-Z0-9_-]+$", courseTagRelationDto.getTagName())) { + return error("标签名称只能包含中文、英文、数字、下划线和中横线"); + } + if (courseTagRelationDto!=null){ + CourseTag courseTag = courseTagService.createTag(courseTagRelationDto); + if (courseTag == null ){ + return error("创建标签失败!"); + } + return success(courseTag); + } + return error("创建标签失败!"); + } + + /** + * 创建新标签,并与当前课程绑定 + * @param courseTagRelationDto + * @return + */ + @RequestMapping(value="/getHotTagList",method= RequestMethod.POST) + public JsonResponse> getHotTagList(CourseTagRelationDto courseTagRelationDto){ + List hotTagList = courseTagService.getHotTagList(courseTagRelationDto); + return success(hotTagList); + } + +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTagDao.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTagDao.java new file mode 100644 index 00000000..a20dcda9 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTagDao.java @@ -0,0 +1,202 @@ +package com.xboe.module.course.dao; + +import com.xboe.common.OrderCondition; +import com.xboe.common.PageList; +import com.xboe.core.SysConstant; +import com.xboe.core.orm.BaseDao; +import com.xboe.core.orm.FieldFilters; +import com.xboe.core.orm.IFieldFilter; +import com.xboe.core.orm.IQuery; +import com.xboe.module.course.entity.Course; +import com.xboe.module.course.entity.CourseFile; +import com.xboe.module.course.entity.CourseTag; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import java.util.ArrayList; +import java.util.List; + +/** + * @ClassName:CourseTagDao + * @author:zhengge@oracle.com + * @since:2025/7/2516:50 + */ + +@Slf4j +@Repository +public class CourseTagDao extends BaseDao { + @PersistenceContext + private EntityManager entityManager; + + /** + * 获取热门标签列表(前10条) + * @return 热门标签列表 + */ + public List getHotTagList() { + // 原生SQL:注意表名和列名需与数据库实际一致 + String sql = "select t.*,COUNT(r.tag_id) AS relation_count\n" + + "from boe_course_tag t\n" + + "left join boe_course_tag_relation r\n" + + "on t.id = r.tag_id AND r.deleted =0 \n" + + "where t.deleted =0 and t.is_hot = true and t.status =0 \n" + + "GROUP BY t.id\n" + + "order by t.last_set_hot_time desc"; // 数据库字段为last_set_hot_time + + // 创建原生查询并指定结果映射到CourseTag实体 + javax.persistence.Query query = entityManager.createNativeQuery(sql, CourseTag.class); + + // 分页:取前10条 + query.setFirstResult(0); + query.setMaxResults(10); + + // 执行查询并返回结果(已映射为CourseTag类型) + return query.getResultList(); + } + + /** + * 根据课程类型获取热门标签列表(前10条) + * @param sysType1 系统类型1 + * @param sysType2 系统类型2 + * @param sysType3 系统类型3 + * @return 热门标签列表 + */ + public List getHotTagListBySysTypes(String sysType1, String sysType2, String sysType3) { + // 原生SQL:注意表名和列名需与数据库实际一致(此处假设表名为course_tag、course_type_tag_relation) + String sql = "SELECT DISTINCT c.* FROM boe_course_tag c " + + "JOIN boe_course_type_tag_relation r ON c.id = r.tag_id " + + "WHERE r.deleted = 0 and c.status =0 " + + "AND c.is_hot = true "; // 假设数据库字段为is_hot(与实体属性isHot对应) + if (StringUtils.isNotBlank(sysType1)){ + sql += "AND r.sys_type1 = ?1 ORDER BY c.last_set_hot_time DESC"; + }else if(StringUtils.isNotBlank(sysType2)){ + sql += "AND r.sys_type2 = ?1 ORDER BY c.last_set_hot_time DESC"; + }else { + sql += "AND r.sys_type3 = ?1 ORDER BY c.last_set_hot_time DESC"; + } + // 创建原生查询并指定结果映射到CourseTag实体 + javax.persistence.Query query = entityManager.createNativeQuery(sql, CourseTag.class); + + // 绑定参数(注意参数索引从1开始) + if (StringUtils.isNotBlank(sysType1)){ + query.setParameter(1, sysType1); + } else if (StringUtils.isNotBlank(sysType2)) { + query.setParameter(1, sysType2); + }else { + query.setParameter(1, sysType3); + } + // 分页:取前10条 + query.setFirstResult(0); + query.setMaxResults(10); + + // 执行查询并返回结果(已映射为CourseTag类型) + return query.getResultList(); + } + + public List getTagsByIds(String id) { + String sql = "select * from " + SysConstant.TABLE_PRE + "course_tag where id in (" + id + "0)"; + // 创建原生查询并指定结果映射到CourseTag实体 + javax.persistence.Query query = entityManager.createNativeQuery(sql, CourseTag.class); + return query.getResultList(); + } + + public CourseTag getTagByName(String tagName) { + CourseTag courseTag = this.findOne(FieldFilters.eq("tag_name", tagName),FieldFilters.eq("deleted", false),FieldFilters.eq("status", 0)); + return courseTag; + } + + public PageList getList() { + log.info("------- getList ----------- "); + String sql = "select * from boe_course_tag order by sys_create_time desc limit 10"; + javax.persistence.Query query = entityManager.createNativeQuery(sql, CourseTag.class); + log.info("------- getList -----------getResultList = " + query.getResultList() ); + PageList pageList = new PageList<>(); + pageList.setCount(query.getResultList().size()); + pageList.setPageSize(1); + pageList.setList(query.getResultList()); + + return pageList; + } + + public List searchTags(String tagName, String userId, String typeId) { + StringBuilder sql = new StringBuilder(); + List parameters = new ArrayList<>(); + + // 只查询实际存在的字段 + sql.append("SELECT id, tag_name, is_public, is_hot, use_count, last_set_public_time, last_set_hot_time, deleted, sys_create_time "); + sql.append("FROM ( "); + sql.append(" SELECT t.id, t.tag_name, t.is_public, t.is_hot, t.use_count, t.last_set_public_time, t.last_set_hot_time, t.deleted, t.sys_create_time "); + sql.append(" FROM boe_course_tag_relation r "); + sql.append(" INNER JOIN boe_course_tag t ON r.tag_id = t.id "); + sql.append(" WHERE r.deleted = 0 AND t.deleted = 0 AND t.is_public = 0 AND t.status = 0 "); + if (StringUtils.isNotBlank(userId)) { + sql.append(" AND r.sys_create_aid = ? "); + parameters.add(Long.valueOf(userId)); + } + if (StringUtils.isNotBlank(tagName)) { + sql.append(" AND t.tag_name LIKE ? "); + parameters.add("%" + tagName + "%"); + } + sql.append(" GROUP BY t.id, t.tag_name, t.is_public, t.is_hot, t.use_count, t.last_set_public_time, t.last_set_hot_time, t.deleted, t.sys_create_time "); + sql.append(" UNION ALL "); + sql.append(" SELECT id, tag_name, is_public, is_hot, use_count, last_set_public_time, last_set_hot_time, deleted, sys_create_time "); + sql.append(" FROM boe_course_tag "); + sql.append(" WHERE deleted = 0 AND is_public = 1 AND status = 0 "); + if (StringUtils.isNotBlank(tagName)) { + sql.append(" AND tag_name LIKE ? "); + parameters.add("%" + tagName + "%"); + } + sql.append(") AS all_tags "); + + if (StringUtils.isNotBlank(typeId)) { + sql.append("ORDER BY "); + sql.append(" CASE WHEN id IN ( "); + sql.append(" SELECT tag_id "); + sql.append(" FROM boe_course_type_tag_relation "); + sql.append(" WHERE deleted = 0 "); + sql.append(" AND (sys_type1 = ? "); + sql.append(" OR sys_type2 = ? "); + sql.append(" OR sys_type3 = ?) "); + sql.append(" GROUP BY tag_id "); + sql.append(" ) THEN 0 ELSE 1 END "); + parameters.add(Long.valueOf(typeId)); + parameters.add(Long.valueOf(typeId)); + parameters.add(Long.valueOf(typeId)); + } + +// sql.append(" sys_update_time DESC"); + log.info("查询标签 searchTags sql = {} ", sql); + // 不使用实体类映射,手动处理结果集 + Query query = entityManager.createNativeQuery(sql.toString()); + + for (int i = 0; i < parameters.size(); i++) { + query.setParameter(i + 1, parameters.get(i)); + } + + @SuppressWarnings("unchecked") + List results = query.getResultList(); + List courseTags = new ArrayList<>(); + + for (Object[] result : results) { + CourseTag tag = new CourseTag(); + // 设置基本字段 + if (result[0] != null) tag.setId(String.valueOf(result[0])); + if (result[1] != null) tag.setTagName(String.valueOf(result[1])); + if (result[2] != null) tag.setIsPublic(Boolean.valueOf(String.valueOf(result[2]))); + if (result[3] != null) tag.setIsHot(Boolean.valueOf(String.valueOf(result[3]))); + if (result[4] != null) tag.setUseCount(Integer.valueOf(String.valueOf(result[4]))); + courseTags.add(tag); + } + + return courseTags; + } + + + + + +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTagRelationDao.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTagRelationDao.java new file mode 100644 index 00000000..dd798a1c --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTagRelationDao.java @@ -0,0 +1,130 @@ +package com.xboe.module.course.dao; + +import com.xboe.common.PageList; +import com.xboe.core.orm.BaseDao; +import com.xboe.module.course.dto.CourseTagRelationDto; +import com.xboe.module.course.entity.CourseTagRelation; +import org.springframework.stereotype.Repository; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @ClassName:CourseTagRelationDao + * @author:zhengge@oracle.com + * @since:2025/7/2815:09 + */ +@Repository +public class CourseTagRelationDao extends BaseDao { + + @PersistenceContext + private EntityManager entityManager; + + private String sqlStr = "SELECT " + + " r1.id as id, " + + " c.id as courseId, " + + " r1.tag_id as tagId, " + + " c.`name` as courseName, " + + " r1.sys_create_by as sysCreateBy, " + + " r1.sys_create_time as sysCreateTime, " + + " COALESCE(GROUP_CONCAT(DISTINCT t.tag_name ORDER BY t.tag_name), '') AS otherTags " + + "FROM " + + " boe_course c " + + "JOIN " + + " boe_course_tag_relation r1 ON c.id = r1.course_id " + + "LEFT JOIN " + + " ( " + + " boe_course_tag_relation r2 " + + " JOIN boe_course_tag t ON r2.tag_id = t.id AND t.deleted = 0 " + + " ) " + + " ON c.id = r2.course_id AND r2.tag_id != r1.tag_id " + + "WHERE " + + " r1.tag_id = :tagId AND r1.deleted = 0 " + + " AND c.id IN ( " + + " SELECT course_id " + + " FROM boe_course_tag_relation " + + " WHERE tag_id = :tagId " + + " ) " + + "GROUP BY " + + " c.id, c.`name` "; + + public PageList findCoursesWithRelatedTagsDesc(Integer pageIndex, Integer pageSize, Long tagId){ + String sql = sqlStr + " ORDER BY r1.sys_create_time DESC"; + Query query = entityManager.createNativeQuery(sql); + query.setParameter("tagId", tagId); + query.setFirstResult((pageIndex - 1) * pageSize); // 设置起始位置 + query.setMaxResults(pageSize); // 设置每页大小 + + Query countQuery = entityManager.createNativeQuery(sql); + countQuery.setParameter("tagId", tagId); + List totalresults = countQuery.getResultList(); + + List results = query.getResultList(); + List list = results.stream() + .map(row -> { + String id = String.valueOf(row[0]); + String courseId = String.valueOf(row[1]); + String tagId2 = String.valueOf(row[2]); + return new CourseTagRelationDto( + id, + courseId, + tagId2, + (String) row[3], + (String) row[4], + (Date) row[5], + (String) row[6] + ); + }) + .collect(Collectors.toList()); + return new PageList(list,totalresults!=null?totalresults.size():0); + } + + public PageList findCoursesWithRelatedTagsAsc(Integer pageIndex, Integer pageSize, Long tagId) { + String sql = sqlStr + " ORDER BY r1.sys_create_time ASC"; + Query query = entityManager.createNativeQuery(sql); + query.setParameter("tagId", tagId); + query.setFirstResult((pageIndex - 1) * pageSize); // 设置起始位置 + query.setMaxResults(pageSize); // 设置每页大小 + + Query countQuery = entityManager.createNativeQuery(sql); + countQuery.setParameter("tagId", tagId); + List totalresults = countQuery.getResultList(); + + List results = query.getResultList(); + List list = results.stream() + .map(row ->{ + String id = String.valueOf(row[0]); + String courseId = String.valueOf(row[1]); + String tagId2 = String.valueOf(row[2]); + return new CourseTagRelationDto( + id, + courseId, + tagId2, + (String) row[3], + (String) row[4], + (Date) row[5], + (String) row[6] + ); + }) + .collect(Collectors.toList()); + return new PageList(list,totalresults!=null?totalresults.size():0); + } + + public void reTagRelDelStatus(String id,String name) { + String sql = "UPDATE boe_course_tag_relation SET deleted = FALSE, sys_update_by = '" + name + + "', sys_update_time = NOW() WHERE id = " + id; + Query query = entityManager.createNativeQuery(sql); + query.executeUpdate(); + } + + public void reTypeTagRelDelStatus(String id,String name) { + String sql = "UPDATE boe_course_type_tag_relation SET deleted = FALSE, sys_update_by = '" + name + + "', sys_update_time = NOW() WHERE id = " + id; + Query query = entityManager.createNativeQuery(sql); + query.executeUpdate(); + } +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTypeTagRelationDao.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTypeTagRelationDao.java new file mode 100644 index 00000000..c9fcba30 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTypeTagRelationDao.java @@ -0,0 +1,17 @@ +package com.xboe.module.course.dao; + +import com.xboe.core.orm.BaseDao; +import com.xboe.module.course.entity.CourseTagRelation; +import com.xboe.module.course.entity.CourseTypeTagRelation; +import org.springframework.stereotype.Repository; + +/** + * @ClassName:CourseTypeTagRelationDao + * @author:zhengge@oracle.com + * @since:2025/8/113:42 + */ +@Repository +public class CourseTypeTagRelationDao extends BaseDao { + + +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/TipDao.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/TipDao.java new file mode 100644 index 00000000..e333b377 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/TipDao.java @@ -0,0 +1,12 @@ +package com.xboe.module.course.dao; + +import com.xboe.core.orm.BaseDao; +import com.xboe.module.course.entity.Tip; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + + +@Slf4j +@Repository +public class TipDao extends BaseDao { +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseQueryDto.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseQueryDto.java index 5829d723..f4a1fb66 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseQueryDto.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseQueryDto.java @@ -1,5 +1,6 @@ package com.xboe.module.course.dto; +import com.xboe.module.course.entity.CourseTag; import lombok.Data; import java.util.List; @@ -140,4 +141,6 @@ public class CourseQueryDto { */ private String userId; + private String tags; + } diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseTagQueryDto.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseTagQueryDto.java new file mode 100644 index 00000000..b276f007 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseTagQueryDto.java @@ -0,0 +1,40 @@ +package com.xboe.module.course.dto; + +import lombok.Data; + +/** + * 课程标签查询的条件对象 + * @ClassName:CourseTagQueryDto + * @author:zhengge@oracle.com + * @since:2025/7/2517:02 + */ +@Data +public class CourseTagQueryDto { + + /** + * 标签id + */ + private String id; + + /** + * 标签名称 + */ + private String tagName; + + + /** + * 是否热点标签( 0-否(默认) 1-是) + */ + private Boolean isHot; + + /** + * 排序字段 + */ + private String orderField; + + /** + * 排序顺序 + */ + private Boolean orderAsc; + +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseTagRelationDto.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseTagRelationDto.java new file mode 100644 index 00000000..c276b15f --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseTagRelationDto.java @@ -0,0 +1,49 @@ +package com.xboe.module.course.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import java.time.LocalDateTime; +import java.util.Date; + +/** + * @ClassName:CourseTagRelationDto + * @author:zhengge@oracle.com + * @since:2025/7/2815:00 + */ +@Data +@NoArgsConstructor +public class CourseTagRelationDto{ + + private String id; + private String courseId; + private String tagId; + private String tagName; + private String courseName; + private String sysCreateBy; + private Date sysCreateTime; + private String otherTags; // 改为字符串类型,与 GROUP_CONCAT 结果匹配 + private String sysType1; + private String sysType2; + private String sysType3; + + // 添加匹配查询字段顺序的构造函数 + public CourseTagRelationDto( + String id, + String courseId, + String tagId, + String courseName, + String sysCreateBy, + Date sysCreateTime, + String otherTags + ) { + this.id = id; + this.courseId = courseId; + this.tagId = tagId; + this.courseName = courseName; + this.sysCreateBy = sysCreateBy; + this.sysCreateTime = sysCreateTime; + this.otherTags = otherTags; + } +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/Course.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/Course.java index 69804e20..83aa02b3 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/Course.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/Course.java @@ -399,7 +399,13 @@ public class Course extends BaseEntity { @Transient private String teacher; - + + /** + * 新增在线课时是否需要标签提示 + */ + @Transient + private Boolean isTip; + public Course(String id,String name,String summary,String coverImg,String sysCreateAid,String sysCreateBy,Integer type,LocalDateTime publishTime){ super.setId(id); this.name=name; diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTag.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTag.java new file mode 100644 index 00000000..f3238dfc --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTag.java @@ -0,0 +1,98 @@ +package com.xboe.module.course.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.xboe.core.SysConstant; +import com.xboe.core.orm.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.time.LocalDateTime; + +/** + * 在线课程的标签类 + * @ClassName:CourseTag + * @author:zhengge@oracle.com + * @since:2025/7/25 16:37 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Entity +@Table(name = SysConstant.TABLE_PRE+"course_tag") +public class CourseTag extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 标签名称 + */ + @Column(name = "tag_name",nullable=false, length = 50) + private String tagName; + + /** + * 是否设置为公共标签 + */ + @Column(name = "is_public",length = 1) + private Boolean isPublic; + + /** + * 是否设置为热点标签 + */ + @Column(name = "is_hot",length = 1) + private Boolean isHot; + + /** + * 使用次数(关联课程数) + */ + @Column(name = "use_count",length = 1) + private Integer useCount; + + /** + * 1临时, 0正式 + */ + @Column(name = "status",length = 1) + private Integer status; + + /** + * 最近设置为公共标签的时间 + */ + @Column(name = "last_set_public_time", nullable = true) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime lastSetPublicTime; + + /** + * 最近设置为热点标签的时间 + */ + @Column(name = "last_set_hot_time", nullable = true) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime lastSetHotTime; + + public CourseTag() { + + } + public CourseTag(Long id, Boolean isPublic,Boolean isHot) { + this.setId(String.valueOf(id)); + this.isPublic=isPublic; + this.isHot=isHot; + } + + + public CourseTag(String id,String tagName,String sysCreateBy,String sysCreateAid,LocalDateTime sysCreateTime, + Boolean isPublic,Boolean isHot,Integer useCount,LocalDateTime lastSetPublicTime,LocalDateTime lastSetHotTime,Boolean deleted){ + this.setId(id); + this.setTagName(tagName); + super.setSysCreateBy(sysCreateBy); + super.setSysCreateAid(sysCreateAid); + super.setSysCreateTime(sysCreateTime); + this.isPublic = isPublic; + this.isHot = isHot; + this.useCount = useCount; + this.lastSetPublicTime = lastSetPublicTime; + this.lastSetHotTime = lastSetHotTime; + super.setDeleted(deleted); + } + + +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTagRelation.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTagRelation.java new file mode 100644 index 00000000..714f9e5e --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTagRelation.java @@ -0,0 +1,37 @@ +package com.xboe.module.course.entity; + +import com.xboe.core.SysConstant; +import com.xboe.core.orm.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * @ClassName:CourseTagRelation + * @author:zhengge@oracle.com + * @since:2025/7/2814:54 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Entity +@Table(name = SysConstant.TABLE_PRE+"course_tag_relation") +public class CourseTagRelation extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 课程Id + */ + @Column(name = "course_id",length = 20) + private Long courseId; + + /** + * 标签id + */ + @Column(name = "tag_id",length = 20) + private Long tagId; + +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTypeTagRelation.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTypeTagRelation.java new file mode 100644 index 00000000..20f37a3c --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTypeTagRelation.java @@ -0,0 +1,39 @@ +package com.xboe.module.course.entity; + +import com.xboe.core.SysConstant; +import com.xboe.core.orm.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * @ClassName:CourseTypeTagRelation + * @author:zhengge@oracle.com + * @since:2025/8/111:02 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Entity +@Table(name = SysConstant.TABLE_PRE+"course_type_tag_relation") +public class CourseTypeTagRelation extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @Column(name = "sys_type1",length = 20) + private String sysType1; + + @Column(name = "sys_type2",length = 20) + private String sysType2; + + @Column(name = "sys_type3",length = 20) + private String sysType3; + + @Column(name = "tag_id",length = 20) + private String tagId; + + + +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/Tip.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/Tip.java new file mode 100644 index 00000000..3b598a4f --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/Tip.java @@ -0,0 +1,36 @@ +package com.xboe.module.course.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.xboe.core.SysConstant; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.persistence.*; +import java.time.LocalDateTime; + +/** + * @author by lyc + * @date 2025/11/10 + */ +@Data +@Entity +@EqualsAndHashCode(callSuper = false) +@Table(name = "tip") +public class Tip { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", length = 20) + private Long id; + + @Column(name = "aid", length = 30) + private String aid; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Column(name = "create_time", length = 30) + private LocalDateTime create_time; + + // 0 标签提示 + @Column(name = "type", length = 3) + private Integer type; + +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/service/CourseToCourseFullText.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/CourseToCourseFullText.java index 09bb6716..5d4ac75c 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/course/service/CourseToCourseFullText.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/CourseToCourseFullText.java @@ -52,6 +52,7 @@ public class CourseToCourseFullText { cft.setTeacher(""); cft.setTeacherCode(""); cft.setType(c.getType()); + cft.setTags(c.getTags()); if(c.getOpenCourse()==null) { cft.setOpenCourse(0); }else { diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/service/ICourseService.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/ICourseService.java index fe942b3d..80cd8997 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/course/service/ICourseService.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/ICourseService.java @@ -343,4 +343,10 @@ public interface ICourseService { List findByIds(List courseIds); void deletedStudyResourceBatchByCourseIdAndType(String courseId,Integer courseType); + + void saveTip(String aid); + + Boolean getCourseTip(String aid); + +// void getPhpCourseData(); } diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/service/ICourseTagService.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/ICourseTagService.java new file mode 100644 index 00000000..75192df1 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/ICourseTagService.java @@ -0,0 +1,97 @@ +package com.xboe.module.course.service; + +import com.xboe.common.OrderCondition; +import com.xboe.common.PageList; +import com.xboe.core.CurrentUser; +import com.xboe.core.JsonResponse; +import com.xboe.core.orm.IFieldFilter; +import com.xboe.module.article.entity.Article; +import com.xboe.module.course.dto.CourseTagQueryDto; +import com.xboe.module.course.dto.CourseTagRelationDto; +import com.xboe.module.course.entity.Course; +import com.xboe.module.course.entity.CourseTag; +import com.xboe.module.course.entity.CourseTagRelation; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.util.List; + +/** + * @InterfaceName:ICourseTagService + * @author:zhengge@oracle.com + * @since:2025/7/2516:53 + */ +public interface ICourseTagService { + + /** + * 分页查询标签列表,使用自定义filter + * @param pageIndex + * @param pageSize + * @return + */ + PageList query(Integer pageIndex, Integer pageSize, List filters, OrderCondition order); + + /** + * 分页查询指定id标签关联的课程列表,使用自定义filter + * @param pageIndex + * @param pageSize + * @return + */ + PageList getCourseByTag(Integer pageIndex, Integer pageSize, Long tagId, Boolean isAsc); + + /** + * 修改指定id的课程标签的公共属性 + * @param id + * @param isPublic + * @return + */ + void changePublicStatus(Long id,Boolean isPublic); + + /** + * 修改指定id的课程标签的热点属性 + * @param id + * @param isHot + * @return + */ + void changeHotStatus(Long id,Boolean isHot); + + /** + * 解除指定id的课程和某个标签之间的关联关系 + * @return + */ + void unbind(String id); + + /** + * 根据标签名称进行检索(模糊查询) + * @param tagName + * @return 符合检索条件的所有公共标签 + */ + List searchTags(String tagName,String userId,String typeId); + + /** + * 创建新标签,并与当前课程绑定 + * @param courseTagRelationDto + * @return + */ + CourseTag createTag(CourseTagRelationDto courseTagRelationDto); + + /** + * 根据课程类型获取热点标签 + * @param courseTagRelationDto + * @return + */ + List getHotTagList(CourseTagRelationDto courseTagRelationDto); + + /** + * 根据多个id获取标签 + * @param id + * @return + */ + List getTagsByIds(String id); + + CourseTag getTagByName(String tagName); + + void updateTags(Course oldCourse,Course newCourse,CurrentUser userInfo); + + List getAllTags(); +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/service/impl/CourseServiceImpl.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/impl/CourseServiceImpl.java index d2639f4d..ad682ae6 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/course/service/impl/CourseServiceImpl.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/impl/CourseServiceImpl.java @@ -18,13 +18,24 @@ import javax.management.Query; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.json.JSONUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; import com.xboe.api.ThirdApi; import com.xboe.core.orm.*; import com.xboe.module.course.dao.*; import com.xboe.module.course.entity.*; +import com.xboe.module.course.dao.*; +import com.xboe.module.course.dto.CourseTagRelationDto; +import com.xboe.module.course.entity.*; +import com.xboe.module.course.service.ICourseTagService; import com.xboe.school.study.dao.StudyCourseDao; import com.xboe.school.study.entity.StudyCourse; import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.HttpClient; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.index.query.BoolQueryBuilder; @@ -87,7 +98,8 @@ public class CourseServiceImpl implements ICourseService { @Resource private CourseHRBPAuditDao courseHRBPAuditDao; - + @Resource + private ICourseTagService courseTagService; @Resource private SysLogAuditDao logAuditDao;//审核日志记录 @@ -113,6 +125,8 @@ public class CourseServiceImpl implements ICourseService { @Resource RestHighLevelClient restHighLevelClient; + @Resource + private TipDao tipDao; @Resource private CourseTeacherDeletedRecordDao courseTeacherDeletedRecordDao; @@ -274,13 +288,13 @@ public class CourseServiceImpl implements ICourseService { //// Setlist=new HashSet<>(); //// if(s!=null&&!s.isEmpty()){ //// list=Arrays.stream(s.split(",")).collect(Collectors.toSet()); -//// }else { +//// }else { //// Set ss = getSeache(dto); //// String courseSearch=String.join(",",ss); //// redisTemplate.opsForValue().set("course_search",courseSearch); //// //设置过期时间为1分钟 //// redisTemplate.expire("course_search", 1, TimeUnit.MINUTES); -//// } +//// } // Set list = getSeache(dto); // //有权限的查询,也同时查询出创建人的数据,在权限上 // if(TempFilterConfig.Manager_CourseFile_ByOrgIds) { @@ -360,9 +374,8 @@ public class CourseServiceImpl implements ICourseService { // // 使用distinct()配合自定义的去重条件 // .filter(distinctByKey(c -> c.getId())) // .collect(Collectors.toList()); - - /// / PageList rs=courseDao.findPage(pageIndex, pageSize, filters, oc); - /// / long endTime = System.nanoTime(); +//// PageList rs=courseDao.findPage(pageIndex, pageSize, filters, oc); +//// long endTime = System.nanoTime(); // //log.info("查询出的条数:"+rs.getCount()); // if(!mergedList.isEmpty()){ // //去掉未发布的课程 @@ -880,6 +893,11 @@ public class CourseServiceImpl implements ICourseService { public Course getAddView(String id) { Course c = courseDao.get(id); courseDao.updateFieldById(id, "views", c.getViews() + 1); + if (StringUtils.isNotBlank(c.getTags()) && c.getTags().matches("[0-9,]+")) { + List tagList = courseTagService.getTagsByIds(c.getTags()); + String tags = tagList.stream().map(CourseTag::getTagName).collect(Collectors.joining(",")); + c.setTags(tags); + } return c; } @@ -993,6 +1011,30 @@ public class CourseServiceImpl implements ICourseService { if (c.getVisible() == null) { c.setVisible(true); } + /*if (c.getTags() != null && !c.getTags().isEmpty()){ + CourseTagRelationDto courseTagRelationDto = new CourseTagRelationDto(); + courseTagRelationDto.setCourseId(c.getId()); + courseTagRelationDto.setSysType1(c.getSysType1()); + courseTagRelationDto.setSysType2(c.getSysType2()); + courseTagRelationDto.setSysType3(c.getSysType3()); + String tags = c.getTags(); + List tagList = courseTagService.getTagsByIds(tags); + if (ObjectUtil.isNotEmpty(tagList)){ + for (CourseTag tag : tagList) { + courseTagRelationDto.setTagName(tag.getTagName()); + courseTagService.createTag(courseTagRelationDto); + } + } + }*/ + + if (!nowCourse.getSysVersion().equals(c.getSysVersion())) { + log.warn("乐观锁冲突 - 课程ID: {}, 期望版本: {}, 实际版本: {}", + c.getId(), c.getSysVersion(), nowCourse.getSysVersion()); +// throw new RuntimeException("数据已被其他用户修改,请刷新页面后重试"); + // 基本无概率同时修改同一课程 如有 以最后提交为准 + c.setSysVersion(courseDao.getVersion(c.getId())); + } + courseDao.update(c); c.setSysVersion(courseDao.getVersion(c.getId())); full.getCourse().setSysVersion(c.getSysVersion()); @@ -1160,7 +1202,7 @@ public class CourseServiceImpl implements ICourseService { /*** * 发布全文索引 - * @param c + * @param */ // private void fullTextPublish(Course c) { // if(fullTextSearch==null) { @@ -2054,4 +2096,24 @@ public class CourseServiceImpl implements ICourseService { } } + + @Override + public void saveTip(String aid) { + Tip item = new Tip(); + item.setAid(aid); + item.setType(0); + item.setCreate_time(LocalDateTime.now()); + tipDao.save(item); + } + + @Override + public Boolean getCourseTip(String aid) { + log.info("getCourseTip aid = {} ",aid); + List list = tipDao.findList(FieldFilters.eq("aid", aid)); + log.info("getCourseTip list = {} ",list); + if (list != null && !list.isEmpty()){ + return false;//已提示 + } + return true; //用户需要提示 + } } diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/course/service/impl/CourseTagServiceImpl.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/impl/CourseTagServiceImpl.java new file mode 100644 index 00000000..744f3456 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/impl/CourseTagServiceImpl.java @@ -0,0 +1,877 @@ +package com.xboe.module.course.service.impl; + +import com.xboe.common.OrderCondition; +import com.xboe.common.PageList; +import com.xboe.core.CurrentUser; +import com.xboe.core.orm.BaseEntity; +import com.xboe.core.orm.FieldFilters; +import com.xboe.core.orm.IFieldFilter; +import com.xboe.core.orm.QueryBuilder; +import com.xboe.module.course.dao.CourseDao; +import com.xboe.module.course.dao.CourseTagDao; +import com.xboe.module.course.dao.CourseTagRelationDao; +import com.xboe.module.course.dao.CourseTypeTagRelationDao; +import com.xboe.module.course.dto.CourseTagRelationDto; +import com.xboe.module.course.entity.Course; +import com.xboe.module.course.entity.CourseTag; +import com.xboe.module.course.entity.CourseTagRelation; +import com.xboe.module.course.entity.CourseTypeTagRelation; +import com.xboe.module.course.service.ICourseService; +import com.xboe.module.course.service.ICourseTagService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @ClassName:CourseTagServiceImpl + * @author:zhengge@oracle.com + * @since:2025/7/2516:55 + */ +@Slf4j +@Service +@Transactional +public class CourseTagServiceImpl implements ICourseTagService { + + @Resource + private CourseTagDao courseTagDao; + @Resource + PublishCourseUtil publishUtil; + @Resource + private CourseTagRelationDao courseTagRelationDao; + + @Resource + private CourseTypeTagRelationDao courseTypeTagRelationDao; + @Resource + private CourseDao courseDao; + + /** + * 课程标签分页查询 + * @param pageIndex + * @param pageSize + * @param filters + * @param order + * @return + */ + @Override + public PageList query(Integer pageIndex, Integer pageSize, List filters, OrderCondition order) { + try { + /* QueryBuilder query=QueryBuilder.from(CourseTag.class); + query.setPageIndex(pageIndex); + query.setPageSize(pageSize); + filters.add(FieldFilters.eq("deleted",false)); + query.addFilters(filters); + if(order!=null) { + query.addOrder(order); + }else { + query.addOrder(OrderCondition.desc("sysCreateTime")); + } + log.info("标签列表:分页查询 调用接口IMPL query = " + query.builder().toString()); + return courseTagDao.findPage(query.builder());*/ + + if(pageSize==100){ + log.info("--- 11 ----------------------"); + return courseTagDao.getList(); + }else{ + log.info("--- 22 ----------------------"); + QueryBuilder query = QueryBuilder.from(CourseTag.class); + query.setPageIndex(pageIndex); + query.setPageSize(pageSize); + filters.add(FieldFilters.eq("deleted", false)); +// query.addFilters(filters); + query.addOrder(OrderCondition.desc("sysCreateTime")); + return courseTagDao.findPage(query.builder()); + } + + } catch (Exception e) { + log.error("课程标签分页查询异常 = " + e.getMessage()); + throw new RuntimeException(e); + } + } + + /** + * 分页查询指定id标签关联的课程 + * @param pageIndex + * @param pageSize + * @param tagId + * @param isAsc + * @return + */ + @Override + public PageList getCourseByTag(Integer pageIndex, Integer pageSize, Long tagId, Boolean isAsc) { + PageList list = null; + if(isAsc) { + list = courseTagRelationDao.findCoursesWithRelatedTagsAsc(pageIndex,pageSize,tagId); + }else { + list = courseTagRelationDao.findCoursesWithRelatedTagsDesc(pageIndex,pageSize,tagId); + } + return list; + } + + /** + * 修改指定id的课程标签的公共属性 + * @param id + * @param isPublic + * @return + */ + @Override + public void changePublicStatus(Long id, Boolean isPublic) { + CourseTag courseTag = courseTagDao.findOne(FieldFilters.eq("id", String.valueOf(id))); + if (courseTag!=null){ + courseTag.setIsPublic(isPublic); + courseTag.setLastSetPublicTime(isPublic?LocalDateTime.now():null); + courseTagDao.update(courseTag); + } + } + + /** + * 修改指定id的课程标签的热点属性 + * @param id + * @param isHot + * @return + */ + @Override + public void changeHotStatus(Long id, Boolean isHot) { + CourseTag courseTag = courseTagDao.findOne(FieldFilters.eq("id", String.valueOf(id))); + if (courseTag!=null){ + courseTag.setIsHot(isHot); + courseTag.setLastSetHotTime(isHot?LocalDateTime.now():null); + courseTagDao.update(courseTag); + } + } + + /** + * 解除指定id的课程和某个标签之间的关联关系 + * @return + */ + @Override + public void unbind(String id) { + //根据主键查询关联关系 + CourseTagRelation courseTagRelation = courseTagRelationDao.findOne(FieldFilters.eq("id", id)); + if (courseTagRelation != null){ + //修改该标签关联课程数 + CourseTag courseTag = courseTagDao.findOne(FieldFilters.eq("id", String.valueOf(courseTagRelation.getTagId()))); + if (courseTag != null){ + courseTag.setUseCount(courseTag.getUseCount()>1?courseTag.getUseCount()-1:0); + courseTagDao.updateFieldById(courseTag.getId(),"useCount",courseTag.getUseCount()); + } + //解绑(删除关联关系) + courseTagRelationDao.setDeleted(id); + Course course = courseDao.get(courseTagRelation.getCourseId().toString()); + String tags = course.getTags(); + if (StringUtils.isNotBlank(tags)){ + String[] tagIds = tags.split(","); + List tagIdList = new ArrayList<>(); + for (String tagId : tagIds){ + if (!tagId.equals(courseTagRelation.getTagId().toString())){ + tagIdList.add(tagId); + } + } + // 数据格式:1,2,3 + String s = StringUtils.join(tagIdList, ","); + if (!"".equals(s)){ + s+=","; + } + course.setTags(s); + } + // 同步ES + publishUtil.fullTextPublish(course); + } + } + + /** + * 根据标签名称进行检索(模糊查询) + * @param tagName + * @return 符合检索条件的所有公共标签 + */ + public List searchTags(String tagName){ + QueryBuilder query=QueryBuilder.from(CourseTag.class); + List filters = new ArrayList<>(); + filters.add(FieldFilters.eq("deleted",false));//未删除 + filters.add(FieldFilters.eq("isPublic",true));//公共标签 + filters.add(FieldFilters.like("tagName",tagName));//模糊检索 + query.addFilters(filters); + List courseTagList = courseTagDao.findList(query.builder()); + return courseTagList; + } + + @Override + public List searchTags(String tagName,String userId,String typeId){ + List tagList = courseTagDao.searchTags(tagName,userId,typeId); + return tagList; + } + + /** + * 创建新标签,并与指定课程绑定 + * @return + */ + /*@Override + public CourseTag createTag(CourseTagRelationDto courseTagRelationDto) { + CourseTag courseTag = null; + String tagName = courseTagRelationDto.getTagName(); + Long courseId = Long.valueOf(courseTagRelationDto.getCourseId()); + //1.创建标签:先判断是否已经存在该标签 + QueryBuilder query=QueryBuilder.from(CourseTag.class); + List filters = new ArrayList<>(); + filters.add(FieldFilters.eq("tagName",tagName));//精确匹配 + query.addFilters(filters); + List courseTagList = courseTagDao.findList(query.builder()); + if (courseTagList==null || courseTagList.size()==0){//1.1 如果该标签不存在,则新建标签 + courseTag = new CourseTag(); + courseTag.setTagName(tagName); + courseTag.setIsPublic(false); + courseTag.setIsHot(false); + courseTag.setUseCount(1); + courseTagDao.save(courseTag); + //新建一条标签和课程的关联关系 + CourseTagRelation courseTagRelation = new CourseTagRelation(); + courseTagRelation.setTagId(Long.valueOf(courseTag.getId())); + courseTagRelation.setCourseId(courseId); + courseTagRelationDao.save(courseTagRelation); + }else {//1.2 否则修改标签 + courseTag=courseTagList.get(0); + // 当同一标签被3个及以上课管创建时,默认开启这个标签的公共化 + if(courseTag.getUseCount() >= 3){ + courseTag.setIsPublic(true); + } + courseTag.setDeleted(false);//有可能是之前被删除的标签,这里恢复为有效 + //查找改课程与这个标签是否已经建立关联关系 + query=QueryBuilder.from(CourseTagRelation.class); + filters = new ArrayList<>(); + filters.add(FieldFilters.eq("tagId",Long.valueOf(courseTag.getId())));//精确匹配 + filters.add(FieldFilters.eq("courseId",courseId));//精确匹配 + query.addFilters(filters); + List courseTagRelationList = courseTagRelationDao.findList(query.builder()); + //1.2.1 如果还未建立关联关系,则新建一条标签和课程的关联关系 + if (courseTagRelationList==null || courseTagRelationList.size()==0){ + CourseTagRelation courseTagRelation = new CourseTagRelation(); + courseTagRelation.setTagId(Long.valueOf(courseTag.getId())); + courseTagRelation.setCourseId(courseId); + courseTagRelationDao.save(courseTagRelation); + //更新该标签的关联课程数量 + courseTag.setUseCount(courseTag.getUseCount()+1); + }else {//1.2.2 否则修改该标签和课程的关联关系 + CourseTagRelation courseTagRelation = courseTagRelationList.get(0); + if (courseTagRelation.getDeleted()){//之前"解绑",这里恢复为有效 + courseTagRelation.setDeleted(false); + courseTagRelationDao.saveOrUpdate(courseTagRelation); + //更新该标签的关联课程数量 + courseTag.setUseCount(courseTag.getUseCount()+1); + } + } + courseTagDao.saveOrUpdate(courseTag); + } + //2.创建该标签和课程分类之间的关联关系 + courseTagRelationDto.setTagId(courseTag.getId()); + createCourseTypeAndTagRelation(courseTagRelationDto); + return courseTag; + } +*/ + @Override + public CourseTag getTagByName(String tagName) { + CourseTag courseTag = courseTagDao.getTagByName(tagName); + return courseTag; + } + + @Override + public List getTagsByIds(String id) { + // id=17,18 + List courseTagList = courseTagDao.getTagsByIds(id); + return courseTagList; + } + + /** + * 获取热门标签 + * @param courseTagRelationDto + * @return + */ + @Override + public List getHotTagList(CourseTagRelationDto courseTagRelationDto) { + List hotTagList = null; + if (StringUtils.isNotBlank(courseTagRelationDto.getSysType1()) || + StringUtils.isNotBlank(courseTagRelationDto.getSysType2()) || + StringUtils.isNotBlank(courseTagRelationDto.getSysType3())){ + String sysType1 = courseTagRelationDto.getSysType1(); + String sysType2 = courseTagRelationDto.getSysType2(); + String sysType3 = courseTagRelationDto.getSysType3(); + hotTagList = courseTagDao.getHotTagListBySysTypes(sysType1,sysType2,sysType3); + }else { + hotTagList = courseTagDao.getHotTagList(); + } + return hotTagList; + } + + /** + * 创建标签和课程分类之间的关联关系 + * @param courseTagRelationDto + */ + private void createCourseTypeAndTagRelation(CourseTagRelationDto courseTagRelationDto){ + String sysType1 = courseTagRelationDto!=null?courseTagRelationDto.getSysType1():null; + String tagId = courseTagRelationDto!=null?courseTagRelationDto.getTagId():null; + if (StringUtils.isNotBlank(sysType1) && StringUtils.isNotBlank(tagId)){ + String sysType2 = courseTagRelationDto.getSysType2(); + String sysType3 = courseTagRelationDto.getSysType3(); + //判断数据库中该课程分类和标签是否已经存在关联关系 + if (!isHadCourseTypeAndTagRelation(courseTagRelationDto,true)){//不存在,则新建 + CourseTypeTagRelation courseTypeTagRelation = new CourseTypeTagRelation(); + courseTypeTagRelation.setSysType1(sysType1); + courseTypeTagRelation.setSysType2(StringUtils.isNotBlank(sysType2)?sysType2:"0"); + courseTypeTagRelation.setSysType3(StringUtils.isNotBlank(sysType3)?sysType3:"0"); + courseTypeTagRelation.setTagId(tagId); + courseTypeTagRelationDao.save(courseTypeTagRelation); + } + } + } + + /** + * 判断数据库制定的课程分类和标签是否已经存在关联关系 + * @param courseTagRelationDto + * @param clearFlag 清理标识 true:清理已存在的数据,只保留一条有效数据 + * @return true:已存在;false:不存在 + */ + private Boolean isHadCourseTypeAndTagRelation(CourseTagRelationDto courseTagRelationDto,Boolean clearFlag){ + QueryBuilder query=QueryBuilder.from(CourseTypeTagRelation.class); + List filters = new ArrayList<>(); + filters.add(FieldFilters.eq("sysType1",courseTagRelationDto.getSysType1()));//一级分类 + filters.add(FieldFilters.eq("sysType2",courseTagRelationDto.getSysType1()));//二级分类 + filters.add(FieldFilters.eq("sysType3",courseTagRelationDto.getSysType1()));//三级分类 + filters.add(FieldFilters.eq("tagId",courseTagRelationDto.getTagId())); + List courseTypeTagRelList = courseTypeTagRelationDao.findList(query.addFilters(filters).builder()); + Boolean isExist = (courseTypeTagRelList!=null && courseTypeTagRelList.size()>0)?true:false; + if (isExist && clearFlag ){ + List toRemove = new ArrayList<>(); + for (CourseTypeTagRelation courseTypeTagRel : courseTypeTagRelList) { + if (courseTypeTagRel.getDeleted()) {//如果是逻辑删的本次物理删除 + courseTypeTagRelationDao.getGenericDao().delete(courseTypeTagRel); + toRemove.add(courseTypeTagRel); + } + } + courseTypeTagRelList.removeAll(toRemove);//移除逻辑删的数据 + //如果还存在有效数据 + if (courseTypeTagRelList!=null && courseTypeTagRelList.size()>0){ + //只保留一条有效数据,其余物理删除 + for (int i = courseTypeTagRelList.size() - 1; i >= 1; i--) { + CourseTypeTagRelation courseTypeTagRel = courseTypeTagRelList.get(i); + if (courseTypeTagRel.getDeleted()) { + courseTypeTagRelationDao.getGenericDao().delete(courseTypeTagRel); + courseTypeTagRelList.remove(i); // 倒序删除不影响未遍历的索引 + } + } + isExist = true;//存在一条有效数据 + }else { + isExist = false;//不存在有效数据了 + } + } + return isExist; + } + + + + /** + * 创建新标签 + * @param courseTagRelationDto + * @return + */ + @Override + public CourseTag createTag(CourseTagRelationDto courseTagRelationDto) { + CourseTag courseTag = null; + String tagName = courseTagRelationDto.getTagName(); + //1.创建标签:先判断是否已经存在该标签 + QueryBuilder query=QueryBuilder.from(CourseTag.class); + List filters = new ArrayList<>(); + filters.add(FieldFilters.eq("tagName",tagName));//精确匹配 + filters.add(FieldFilters.eq("status",0));//正式 + filters.add(FieldFilters.eq("deleted",false));//未删除的 + query.addFilters(filters); + List courseTagList = courseTagDao.findList(query.builder()); + if (courseTagList==null || courseTagList.isEmpty() || !courseTagList.get(0).getIsPublic()){//1.1 如果该标签不存在 或私有标签,则新建标签 + courseTag = new CourseTag(); + courseTag.setTagName(tagName); + courseTag.setIsPublic(false); + courseTag.setIsHot(false); + courseTag.setStatus(1); + courseTag.setUseCount(1); + //初始给个时间, 变更公共会更新时间 关闭公共会设置null 后续不在自动变更为公共 + courseTag.setLastSetPublicTime(LocalDateTime.now()); + courseTagDao.save(courseTag); + } + return courseTag; + } + + + @Override + public void updateTags(Course oldCourse, Course newCourse, CurrentUser userInfo) { + log.info(" --- 标签修改 --- 在线课参数 oldCourse = {} " , oldCourse); + log.info(" --- 标签修改 --- 在线课参数 newCourse = {} " , newCourse); + log.info(" --- 标签修改 --- 用户信息 userInfo = {} " , userInfo); + + // 获取新旧课程的标签ID列表 + List oldTagIds = getTagIdsFromCourse(oldCourse); + List newTagIds = getTagIdsFromCourse(newCourse); + log.info(" --- 旧标签 oldTagIds = {} " , oldTagIds); + log.info(" --- 新修改 newTagIds = {} " , newTagIds); + if (oldCourse == null || oldTagIds.isEmpty()) { + // 新增课程 - 处理所有新标签 + handleNewCourseTags(newCourse, newTagIds, userInfo); + } else { + // 编辑课程 - 比较差异并处理 + handleEditCourseTags(oldCourse, newCourse, oldTagIds, newTagIds, userInfo); + } + log.info("完成课程标签更新: courseId={}", newCourse != null ? newCourse.getId() : "null"); + } + + @Override + public List getAllTags() { + QueryBuilder query=QueryBuilder.from(CourseTag.class); + List filters = new ArrayList<>(); + filters.add(FieldFilters.eq("deleted",false));//未删除 + filters.add(FieldFilters.eq("status",0));//正式标签 + query.addFilters(filters); + List courseTagList = courseTagDao.findList(query.builder()); + return courseTagList; + } + + /** + * 从课程对象中提取标签ID列表 + */ + private List getTagIdsFromCourse(Course course) { + if (course == null || StringUtils.isBlank(course.getTags())) { + return new ArrayList<>(); + } + + String tags = course.getTags(); + // 去除结尾的逗号并分割 + if (tags.endsWith(",")) { + tags = tags.substring(0, tags.length() - 1); + } + + if (StringUtils.isBlank(tags)) { + return new ArrayList<>(); + } + + return Arrays.asList(tags.split(",")); + } + + /** + * 处理新增课程的标签逻辑 + */ + private void handleNewCourseTags(Course newCourse, List newTagIds, CurrentUser userInfo) { + log.info("处理新增课程的标签逻辑: courseId={}, tagCount={}", newCourse != null ? newCourse.getId() : "null", newTagIds.size()); + String courseId = newCourse.getId(); + + for (String tagId : newTagIds) { + if (StringUtils.isBlank(tagId)) { + continue; + } + + // 获取标签信息 + CourseTag tag = courseTagDao.findOne(FieldFilters.eq("id", tagId.trim())); + if (tag == null) { + log.warn("标签不存在: {}", tagId); + continue; + } + + //合并临时标签 + tag = mergeTag(tag); + + // 创建课程-标签关联关系 + createCourseTagRelation(courseId, tag, userInfo); + + // 创建分类-标签关联关系 + createCourseTypeTagRelations(newCourse, tag, userInfo); + + // 更新标签使用计数并检查是否设置为公共标签 + updateTagUseCountAndPublicStatus(tag, userInfo); + } + log.info("完成新增课程标签处理: courseId={}", newCourse != null ? newCourse.getId() : "null"); + } + + /** + * 处理编辑课程的标签逻辑 + */ + private void handleEditCourseTags(Course oldCourse, Course newCourse, + List oldTagIds, List newTagIds, CurrentUser userInfo) { + log.info("处理编辑课程的标签逻辑: courseId={}, oldTagCount={}, newTagCount={}, toRemove={}, toAdd={}", + newCourse != null ? newCourse.getId() : "null", + oldTagIds.size(), newTagIds.size(), + oldTagIds.stream().filter(tagId -> !newTagIds.contains(tagId)).count(), + newTagIds.stream().filter(tagId -> !oldTagIds.contains(tagId)).count()); + + String courseId = newCourse.getId(); + + // 找出需要删除的标签(存在于旧课程但不在新课程中) + List tagsToRemove = oldTagIds.stream() + .filter(tagId -> !newTagIds.contains(tagId)) + .collect(Collectors.toList()); + + // 找出需要新增的标签(存在于新课程但不在旧课程中) + List tagsToAdd = newTagIds.stream() + .filter(tagId -> !oldTagIds.contains(tagId)) + .collect(Collectors.toList()); + + // 处理标签删除 + for (String tagId : tagsToRemove) { + removeCourseTagRelation(courseId, tagId, userInfo); + } + + // 处理标签新增 + for (String tagId : tagsToAdd) { + CourseTag tag = courseTagDao.findOne(FieldFilters.eq("id", tagId.trim())); + if (tag == null) { + log.warn("标签不存在: {}", tagId); + continue; + } + //如果已有同名的正式标签 则需要合并 + //合并临时标签 + tag = mergeTag(tag); + + // 创建课程-标签关联关系 + createCourseTagRelation(courseId, tag, userInfo); + + // 创建分类-标签关联关系 + createCourseTypeTagRelations(newCourse, tag, userInfo); + + // 更新标签使用计数并检查是否设置为公共标签 + updateTagUseCountAndPublicStatus(tag, userInfo); + } + + // 处理分类变化导致的标签关联关系更新 + if (hasCourseTypeChanged(oldCourse, newCourse)) { + updateCourseTypeTagRelations(oldCourse, newCourse, newTagIds, userInfo); + } + log.info("完成编辑课程标签处理: courseId={}", newCourse != null ? newCourse.getId() : "null"); + } + + + /** + * 合并标签 + */ + private CourseTag mergeTag(CourseTag tag){ + //只处理临时标签 正式的忽略 + if (tag.getStatus()==1){ + QueryBuilder query=QueryBuilder.from(CourseTag.class); + List filters = new ArrayList<>(); + filters.add(FieldFilters.eq("tagName",tag.getTagName()));//精确匹配 + filters.add(FieldFilters.eq("status",0));//正式 + filters.add(FieldFilters.eq("deleted",false));//未删除的 + query.addFilters(filters); + List courseTagList = courseTagDao.findList(query.builder()); + log.info("标签合并 createTag courseTagList = {} " , courseTagList); + //如果无同名正式标签 则转正 + //有同名正式标签 则合并 + if (courseTagList != null && !courseTagList.isEmpty()) { + //删除临时标签 + tag.setSysUpdateBy("系统合并删除"); + tag.setSysUpdateTime(LocalDateTime.now()); + courseTagDao.setDeleted(tag.getId()); + //返回同名正式标签 + tag = courseTagList.get(0); + } + } + return tag; + } + + /** + * 创建课程-标签关联关系 + */ + private void createCourseTagRelation(String courseId, CourseTag tag, CurrentUser userInfo) { + log.debug("创建课程-标签关联关系: courseId={}, tagId={}, tagName={}", + courseId, tag != null ? tag.getId() : "null", tag != null ? tag.getTagName() : "null"); + + // 检查是否已存在关联关系 + QueryBuilder query = QueryBuilder.from(CourseTagRelation.class); + List filters = new ArrayList<>(); + filters.add(FieldFilters.eq("courseId", Long.valueOf(courseId))); + filters.add(FieldFilters.eq("tagId", Long.valueOf(tag.getId()))); +// filters.add(FieldFilters.eq("deleted", false)); + query.addFilters(filters); + + List existingRelations = courseTagRelationDao.findList(query.builder()); + + LocalDateTime now = LocalDateTime.now(); + + if (existingRelations.isEmpty()) { + // 新建关联关系 + CourseTagRelation relation = new CourseTagRelation(); + relation.setCourseId(Long.valueOf(courseId)); + relation.setTagId(Long.valueOf(tag.getId())); + + // 设置创建信息 + relation.setSysCreateAid(userInfo.getAccountId()); + relation.setSysCreateBy(userInfo.getName()); + relation.setSysCreateTime(now); + + // 设置更新信息 + relation.setSysUpdateBy(userInfo.getName()); + relation.setSysUpdateTime(now); + + courseTagRelationDao.save(relation); + } else { + // 恢复已删除的关联关系 + CourseTagRelation relation = existingRelations.get(0); + if (relation.getDeleted()) { + courseTagRelationDao.reTagRelDelStatus(relation.getId(),userInfo.getName()); +// relation.setDeleted(false); + // 设置更新信息 +// relation.setSysUpdateBy(userInfo.getName()); +// relation.setSysUpdateTime(now); +// courseTagRelationDao.saveOrUpdate(relation); + } + } + log.debug("完成课程-标签关联关系创建: courseId={}, tagId={}", courseId, tag != null ? tag.getId() : "null"); + + } + + /** + * 创建分类-标签关联关系 + */ + private void createCourseTypeTagRelations(Course course, CourseTag tag, CurrentUser userInfo) { + log.debug("创建分类-标签关联关系: courseId={}, tagId={}, sysType1={}, sysType2={}, sysType3={}", + course != null ? course.getId() : "null", + tag != null ? tag.getId() : "null", + course != null ? course.getSysType1() : "null", + course != null ? course.getSysType2() : "null", + course != null ? course.getSysType3() : "null"); + + String sysType1 = course.getSysType1(); + String sysType2 = course.getSysType2(); + String sysType3 = course.getSysType3(); + + // 根据分类级别创建相应的关联关系 + if (StringUtils.isNotBlank(sysType3)) { + createSingleCourseTypeTagRelation(sysType1, sysType2, sysType3, tag.getId(), userInfo); + }else if (StringUtils.isNotBlank(sysType2)) { + createSingleCourseTypeTagRelation(sysType1, sysType2, "0", tag.getId(), userInfo); + }else if (StringUtils.isNotBlank(sysType1)) { + createSingleCourseTypeTagRelation(sysType1, "0", "0", tag.getId(), userInfo); + } + } + + /** + * 创建单个分类-标签关联关系 + */ + private void createSingleCourseTypeTagRelation(String sysType1, String sysType2, String sysType3, + String tagId, CurrentUser userInfo) { + // 检查是否已存在关联关系 + QueryBuilder query = QueryBuilder.from(CourseTypeTagRelation.class); + List filters = new ArrayList<>(); + filters.add(FieldFilters.eq("sysType1", sysType1)); + filters.add(FieldFilters.eq("sysType2", sysType2)); + filters.add(FieldFilters.eq("sysType3", sysType3)); + filters.add(FieldFilters.eq("tagId", tagId)); +// filters.add(FieldFilters.eq("deleted", false)); + query.addFilters(filters); + + List existingRelations = courseTypeTagRelationDao.findList(query.builder()); + + LocalDateTime now = LocalDateTime.now(); + + if (existingRelations.isEmpty()) { + // 新建关联关系 + CourseTypeTagRelation relation = new CourseTypeTagRelation(); + relation.setSysType1(sysType1); + relation.setSysType2(sysType2); + relation.setSysType3(sysType3); + relation.setTagId(tagId); + + // 设置创建信息 + relation.setSysCreateAid(userInfo.getAccountId()); + relation.setSysCreateBy(userInfo.getName()); + relation.setSysCreateTime(now); + + // 设置更新信息 + relation.setSysUpdateBy(userInfo.getName()); + relation.setSysUpdateTime(now); + + courseTypeTagRelationDao.save(relation); + } else { + // 恢复已删除的关联关系 + CourseTypeTagRelation relation = existingRelations.get(0); + if (relation.getDeleted()) { + courseTagRelationDao.reTypeTagRelDelStatus(relation.getId(),userInfo.getName()); +// relation.setDeleted(false); +// // 设置更新信息 +// relation.setSysUpdateBy(userInfo.getName()); +// relation.setSysUpdateTime(now); +// courseTypeTagRelationDao.saveOrUpdate(relation); + } + } + } + + /** + * 移除课程-标签关联关系 + */ + private void removeCourseTagRelation(String courseId, String tagId, CurrentUser userInfo) { + log.debug("移除课程-标签关联关系: courseId={}, tagId={}", courseId, tagId); + // 查找关联关系 + QueryBuilder query = QueryBuilder.from(CourseTagRelation.class); + List filters = new ArrayList<>(); + filters.add(FieldFilters.eq("courseId", Long.valueOf(courseId))); + filters.add(FieldFilters.eq("tagId", Long.valueOf(tagId))); + query.addFilters(filters); + + List relations = courseTagRelationDao.findList(query.builder()); + + if (!relations.isEmpty()) { + CourseTagRelation relation = relations.get(0); + + // 设置更新信息 + relation.setSysUpdateBy(userInfo.getName()); + relation.setSysUpdateTime(LocalDateTime.now()); + + // 逻辑删除关联关系 + courseTagRelationDao.setDeleted(relation.getId()); + + // 更新标签使用计数 + CourseTag tag = courseTagDao.findOne(FieldFilters.eq("id", tagId)); + if (tag != null) { + tag.setUseCount(Math.max(0, tag.getUseCount() - 1)); + + // 设置更新信息 + tag.setSysUpdateBy(userInfo.getName()); + tag.setSysUpdateTime(LocalDateTime.now()); + + courseTagDao.update(tag); + } + + // 检查是否需要删除分类-标签关联关系 + checkAndRemoveCourseTypeTagRelation(tagId, userInfo); + } + log.debug("完成课程-标签关联关系移除: courseId={}, tagId={}", courseId, tagId); + } + + /** + * 检查并删除分类-标签关联关系(如果没有其他课程使用) + */ + private void checkAndRemoveCourseTypeTagRelation(String tagId, CurrentUser userInfo) { + // 检查是否还有其他课程使用这个标签 + QueryBuilder query = QueryBuilder.from(CourseTagRelation.class); + List filters = new ArrayList<>(); + filters.add(FieldFilters.eq("tagId", Long.valueOf(tagId))); + filters.add(FieldFilters.eq("deleted", false)); + query.addFilters(filters); + + List activeRelations = courseTagRelationDao.findList(query.builder()); + + // 如果没有其他活跃的关联关系,删除分类-标签关联 + if (activeRelations.isEmpty()) { + QueryBuilder typeQuery = QueryBuilder.from(CourseTypeTagRelation.class); + List typeFilters = new ArrayList<>(); + typeFilters.add(FieldFilters.eq("tagId", tagId)); + typeQuery.addFilters(typeFilters); + + List typeRelations = courseTypeTagRelationDao.findList(typeQuery.builder()); + + LocalDateTime now = LocalDateTime.now(); + + for (CourseTypeTagRelation relation : typeRelations) { + // 设置更新信息 + relation.setSysUpdateBy(userInfo.getName()); + relation.setSysUpdateTime(now); + + courseTypeTagRelationDao.setDeleted(relation.getId()); + } + } + } + + /** + * 更新标签使用计数并检查公共标签状态 + */ + private void updateTagUseCountAndPublicStatus(CourseTag tag, CurrentUser userInfo) { + log.debug("更新标签使用计数和公共状态: tagId={}, tagName={}, beforeUseCount={}", + tag != null ? tag.getId() : "null", + tag != null ? tag.getTagName() : "null", + tag != null ? tag.getUseCount() : "null"); + + // 将标签状态设置为正式(status=0) + if (tag != null && tag.getStatus() == 1) { + tag.setStatus(0); // 正式标签 + } + // 统计当前活跃的关联关系数量 + QueryBuilder query = QueryBuilder.from(CourseTagRelation.class); + List filters = new ArrayList<>(); + filters.add(FieldFilters.eq("tagId", Long.valueOf(tag.getId()))); + filters.add(FieldFilters.eq("deleted", false)); + query.addFilters(filters); + + List activeRelations = courseTagRelationDao.findList(query.builder()); + int activeCount = activeRelations.size(); + + tag.setUseCount(activeCount); + + LocalDateTime now = LocalDateTime.now(); + + // 检查是否满足设置为公共标签的条件 + if (activeCount >= 3 && tag.getLastSetPublicTime() != null) { + // 只有从未手动关闭过公共标签的才自动开启 + tag.setIsPublic(true); + tag.setLastSetPublicTime(now); + } + + // 设置更新信息 + tag.setSysUpdateBy(userInfo.getName()); + tag.setSysUpdateTime(now); + + courseTagDao.update(tag); + log.debug("完成标签使用计数和公共状态更新: tagId={}, tagName={}, afterUseCount={}, isPublic={}", + tag != null ? tag.getId() : "null", + tag != null ? tag.getTagName() : "null", + tag != null ? tag.getUseCount() : "null", + tag != null ? tag.getIsPublic() : "null"); + } + + /** + * 检查课程分类是否发生变化 + */ + private boolean hasCourseTypeChanged(Course oldCourse, Course newCourse) { + return !Objects.equals(oldCourse.getSysType1(), newCourse.getSysType1()) || + !Objects.equals(oldCourse.getSysType2(), newCourse.getSysType2()) || + !Objects.equals(oldCourse.getSysType3(), newCourse.getSysType3()); + } + + /** + * 更新分类-标签关联关系(当分类变化时) + */ + private void updateCourseTypeTagRelations(Course oldCourse, Course newCourse, + List tagIds, CurrentUser userInfo) { + // 移除旧的分类-标签关联关系 + for (String tagId : tagIds) { + checkAndRemoveCourseTypeTagRelation(tagId, userInfo); + } + + // 创建新的分类-标签关联关系 + for (String tagId : tagIds) { + CourseTag tag = courseTagDao.findOne(FieldFilters.eq("id", tagId.trim())); + if (tag != null) { + createCourseTypeTagRelations(newCourse, tag, userInfo); + } + } + } + + /** + * 设置实体的创建信息(新增时使用) + */ + private void setCreateInfo(BaseEntity entity, CurrentUser userInfo) { + LocalDateTime now = LocalDateTime.now(); + entity.setSysCreateAid(userInfo.getAccountId()); + entity.setSysCreateBy(userInfo.getName()); + entity.setSysCreateTime(now); + entity.setSysUpdateBy(userInfo.getName()); + entity.setSysUpdateTime(now); + } + + /** + * 设置实体的更新信息(编辑时使用) + */ + private void setUpdateInfo(BaseEntity entity, CurrentUser userInfo) { + entity.setSysUpdateBy(userInfo.getName()); + entity.setSysUpdateTime(LocalDateTime.now()); + } + + + +}