You've already forked FrameTour-BE
feat(video-review): 支持机位多维度评价功能
All checks were successful
ZhenTu-BE/pipeline/head This commit looks good
All checks were successful
ZhenTu-BE/pipeline/head This commit looks good
- 新增NestedMapTypeHandler处理嵌套Map与JSON互转 - 修改VideoReview相关实体类和DTO以支持嵌套Map结构 - 更新数据库查询逻辑以适配新的评价数据结构 - 优化平均分计算方法以处理多机位多维度评分 - 完善MyBatis配置中的typeHandler引用 - 补充视频查询接口返回任务开始结束时间字段 - 修正SQL关联查询条件确保数据准确性
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
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 java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 嵌套Map类型的TypeHandler,用于处理JSON字段与Map<String, Map<String, Integer>>的互转
|
||||
* 主要用于机位评价功能:外层key为机位ID,内层Map为该机位的各维度评分
|
||||
*
|
||||
* 数据格式示例:
|
||||
* {
|
||||
* "12345": {"清晰度": 5, "构图": 4, "色彩": 5, "整体效果": 4},
|
||||
* "12346": {"清晰度": 4, "构图": 5, "色彩": 4, "整体效果": 5}
|
||||
* }
|
||||
*/
|
||||
@Slf4j
|
||||
public class NestedMapTypeHandler extends BaseTypeHandler<Map<String, Map<String, Integer>>> {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final TypeReference<Map<String, Map<String, Integer>>> typeReference =
|
||||
new TypeReference<Map<String, Map<String, Integer>>>() {};
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Map<String, Integer>> parameter, JdbcType jdbcType) throws SQLException {
|
||||
try {
|
||||
String json = objectMapper.writeValueAsString(parameter);
|
||||
ps.setString(i, json);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("序列化嵌套Map为JSON失败", e);
|
||||
throw new SQLException("序列化嵌套Map为JSON失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Map<String, Integer>> getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
String json = rs.getString(columnName);
|
||||
return parseJson(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Map<String, Integer>> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
String json = rs.getString(columnIndex);
|
||||
return parseJson(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Map<String, Integer>> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
String json = cs.getString(columnIndex);
|
||||
return parseJson(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析JSON字符串为嵌套Map
|
||||
*/
|
||||
private Map<String, Map<String, Integer>> parseJson(String json) {
|
||||
if (json == null || json.trim().isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(json, typeReference);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("解析JSON为嵌套Map失败, json={}", json, e);
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public interface VideoReviewMapper extends BaseMapper<VideoReviewEntity> {
|
||||
/**
|
||||
* 查询所有机位评价数据(用于后端计算平均值)
|
||||
*
|
||||
* @return 机位评价列表
|
||||
* @return 机位评价列表(嵌套Map结构)
|
||||
*/
|
||||
List<Map<String, Integer>> selectAllCameraPositionRatings();
|
||||
List<Map<String, Map<String, Integer>>> selectAllCameraPositionRatings();
|
||||
}
|
||||
|
||||
@@ -63,6 +63,10 @@ public class VideoRespVO {
|
||||
private Date createTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date updateTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date startTime;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date endTime;
|
||||
private Integer height;
|
||||
private Integer width;
|
||||
private BigDecimal duration;
|
||||
|
||||
@@ -26,8 +26,10 @@ public class VideoReviewAddReqDTO {
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 机位快速评价JSON(可选)
|
||||
* 格式: {"角度":5,"清晰度":4,"构图":5}
|
||||
* 机位评价JSON(可选)
|
||||
* 格式: {"12345": {"清晰度":5,"构图":4,"色彩":5,"整体效果":4}, "12346": {...}}
|
||||
* 外层key为机位ID,内层Map为该机位的各维度评分
|
||||
* 评分维度: 清晰度, 构图, 色彩, 整体效果
|
||||
*/
|
||||
private Map<String, Integer> cameraPositionRating;
|
||||
private Map<String, Map<String, Integer>> cameraPositionRating;
|
||||
}
|
||||
|
||||
@@ -58,9 +58,11 @@ public class VideoReviewRespDTO {
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 机位快速评价JSON
|
||||
* 机位评价JSON
|
||||
* 格式: {"12345": {"清晰度":5,"构图":4,"色彩":5,"整体效果":4}, "12346": {...}}
|
||||
* 外层key为机位ID,内层Map为该机位的各维度评分
|
||||
*/
|
||||
private Map<String, Integer> cameraPositionRating;
|
||||
private Map<String, Map<String, Integer>> cameraPositionRating;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
|
||||
@@ -4,7 +4,7 @@ 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.NestedMapTypeHandler;
|
||||
import lombok.Data;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
|
||||
@@ -50,11 +50,12 @@ public class VideoReviewEntity {
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 机位快速评价JSON
|
||||
* 格式: {"角度":5,"清晰度":4,"构图":5}
|
||||
* 机位评价JSON
|
||||
* 格式: {"12345": {"清晰度":5,"构图":4,"色彩":5,"整体效果":4}, "12346": {...}}
|
||||
* 外层key为机位ID,内层Map为该机位的各维度评分
|
||||
*/
|
||||
@TableField(typeHandler = MapTypeHandler.class, jdbcType = JdbcType.VARCHAR)
|
||||
private Map<String, Integer> cameraPositionRating;
|
||||
@TableField(typeHandler = NestedMapTypeHandler.class, jdbcType = JdbcType.VARCHAR)
|
||||
private Map<String, Map<String, Integer>> cameraPositionRating;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
|
||||
@@ -218,7 +218,7 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
* 计算机位评价各维度的平均值
|
||||
*/
|
||||
private Map<String, BigDecimal> calculateCameraPositionAverage() {
|
||||
List<Map<String, Integer>> allRatings = videoReviewMapper.selectAllCameraPositionRatings();
|
||||
List<Map<String, Map<String, Integer>>> allRatings = videoReviewMapper.selectAllCameraPositionRatings();
|
||||
|
||||
if (allRatings == null || allRatings.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
@@ -226,10 +226,15 @@ public class VideoReviewServiceImpl implements VideoReviewService {
|
||||
|
||||
// 统计各维度的总分和次数
|
||||
Map<String, List<Integer>> dimensionScores = new HashMap<>();
|
||||
for (Map<String, Integer> rating : allRatings) {
|
||||
for (Map<String, Map<String, Integer>> rating : allRatings) {
|
||||
if (rating == null) continue;
|
||||
for (Map.Entry<String, Integer> entry : rating.entrySet()) {
|
||||
dimensionScores.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()).add(entry.getValue());
|
||||
// 遍历每个机位
|
||||
for (Map<String, Integer> deviceRatings : rating.values()) {
|
||||
if (deviceRatings == null) continue;
|
||||
// 遍历该机位的每个维度
|
||||
for (Map.Entry<String, Integer> entry : deviceRatings.entrySet()) {
|
||||
dimensionScores.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()).add(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,17 +76,17 @@
|
||||
)
|
||||
</delete>
|
||||
<select id="list" resultType="com.ycwl.basic.model.pc.video.resp.VideoRespVO">
|
||||
select v.id, v.scenic_id, template_id, task_id, worker_id, video_url, v.create_time, v.update_time,
|
||||
t.name templateName, t.price templatePrice,t.cover_url templateCoverUrl,
|
||||
tk.task_params taskParams
|
||||
select v.id, v.scenic_id, v.template_id, task_id, tk.worker_id, v.video_url, v.create_time, v.update_time,
|
||||
t.name templateName, t.cover_url templateCoverUrl,
|
||||
tk.task_params taskParams, tk.start_time, tk.end_time
|
||||
from video v
|
||||
left join template t on v.template_id = t.id
|
||||
left join task tk on v.task_id = tk.id
|
||||
<where>
|
||||
<if test="scenicId!= null">and v.scenic_id = #{scenicId} </if>
|
||||
<if test="templateId!= null">and template_id = #{templateId} </if>
|
||||
<if test="templateId!= null">and v.template_id = #{templateId} </if>
|
||||
<if test="taskId!=null">and task_id = #{taskId}</if>
|
||||
<if test="workerId!= null">and worker_id = #{workerId} </if>
|
||||
<if test="workerId!= null">and t.worker_id = #{workerId} </if>
|
||||
<if test="startTime!= null">and v.create_time >= #{startTime} </if>
|
||||
<if test="endTime!= null">and v.create_time <= #{endTime} </if>
|
||||
<if test="faceId!= null">
|
||||
@@ -98,10 +98,9 @@
|
||||
order by v.create_time desc
|
||||
</select>
|
||||
<select id="getById" resultType="com.ycwl.basic.model.pc.video.resp.VideoRespVO">
|
||||
select v.id, v.scenic_id, template_id, task_id, worker_id, video_url, v.create_time, v.update_time,
|
||||
t.name templateName,t.price templatePrice, t.cover_url templateCoverUrl, t.slash_price slashPrice,
|
||||
v.height, v.width, v.duration,
|
||||
tk.task_params taskParams
|
||||
select v.id, v.scenic_id, v.template_id, task_id, tk.worker_id, v.video_url, v.create_time, v.update_time,
|
||||
t.name templateName, t.cover_url templateCoverUrl,
|
||||
tk.task_params taskParams, tk.start_time, tk.end_time
|
||||
from video v
|
||||
left join template t on v.template_id = t.id
|
||||
left join task tk on v.task_id = tk.id
|
||||
@@ -111,7 +110,7 @@
|
||||
select * from video where task_id = #{taskId} limit 1
|
||||
</select>
|
||||
<select id="queryByRelation" resultType="com.ycwl.basic.model.pc.video.resp.VideoRespVO">
|
||||
select v.id, mv.scenic_id, v.template_id, mv.task_id, mv.face_id, worker_id, video_url, v.create_time, v.update_time,
|
||||
select v.id, mv.scenic_id, v.template_id, mv.task_id, mv.face_id, tk.worker_id, v.video_url, v.create_time, v.update_time,
|
||||
t.name templateName, t.price templatePrice,t.cover_url templateCoverUrl, mv.is_buy,
|
||||
tk.task_params taskParams
|
||||
from member_video mv
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<result property="rating" column="rating"/>
|
||||
<result property="content" column="content"/>
|
||||
<result property="cameraPositionRating" column="camera_position_rating"
|
||||
typeHandler="com.ycwl.basic.handler.MapTypeHandler"/>
|
||||
typeHandler="com.ycwl.basic.handler.NestedMapTypeHandler"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
</resultMap>
|
||||
@@ -38,7 +38,7 @@
|
||||
FROM video_review vr
|
||||
LEFT JOIN video v ON vr.video_id = v.id
|
||||
LEFT JOIN scenic s ON vr.scenic_id = s.id
|
||||
LEFT JOIN sys_user u ON vr.creator = u.id
|
||||
LEFT JOIN admin_user u ON vr.creator = u.id
|
||||
<where>
|
||||
<if test="videoId != null">
|
||||
AND vr.video_id = #{videoId}
|
||||
|
||||
Reference in New Issue
Block a user