Преглед изворни кода

课程留言和介绍图片

yuhongqi пре 3 недеља
родитељ
комит
a18d65e74f

+ 163 - 8
fs-admin/src/main/java/com/fs/course/controller/FsUserCourseCommentController.java

@@ -2,21 +2,20 @@ package com.fs.course.controller;
 
 import java.util.List;
 
+import cn.hutool.core.util.IdUtil;
+import com.fs.common.utils.DateUtils;
+import com.fs.config.cloud.CloudHostProper;
 import com.fs.course.param.FsUserCourseCommentParam;
 import com.fs.course.param.FsCourseWatchCommentPageParam;
 import com.fs.course.vo.FsUserCourseCommentListVO;
 import com.fs.course.vo.FsUserCourseCommentVO;
 import com.fs.course.vo.FsCourseWatchCommentListVO;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -27,6 +26,12 @@ import com.fs.course.service.IFsCourseWatchCommentService;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.core.page.TableDataInfo;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.util.Date;
+
 /**
  * 课堂评论Controller
  *
@@ -150,4 +155,154 @@ public class FsUserCourseCommentController extends BaseController
     {
         return toAjax(fsUserCourseCommentService.deleteFsUserCourseCommentByCommentIds(commentIds));
     }
+
+    /**
+     * 下载精选留言Excel导入模板
+     * 模板字段:昵称、评论内容、图片URL、视频URL
+     */
+    @PreAuthorize("@ss.hasPermi('course:userCourseComment:add')")
+    @GetMapping("/importTemplate")
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        String fileName = URLEncoder.encode("精选留言导入模板", "UTF-8");
+        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
+
+        try (Workbook workbook = new XSSFWorkbook(); OutputStream out = response.getOutputStream()) {
+            Sheet sheet = workbook.createSheet("精选留言");
+
+            // 表头样式
+            CellStyle headerStyle = workbook.createCellStyle();
+            Font headerFont = workbook.createFont();
+            headerFont.setBold(true);
+            headerStyle.setFont(headerFont);
+
+            // 表头行
+            Row headerRow = sheet.createRow(0);
+            String[] headers = {"昵称", "评论内容", "图片URL", "视频URL"};
+            for (int i = 0; i < headers.length; i++) {
+                Cell cell = headerRow.createCell(i);
+                cell.setCellValue(headers[i]);
+                cell.setCellStyle(headerStyle);
+            }
+
+            // 示例行
+            Row exampleRow = sheet.createRow(1);
+            String[] examples = {"张三", "课程内容非常棒!", "", ""};
+            for (int i = 0; i < examples.length; i++) {
+                exampleRow.createCell(i).setCellValue(examples[i]);
+            }
+
+            // 自适应列宽
+            for (int i = 0; i < headers.length; i++) {
+                sheet.autoSizeColumn(i);
+            }
+
+            workbook.write(out);
+        }
+    }
+
+    /**
+     * 导入精选留言(Excel解析)
+     * 解析Excel表格数据,按 type=3 保存为精选留言
+     */
+    @PreAuthorize("@ss.hasPermi('course:userCourseComment:add')")
+    @Log(title = "精选留言导入", businessType = BusinessType.IMPORT)
+    @PostMapping("/importComments/{courseId}")
+    public AjaxResult importComments(@PathVariable Long courseId, @RequestParam("file") MultipartFile file) throws IOException {
+        if (file.isEmpty()) {
+            return AjaxResult.error("请选择要导入的文件");
+        }
+        String originalFilename = file.getOriginalFilename();
+        if (originalFilename == null || (!originalFilename.endsWith(".xlsx") && !originalFilename.endsWith(".xls"))) {
+            return AjaxResult.error("仅支持Excel文件(.xls/.xlsx)格式");
+        }
+
+        try (Workbook workbook = WorkbookFactory.create(file.getInputStream())) {
+            Sheet sheet = workbook.getSheetAt(0);
+            if (sheet == null) {
+                return AjaxResult.error("Excel文件中未找到数据");
+            }
+
+            int count = 0;
+            // 从第2行开始读取(第1行是表头)
+            for (int i = 1; i <= sheet.getLastRowNum(); i++) {
+                Row row = sheet.getRow(i);
+                if (row == null) continue;
+
+                String nickName = getCellStringValue(row, 0);
+                String content = getCellStringValue(row, 1);
+                String imgUrl = getCellStringValue(row, 2);
+                String videoUrl = getCellStringValue(row, 3);
+
+                // 昵称和内容至少有一个非空才导入
+                if ((nickName == null || nickName.trim().isEmpty()) && (content == null || content.trim().isEmpty())) {
+                    continue;
+                }
+
+                // 拼接内容:文字 + 图片URL + 视频URL
+                StringBuilder contentBuilder = new StringBuilder();
+                if (content != null && !content.trim().isEmpty()) {
+                    contentBuilder.append(content.trim());
+                }
+                if (imgUrl != null && !imgUrl.trim().isEmpty()) {
+                    contentBuilder.append("\n").append(imgUrl.trim());
+                }
+                if (videoUrl != null && !videoUrl.trim().isEmpty()) {
+                    contentBuilder.append("\n").append(videoUrl.trim());
+                }
+
+                FsUserCourseComment comment = new FsUserCourseComment();
+                comment.setCourseId(courseId);
+                comment.setType(3L); // 精选留言
+                comment.setParentId(0L);
+                comment.setNickName(nickName != null ? nickName.trim() : "");
+                comment.setContent(contentBuilder.toString());
+                comment.setReplyCount(0L);
+                comment.setLikes(0L);
+                comment.setIsDel(0);
+                comment.setCreateTime(new Date());
+                fsUserCourseCommentService.insertFsUserCourseComment(comment);
+                count++;
+            }
+            return AjaxResult.success("成功导入" + count + "条精选留言");
+        }
+    }
+
+    /**
+     * 查询精选留言历史列表(type=3)
+     */
+    @PreAuthorize("@ss.hasPermi('course:userCourseComment:list')")
+    @GetMapping("/featuredComments/{courseId}")
+    public TableDataInfo featuredComments(@PathVariable Long courseId) {
+        startPage();
+        FsUserCourseComment param = new FsUserCourseComment();
+        param.setCourseId(courseId);
+        param.setType(3L);
+        List<FsUserCourseComment> list = fsUserCourseCommentService.selectFsUserCourseCommentList(param);
+        return getDataTable(list);
+    }
+
+    /**
+     * 提取Excel单元格文本
+     */
+    private String getCellStringValue(Row row, int colIndex) {
+        if (colIndex >= row.getLastCellNum()) {
+            return "";
+        }
+        Cell cell = row.getCell(colIndex);
+        if (cell == null) {
+            return "";
+        }
+        switch (cell.getCellType()) {
+            case STRING:
+                return cell.getStringCellValue();
+            case NUMERIC:
+                return String.valueOf((long) cell.getNumericCellValue());
+            case BOOLEAN:
+                return String.valueOf(cell.getBooleanCellValue());
+            default:
+                return "";
+        }
+    }
 }

+ 8 - 8
fs-service/src/main/java/com/fs/course/domain/FsUserCourseComment.java

@@ -7,6 +7,11 @@ import lombok.Data;
 /**
  * 课堂评论对象 fs_user_course_comment
  *
+ * type 说明:
+ *   1:评论
+ *   2:回复
+ *   3:精选留言(通过Word文档批量导入的用户留言)
+ *
  * @author fs
  * @date 2024-05-15
  */
@@ -26,7 +31,7 @@ public class FsUserCourseComment extends BaseEntity
     @Excel(name = "课堂id")
     private Long courseId;
 
-    /** 评论类型 1:评论,2:回复 */
+    /** 评论类型 1:评论,2:回复,3:精选留言(Word批量导入) */
     @Excel(name = "评论类型")
     private Long type;
 
@@ -50,12 +55,7 @@ public class FsUserCourseComment extends BaseEntity
 
     private Integer isDel;
 
-    /** 图片URL列表(JSON数组) */
-    @Excel(name = "图片")
-    private String images;
-
-    /** 视频URL列表(JSON数组) */
-    @Excel(name = "视频")
-    private String videos;
+    /** 昵称(精选留言导入时使用) */
+    private String nickName;
 
 }

+ 3 - 0
fs-service/src/main/java/com/fs/course/domain/FsUserCourseVideo.java

@@ -139,6 +139,9 @@ public class FsUserCourseVideo extends BaseEntity
     /** 看课奖励类型(展示文案,可配置) */
     private String rewardType;
 
+    /** 课程介绍图片URL */
+    private String courseIntroImg;
+
     @TableField(exist = false)
     private Integer showProduct; //1不展示疗法,0展示疗法
 

+ 5 - 2
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseCommentMapper.java

@@ -11,6 +11,7 @@ import com.fs.course.vo.FsUserCourseCommentVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
+import org.checkerframework.checker.units.qual.A;
 
 /**
  * 课堂评论Mapper接口
@@ -119,9 +120,9 @@ public interface FsUserCourseCommentMapper
     List<FsUserCourseCommentListUVO> selectFsUserCourseCommentListVOByCourseId(Long courseId);
 
     @Select({"<script> " +
-            "SELECT c.*, u.nick_name, u.avatar, tu.nick_name to_nick_name, " +
+            "SELECT c.*, u.nick_name, u.avatar, tu.nick_name to_nick_name " +
             "<if test='userId != null and userId != \"\"'>" +
-            "   IFNULL(l.id, 0) AS is_like " +
+            "   ,IFNULL(l.id, 0) AS is_like " +
             "</if>" +
             "FROM fs_user_course_comment c " +
             "LEFT JOIN fs_user u ON c.user_id = u.user_id " +
@@ -144,4 +145,6 @@ public interface FsUserCourseCommentMapper
             "<if test=\"sortType != null and sortType == 2\"> order by c.comment_id desc </if>" +
             "</script>"})
     List<FsUserCourseCommentListUVO> selectFsUserCourseCommentListUVOAll(FsUserCourseCommentUParam param);
+
+    List<FsUserCourseComment> selectFsUserCourseCommentListByTypeAndCourseId(@Param("type") int type,@Param("courseId") Long courseId);
 }

+ 1 - 1
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseMapper.java

@@ -204,7 +204,7 @@ public interface FsUserCourseMapper
     List<FsUserCourseListUVO> selectFsUserCourseCommentListUVO(@Param("maps") FsUserCourseListUParam param);
 
 
-    @Select("select v.video_id,v.title,v.course_id,v.video_url,v.question_bank_id,v.is_del,v.is_on_put," +
+    @Select("select v.video_id,v.title,v.course_id,v.video_url,v.question_bank_id,v.is_del,v.is_on_put,v.course_intro_img," +
             "SEC_TO_TIME(c.duration) as total_duration,c.views,c.course_name,c.description,c.img_url,c.config_json  " +
             " from fs_user_course_video v " +
             "left join fs_user_course c on v.course_id = c.course_id " +

+ 2 - 0
fs-service/src/main/java/com/fs/course/service/IFsUserCourseCommentService.java

@@ -81,4 +81,6 @@ public interface IFsUserCourseCommentService
 
     int addComment(Long parentId);
     int minusComment(Long parentId);
+
+    List<FsUserCourseComment> selectFsUserCourseCommentListByTypeAndCourseId(int i, Long courseId);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseCommentServiceImpl.java

@@ -1,5 +1,6 @@
 package com.fs.course.service.impl;
 
+import java.util.Collections;
 import java.util.List;
 import com.fs.common.utils.DateUtils;
 import com.fs.course.param.FsUserCourseCommentParam;
@@ -149,6 +150,11 @@ public class FsUserCourseCommentServiceImpl implements IFsUserCourseCommentServi
         return fsUserCourseCommentMapper.minusComment(parentId);
     }
 
+    @Override
+    public List<FsUserCourseComment> selectFsUserCourseCommentListByTypeAndCourseId(int type, Long courseId) {
+        return fsUserCourseCommentMapper.selectFsUserCourseCommentListByTypeAndCourseId(type,courseId);
+    }
+
     @Override
     public List<FsUserCourseCommentReplyListUVO> selectFsUserCourseCommentReplyListUVO(Long commentId, String userId) {
         return fsUserCourseCommentMapper.selectFsUserCourseCommentReplyListUVO(commentId, userId);

+ 4 - 0
fs-service/src/main/java/com/fs/course/vo/FsUserCourseVideoH5VO.java

@@ -43,4 +43,8 @@ public class FsUserCourseVideoH5VO extends BaseEntity
     /** 是否上下架 **/
     private Integer isOnPut;
 
+    /** 课程介绍图片URL */
+    private String courseIntroImg;
+
+
 }

+ 2 - 0
fs-service/src/main/java/com/fs/course/vo/FsUserCourseVideoQVO.java

@@ -72,6 +72,8 @@ public class FsUserCourseVideoQVO extends BaseEntity {
 
     private String lineTwo; //线路二 电信 前缀ctev
     private String lineThree; //线路三 华为云obs
+    /** 课程介绍图片URL */
+    private String courseIntroImg;
     private Integer uploadType;
     private String redPacketMoney;
 

+ 4 - 0
fs-service/src/main/resources/mapper/course/FsUserCourseCategoryMapper.xml

@@ -55,6 +55,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             c.is_del = 0
             AND p.is_del = 0
             AND p.pid = 0
+            <if test="homePage != null and homePage == 1">
+                and d.is_del = 0
+                and d.is_show = 1
+            </if>
                 <if test="yxxTag != null and yxxTag == 1 and homePage != null and homePage == 1">
                     and d.rec_home_course_top_enabled = 1
                 </if>

+ 9 - 1
fs-service/src/main/resources/mapper/course/FsUserCourseCommentMapper.xml

@@ -17,10 +17,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="likes"    column="likes"    />
         <result property="toUserId"    column="to_user_id"    />
         <result property="isDel"    column="is_del"    />
+        <result property="nickName"    column="nick_name"    />
     </resultMap>
 
     <sql id="selectFsUserCourseCommentVo">
-        select comment_id,to_user_id, user_id,is_del, course_id, type, parent_id, content, reply_count, create_time, update_time, likes from fs_user_course_comment
+        select comment_id,to_user_id, user_id,is_del, course_id, type, parent_id, content, reply_count, create_time, update_time, likes, nick_name from fs_user_course_comment
     </sql>
 
     <select id="selectFsUserCourseCommentList" parameterType="FsUserCourseComment" resultMap="FsUserCourseCommentResult">
@@ -55,6 +56,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="likes != null">likes,</if>
             <if test="toUserId != null">to_user_id,</if>
             <if test="isDel != null">is_del,</if>
+            <if test="nickName != null">nick_name,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="userId != null">#{userId},</if>
@@ -68,6 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="likes != null">#{likes},</if>
             <if test="toUserId != null">#{toUserId},</if>
             <if test="isDel != null">#{isDel},</if>
+            <if test="nickName != null">#{nickName},</if>
          </trim>
     </insert>
 
@@ -85,6 +88,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="likes != null">likes = #{likes},</if>
             <if test="toUserId != null">to_user_id = #{toUserId},</if>
             <if test="isDel != null">is_del = #{isDel},</if>
+            <if test="nickName != null">nick_name = #{nickName},</if>
         </trim>
         where comment_id = #{commentId}
     </update>
@@ -99,4 +103,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{commentId}
         </foreach>
     </update>
+
+    <select id="selectFsUserCourseCommentListByTypeAndCourseId" resultType="com.fs.course.domain.FsUserCourseComment">
+        select * from fs_user_course_comment where type = #{type} and course_id = #{courseId}
+    </select>
 </mapper>

+ 5 - 0
fs-service/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml

@@ -39,6 +39,7 @@
         <result property="lastJoinTime"    column="last_join_time"    />
         <result property="projectId"    column="project_id"    />
         <result property="isFirst"    column="is_first"    />
+        <result property="courseIntroImg"    column="course_intro_img"    />
     </resultMap>
 
     <sql id="selectFsUserCourseVideoVo">
@@ -116,6 +117,7 @@
             <if test="isSpeed != null">is_speed,</if>
             <if test="jobId != null">job_id,</if>
             <if test="vid != null">vid,</if>
+            <if test="courseIntroImg != null">course_intro_img,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="fileId != null">#{fileId},</if>
@@ -160,6 +162,7 @@
             <if test="isSpeed != null">#{isSpeed},</if>
             <if test="jobId != null">#{jobId},</if>
             <if test="vid != null">#{vid},</if>
+            <if test="courseIntroImg != null">#{courseIntroImg},</if>
         </trim>
     </insert>
     <insert id="insertBatchFsUserCourseVideo" parameterType="FsUserCourseVideo" useGeneratedKeys="true" keyProperty="videoId">
@@ -254,6 +257,8 @@
             <if test="isOnPut != null">is_on_put = #{isOnPut},</if>
             <if test="jobId != null">job_id = #{jobId},</if>
             <if test="vid != null">vid = #{vid},</if>
+            <if test="courseIntroImg != null">course_intro_img = #{courseIntroImg},</if>
+            <if test="courseIntroImg == null">course_intro_img = null,</if>
         </trim>
         where video_id = #{videoId}
     </update>

+ 10 - 1
fs-user-app/src/main/java/com/fs/app/controller/CourseCommentController.java

@@ -55,6 +55,7 @@ import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
@@ -107,8 +108,16 @@ public class CourseCommentController extends AppBaseController
             List<FsUserCourseCommentReplyListUVO> replies = fsUserCourseCommentService.selectFsUserCourseCommentReplyListUVO(comment.getCommentId(), getUserId());
             comment.setReplyList(replies);
         });
+        List<FsUserCourseComment> fsUserCourseComments = new ArrayList<>();
+        PageInfo<FsUserCourseComment> defaultPageInfo= new PageInfo<>(fsUserCourseComments);
+        try {
+            PageHelper.startPage(param.getPageNum(), param.getPageSize());
+            fsUserCourseComments = fsUserCourseCommentService.selectFsUserCourseCommentListByTypeAndCourseId(3, param.getCourseId());
+            defaultPageInfo = new PageInfo<>(fsUserCourseComments);
+        } catch (Exception ignore) {}
+
         PageInfo<FsUserCourseCommentListUVO> listPageInfo=new PageInfo<>(list);
-        return R.ok().put("data",listPageInfo);
+        return R.ok().put("data",listPageInfo).put("defaultPageInfo",defaultPageInfo);
     }
 
     /**

+ 15 - 6
fs-user-app/src/main/java/com/fs/app/controller/CourseController.java

@@ -136,10 +136,15 @@ public class CourseController extends  AppBaseController{
         PageInfo<FsUserCourseCategory> pageInfo = courseCategoryService.selectFsUserCourseCategoryAppPage(param);
         if (StringUtils.isNotEmpty(param.getCateName())) {
             int relatedCourseCount = (int) pageInfo.getTotal();
-            Long userId = StringUtils.isNotEmpty(getUserId()) ? Long.parseLong(getUserId()) : null;
-            if (userId != null) {
-                publicCourseSearchKeywordStatService.recordKeywordSearch(param.getKeyword(), relatedCourseCount, userId);
+            try {
+                Long userId = StringUtils.isNotEmpty(getUserId()) ? Long.parseLong(getUserId()) : null;
+                if (userId != null) {
+                    publicCourseSearchKeywordStatService.recordKeywordSearch(param.getKeyword(), relatedCourseCount, userId);
+                }
+            } catch (Exception ignored){
+
             }
+
         }
         return R.ok().put("data", pageInfo);
     }
@@ -159,9 +164,13 @@ public class CourseController extends  AppBaseController{
         PageInfo<FsUserCoursePublicAppVO> pageInfo = courseService.selectFsUserCoursePublicAppPage(param);
         if (StringUtils.isNotEmpty(param.getKeyword())) {
             int relatedCourseCount = (int) pageInfo.getTotal();
-            Long userId = StringUtils.isNotEmpty(getUserId()) ? Long.parseLong(getUserId()) : null;
-            if (userId != null) {
-                publicCourseSearchKeywordStatService.recordKeywordSearch(param.getKeyword(), relatedCourseCount, userId);
+            try {
+                Long userId = StringUtils.isNotEmpty(getUserId()) ? Long.parseLong(getUserId()) : null;
+                if (userId != null) {
+                    publicCourseSearchKeywordStatService.recordKeywordSearch(param.getKeyword(), relatedCourseCount, userId);
+                }
+            } catch (Exception ignored){
+
             }
         }
         return R.ok().put("data", pageInfo);

+ 10 - 1
fs-user-app/src/main/java/com/fs/app/controller/store/CourseCommentScrmController.java

@@ -35,6 +35,7 @@ import com.fs.common.utils.DateUtils;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
@@ -86,8 +87,16 @@ public class CourseCommentScrmController extends AppBaseController
             List<FsUserCourseCommentReplyListUVO> replies = fsUserCourseCommentService.selectFsUserCourseCommentReplyListUVO(comment.getCommentId(), getUserId());
             comment.setReplyList(replies);
         });
+        List<FsUserCourseComment> fsUserCourseComments = new ArrayList<>();
+        PageInfo<FsUserCourseComment> defaultPageInfo= new PageInfo<>(fsUserCourseComments);
+        try {
+            PageHelper.startPage(param.getPageNum(), param.getPageSize());
+            fsUserCourseComments = fsUserCourseCommentService.selectFsUserCourseCommentListByTypeAndCourseId(3, param.getCourseId());
+            defaultPageInfo = new PageInfo<>(fsUserCourseComments);
+        } catch (Exception ignore) {}
+
         PageInfo<FsUserCourseCommentListUVO> listPageInfo=new PageInfo<>(list);
-        return R.ok().put("data",listPageInfo);
+        return R.ok().put("data",listPageInfo).put("defaultPageInfo",defaultPageInfo);
     }
 
     /**