From 5aba4ef45fc7772874b950f96158b94b34afca40 Mon Sep 17 00:00:00 2001 From: zhengsongbo Date: Sat, 2 Aug 2025 12:48:57 +0800 Subject: [PATCH] =?UTF-8?q?zhengsongbo:=E6=A0=87=E7=AD=BE=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=AC=AC=E4=B8=80=E6=AC=A1=E6=8F=90=E6=B5=8B=20on=202?= =?UTF-8?q?025-08-02?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xboe/module/course/api/CourseTagApi.java | 174 ++++++++++ .../xboe/module/course/dao/CourseTagDao.java | 89 ++++++ .../course/dao/CourseTagRelationDao.java | 116 +++++++ .../course/dao/CourseTypeTagRelationDao.java | 17 + .../module/course/dto/CourseTagQueryDto.java | 40 +++ .../course/dto/CourseTagRelationDto.java | 49 +++ .../xboe/module/course/entity/CourseTag.java | 92 ++++++ .../course/entity/CourseTagRelation.java | 37 +++ .../course/entity/CourseTypeTagRelation.java | 39 +++ .../course/service/ICourseTagService.java | 83 +++++ .../service/impl/CourseTagServiceImpl.java | 299 ++++++++++++++++++ 11 files changed, 1035 insertions(+) create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseTagApi.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTagDao.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTagRelationDao.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTypeTagRelationDao.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseTagQueryDto.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/course/dto/CourseTagRelationDto.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTag.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTagRelation.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTypeTagRelation.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/course/service/ICourseTagService.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/course/service/impl/CourseTagServiceImpl.java 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..b57826e0 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/api/CourseTagApi.java @@ -0,0 +1,174 @@ +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 java.util.ArrayList; +import java.util.List; + +/** + * @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){ + 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); // 链式追加排序条件 + } + } + } + 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){ + if (StringUtils.isNotBlank(tagName)){ + List courseTagList = courseTagService.searchTags(tagName); + return success(courseTagList); + } + return error("服务器端异常!"); + } + + /** + * 创建新标签,并与当前课程绑定 + * @param courseTagRelationDto + * @return + */ + @RequestMapping(value="/createTag",method= RequestMethod.POST) + public JsonResponse createTag(CourseTagRelationDto courseTagRelationDto){ + if (courseTagRelationDto!=null){ + CourseTag courseTag = courseTagService.createTag(courseTagRelationDto); + 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..6df7427a --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTagDao.java @@ -0,0 +1,89 @@ +package com.xboe.module.course.dao; + +import com.xboe.common.OrderCondition; +import com.xboe.common.PageList; +import com.xboe.core.orm.BaseDao; +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 org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import java.util.List; + +/** + * @ClassName:CourseTagDao + * @author:zhengge@oracle.com + * @since:2025/7/2516:50 + */ +@Repository +public class CourseTagDao extends BaseDao { + @PersistenceContext + private EntityManager entityManager; + + /** + * 获取热门标签列表(前10条) + * @return 热门标签列表 + */ + public List getHotTagList() { + // 原生SQL:注意表名和列名需与数据库实际一致 + String sql = "SELECT 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.is_hot = true " + // 数据库字段为is_hot(与实体属性isHot对应) + "ORDER BY c.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) { + if (StringUtils.isNotBlank(sysType1)){ + // 原生SQL:注意表名和列名需与数据库实际一致(此处假设表名为course_tag、course_type_tag_relation) + String sql = "SELECT 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.is_hot = true " + // 假设数据库字段为is_hot(与实体属性isHot对应) + "AND r.sys_type1 = ?1 " + + "AND r.sys_type2 = ?2 " + + "AND r.sys_type3 = ?3 " + + "ORDER BY c.last_set_hot_time DESC"; // 假设数据库字段为last_set_hot_time + + // 创建原生查询并指定结果映射到CourseTag实体 + javax.persistence.Query query = entityManager.createNativeQuery(sql, CourseTag.class); + + // 绑定参数(注意参数索引从1开始) + query.setParameter(1, sysType1); + query.setParameter(2, StringUtils.isNotBlank(sysType2)?sysType2:"0"); + query.setParameter(3, StringUtils.isNotBlank(sysType3)?sysType3:"0"); + + // 分页:取前10条 + query.setFirstResult(0); + query.setMaxResults(10); + + // 执行查询并返回结果(已映射为CourseTag类型) + return query.getResultList(); + } + return null; + } + +} 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..7cc78aef --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/dao/CourseTagRelationDao.java @@ -0,0 +1,116 @@ +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); + } +} 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/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/CourseTag.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTag.java new file mode 100644 index 00000000..77199d13 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/entity/CourseTag.java @@ -0,0 +1,92 @@ +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; + + /** + * 最近设置为公共标签的时间 + */ + @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/service/ICourseTagService.java b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/ICourseTagService.java new file mode 100644 index 00000000..c06832b1 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/ICourseTagService.java @@ -0,0 +1,83 @@ +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.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); + + /** + * 创建新标签,并与当前课程绑定 + * @param courseTagRelationDto + * @return + */ + CourseTag createTag(CourseTagRelationDto courseTagRelationDto); + + /** + * 根据课程类型获取热点标签 + * @param courseTagRelationDto + * @return + */ + List getHotTagList(CourseTagRelationDto courseTagRelationDto); +} 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..14b75384 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/course/service/impl/CourseTagServiceImpl.java @@ -0,0 +1,299 @@ +package com.xboe.module.course.service.impl; + +import com.xboe.common.OrderCondition; +import com.xboe.common.PageList; +import com.xboe.core.orm.FieldFilters; +import com.xboe.core.orm.IFieldFilter; +import com.xboe.core.orm.QueryBuilder; +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.CourseTag; +import com.xboe.module.course.entity.CourseTagRelation; +import com.xboe.module.course.entity.CourseTypeTagRelation; +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.*; + +/** + * @ClassName:CourseTagServiceImpl + * @author:zhengge@oracle.com + * @since:2025/7/2516:55 + */ +@Slf4j +@Service +@Transactional +public class CourseTagServiceImpl implements ICourseTagService { + + @Resource + private CourseTagDao courseTagDao; + + @Resource + private CourseTagRelationDao courseTagRelationDao; + + @Resource + private CourseTypeTagRelationDao courseTypeTagRelationDao; + + /** + * 课程标签分页查询 + * @param pageIndex + * @param pageSize + * @param filters + * @param order + * @return + */ + @Override + public PageList query(Integer pageIndex, Integer pageSize, List filters, OrderCondition order) { + 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")); + } + return courseTagDao.findPage(query.builder()); + } + + /** + * 分页查询指定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); + } + } + + /** + * 根据标签名称进行检索(模糊查询) + * @param tagName + * @return 符合检索条件的所有公共标签 + */ + @Override + 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; + } + + /** + * 创建新标签,并与指定课程绑定 + * @param courseTagRelationDto + * @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); + 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; + } + + /** + * 获取热门标签 + * @param courseTagRelationDto + * @return + */ + @Override + public List getHotTagList(CourseTagRelationDto courseTagRelationDto) { + List hotTagList = null; + if (StringUtils.isNotBlank(courseTagRelationDto.getSysType1())){ + 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; + } +}