Sfoglia il codice sorgente

95:红德堂APP调试

同步代码
Long 2 giorni fa
parent
commit
9e1ee688d0

+ 74 - 0
fs-service/src/main/java/com/fs/course/domain/FsVideoBarrage.java

@@ -0,0 +1,74 @@
+package com.fs.course.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 视频弹幕对象 fs_video_barrage
+ *
+ * @author fs
+ * @date 2025-04-07
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class FsVideoBarrage extends BaseEntity{
+    private String cmd;
+    /** 弹幕ID */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 视频ID */
+    @Excel(name = "视频ID")
+    private Long videoId;
+
+    /** 弹幕内容 */
+    @Excel(name = "弹幕内容")
+    private String content;
+
+    /** 弹幕对应视频时间点(毫秒) */
+    @Excel(name = "弹幕对应视频时间点", readConverterExp = "秒")
+    private Long timePoint;
+
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    /** 用户名 */
+    @Excel(name = "用户名")
+    private String username;
+
+    /** 发送平台(app / mini_app) */
+    @Excel(name = "发送平台", readConverterExp = "app,mini_app")
+    private String platform;
+
+    /** 状态:1-正常 0-隐藏 */
+    @Excel(name = "状态:1-正常 0-隐藏")
+    private String status;
+
+    /** 弹幕持续时间 */
+    @Excel(name = "弹幕持续时间")
+    private Long duration;
+
+    /** 是否优先显示1是0否,默认为0 */
+    @Excel(name = "是否优先显示1是0否,默认为0")
+    private String isPrior;
+
+    /** 是否彩色弹幕1是0否,默认为0 */
+    @Excel(name = "是否彩色弹幕1是0否,默认为0")
+    private String isColor;
+    // 例:'top',显示模式,top顶部居中,bottom底部居中,scroll滚动,默认为scroll
+    private String mode;
+    //颜色
+    private String color;
+    //字号
+    private String fontSize;
+
+
+
+}

+ 85 - 0
fs-service/src/main/java/com/fs/course/mapper/FsVideoBarrageMapper.java

@@ -0,0 +1,85 @@
+package com.fs.course.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
+import com.fs.course.domain.FsVideoBarrage;
+import com.fs.course.param.FsVideoBarrageQueryParam;
+import com.fs.course.vo.FsVideoBarrageVO;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 视频弹幕Mapper接口
+ *
+ * @author fs
+ * @date 2025-04-07
+ */
+@Repository
+public interface FsVideoBarrageMapper extends BaseMapper<FsVideoBarrage>{
+    /**
+     * 查询视频弹幕
+     *
+     * @param id 视频弹幕主键
+     * @return 视频弹幕
+     */
+    FsVideoBarrage selectFsVideoBarrageById(String id);
+
+    /**
+     * 查询视频弹幕列表
+     *
+     * @param fsVideoBarrage 视频弹幕
+     * @return 视频弹幕集合
+     */
+    @DataSource(DataSourceType.SLAVE)
+    List<FsVideoBarrageVO> selectFsVideoBarrageList(FsVideoBarrageQueryParam fsVideoBarrage);
+
+    /**
+     * 新增视频弹幕
+     *
+     * @param fsVideoBarrage 视频弹幕
+     * @return 结果
+     */
+    int insertFsVideoBarrage(FsVideoBarrage fsVideoBarrage);
+
+    /**
+     * 修改视频弹幕
+     *
+     * @param fsVideoBarrage 视频弹幕
+     * @return 结果
+     */
+    int updateFsVideoBarrage(FsVideoBarrage fsVideoBarrage);
+
+    /**
+     * 删除视频弹幕
+     *
+     * @param id 视频弹幕主键
+     * @return 结果
+     */
+    int deleteFsVideoBarrageById(String id);
+
+    /**
+     * 批量删除视频弹幕
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsVideoBarrageByIds(String[] ids);
+
+
+    int insertBarrage(FsVideoBarrage barrage);
+
+
+    List<FsVideoBarrage> selectByVideoId(@Param("videoId") Long videoId);
+
+
+    List<FsVideoBarrage> selectBySegment(@Param("videoId") Long videoId, @Param("start") int start, @Param("end") int end);
+
+    int updateStatus(@Param("id") Long id, @Param("status") String status);
+
+    int batchUpdateStatus(@Param("ids") ArrayList ids, @Param("status")String status);
+
+}

+ 46 - 0
fs-service/src/main/java/com/fs/course/param/FsVideoBarrageQueryParam.java

@@ -0,0 +1,46 @@
+package com.fs.course.param;
+
+import com.fs.watch.param.BaseQueryParam;
+import lombok.Data;
+
+@Data
+public class FsVideoBarrageQueryParam extends BaseQueryParam {
+    private String cmd;
+    private Long id;
+
+    /** 视频ID */
+    private Long videoId;
+
+    /** 弹幕内容 */
+    private String content;
+
+    /** 弹幕对应视频时间点(毫秒) */
+    private Long timePoint;
+
+    /** 用户ID */
+    private Long userId;
+
+    /** 用户名 */
+    private String username;
+
+    /** 发送平台(app / mini_app) */
+    private String platform;
+
+    /** 状态:1-正常 0-隐藏 */
+    private String status;
+
+    /** 弹幕持续时间 */
+    private Long duration;
+
+    /** 是否优先显示1是0否,默认为0 */
+    private String isPrior;
+
+    /** 是否彩色弹幕1是0否,默认为0 */
+    private String isColor;
+    // 例:'top',显示模式,top顶部居中,bottom底部居中,scroll滚动,默认为scroll
+    private String mode;
+    //颜色
+    private String color;
+    //字号
+    private String fontSize;
+}

+ 77 - 0
fs-service/src/main/java/com/fs/course/service/IFsVideoBarrageService.java

@@ -0,0 +1,77 @@
+package com.fs.course.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.course.domain.FsVideoBarrage;
+import com.fs.course.param.FsVideoBarrageQueryParam;
+import com.fs.course.vo.FsVideoBarrageExcelVo;
+import com.fs.course.vo.FsVideoBarrageVO;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 视频弹幕Service接口
+ *
+ * @author fs
+ * @date 2025-04-07
+ */
+public interface IFsVideoBarrageService extends IService<FsVideoBarrage>{
+    /**
+     * 查询视频弹幕
+     *
+     * @param id 视频弹幕主键
+     * @return 视频弹幕
+     */
+    FsVideoBarrage selectFsVideoBarrageById(String id);
+
+    /**
+     * 查询视频弹幕列表
+     *
+     * @param fsVideoBarrage 视频弹幕
+     * @return 视频弹幕集合
+     */
+    List<FsVideoBarrageVO> selectFsVideoBarrageList(FsVideoBarrageQueryParam fsVideoBarrage);
+
+    /**
+     * 新增视频弹幕
+     *
+     * @param fsVideoBarrage 视频弹幕
+     * @return 结果
+     */
+    int insertFsVideoBarrage(FsVideoBarrage fsVideoBarrage);
+
+    /**
+     * 修改视频弹幕
+     *
+     * @param fsVideoBarrage 视频弹幕
+     * @return 结果
+     */
+    int updateFsVideoBarrage(FsVideoBarrage fsVideoBarrage);
+
+    /**
+     * 批量删除视频弹幕
+     *
+     * @param ids 需要删除的视频弹幕主键集合
+     * @return 结果
+     */
+    int deleteFsVideoBarrageByIds(String[] ids);
+
+    /**
+     * 删除视频弹幕信息
+     *
+     * @param id 视频弹幕主键
+     * @return 结果
+     */
+    int deleteFsVideoBarrageById(String id);
+
+    int saveBarrage(FsVideoBarrage barrage);
+    List<FsVideoBarrage> getBarrageByVideoId(Long videoId);
+    List<FsVideoBarrage> getSegmentBarrage(Long videoId, int start, int end);
+    int updateStatus(FsVideoBarrage barrage);
+    int batchUpdateStatus(ArrayList ids, String status);
+    R importBarrageData(List<FsVideoBarrageExcelVo> list, boolean updateSupport, LoginUser loginUser);
+
+
+}

+ 172 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsVideoBarrageServiceImpl.java

@@ -0,0 +1,172 @@
+package com.fs.course.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.BeanCopyUtils;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.DateUtils;
+import com.fs.course.domain.FsUserCourseVideo;
+import com.fs.course.domain.FsVideoBarrage;
+import com.fs.course.mapper.FsVideoBarrageMapper;
+import com.fs.course.param.FsVideoBarrageQueryParam;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.service.IFsVideoBarrageService;
+import com.fs.course.vo.FsVideoBarrageExcelVo;
+import com.fs.course.vo.FsVideoBarrageVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 视频弹幕Service业务层处理
+ *
+ * @author fs
+ * @date 2025-04-07
+ */
+@Service
+@Slf4j
+public class FsVideoBarrageServiceImpl extends ServiceImpl<FsVideoBarrageMapper, FsVideoBarrage> implements IFsVideoBarrageService {
+
+
+    @Autowired
+    private FsVideoBarrageMapper barrageMapper;
+    @Autowired
+    private IFsUserCourseVideoService courseVideoService;
+    /**
+     * 查询视频弹幕
+     *
+     * @param id 视频弹幕主键
+     * @return 视频弹幕
+     */
+    @Override
+    public FsVideoBarrage selectFsVideoBarrageById(String id)
+    {
+        return baseMapper.selectFsVideoBarrageById(id);
+    }
+
+    /**
+     * 查询视频弹幕列表
+     *
+     * @param fsVideoBarrage 视频弹幕
+     * @return 视频弹幕
+     */
+    @Override
+    public List<FsVideoBarrageVO> selectFsVideoBarrageList(FsVideoBarrageQueryParam fsVideoBarrage)
+    {
+        return baseMapper.selectFsVideoBarrageList(fsVideoBarrage);
+    }
+
+    /**
+     * 新增视频弹幕
+     *
+     * @param fsVideoBarrage 视频弹幕
+     * @return 结果
+     */
+    @Override
+    public int insertFsVideoBarrage(FsVideoBarrage fsVideoBarrage)
+    {
+        FsUserCourseVideo fsUserCourseVideo = courseVideoService.selectFsUserCourseVideoByVideoId(fsVideoBarrage.getVideoId());
+        if (null==fsUserCourseVideo.getDuration()||0>fsUserCourseVideo.getDuration()){
+            throw new ServiceException("视频时长异常,请检查");
+        }
+        if (fsVideoBarrage.getTimePoint()>fsUserCourseVideo.getDuration()){
+            throw new ServiceException("弹幕时间节点不能大于视频时长,视频时长:"+fsUserCourseVideo.getDuration()+"秒");
+        }
+        fsVideoBarrage.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertFsVideoBarrage(fsVideoBarrage);
+    }
+
+    /**
+     * 修改视频弹幕
+     *
+     * @param fsVideoBarrage 视频弹幕
+     * @return 结果
+     */
+    @Override
+    public int updateFsVideoBarrage(FsVideoBarrage fsVideoBarrage)
+    {
+        FsUserCourseVideo fsUserCourseVideo = courseVideoService.selectFsUserCourseVideoByVideoId(fsVideoBarrage.getVideoId());
+        if (fsUserCourseVideo != null && fsUserCourseVideo.getDuration() != null){
+            if (fsVideoBarrage.getTimePoint()>fsUserCourseVideo.getDuration()){
+                throw new ServiceException("弹幕时间节点不能大于视频时长,视频时长:"+fsUserCourseVideo.getDuration()+"秒");
+            }
+        }
+        return baseMapper.updateFsVideoBarrage(fsVideoBarrage);
+    }
+
+    /**
+     * 批量删除视频弹幕
+     *
+     * @param ids 需要删除的视频弹幕主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsVideoBarrageByIds(String[] ids)
+    {
+        return baseMapper.deleteFsVideoBarrageByIds(ids);
+    }
+
+    /**
+     * 删除视频弹幕信息
+     *
+     * @param id 视频弹幕主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsVideoBarrageById(String id)
+    {
+        return baseMapper.deleteFsVideoBarrageById(id);
+    }
+
+    @Override
+    public int saveBarrage(FsVideoBarrage barrage) {
+        return barrageMapper.insertBarrage(barrage);
+    }
+
+    @Override
+    public List<FsVideoBarrage> getBarrageByVideoId(Long videoId) {
+        return barrageMapper.selectByVideoId(videoId);
+    }
+
+    @Override
+    public List<FsVideoBarrage> getSegmentBarrage(Long videoId, int start, int end) {
+        return barrageMapper.selectBySegment(videoId, start, end);
+    }
+
+    @Override
+    public int updateStatus(FsVideoBarrage barrage) {
+        return barrageMapper.updateStatus(barrage.getId(), barrage.getStatus());
+    }
+
+    @Override
+    public int batchUpdateStatus(ArrayList ids, String status) {
+        return barrageMapper.batchUpdateStatus(ids,status);
+    }
+
+    @Override
+    public R importBarrageData(List<FsVideoBarrageExcelVo> list, boolean updateSupport, LoginUser loginUser) {
+        if (com.fs.common.utils.StringUtils.isNull(list) || list.size() == 0)
+        {
+            throw new ServiceException("导入弹幕数据不能为空!");
+        }
+            for (FsVideoBarrageExcelVo barrageExcelVo : list) {
+                try {
+                FsVideoBarrage fsVideoBarrage = new FsVideoBarrage();
+                BeanCopyUtils.copy(barrageExcelVo,fsVideoBarrage);
+                fsVideoBarrage.setUserId(loginUser.getUserId());
+                fsVideoBarrage.setUsername(loginUser.getUsername());
+                fsVideoBarrage.setMode("scroll");
+                barrageMapper.insertBarrage(fsVideoBarrage);
+                log.info("复制到实体类:{}",fsVideoBarrage);
+                }catch (Exception e){
+                    throw new ServiceException("导入失败");
+                }
+            }
+        return R.ok("导入成功");
+    }
+
+}

+ 61 - 0
fs-service/src/main/java/com/fs/course/vo/FsVideoBarrageExcelVo.java

@@ -0,0 +1,61 @@
+package com.fs.course.vo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+
+@Data
+public class FsVideoBarrageExcelVo extends BaseEntity {
+
+    /** 弹幕ID */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 视频ID */
+    @Excel(name = "视频ID")
+    private Long videoId;
+    /** 视频名称 */
+    /** 弹幕内容 */
+    @Excel(name = "弹幕内容")
+    private String content;
+
+    /** 弹幕对应视频时间点(毫秒) */
+    @Excel(name = "对应时间")
+    private Long timePoint;
+
+    /** 用户ID */
+    //@Excel(name = "用户ID")
+    private Long userId;
+
+    /** 用户名 */
+    //@Excel(name = "用户名")
+    private String username;
+
+    /** 发送平台(app / uniapp) */
+    @Excel(name = "发送平台", combo = {"app", "uniapp"},defaultValue="app")
+    private String platform;
+
+    /** 状态:1-正常 0-隐藏 */
+    @Excel(name = "状态",combo = {"正常", "隐藏"},defaultValue="正常",readConverterExp = "0=隐藏,1=正常")
+    private String status;
+
+    /** 弹幕持续时间 */
+    @Excel(name = "持续时间")
+    private Long duration;
+
+    /** 是否优先显示1是0否,默认为0 */
+    @Excel(name = "是否优先显示", combo = {"是", "否"},defaultValue="否",readConverterExp = "0=否,1=是")
+    private String isPrior;
+
+    /** 是否彩色弹幕1是0否,默认为0 */
+    @Excel(name = "是否彩色弹幕", combo = {"是", "否"},defaultValue="否",readConverterExp = "0=否,1=是")
+    private String isColor;
+    // 例:'top',显示模式,top顶部居中,bottom底部居中,scroll滚动,默认为scroll
+    private String mode;
+    //颜色
+    private String color;
+    //字号
+    private String fontSize;
+}

+ 63 - 0
fs-service/src/main/java/com/fs/course/vo/FsVideoBarrageVO.java

@@ -0,0 +1,63 @@
+package com.fs.course.vo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+@Data
+public class FsVideoBarrageVO {
+    private String cmd;
+    /** 弹幕ID */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 视频ID */
+
+    private Long videoId;
+
+    @Excel(name = "视频ID")
+    private String videoTitle;
+
+    /** 弹幕内容 */
+    @Excel(name = "弹幕内容")
+    private String content;
+
+    /** 弹幕对应视频时间点(毫秒) */
+    @Excel(name = "弹幕对应视频时间点", readConverterExp = "秒")
+    private Long timePoint;
+
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    /** 用户名 */
+    @Excel(name = "用户名")
+    private String username;
+
+    /** 发送平台(app / mini_app) */
+    @Excel(name = "发送平台", readConverterExp = "app,mini_app")
+    private String platform;
+
+    /** 状态:1-正常 0-隐藏 */
+    @Excel(name = "状态:1-正常 0-隐藏")
+    private String status;
+
+    /** 弹幕持续时间 */
+    @Excel(name = "弹幕持续时间")
+    private Long duration;
+
+    /** 是否优先显示1是0否,默认为0 */
+    @Excel(name = "是否优先显示1是0否,默认为0")
+    private String isPrior;
+
+    /** 是否彩色弹幕1是0否,默认为0 */
+    @Excel(name = "是否彩色弹幕1是0否,默认为0")
+    private String isColor;
+    // 例:'top',显示模式,top顶部居中,bottom底部居中,scroll滚动,默认为scroll
+    private String mode;
+    //颜色
+    private String color;
+    //字号
+    private String fontSize;
+}

+ 171 - 0
fs-service/src/main/resources/mapper/course/FsVideoBarrageMapper.xml

@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.course.mapper.FsVideoBarrageMapper">
+
+    <resultMap type="FsVideoBarrage" id="FsVideoBarrageResult">
+        <result property="id"    column="id"    />
+        <result property="videoId"    column="video_id"    />
+        <result property="content"    column="content"    />
+        <result property="timePoint"    column="time_point"    />
+        <result property="userId"    column="user_id"    />
+        <result property="username"    column="username"    />
+        <result property="platform"    column="platform"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="status"    column="status"    />
+        <result property="duration"    column="duration"    />
+        <result property="isPrior"    column="is_prior"    />
+        <result property="isColor"    column="is_color"    />
+        <result property="color"    column="color"    />
+        <result property="mode"    column="mode"    />
+        <result property="fontSize"    column="font_size"    />
+    </resultMap>
+
+    <sql id="selectFsVideoBarrageVo">
+        select id, video_id, content, time_point, user_id, username, platform, create_time, status,duration,is_prior,is_color,color,mode,font_size from fs_video_barrage
+    </sql>
+
+    <select id="selectFsVideoBarrageList" parameterType="com.fs.course.param.FsVideoBarrageQueryParam" resultType="com.fs.course.vo.FsVideoBarrageVO">
+        select b.id, b.video_id, b.content, b.time_point, b.username, b.platform, b.create_time, b.status,b.duration,b.is_prior,
+        b.is_color,b.color,b.mode,b.font_size ,v.title video_title
+        from fs_video_barrage b
+        LEFT JOIN fs_user_course_video v on b.video_id = v.video_id
+        <where>
+            <if test="videoId != null "> and b.video_id = #{videoId}</if>
+            <if test="content != null  and content != ''"> and b.content = #{content}</if>
+            <if test="timePoint != null "> and b.time_point = #{timePoint}</if>
+            <if test="userId != null "> and b.user_id = #{userId}</if>
+            <if test="username != null  and username != ''"> and b.username like concat('%', #{username}, '%')</if>
+            <if test="platform != null  and platform != ''"> and b.platform = #{platform}</if>
+            <if test="status != null "> and b.status = #{status}</if>
+            <if test="duration != null "> and b.duration = #{duration}</if>
+            <if test="isPrior != null "> and b.is_prior = #{isPrior}</if>
+            <if test="isColor != null "> and b.is_color = #{isColor}</if>
+            <if test="color != null "> and b.color = #{color}</if>
+            <if test="mode != null "> and b.mode = #{mode}</if>
+            <if test="fontSize != null "> and b.font_size = #{fontSize}</if>
+            <if test="keyword != null and keyword !=''"> and (b.content like concat('%',#{keyword},'%') or v.title like concat('%',#{keyword},'%'))</if>
+        </where>
+    </select>
+
+    <select id="selectFsVideoBarrageList_COUNT" parameterType="com.fs.course.param.FsVideoBarrageQueryParam" resultType="long">
+        select count(*)  from fs_video_barrage b
+        <if test="videoId != null or (keyword != null and keyword !='')">
+            LEFT JOIN fs_user_course_video v on b.video_id = v.video_id
+        </if>
+        <where>
+            <if test="videoId != null "> and b.video_id = #{videoId}</if>
+            <if test="content != null  and content != ''"> and b.content = #{content}</if>
+            <if test="timePoint != null "> and b.time_point = #{timePoint}</if>
+            <if test="userId != null "> and b.user_id = #{userId}</if>
+            <if test="username != null  and username != ''"> and b.username like concat('%', #{username}, '%')</if>
+            <if test="platform != null  and platform != ''"> and b.platform = #{platform}</if>
+            <if test="status != null "> and b.status = #{status}</if>
+            <if test="duration != null "> and b.duration = #{duration}</if>
+            <if test="isPrior != null "> and b.is_prior = #{isPrior}</if>
+            <if test="isColor != null "> and b.is_color = #{isColor}</if>
+            <if test="color != null "> and b.color = #{color}</if>
+            <if test="mode != null "> and b.mode = #{mode}</if>
+            <if test="fontSize != null "> and b.font_size = #{fontSize}</if>
+            <if test="keyword != null and keyword !=''"> and (b.content like concat('%',#{keyword},'%') or v.title like concat('%',#{keyword},'%'))</if>
+        </where>
+    </select>
+
+    <select id="selectFsVideoBarrageById" parameterType="String" resultMap="FsVideoBarrageResult">
+        <include refid="selectFsVideoBarrageVo"/>
+        where id = #{id}
+    </select>
+    <select id="selectByVideoId" resultMap="FsVideoBarrageResult">
+        <include refid="selectFsVideoBarrageVo"/>
+        WHERE video_id = #{videoId} AND status = 1 ORDER BY time_point ASC
+    </select>
+    <select id="selectBySegment" resultType="com.fs.course.domain.FsVideoBarrage">
+        <include refid="selectFsVideoBarrageVo"/>
+        WHERE video_id = #{videoId} AND time_point BETWEEN #{start} AND #{end} AND status = 1
+    </select>
+
+    <insert id="insertFsVideoBarrage" parameterType="FsVideoBarrage" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_video_barrage
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="videoId != null">video_id,</if>
+            <if test="content != null and content != ''">content,</if>
+            <if test="timePoint != null">time_point,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="username != null">username,</if>
+            <if test="platform != null">platform,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="status != null">status,</if>
+            <if test="duration != null">duration,</if>
+            <if test="isPrior != null">is_prior,</if>
+            <if test="isColor != null">is_color,</if>
+            <if test="color != null">color,</if>
+            <if test="mode != null">mode,</if>
+            <if test="fontSize != null">font_size,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="videoId != null">#{videoId},</if>
+            <if test="content != null and content != ''">#{content},</if>
+            <if test="timePoint != null">#{timePoint},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="username != null">#{username},</if>
+            <if test="platform != null">#{platform},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="status != null">#{status},</if>
+            <if test="duration != null">#{duration},</if>
+            <if test="isPrior != null">#{isPrior},</if>
+            <if test="isColor != null">#{isColor},</if>
+            <if test="color != null">#{color},</if>
+            <if test="mode != null">#{mode},</if>
+            <if test="fontSize != null">#{fontSize},</if>
+         </trim>
+    </insert>
+    <insert id="insertBarrage">
+        INSERT INTO
+        fs_video_barrage(video_id, content, time_point, user_id, username, platform, status,duration,is_prior,is_color,color,mode,font_size) VALUES
+        (#{videoId}, #{content}, #{timePoint}, #{userId}, #{username}, #{platform}, 1,#{duration}, #{isPrior},#{isColor},#{color},#{mode},#{fontSize})
+    </insert>
+
+    <update id="updateFsVideoBarrage" parameterType="FsVideoBarrage">
+        update fs_video_barrage
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="videoId != null">video_id = #{videoId},</if>
+            <if test="content != null and content != ''">content = #{content},</if>
+            <if test="timePoint != null">time_point = #{timePoint},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="username != null">username = #{username},</if>
+            <if test="platform != null">platform = #{platform},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="duration != null">duration = #{duration},</if>
+            <if test="isPrior != null">is_prior = #{isPrior},</if>
+            <if test="isColor != null">is_color = #{isColor},</if>
+            <if test="color != null">color = #{color},</if>
+            <if test="mode != null">mode = #{mode},</if>
+            <if test="fontSize != null">font_size = #{fontSize},</if>
+        </trim>
+        where id = #{id}
+    </update>
+    <update id="updateStatus">
+        update fs_video_barrage set status = #{status} where id = #{id}
+    </update>
+    <update id="batchUpdateStatus">
+        UPDATE fs_video_barrage
+        SET status = #{status}
+        WHERE id IN
+        <foreach collection="ids" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+
+    <delete id="deleteFsVideoBarrageById" parameterType="String">
+        delete from fs_video_barrage where id = #{id}
+    </delete>
+
+    <delete id="deleteFsVideoBarrageByIds" parameterType="String">
+        delete from fs_video_barrage where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 53 - 0
fs-user-app/src/main/java/com/fs/app/controller/FsVideoBarrageController.java

@@ -0,0 +1,53 @@
+package com.fs.app.controller;
+
+import com.fs.app.annotation.Login;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.R;
+import com.fs.course.domain.FsVideoBarrage;
+import com.fs.course.service.IFsVideoBarrageService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * app+小程序端视频弹幕Controller
+ *
+ * @author fs
+ * @date 2025-04-07
+ */
+@RestController
+@RequestMapping("/barrage/barrage")
+public class FsVideoBarrageController extends BaseController
+{
+    @Autowired
+    private IFsVideoBarrageService fsVideoBarrageService;
+
+    // 发送弹幕
+    @Login
+    @PostMapping("/send")
+    public R send(@RequestBody FsVideoBarrage barrage) {
+        barrage.setUserId(getUserId());
+        barrage.setCreateTime(new Date());
+        barrage.setStatus("1"); // 默认正常
+        fsVideoBarrageService.saveBarrage(barrage);
+        return R.ok();
+    }
+
+
+    // 查询视频某个时间段内的弹幕(用于按需加载)
+    @GetMapping("/video/{videoId}/segment")
+    public R getSegment(@PathVariable Long videoId,
+                        @RequestParam int from,
+                        @RequestParam int to) {
+        List<FsVideoBarrage> list = fsVideoBarrageService.getSegmentBarrage(videoId, from, to);
+        return R.ok().put("data",list);
+    }
+    // 获取视频弹幕列表
+    @GetMapping("/list/{videoId}")
+    public R getByVideoId(@PathVariable Long videoId) {
+        List<FsVideoBarrage> list = fsVideoBarrageService.getBarrageByVideoId(videoId);
+        return R.ok().put("data",list);
+    }
+}