You've already forked FrameTour-BE
feat(video): 完善视频评价功能,增加问题机位和标签管理
- 新增VideoReviewSourceEnum枚举,定义评价来源类型(订单、渲染) - 添加LongListTypeHandler和StringListTypeHandler,处理数据库JSON字段与Java列表转换 - 修改VideoReviewEntity实体类,将机位评价改为问题机位ID列表和问题标签列表 - 创建AdminVideoReviewLogReqDTO和AdminVideoReviewLogRespDTO,实现管理后台评价日志查询 - 在VideoReviewController中增加管理后台分页查询评价日志接口 - 更新视频评价添加逻辑,验证来源参数并记录问题机位和标签信息 - 修改
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package com.ycwl.basic.controller;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.AdminVideoReviewLogReqDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.AdminVideoReviewLogRespDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.VideoPurchaseCheckReqDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.VideoPurchaseCheckRespDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewAddReqDTO;
|
||||
@@ -55,6 +57,20 @@ public class VideoReviewController {
|
||||
return ApiResponse.success(pageInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理后台分页查询评价日志
|
||||
* 提供更详细的管理信息,包括评价人账号、机位评价统计等
|
||||
*
|
||||
* @param reqDTO 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
@GetMapping("/admin/logs")
|
||||
public ApiResponse<PageInfo<AdminVideoReviewLogRespDTO>> getAdminReviewLogList(AdminVideoReviewLogReqDTO reqDTO) {
|
||||
log.info("管理后台查询评价日志,pageNum: {}, pageSize: {}", reqDTO.getPageNum(), reqDTO.getPageSize());
|
||||
PageInfo<AdminVideoReviewLogRespDTO> pageInfo = videoReviewService.getAdminReviewLogList(reqDTO);
|
||||
return ApiResponse.success(pageInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取评价统计数据
|
||||
*
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.ycwl.basic.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 视频评价来源枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum VideoReviewSourceEnum {
|
||||
|
||||
/**
|
||||
* 订单
|
||||
*/
|
||||
ORDER("ORDER", "订单"),
|
||||
|
||||
/**
|
||||
* 渲染
|
||||
*/
|
||||
RENDER("RENDER", "渲染");
|
||||
|
||||
/**
|
||||
* 枚举代码
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* 枚举描述
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
VideoReviewSourceEnum(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取枚举
|
||||
*
|
||||
* @param code 枚举代码
|
||||
* @return 枚举对象,不存在则返回null
|
||||
*/
|
||||
public static VideoReviewSourceEnum fromCode(String code) {
|
||||
if (code == null || code.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (VideoReviewSourceEnum value : VideoReviewSourceEnum.values()) {
|
||||
if (value.getCode().equals(code)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证code是否有效
|
||||
*
|
||||
* @param code 枚举代码
|
||||
* @return true-有效, false-无效
|
||||
*/
|
||||
public static boolean isValid(String code) {
|
||||
return fromCode(code) != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.ycwl.basic.handler;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||
import org.apache.ibatis.type.MappedTypes;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Long类型列表的TypeHandler
|
||||
* 用于处理数据库JSON字段与Java List<Long>之间的转换
|
||||
*/
|
||||
@Slf4j
|
||||
@MappedTypes(List.class)
|
||||
@MappedJdbcTypes(JdbcType.VARCHAR)
|
||||
public class LongListTypeHandler extends BaseTypeHandler<List<Long>> {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final TypeReference<List<Long>> typeReference = new TypeReference<List<Long>>() {};
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, List<Long> parameter, JdbcType jdbcType) throws SQLException {
|
||||
try {
|
||||
String json = objectMapper.writeValueAsString(parameter);
|
||||
ps.setString(i, json);
|
||||
log.debug("序列化Long列表: {}", json);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("序列化Long列表失败", e);
|
||||
throw new SQLException("序列化Long列表失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
String json = rs.getString(columnName);
|
||||
return parseJson(json, columnName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
String json = rs.getString(columnIndex);
|
||||
return parseJson(json, "columnIndex:" + columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
String json = cs.getString(columnIndex);
|
||||
return parseJson(json, "columnIndex:" + columnIndex);
|
||||
}
|
||||
|
||||
private List<Long> parseJson(String json, String source) {
|
||||
if (json == null || json.trim().isEmpty() || "null".equals(json)) {
|
||||
log.debug("从{}获取的JSON为空,返回空列表", source);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
try {
|
||||
List<Long> result = objectMapper.readValue(json, typeReference);
|
||||
if (result == null) {
|
||||
log.debug("从{}反序列化得到null,返回空列表", source);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
log.debug("从{}反序列化Long列表成功,数量: {}", source, result.size());
|
||||
return result;
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("从{}反序列化Long列表失败,JSON: {}", source, json, e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.ycwl.basic.handler;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||
import org.apache.ibatis.type.MappedTypes;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* String列表类型处理器
|
||||
* 用于将数据库中的JSON字符串转换为Java的List<String>对象
|
||||
*/
|
||||
@Slf4j
|
||||
@MappedTypes(List.class)
|
||||
@MappedJdbcTypes(JdbcType.VARCHAR)
|
||||
public class StringListTypeHandler extends BaseTypeHandler<List<String>> {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final TypeReference<List<String>> typeReference = new TypeReference<List<String>>() {};
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
|
||||
try {
|
||||
String json = objectMapper.writeValueAsString(parameter);
|
||||
ps.setString(i, json);
|
||||
log.debug("序列化String列表: {}", json);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("序列化String列表失败", e);
|
||||
throw new SQLException("序列化String列表失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return parseJson(rs.getString(columnName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return parseJson(rs.getString(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return parseJson(cs.getString(columnIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析JSON字符串为List<String>
|
||||
*/
|
||||
private List<String> parseJson(String json) {
|
||||
if (json == null || json.isEmpty() || "null".equals(json)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
List<String> result = objectMapper.readValue(json, typeReference);
|
||||
log.debug("反序列化String列表: {}", result);
|
||||
return result;
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("反序列化String列表失败: {}", json, e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.ycwl.basic.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.AdminVideoReviewLogReqDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.AdminVideoReviewLogRespDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewListReqDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewRespDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewStatisticsRespDTO;
|
||||
@@ -25,6 +27,14 @@ public interface VideoReviewMapper extends BaseMapper<VideoReviewEntity> {
|
||||
*/
|
||||
List<VideoReviewRespDTO> selectReviewList(VideoReviewListReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 管理后台分页查询评价日志(带更详细的管理信息)
|
||||
*
|
||||
* @param reqDTO 查询条件
|
||||
* @return 评价日志列表
|
||||
*/
|
||||
List<AdminVideoReviewLogRespDTO> selectAdminReviewLogList(AdminVideoReviewLogReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 统计总评价数
|
||||
*
|
||||
@@ -63,9 +73,9 @@ public interface VideoReviewMapper extends BaseMapper<VideoReviewEntity> {
|
||||
List<VideoReviewStatisticsRespDTO.ScenicReviewRank> countScenicRank(@Param("limit") int limit);
|
||||
|
||||
/**
|
||||
* 查询所有机位评价数据(用于后端计算平均值)
|
||||
* 查询所有问题机位ID列表(用于后端统计问题机位)
|
||||
*
|
||||
* @return 机位评价列表(Map结构: 机位ID -> 评分)
|
||||
* @return 问题机位ID列表
|
||||
*/
|
||||
List<Map<String, Integer>> selectAllCameraPositionRatings();
|
||||
List<List<Long>> selectAllProblemDeviceIds();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.ycwl.basic.model.pc.videoreview.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 管理后台视频评价日志查询请求DTO
|
||||
*/
|
||||
@Data
|
||||
public class AdminVideoReviewLogReqDTO {
|
||||
|
||||
/**
|
||||
* 评价ID(可选,精确查询)
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 视频ID(可选)
|
||||
*/
|
||||
private Long videoId;
|
||||
|
||||
/**
|
||||
* 景区ID(可选)
|
||||
*/
|
||||
private Long scenicId;
|
||||
|
||||
/**
|
||||
* 评价人ID(可选)
|
||||
*/
|
||||
private Long creator;
|
||||
|
||||
/**
|
||||
* 评价人名称(可选,模糊查询)
|
||||
*/
|
||||
private String creatorName;
|
||||
|
||||
/**
|
||||
* 评分(可选,精确匹配)
|
||||
*/
|
||||
private Integer rating;
|
||||
|
||||
/**
|
||||
* 最小评分(可选,范围查询)
|
||||
*/
|
||||
private Integer minRating;
|
||||
|
||||
/**
|
||||
* 最大评分(可选,范围查询)
|
||||
*/
|
||||
private Integer maxRating;
|
||||
|
||||
/**
|
||||
* 开始时间(可选,格式: yyyy-MM-dd HH:mm:ss)
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间(可选,格式: yyyy-MM-dd HH:mm:ss)
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* 关键词搜索(可选,搜索评价内容、景区名称、模板名称)
|
||||
*/
|
||||
private String keyword;
|
||||
|
||||
/**
|
||||
* 模板ID(可选)
|
||||
*/
|
||||
private Long templateId;
|
||||
|
||||
/**
|
||||
* 模板名称(可选,模糊查询)
|
||||
*/
|
||||
private String templateName;
|
||||
|
||||
/**
|
||||
* 是否有机位评价(可选)
|
||||
* true: 仅查询有机位评价的记录
|
||||
* false: 仅查询无机位评价的记录
|
||||
* null: 不限制
|
||||
*/
|
||||
private Boolean hasCameraRating;
|
||||
|
||||
/**
|
||||
* 问题机位ID(可选,筛选包含该机位ID的评价)
|
||||
* 任意一个问题机位匹配即可
|
||||
*/
|
||||
private Long problemDeviceId;
|
||||
|
||||
/**
|
||||
* 问题标签(可选,筛选包含该标签的评价)
|
||||
* 任意一个标签匹配即可
|
||||
*/
|
||||
private String problemTag;
|
||||
|
||||
/**
|
||||
* 来源(可选,筛选指定来源的评价)
|
||||
* 固定值: ORDER(订单), RENDER(渲染)
|
||||
*/
|
||||
private String source;
|
||||
|
||||
/**
|
||||
* 页码(必填,默认1)
|
||||
*/
|
||||
private Integer pageNum = 1;
|
||||
|
||||
/**
|
||||
* 每页数量(必填,默认20)
|
||||
*/
|
||||
private Integer pageSize = 20;
|
||||
|
||||
/**
|
||||
* 排序字段(可选,默认create_time)
|
||||
* 可选值: create_time, rating, update_time, id
|
||||
*/
|
||||
private String orderBy = "create_time";
|
||||
|
||||
/**
|
||||
* 排序方向(可选,默认DESC)
|
||||
* 可选值: ASC, DESC
|
||||
*/
|
||||
private String orderDirection = "DESC";
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.ycwl.basic.model.pc.videoreview.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 管理后台视频评价日志响应DTO
|
||||
*/
|
||||
@Data
|
||||
public class AdminVideoReviewLogRespDTO {
|
||||
|
||||
/**
|
||||
* 评价ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 视频ID
|
||||
*/
|
||||
private Long videoId;
|
||||
|
||||
/**
|
||||
* 视频URL(关联查询)
|
||||
*/
|
||||
private String videoUrl;
|
||||
|
||||
/**
|
||||
* 模板ID(关联查询video表)
|
||||
*/
|
||||
private Long templateId;
|
||||
|
||||
/**
|
||||
* 模板名称(关联查询)
|
||||
*/
|
||||
private String templateName;
|
||||
|
||||
/**
|
||||
* 景区ID
|
||||
*/
|
||||
private Long scenicId;
|
||||
|
||||
/**
|
||||
* 景区名称(关联查询)
|
||||
*/
|
||||
private String scenicName;
|
||||
|
||||
/**
|
||||
* 评价人ID(管理员ID)
|
||||
*/
|
||||
private Long creator;
|
||||
|
||||
/**
|
||||
* 评价人名称(关联查询)
|
||||
*/
|
||||
private String creatorName;
|
||||
|
||||
/**
|
||||
* 评价人账号(关联查询,管理后台显示)
|
||||
*/
|
||||
private String creatorAccount;
|
||||
|
||||
/**
|
||||
* 购买评分 1-5
|
||||
*/
|
||||
private Integer rating;
|
||||
|
||||
/**
|
||||
* 文字评价内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 有问题的机位ID列表
|
||||
* 格式: [12345, 12346, 12347]
|
||||
*/
|
||||
private List<Long> problemDeviceIds;
|
||||
|
||||
/**
|
||||
* 问题机位数量(方便前端展示)
|
||||
*/
|
||||
private Integer problemDeviceCount;
|
||||
|
||||
/**
|
||||
* 问题标签列表
|
||||
* 格式: ["画面模糊", "抖动严重", "色彩异常"]
|
||||
*/
|
||||
private List<String> problemTags;
|
||||
|
||||
/**
|
||||
* 来源
|
||||
* 固定值: ORDER(订单), RENDER(渲染)
|
||||
*/
|
||||
private String source;
|
||||
|
||||
/**
|
||||
* 来源ID
|
||||
* 用于溯源,关联订单ID或渲染任务ID等
|
||||
*/
|
||||
private Long sourceId;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 操作时长(创建到更新的时间差,单位:秒)
|
||||
*/
|
||||
private Long operationDuration;
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package com.ycwl.basic.model.pc.videoreview.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 新增视频评价请求DTO
|
||||
@@ -26,9 +26,28 @@ public class VideoReviewAddReqDTO {
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 机位评价JSON(可选)
|
||||
* 格式: {"12345": 5, "12346": 4}
|
||||
* key为机位ID,value为该机位的评分(1-5)
|
||||
* 有问题的机位ID列表(可选)
|
||||
* 格式: [12345, 12346, 12347]
|
||||
* 选择有问题的机位ID
|
||||
*/
|
||||
private Map<String, Integer> cameraPositionRating;
|
||||
private List<Long> problemDeviceIds;
|
||||
|
||||
/**
|
||||
* 问题标签列表(可选)
|
||||
* 格式: ["画面模糊", "抖动严重", "色彩异常"]
|
||||
* 可多选问题标签
|
||||
*/
|
||||
private List<String> problemTags;
|
||||
|
||||
/**
|
||||
* 来源(必填)
|
||||
* 固定值: ORDER(订单), RENDER(渲染)
|
||||
*/
|
||||
private String source;
|
||||
|
||||
/**
|
||||
* 来源ID(可选)
|
||||
* 用于溯源,关联订单ID或渲染任务ID等
|
||||
*/
|
||||
private Long sourceId;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,24 @@ public class VideoReviewListReqDTO {
|
||||
*/
|
||||
private String keyword;
|
||||
|
||||
/**
|
||||
* 问题机位ID(可选,筛选包含该机位ID的评价)
|
||||
* 任意一个问题机位匹配即可
|
||||
*/
|
||||
private Long problemDeviceId;
|
||||
|
||||
/**
|
||||
* 问题标签(可选,筛选包含该标签的评价)
|
||||
* 任意一个标签匹配即可
|
||||
*/
|
||||
private String problemTag;
|
||||
|
||||
/**
|
||||
* 来源(可选,筛选指定来源的评价)
|
||||
* 固定值: ORDER(订单), RENDER(渲染)
|
||||
*/
|
||||
private String source;
|
||||
|
||||
/**
|
||||
* 页码(必填,默认1)
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 视频评价详情响应DTO
|
||||
@@ -68,11 +68,28 @@ public class VideoReviewRespDTO {
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 机位评价JSON
|
||||
* 格式: {"12345": 5, "12346": 4}
|
||||
* key为机位ID,value为该机位的评分(1-5)
|
||||
* 有问题的机位ID列表
|
||||
* 格式: [12345, 12346, 12347]
|
||||
*/
|
||||
private Map<String, Integer> cameraPositionRating;
|
||||
private List<Long> problemDeviceIds;
|
||||
|
||||
/**
|
||||
* 问题标签列表
|
||||
* 格式: ["画面模糊", "抖动严重", "色彩异常"]
|
||||
*/
|
||||
private List<String> problemTags;
|
||||
|
||||
/**
|
||||
* 来源
|
||||
* 固定值: ORDER(订单), RENDER(渲染)
|
||||
*/
|
||||
private String source;
|
||||
|
||||
/**
|
||||
* 来源ID
|
||||
* 用于溯源,关联订单ID或渲染任务ID等
|
||||
*/
|
||||
private Long sourceId;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
|
||||
@@ -40,10 +40,10 @@ public class VideoReviewStatisticsRespDTO {
|
||||
private List<ScenicReviewRank> scenicRankList;
|
||||
|
||||
/**
|
||||
* 机位评价统计
|
||||
* key: 机位ID, value: 该机位的平均评分
|
||||
* 问题机位统计
|
||||
* key: 机位ID, value: 该机位被标记为问题的次数
|
||||
*/
|
||||
private Map<String, BigDecimal> cameraPositionAverage;
|
||||
private Map<Long, Long> problemDeviceStatistics;
|
||||
|
||||
/**
|
||||
* 景区评价排行内部类
|
||||
|
||||
@@ -4,12 +4,13 @@ import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ycwl.basic.handler.MapTypeHandler;
|
||||
import com.ycwl.basic.handler.LongListTypeHandler;
|
||||
import com.ycwl.basic.handler.StringListTypeHandler;
|
||||
import lombok.Data;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 视频评价实体类
|
||||
@@ -50,12 +51,32 @@ public class VideoReviewEntity {
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 机位评价JSON
|
||||
* 格式: {"12345": 5, "12346": 4}
|
||||
* key为机位ID,value为该机位的评分(1-5)
|
||||
* 有问题的机位ID列表
|
||||
* 格式: [12345, 12346, 12347]
|
||||
* 存储被标记为有问题的机位ID
|
||||
*/
|
||||
@TableField(typeHandler = MapTypeHandler.class, jdbcType = JdbcType.VARCHAR)
|
||||
private Map<String, Integer> cameraPositionRating;
|
||||
@TableField(typeHandler = LongListTypeHandler.class, jdbcType = JdbcType.VARCHAR)
|
||||
private List<Long> problemDeviceIds;
|
||||
|
||||
/**
|
||||
* 问题标签列表
|
||||
* 格式: ["画面模糊", "抖动严重", "色彩异常"]
|
||||
* 存储视频或机位的问题标签,可多选
|
||||
*/
|
||||
@TableField(typeHandler = StringListTypeHandler.class, jdbcType = JdbcType.VARCHAR)
|
||||
private List<String> problemTags;
|
||||
|
||||
/**
|
||||
* 来源
|
||||
* 固定值: ORDER(订单), RENDER(渲染)
|
||||
*/
|
||||
private String source;
|
||||
|
||||
/**
|
||||
* 来源ID
|
||||
* 用于溯源,关联订单ID或渲染任务ID等
|
||||
*/
|
||||
private Long sourceId;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.ycwl.basic.service;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.AdminVideoReviewLogReqDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.AdminVideoReviewLogRespDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.VideoPurchaseCheckReqDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.VideoPurchaseCheckRespDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewAddReqDTO;
|
||||
@@ -32,6 +34,14 @@ public interface VideoReviewService {
|
||||
*/
|
||||
PageInfo<VideoReviewRespDTO> getReviewList(VideoReviewListReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 管理后台分页查询评价日志
|
||||
*
|
||||
* @param reqDTO 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
PageInfo<AdminVideoReviewLogRespDTO> getAdminReviewLogList(AdminVideoReviewLogReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 获取评价统计数据
|
||||
*
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.ycwl.basic.constant.BaseContextHandler;
|
||||
import com.ycwl.basic.enums.VideoReviewSourceEnum;
|
||||
import com.ycwl.basic.exception.BaseException;
|
||||
import com.ycwl.basic.exception.BizException;
|
||||
import com.ycwl.basic.mapper.OrderMapper;
|
||||
@@ -14,6 +15,8 @@ import com.ycwl.basic.mapper.VideoReviewMapper;
|
||||
import com.ycwl.basic.model.pc.order.entity.OrderEntity;
|
||||
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
|
||||
import com.ycwl.basic.model.pc.video.entity.VideoEntity;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.AdminVideoReviewLogReqDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.AdminVideoReviewLogRespDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.VideoPurchaseCheckReqDTO;
|
||||
import com.ycwl.basic.model.pc.videoreview.dto.VideoPurchaseCheckRespDTO;
|
||||
import com.ycwl.basic.repository.DeviceRepository;
|
||||
@@ -72,6 +75,12 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
if (reqDTO.getRating() == null || reqDTO.getRating() < 1 || reqDTO.getRating() > 5) {
|
||||
throw new BaseException("评分必须在1-5之间");
|
||||
}
|
||||
if (reqDTO.getSource() == null || reqDTO.getSource().isEmpty()) {
|
||||
throw new BaseException("来源不能为空");
|
||||
}
|
||||
if (!VideoReviewSourceEnum.isValid(reqDTO.getSource())) {
|
||||
throw new BaseException("来源值无效,仅支持: ORDER(订单), RENDER(渲染)");
|
||||
}
|
||||
|
||||
// 2. 查询视频信息,获取景区ID
|
||||
VideoEntity video = videoMapper.getEntity(reqDTO.getVideoId());
|
||||
@@ -93,12 +102,16 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
entity.setCreator(creator);
|
||||
entity.setRating(reqDTO.getRating());
|
||||
entity.setContent(reqDTO.getContent());
|
||||
entity.setCameraPositionRating(reqDTO.getCameraPositionRating());
|
||||
entity.setProblemDeviceIds(reqDTO.getProblemDeviceIds());
|
||||
entity.setProblemTags(reqDTO.getProblemTags());
|
||||
entity.setSource(reqDTO.getSource());
|
||||
entity.setSourceId(reqDTO.getSourceId());
|
||||
|
||||
// 5. 插入数据库
|
||||
videoReviewMapper.insert(entity);
|
||||
|
||||
log.info("管理员[{}]对视频[{}]添加评价成功,评价ID: {}", creator, reqDTO.getVideoId(), entity.getId());
|
||||
log.info("管理员[{}]对视频[{}]添加评价成功,评价ID: {}, 来源: {}, 来源ID: {}",
|
||||
creator, reqDTO.getVideoId(), entity.getId(), reqDTO.getSource(), reqDTO.getSourceId());
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@@ -114,6 +127,18 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
return new PageInfo<>(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageInfo<AdminVideoReviewLogRespDTO> getAdminReviewLogList(AdminVideoReviewLogReqDTO reqDTO) {
|
||||
// 设置分页参数
|
||||
PageHelper.startPage(reqDTO.getPageNum(), reqDTO.getPageSize());
|
||||
|
||||
// 查询列表
|
||||
List<AdminVideoReviewLogRespDTO> list = videoReviewMapper.selectAdminReviewLogList(reqDTO);
|
||||
|
||||
// 封装分页结果
|
||||
return new PageInfo<>(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VideoReviewStatisticsRespDTO getStatistics() {
|
||||
VideoReviewStatisticsRespDTO statistics = new VideoReviewStatisticsRespDTO();
|
||||
@@ -154,9 +179,9 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
List<VideoReviewStatisticsRespDTO.ScenicReviewRank> scenicRankList = videoReviewMapper.countScenicRank(10);
|
||||
statistics.setScenicRankList(scenicRankList);
|
||||
|
||||
// 6. 机位评价维度平均值
|
||||
Map<String, BigDecimal> cameraPositionAverage = calculateCameraPositionAverage();
|
||||
statistics.setCameraPositionAverage(cameraPositionAverage);
|
||||
// 6. 问题机位统计
|
||||
Map<Long, Long> problemDeviceStatistics = calculateProblemDeviceStatistics();
|
||||
statistics.setProblemDeviceStatistics(problemDeviceStatistics);
|
||||
|
||||
return statistics;
|
||||
}
|
||||
@@ -168,37 +193,11 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
reqDTO.setPageSize(Integer.MAX_VALUE);
|
||||
List<VideoReviewRespDTO> list = videoReviewMapper.selectReviewList(reqDTO);
|
||||
|
||||
// 2. 收集所有机位ID并批量查询机位名称
|
||||
Set<Long> allDeviceIds = new LinkedHashSet<>();
|
||||
for (VideoReviewRespDTO review : list) {
|
||||
Map<String, Integer> cameraRating = review.getCameraPositionRating();
|
||||
if (cameraRating != null && !cameraRating.isEmpty()) {
|
||||
// 收集机位ID (按顺序)
|
||||
for (String deviceIdStr : cameraRating.keySet()) {
|
||||
try {
|
||||
allDeviceIds.add(Long.valueOf(deviceIdStr));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("无效的机位ID: {}", deviceIdStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 批量查询机位名称
|
||||
Map<Long, String> deviceNames = new HashMap<>();
|
||||
if (!allDeviceIds.isEmpty()) {
|
||||
deviceNames = deviceRepository.batchGetDeviceNames(new ArrayList<>(allDeviceIds));
|
||||
}
|
||||
|
||||
// 对机位ID按ID排序,保证表头顺序一致
|
||||
List<Long> sortedDeviceIds = new ArrayList<>(allDeviceIds);
|
||||
sortedDeviceIds.sort(Long::compareTo);
|
||||
|
||||
// 3. 创建Excel工作簿
|
||||
// 2. 创建Excel工作簿
|
||||
Workbook workbook = new XSSFWorkbook();
|
||||
Sheet sheet = workbook.createSheet("视频评价数据");
|
||||
|
||||
// 4. 创建标题行样式
|
||||
// 3. 创建标题行样式
|
||||
CellStyle headerStyle = workbook.createCellStyle();
|
||||
Font headerFont = workbook.createFont();
|
||||
headerFont.setBold(true);
|
||||
@@ -206,7 +205,7 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
|
||||
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
|
||||
// 5. 生成动态表头 - 使用机位名称作为表头
|
||||
// 4. 生成表头
|
||||
Row headerRow = sheet.createRow(0);
|
||||
List<String> headerList = new ArrayList<>();
|
||||
headerList.add("评价ID");
|
||||
@@ -216,14 +215,8 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
headerList.add("评价人名称");
|
||||
headerList.add("评分");
|
||||
headerList.add("文字评价");
|
||||
|
||||
// 添加机位列 - 表头直接使用机位名称
|
||||
Map<Long, String> finalDeviceNames = deviceNames;
|
||||
for (Long deviceId : sortedDeviceIds) {
|
||||
String deviceName = finalDeviceNames.getOrDefault(deviceId, "未知设备(ID:" + deviceId + ")");
|
||||
headerList.add(deviceName);
|
||||
}
|
||||
|
||||
headerList.add("问题机位ID列表");
|
||||
headerList.add("问题标签");
|
||||
headerList.add("创建时间");
|
||||
headerList.add("更新时间");
|
||||
|
||||
@@ -234,7 +227,7 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
cell.setCellStyle(headerStyle);
|
||||
}
|
||||
|
||||
// 6. 填充数据
|
||||
// 5. 填充数据
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
int rowNum = 1;
|
||||
|
||||
@@ -249,41 +242,37 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
row.createCell(colIndex++).setCellValue(review.getScenicName());
|
||||
row.createCell(colIndex++).setCellValue(review.getCreatorName());
|
||||
row.createCell(colIndex++).setCellValue(review.getRating());
|
||||
row.createCell(colIndex++).setCellValue(review.getContent());
|
||||
row.createCell(colIndex++).setCellValue(review.getContent() != null ? review.getContent() : "");
|
||||
|
||||
// 机位评价列 - 按表头顺序填充
|
||||
Map<String, Integer> cameraRating = review.getCameraPositionRating();
|
||||
for (Long deviceId : sortedDeviceIds) {
|
||||
String deviceIdStr = String.valueOf(deviceId);
|
||||
Integer rating = null;
|
||||
// 问题机位ID列表
|
||||
List<Long> problemDeviceIds = review.getProblemDeviceIds();
|
||||
String problemDeviceIdsStr = (problemDeviceIds != null && !problemDeviceIds.isEmpty())
|
||||
? problemDeviceIds.toString()
|
||||
: "";
|
||||
row.createCell(colIndex++).setCellValue(problemDeviceIdsStr);
|
||||
|
||||
if (cameraRating != null && cameraRating.containsKey(deviceIdStr)) {
|
||||
rating = cameraRating.get(deviceIdStr);
|
||||
}
|
||||
|
||||
Cell cell = row.createCell(colIndex++);
|
||||
if (rating != null) {
|
||||
cell.setCellValue(rating);
|
||||
} else {
|
||||
cell.setCellValue("");
|
||||
}
|
||||
}
|
||||
// 问题标签
|
||||
List<String> problemTags = review.getProblemTags();
|
||||
String problemTagsStr = (problemTags != null && !problemTags.isEmpty())
|
||||
? String.join(", ", problemTags)
|
||||
: "";
|
||||
row.createCell(colIndex++).setCellValue(problemTagsStr);
|
||||
|
||||
// 时间列
|
||||
row.createCell(colIndex++).setCellValue(review.getCreateTime() != null ? sdf.format(review.getCreateTime()) : "");
|
||||
row.createCell(colIndex).setCellValue(review.getUpdateTime() != null ? sdf.format(review.getUpdateTime()) : "");
|
||||
}
|
||||
|
||||
// 7. 自动调整列宽
|
||||
// 6. 自动调整列宽
|
||||
for (int i = 0; i < headerList.size(); i++) {
|
||||
sheet.autoSizeColumn(i);
|
||||
}
|
||||
|
||||
// 8. 写入输出流
|
||||
// 7. 写入输出流
|
||||
workbook.write(outputStream);
|
||||
workbook.close();
|
||||
|
||||
log.info("导出视频评价数据成功,共{}条,机位数:{}", list.size(), sortedDeviceIds.size());
|
||||
log.info("导出视频评价数据成功,共{}条", list.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -338,32 +327,25 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算各机位的平均评分
|
||||
* 统计问题机位
|
||||
* 统计每个机位被标记为问题的次数
|
||||
*/
|
||||
private Map<String, BigDecimal> calculateCameraPositionAverage() {
|
||||
List<Map<String, Integer>> allRatings = videoReviewMapper.selectAllCameraPositionRatings();
|
||||
private Map<Long, Long> calculateProblemDeviceStatistics() {
|
||||
List<List<Long>> allProblemDeviceIds = videoReviewMapper.selectAllProblemDeviceIds();
|
||||
|
||||
if (allRatings == null || allRatings.isEmpty()) {
|
||||
if (allProblemDeviceIds == null || allProblemDeviceIds.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
// 统计各机位的总分和次数
|
||||
Map<String, List<Integer>> deviceScores = new HashMap<>();
|
||||
for (Map<String, Integer> rating : allRatings) {
|
||||
if (rating == null) continue;
|
||||
// 遍历每个机位的评分
|
||||
for (Map.Entry<String, Integer> entry : rating.entrySet()) {
|
||||
deviceScores.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()).add(entry.getValue());
|
||||
// 统计各机位被标记为问题的次数
|
||||
Map<Long, Long> deviceProblemCount = new HashMap<>();
|
||||
for (List<Long> problemDeviceIds : allProblemDeviceIds) {
|
||||
if (problemDeviceIds == null || problemDeviceIds.isEmpty()) continue;
|
||||
for (Long deviceId : problemDeviceIds) {
|
||||
deviceProblemCount.put(deviceId, deviceProblemCount.getOrDefault(deviceId, 0L) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算平均值
|
||||
Map<String, BigDecimal> result = new HashMap<>();
|
||||
for (Map.Entry<String, List<Integer>> entry : deviceScores.entrySet()) {
|
||||
double avg = entry.getValue().stream().mapToInt(Integer::intValue).average().orElse(0.0);
|
||||
result.put(entry.getKey(), BigDecimal.valueOf(avg).setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
return result;
|
||||
return deviceProblemCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,12 @@
|
||||
<result property="creatorName" column="creator_name"/>
|
||||
<result property="rating" column="rating"/>
|
||||
<result property="content" column="content"/>
|
||||
<result property="cameraPositionRating" column="camera_position_rating"
|
||||
typeHandler="com.ycwl.basic.handler.MapTypeHandler"/>
|
||||
<result property="problemDeviceIds" column="problem_device_ids"
|
||||
typeHandler="com.ycwl.basic.handler.LongListTypeHandler"/>
|
||||
<result property="problemTags" column="problem_tags"
|
||||
typeHandler="com.ycwl.basic.handler.StringListTypeHandler"/>
|
||||
<result property="source" column="source"/>
|
||||
<result property="sourceId" column="source_id"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
@@ -31,7 +35,10 @@
|
||||
vr.creator,
|
||||
vr.rating,
|
||||
vr.content,
|
||||
vr.camera_position_rating,
|
||||
vr.problem_device_ids,
|
||||
vr.problem_tags,
|
||||
vr.source,
|
||||
vr.source_id,
|
||||
vr.create_time,
|
||||
vr.update_time,
|
||||
v.video_url,
|
||||
@@ -72,6 +79,15 @@
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND vr.content LIKE CONCAT('%', #{keyword}, '%')
|
||||
</if>
|
||||
<if test="problemDeviceId != null">
|
||||
AND JSON_CONTAINS(vr.problem_device_ids, CAST(#{problemDeviceId} AS CHAR), '$')
|
||||
</if>
|
||||
<if test="problemTag != null and problemTag != ''">
|
||||
AND JSON_CONTAINS(vr.problem_tags, JSON_QUOTE(#{problemTag}), '$')
|
||||
</if>
|
||||
<if test="source != null and source != ''">
|
||||
AND vr.source = #{source}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY
|
||||
<choose>
|
||||
@@ -140,11 +156,156 @@
|
||||
LIMIT #{limit}
|
||||
</select>
|
||||
|
||||
<!-- 查询所有机位评价数据 -->
|
||||
<select id="selectAllCameraPositionRatings" resultType="java.util.Map">
|
||||
SELECT camera_position_rating
|
||||
<!-- 查询所有问题机位ID列表 -->
|
||||
<select id="selectAllProblemDeviceIds" resultType="java.util.List">
|
||||
SELECT problem_device_ids
|
||||
FROM video_review
|
||||
WHERE camera_position_rating IS NOT NULL AND camera_position_rating != ''
|
||||
WHERE problem_device_ids IS NOT NULL
|
||||
AND problem_device_ids != ''
|
||||
AND problem_device_ids != '[]'
|
||||
</select>
|
||||
|
||||
<!-- 管理后台评价日志结果映射 -->
|
||||
<resultMap id="AdminVideoReviewLogRespMap" type="com.ycwl.basic.model.pc.videoreview.dto.AdminVideoReviewLogRespDTO">
|
||||
<id property="id" column="id"/>
|
||||
<result property="videoId" column="video_id"/>
|
||||
<result property="videoUrl" column="video_url"/>
|
||||
<result property="templateId" column="template_id"/>
|
||||
<result property="templateName" column="template_name"/>
|
||||
<result property="scenicId" column="scenic_id"/>
|
||||
<result property="scenicName" column="scenic_name"/>
|
||||
<result property="creator" column="creator"/>
|
||||
<result property="creatorName" column="creator_name"/>
|
||||
<result property="creatorAccount" column="creator_account"/>
|
||||
<result property="rating" column="rating"/>
|
||||
<result property="content" column="content"/>
|
||||
<result property="problemDeviceIds" column="problem_device_ids"
|
||||
typeHandler="com.ycwl.basic.handler.LongListTypeHandler"/>
|
||||
<result property="problemDeviceCount" column="problem_device_count"/>
|
||||
<result property="problemTags" column="problem_tags"
|
||||
typeHandler="com.ycwl.basic.handler.StringListTypeHandler"/>
|
||||
<result property="source" column="source"/>
|
||||
<result property="sourceId" column="source_id"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="operationDuration" column="operation_duration"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 管理后台分页查询评价日志 -->
|
||||
<select id="selectAdminReviewLogList" parameterType="com.ycwl.basic.model.pc.videoreview.dto.AdminVideoReviewLogReqDTO"
|
||||
resultMap="AdminVideoReviewLogRespMap">
|
||||
SELECT
|
||||
vr.id,
|
||||
vr.video_id,
|
||||
vr.scenic_id,
|
||||
vr.creator,
|
||||
vr.rating,
|
||||
vr.content,
|
||||
vr.problem_device_ids,
|
||||
vr.problem_tags,
|
||||
vr.source,
|
||||
vr.source_id,
|
||||
vr.create_time,
|
||||
vr.update_time,
|
||||
v.video_url,
|
||||
v.template_id,
|
||||
t.name AS template_name,
|
||||
s.name AS scenic_name,
|
||||
u.name AS creator_name,
|
||||
u.account AS creator_account,
|
||||
<!-- 计算问题机位数量 -->
|
||||
CASE
|
||||
WHEN vr.problem_device_ids IS NOT NULL AND vr.problem_device_ids != '' AND vr.problem_device_ids != '[]'
|
||||
THEN JSON_LENGTH(vr.problem_device_ids)
|
||||
ELSE 0
|
||||
END AS problem_device_count,
|
||||
<!-- 计算操作时长(秒) -->
|
||||
TIMESTAMPDIFF(SECOND, vr.create_time, vr.update_time) AS operation_duration
|
||||
FROM video_review vr
|
||||
LEFT JOIN video v ON vr.video_id = v.id
|
||||
LEFT JOIN template t ON v.template_id = t.id
|
||||
LEFT JOIN scenic s ON vr.scenic_id = s.id
|
||||
LEFT JOIN admin_user u ON vr.creator = u.id
|
||||
<where>
|
||||
<if test="id != null">
|
||||
AND vr.id = #{id}
|
||||
</if>
|
||||
<if test="videoId != null">
|
||||
AND vr.video_id = #{videoId}
|
||||
</if>
|
||||
<if test="scenicId != null">
|
||||
AND vr.scenic_id = #{scenicId}
|
||||
</if>
|
||||
<if test="creator != null">
|
||||
AND vr.creator = #{creator}
|
||||
</if>
|
||||
<if test="creatorName != null and creatorName != ''">
|
||||
AND u.name LIKE CONCAT('%', #{creatorName}, '%')
|
||||
</if>
|
||||
<if test="rating != null">
|
||||
AND vr.rating = #{rating}
|
||||
</if>
|
||||
<if test="minRating != null">
|
||||
AND vr.rating >= #{minRating}
|
||||
</if>
|
||||
<if test="maxRating != null">
|
||||
AND vr.rating <= #{maxRating}
|
||||
</if>
|
||||
<if test="startTime != null and startTime != ''">
|
||||
AND vr.create_time >= #{startTime}
|
||||
</if>
|
||||
<if test="endTime != null and endTime != ''">
|
||||
AND vr.create_time <= #{endTime}
|
||||
</if>
|
||||
<if test="templateId != null">
|
||||
AND v.template_id = #{templateId}
|
||||
</if>
|
||||
<if test="templateName != null and templateName != ''">
|
||||
AND t.name LIKE CONCAT('%', #{templateName}, '%')
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND (
|
||||
vr.content LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR s.name LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR t.name LIKE CONCAT('%', #{keyword}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="hasCameraRating != null">
|
||||
<!-- hasCameraRating 参数已废弃,保留以兼容旧接口 -->
|
||||
</if>
|
||||
<if test="problemDeviceId != null">
|
||||
AND JSON_CONTAINS(vr.problem_device_ids, CAST(#{problemDeviceId} AS CHAR), '$')
|
||||
</if>
|
||||
<if test="problemTag != null and problemTag != ''">
|
||||
AND JSON_CONTAINS(vr.problem_tags, JSON_QUOTE(#{problemTag}), '$')
|
||||
</if>
|
||||
<if test="source != null and source != ''">
|
||||
AND vr.source = #{source}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY
|
||||
<choose>
|
||||
<when test="orderBy == 'rating'">
|
||||
vr.rating
|
||||
</when>
|
||||
<when test="orderBy == 'update_time'">
|
||||
vr.update_time
|
||||
</when>
|
||||
<when test="orderBy == 'id'">
|
||||
vr.id
|
||||
</when>
|
||||
<otherwise>
|
||||
vr.create_time
|
||||
</otherwise>
|
||||
</choose>
|
||||
<choose>
|
||||
<when test="orderDirection == 'ASC'">
|
||||
ASC
|
||||
</when>
|
||||
<otherwise>
|
||||
DESC
|
||||
</otherwise>
|
||||
</choose>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user