渲染端渲染流程

This commit is contained in:
Jerry Yan 2024-12-08 17:54:27 +08:00
parent 18b1776ac2
commit aa6da15c8f
21 changed files with 448 additions and 13 deletions

View File

@ -0,0 +1,7 @@
package com.ycwl.basic.constant;
public class TaskConstant {
public static final String TASK_TEMPLATE_KEY_PFX="task:template:cache:";
public static final String TASK_ONLINE_WORKER_KEY_PFX="task:online_worker:";
public static final String TASK_WORKER_TASK_KEY_PFX="task:worker:task:";
}

View File

@ -0,0 +1,60 @@
package com.ycwl.basic.controller.task;
import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
import com.ycwl.basic.model.task.req.TaskReqVo;
import com.ycwl.basic.model.task.req.WorkerAuthReqVo;
import com.ycwl.basic.model.task.resp.TaskSyncRespVo;
import com.ycwl.basic.service.task.TaskService;
import com.ycwl.basic.utils.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@IgnoreToken
@RestController
@RequestMapping("/task/v1/")
public class TaskTaskController {
@Autowired
private TaskService taskService;
@PostMapping("/sync")
public ApiResponse<TaskSyncRespVo> sync(@RequestBody TaskReqVo req) {
TaskSyncRespVo respVo = taskService.handleSyncTask(req);
if (respVo == null) {
return ApiResponse.fail("错误");
}
return ApiResponse.success(respVo);
}
@PostMapping("/template/{templateId}")
public ApiResponse<TemplateRespVO> getTemplateById(@PathVariable Long templateId, @RequestBody WorkerAuthReqVo req) {
return ApiResponse.success(taskService.workerGetTemplate(templateId, req));
}
@PostMapping("/{taskId}/uploadUrl")
public ApiResponse<String> getUploadUrl(@PathVariable Long taskId, @RequestBody WorkerAuthReqVo req) {
return ApiResponse.success(taskService.getUploadUrl(taskId, req));
}
@PostMapping("/{taskId}/success")
public ApiResponse taskSuccess(@PathVariable Long taskId, @RequestBody WorkerAuthReqVo req) {
taskService.taskSuccess(taskId, req);
return ApiResponse.success("OK");
}
@PostMapping("/{taskId}/fail")
public ApiResponse taskFail(@PathVariable Long taskId, @RequestBody WorkerAuthReqVo req) {
taskService.taskFail(taskId, req);
return ApiResponse.success("OK");
}
@PostMapping("/test/createRenderTask/{scenicId}/{templateId}/{faceId}")
public ApiResponse<String> createRenderTask(@PathVariable Long scenicId, @PathVariable Long templateId, @PathVariable Long faceId) {
taskService.createRenderTask(scenicId, templateId, faceId);
return ApiResponse.success("ok");
}
}

View File

@ -19,4 +19,6 @@ public interface RenderWorkerMapper {
int deleteById(Long id);
int update(RenderWorkerEntity renderWorker);
int updateStatus(Long id);
RenderWorkerEntity findByAccessKey(String accessKey);
}

View File

@ -4,6 +4,7 @@ import com.ycwl.basic.model.pc.task.entity.TaskEntity;
import com.ycwl.basic.model.pc.task.req.TaskReqQuery;
import com.ycwl.basic.model.pc.task.resp.TaskRespVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -41,4 +42,9 @@ public interface TaskMapper {
* @return
*/
int countByMemberIdStauFinish(String userId);
List<TaskRespVO> selectNotRunning();
void assignToWorker(@Param("taskId") Long taskId, @Param("workerId") Long workerId);
void deassign(@Param("taskId") Long taskId);
}

View File

@ -55,4 +55,5 @@ public class FaceSampleRespVO {
private Date createAt;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateAt;
private Long sourceId;
}

View File

@ -70,4 +70,5 @@ public class MemberRespVO {
private Date createDate;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateAt;
private Integer orderCount;
}

View File

@ -17,6 +17,7 @@ import java.util.Date;
@Data
@ApiModel(value = "订单查询对象")
public class OrderReqQuery extends BaseQueryParameterReq {
private Long id;
private Long memberId;
@ApiModelProperty("用户昵称")
private String memberNickname;

View File

@ -24,6 +24,10 @@ public class TaskEntity {
* 用户ID可以不和用户关联
*/
private Long memberId;
/**
* 人脸IDface.id
*/
private Long faceId;
/**
* 模板ID
*/

View File

@ -0,0 +1,18 @@
package com.ycwl.basic.model.task.req;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
public class ClientStatusReqVo {
private String platform;
private String runtime_version;
private String version;
private Integer cpu_count;
private BigDecimal cpu_usage;
private BigDecimal memory_total;
private BigDecimal memory_available;
private List<String> support_feature;
}

View File

@ -0,0 +1,10 @@
package com.ycwl.basic.model.task.req;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class TaskReqVo extends WorkerAuthReqVo {
private ClientStatusReqVo clientStatus;
}

View File

@ -0,0 +1,9 @@
package com.ycwl.basic.model.task.req;
import lombok.Data;
@Data
public class WorkerAuthReqVo {
private String accessKey;
}

View File

@ -0,0 +1,7 @@
package com.ycwl.basic.model.task.resp;
import lombok.Data;
@Data
public class TaskItemRespVo {
}

View File

@ -0,0 +1,14 @@
package com.ycwl.basic.model.task.resp;
import com.ycwl.basic.model.pc.task.resp.TaskRespVO;
import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class TaskSyncRespVo {
private List<TaskRespVO> tasks = new ArrayList<>();
private List<TemplateRespVO> templates = new ArrayList<>();
}

View File

@ -0,0 +1,216 @@
package com.ycwl.basic.service.impl.task;
import com.alibaba.fastjson.JSON;
import com.ycwl.basic.constant.TaskConstant;
import com.ycwl.basic.mapper.pc.FaceMapper;
import com.ycwl.basic.mapper.pc.FaceSampleMapper;
import com.ycwl.basic.mapper.pc.RenderWorkerMapper;
import com.ycwl.basic.mapper.pc.SourceMapper;
import com.ycwl.basic.mapper.pc.TaskMapper;
import com.ycwl.basic.mapper.pc.TemplateMapper;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO;
import com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity;
import com.ycwl.basic.model.pc.source.resp.SourceRespVO;
import com.ycwl.basic.model.pc.task.entity.TaskEntity;
import com.ycwl.basic.model.pc.task.resp.TaskRespVO;
import com.ycwl.basic.model.pc.template.req.TemplateReqQuery;
import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
import com.ycwl.basic.model.task.req.ClientStatusReqVo;
import com.ycwl.basic.model.task.req.TaskReqVo;
import com.ycwl.basic.model.task.req.WorkerAuthReqVo;
import com.ycwl.basic.model.task.resp.TaskSyncRespVo;
import com.ycwl.basic.service.task.TaskService;
import com.ycwl.basic.utils.OssUtil;
import com.ycwl.basic.utils.SnowFlakeUtil;
import lombok.NonNull;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Service
public class TaskTaskServiceImpl implements TaskService {
@Autowired
private TemplateMapper templateMapper;
@Autowired
private RenderWorkerMapper renderWorkerMapper;
@Autowired
private TaskMapper taskMapper;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private FaceMapper faceMapper;
@Autowired
private FaceSampleMapper faceSampleMapper;
@Autowired
private SourceMapper sourceMapper;
@Autowired
private OssUtil ossUtil;
private RenderWorkerEntity getWorker(@NonNull WorkerAuthReqVo req) {
String accessKey = req.getAccessKey();
if (accessKey == null) {
return null;
}
RenderWorkerEntity worker = renderWorkerMapper.findByAccessKey(accessKey);
return worker;
}
@Override
public TaskSyncRespVo handleSyncTask(@NonNull TaskReqVo req) {
RenderWorkerEntity worker = getWorker(req);
if (worker == null) {
return null;
}
worker.setOnline(1);
worker.setName(null);
worker.setStatus(null);
// get status
ClientStatusReqVo clientStatus = req.getClientStatus();
if (clientStatus != null) {
// 临时这么用下
worker.setCpuCount(clientStatus.getCpu_count());
worker.setCpuUsage(clientStatus.getCpu_usage());
// 上报的是字节存储的是兆
worker.setMemoryAvailable(clientStatus.getMemory_available().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setMemoryTotal(clientStatus.getMemory_total().divide(BigDecimal.valueOf(1024 * 1024), RoundingMode.CEILING));
worker.setPlatform(clientStatus.getPlatform());
worker.setRuntimeVersion(clientStatus.getRuntime_version());
worker.setSupportFeature(String.join(",", clientStatus.getSupport_feature()));
worker.setVersion(clientStatus.getVersion());
worker.setUpdateAt(new Date());
redisTemplate.opsForValue().set(TaskConstant.TASK_ONLINE_WORKER_KEY_PFX + worker.getId(), JSON.toJSONString(clientStatus), 60, TimeUnit.SECONDS);
}
renderWorkerMapper.update(worker);
TaskSyncRespVo resp = new TaskSyncRespVo();
// Template
String cacheJson = redisTemplate.opsForValue().get(TaskConstant.TASK_TEMPLATE_KEY_PFX + worker.getId());
if (cacheJson == null) {
List<TemplateRespVO> templateList = templateMapper.list(new TemplateReqQuery());
for (TemplateRespVO template : templateList) {
template.setChildren(templateMapper.getByPid(template.getId()));
}
resp.setTemplates(templateList);
redisTemplate.opsForValue().set(TaskConstant.TASK_TEMPLATE_KEY_PFX + worker.getId(), JSON.toJSONString(templateList), 60, TimeUnit.SECONDS);
} else {
resp.setTemplates(JSON.parseArray(cacheJson, TemplateRespVO.class));
}
List<TaskRespVO> taskList = taskMapper.selectNotRunning();
resp.setTasks(taskList);
taskList.forEach(task -> {
taskMapper.assignToWorker(task.getId(), worker.getId());
});
// return Task
return resp;
}
@Override
public void createRenderTask(Long scenicId, Long templateId, Long faceId) {
// 有人脸找视频
if (faceId == null) {
return;
}
FaceRespVO faceRespVO = faceMapper.getById(faceId);
if (faceRespVO == null) {
return;
}
Map<String, List<SourceRespVO>> sourcesMap = Arrays.stream(faceRespVO.getMatchSampleIds().split(","))
.map(Long::valueOf)
.map(sampleId -> {
return faceSampleMapper.getById(sampleId);
})
.filter(Objects::nonNull)
.map(FaceSampleRespVO::getSourceId)
.map(sourceId -> sourceMapper.getById(sourceId))
.collect(Collectors.groupingBy(item -> item.getDeviceId().toString()));
TaskEntity taskEntity = new TaskEntity();
taskEntity.setId(SnowFlakeUtil.getLongId());
taskEntity.setFaceId(faceId);
taskEntity.setMemberId(faceRespVO.getMemberId());
taskEntity.setTemplateId(templateId);
taskEntity.setScenicId(scenicId);
taskEntity.setTaskParams(JSON.toJSONString(sourcesMap));
taskEntity.setStatus(0);
taskMapper.add(taskEntity);
}
@Override
public TemplateRespVO workerGetTemplate(@NonNull Long templateId, @NonNull WorkerAuthReqVo req) {
if (templateId == null) {
return null;
}
RenderWorkerEntity worker = getWorker(req);
if (worker == null) {
return null;
}
TemplateRespVO respVO = templateMapper.getById(templateId);
respVO.setChildren(templateMapper.getByPid(templateId));
return respVO;
}
@Override
public void taskSuccess(@NonNull Long taskId, @NonNull WorkerAuthReqVo req) {
TaskRespVO task = taskMapper.getById(taskId);
if (task == null) {
return;
}
RenderWorkerEntity worker = getWorker(req);
if (worker == null) {
return;
}
TaskEntity taskUpdate = new TaskEntity();
taskUpdate.setId(taskId);
taskUpdate.setStatus(1);
taskUpdate.setWorkerId(worker.getId());
taskMapper.update(taskUpdate);
}
@Override
public void taskFail(Long taskId, WorkerAuthReqVo req) {
TaskRespVO task = taskMapper.getById(taskId);
if (task == null) {
return;
}
RenderWorkerEntity worker = getWorker(req);
if (worker == null) {
return;
}
TaskEntity taskUpdate = new TaskEntity();
taskUpdate.setId(taskId);
taskUpdate.setStatus(2);
taskUpdate.setWorkerId(worker.getId());
taskMapper.update(taskUpdate);
taskMapper.deassign(taskId);
}
@Override
public String getUploadUrl(Long taskId, WorkerAuthReqVo req) {
TaskRespVO task = taskMapper.getById(taskId);
if (task == null) {
return null;
}
String filename = task.getId() + "_" + task.getScenicId() + ".mp4";
if (StringUtils.isBlank(task.getVideoUrl())) {
// 生成
String url = ossUtil.generateUrlOfFile("user-video/", filename);
TaskEntity updateTask = new TaskEntity();
updateTask.setId(taskId);
updateTask.setVideoUrl(url);
taskMapper.update(updateTask);
}
return ossUtil.generateSignedUrlForUpload("user-video/", filename);
}
}

View File

@ -0,0 +1,19 @@
package com.ycwl.basic.service.task;
import com.ycwl.basic.model.pc.template.resp.TemplateRespVO;
import com.ycwl.basic.model.task.req.TaskReqVo;
import com.ycwl.basic.model.task.req.WorkerAuthReqVo;
import com.ycwl.basic.model.task.resp.TaskSyncRespVo;
public interface TaskService {
TaskSyncRespVo handleSyncTask(TaskReqVo req);
void createRenderTask(Long scenicId, Long templateId, Long faceId);
TemplateRespVO workerGetTemplate(Long templateId, WorkerAuthReqVo req);
void taskSuccess(Long taskId, WorkerAuthReqVo req);
void taskFail(Long taskId, WorkerAuthReqVo req);
String getUploadUrl(Long taskId, WorkerAuthReqVo req);
}

View File

@ -1,6 +1,8 @@
package com.ycwl.basic.utils;
import cn.hutool.core.date.DateUtil;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
@ -12,6 +14,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.util.Date;
@Slf4j
@Component
@ -27,7 +30,15 @@ public class OssUtil {
* @return
*/
public String uploadFile(InputStream inputStream, String filename) {
String uploadFileName = ossConfig.getObjectName() + filename;
return uploadFile(inputStream, ossConfig.getObjectName(), filename);
}
public String uploadAssetFile(InputStream inputStream, String filename) {
return uploadFile(inputStream, "assets/", filename);
}
public String uploadFile(InputStream inputStream, String path, String filename) {
String uploadFileName = path + filename;
OSS ossClient = new OSSClientBuilder().build(ossConfig.getEndPoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());
try {
PutObjectRequest putObjectRequest = new PutObjectRequest(ossConfig.getBucketName(), uploadFileName, inputStream);
@ -57,6 +68,24 @@ public class OssUtil {
return BizCodeEnum.UPLOAD_FAILED.getMessage();
}
public String generateSignedUrlForDownload(String path, String filename) {
String downloadFile = path + filename;
return generateSignedUrl(downloadFile, HttpMethod.GET);
}
public String generateSignedUrlForUpload(String path, String filename) {
String uploadFileName = path + filename;
return generateSignedUrl(uploadFileName, HttpMethod.PUT);
}
public String generateSignedUrl(String objectName, HttpMethod method) {
OSS ossClient = new OSSClientBuilder().build(ossConfig.getEndPoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());
return ossClient.generatePresignedUrl(ossConfig.getBucketName(), objectName, DateUtil.offsetHour(new Date(), 1), method).toString();
}
public String generateUrlOfFile(String path, String filename) {
String objectName = path + filename;
return ossConfig.getUrl() + objectName;
}
public boolean deleteFile(String filename) {
// 填写文件完整路径文件完整路径中不能包含Bucket名称

View File

@ -2,15 +2,21 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ycwl.basic.mapper.pc.FaceSampleMapper">
<insert id="add">
insert into face_sample(id, device_id, face_url, match_sample_ids, first_match_rate, match_result,`status`)
values (#{id}, #{deviceId}, #{faceUrl}, #{matchSampleIds}, #{firstMatchRate}, #{matchResult},#{status})
insert into face_sample(id, scenic_id, device_id, source_id, face_url, match_sample_ids, first_match_rate, match_result,`status`)
values (#{id}, #{scenicId}, #{deviceId}, #{sourceId}, #{faceUrl}, #{matchSampleIds}, #{firstMatchRate}, #{matchResult},#{status})
</insert>
<update id="update">
update face_sample
<set>
<if test="scenicId!= null ">
scenic_id = #{scenicId},
</if>
<if test="deviceId!= null ">
device_id = #{deviceId},
</if>
<if test="sourceId!= null ">
source_id = #{sourceId},
</if>
<if test="faceUrl!= null and faceUrl!= ''">
face_url = #{faceUrl},
</if>
@ -42,15 +48,18 @@
</if>
</delete>
<select id="list" resultType="com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO">
select id, scenic_id, device_id, face_url, match_sample_ids, first_match_rate, match_result,`status`
select id, scenic_id, device_id, face_url, source_id, match_sample_ids, first_match_rate, match_result,`status`
from face_sample
<where>
<if test="scenicId!= null and scenicId!= ''">
and device_id = #{deviceId}
and scenic_id = #{scenicId}
</if>
<if test="deviceId!= null and deviceId!= ''">
and device_id = #{deviceId}
</if>
<if test="source_id!= null and source_id!= ''">
and source_id = #{source_id}
</if>
<if test="matchSampleIds!= null and matchSampleIds!= ''">
and match_sample_ids like concat('%', #{matchSampleIds}, '%')
</if>
@ -72,7 +81,7 @@
</where>
</select>
<select id="getById" resultType="com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO">
select id, scenic_id, device_id, face_url, match_sample_ids, first_match_rate, match_result,`status`
select id, scenic_id, device_id, face_url, match_sample_ids, first_match_rate, source_id, match_result,`status`
from face_sample
where id = #{id}
</select>

View File

@ -3,13 +3,13 @@
<mapper namespace="com.ycwl.basic.mapper.pc.MemberMapper">
<insert id="add">
insert into member(id, openid, nickname, real_name, promo_code, broker_id, agreement, phone, country, province, city)
values (#{id}, #{openid}, #{nickname}, #{realName}, #{promoCode}, #{brokerId}, #{agreement}, #{phone}, #{country}, #{province}, #{city})
values (#{id}, #{openId}, #{nickname}, #{realName}, #{promoCode}, #{brokerId}, #{agreement}, #{phone}, #{country}, #{province}, #{city})
</insert>
<update id="update">
update member
<set>
<if test="openid!= null and openid!= ''">
openid = #{openid},
<if test="openId!= null and openId!= ''">
openid = #{openId},
</if>
<if test="nickname!= null and nickname!= ''">
nickname = #{nickname},
@ -45,11 +45,13 @@
delete from member where id = #{id}
</delete>
<select id="list" resultType="com.ycwl.basic.model.pc.member.resp.MemberRespVO">
select id, openid, nickname, real_name, promo_code, broker_id, agreement, phone, country, province, city
select id, openid, nickname, real_name, promo_code, broker_id, agreement, phone, country, province, city,
(select count(1) from `order` where `order`.member_id = member.id) as order_count,
create_date
from member
<where>
<if test="openid!= null and openid!= ''">
and openid like concat('%',#{openid},'%')
<if test="openId!= null and openId!= ''">
and openid like concat('%',#{openId},'%')
</if>
<if test="nickname!= null and nickname!= ''">
and nickname like concat('%',#{nickname},'%')

View File

@ -121,4 +121,9 @@
from render_worker
where id = #{id}
</select>
<select id="findByAccessKey" resultType="com.ycwl.basic.model.pc.renderWorker.entity.RenderWorkerEntity">
select id, `name`, scenic_only, test_only, `online`, `status`, create_at, update_at
from render_worker
where access_key = #{accessKey} and status = 1
</select>
</mapper>

View File

@ -24,6 +24,16 @@
set status = #{status}
where id = #{id}
</update>
<update id="assignToWorker">
update task
set worker_id = #{workerId}
where id = #{taskId}
</update>
<update id="deassign">
update task
set worker_id = null
where id = #{taskId}
</update>
<delete id="deleteById">
delete from task where id = #{id}
</delete>
@ -55,4 +65,9 @@ from task
where member_id = #{userId}
and status = 1
</select>
<select id="selectNotRunning" resultType="com.ycwl.basic.model.pc.task.resp.TaskRespVO">
select id, worker_id, member_id, template_id, scenic_id, task_params, video_url, `status`, result, create_time, update_time
from task
where status = 0 and worker_id is null
</select>
</mapper>

View File

@ -45,7 +45,7 @@
delete from template where scenic_id = #{id}
</delete>
<select id="list" resultType="com.ycwl.basic.model.pc.template.resp.TemplateRespVO">
select t.id, t.scenic_id, s.name as scenic_name, t.`name`, pid, is_placeholder, source_url, luts, overlays, audios, frame_rate, speed, cover_url, t.status, t.create_time, t.update_time
select t.id, t.scenic_id, s.name as scenic_name, t.`name`, cover_url, t.status, t.create_time, t.update_time
from template t left join scenic s on s.id = t.scenic_id
<where>
pid = 0