对接AI工牌
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
package com.rj.controller;
|
||||
|
||||
import com.rj.service.ISoundRecordingUploadService;
|
||||
import com.rj.service.impl.SoundRecordingUploadServiceImpl;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
* 录音上传控制器
|
||||
* 除座席机外,本接口适用于本公司所有硬件产品
|
||||
*
|
||||
* @author rj
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/recording")
|
||||
@Tag(name = "录音上传", description = "设备端录音文件上传接口")
|
||||
@Slf4j
|
||||
public class SoundRecordingUploadController {
|
||||
|
||||
@Autowired
|
||||
private ISoundRecordingUploadService soundRecordingUploadService;
|
||||
|
||||
/**
|
||||
* 录音上传接口
|
||||
* 设备端以http协议发送文件,Content-Type:multipart/form-data
|
||||
* 认证方式:暂定无
|
||||
*
|
||||
* @param deviceNo 设备序列号(必填)
|
||||
* @param deviceType 设备类型(可选)
|
||||
* @param soundRecording 文件数据流(必填)
|
||||
* @param fileName 文件名称(必填)
|
||||
* @param chunkIndex 分片索引(必填),A开头表示启动/运行段,Z开头表示停止段
|
||||
* @param startTime 录音开始时间,格式:yyMMddHHmmss(必填)
|
||||
* @param endTime 录音结束时间,格式:yyMMddHHmmss(必填)
|
||||
* @param usrNo 自定义编号(可选)
|
||||
* @return 上传结果
|
||||
*/
|
||||
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
@Operation(summary = "录音上传", description = "设备端录音文件上传接口,支持分片上传")
|
||||
public ResponseEntity<SoundRecordingUploadResponse> uploadRecording(
|
||||
@Parameter(description = "设备序列号", required = true)
|
||||
@RequestParam("deviceNo") String deviceNo,
|
||||
@Parameter(description = "设备类型")
|
||||
@RequestParam(value = "deviceType", required = false) String deviceType,
|
||||
@Parameter(description = "录音文件数据流", required = true)
|
||||
@RequestParam("soundRecording") MultipartFile soundRecording,
|
||||
@Parameter(description = "文件名称", required = true)
|
||||
@RequestParam("fileName") String fileName,
|
||||
@Parameter(description = "分片索引,A开头表示启动/运行段,Z开头表示停止段", required = true)
|
||||
@RequestParam("chunkIndex") String chunkIndex,
|
||||
@Parameter(description = "录音开始时间,格式:yyMMddHHmmss", required = true)
|
||||
@RequestParam("startTime") String startTime,
|
||||
@Parameter(description = "录音结束时间,格式:yyMMddHHmmss", required = true)
|
||||
@RequestParam("endTime") String endTime,
|
||||
@Parameter(description = "自定义编号")
|
||||
@RequestParam(value = "usrNo", required = false) String usrNo) {
|
||||
|
||||
log.info("收到录音上传请求 - deviceNo: {}, fileName: {}, chunkIndex: {}, startTime: {}, endTime: {}",
|
||||
deviceNo, fileName, chunkIndex, startTime, endTime);
|
||||
|
||||
// 调用服务层处理业务逻辑
|
||||
SoundRecordingUploadServiceImpl.ResponseData serviceResponse = soundRecordingUploadService.uploadRecording(
|
||||
deviceNo, deviceType, soundRecording, fileName, chunkIndex, startTime, endTime, usrNo);
|
||||
|
||||
// 转换服务层返回的结果为响应对象
|
||||
SoundRecordingUploadResponse response = new SoundRecordingUploadResponse();
|
||||
response.setCode(serviceResponse.code);
|
||||
response.setStatus(serviceResponse.status);
|
||||
response.setSuccess(serviceResponse.success);
|
||||
response.setMessage(serviceResponse.message);
|
||||
response.setTimestamp(serviceResponse.timestamp);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 录音上传请求DTO(内部类)
|
||||
*/
|
||||
@Schema(description = "录音上传请求参数")
|
||||
public static class SoundRecordingUploadRequest {
|
||||
private String deviceNo;
|
||||
private String deviceType;
|
||||
private MultipartFile soundRecording;
|
||||
private String fileName;
|
||||
private String chunkIndex;
|
||||
private String startTime;
|
||||
private String endTime;
|
||||
private String usrNo;
|
||||
|
||||
// Getters and Setters
|
||||
public String getDeviceNo() { return deviceNo; }
|
||||
public void setDeviceNo(String deviceNo) { this.deviceNo = deviceNo; }
|
||||
public String getDeviceType() { return deviceType; }
|
||||
public void setDeviceType(String deviceType) { this.deviceType = deviceType; }
|
||||
public MultipartFile getSoundRecording() { return soundRecording; }
|
||||
public void setSoundRecording(MultipartFile soundRecording) { this.soundRecording = soundRecording; }
|
||||
public String getFileName() { return fileName; }
|
||||
public void setFileName(String fileName) { this.fileName = fileName; }
|
||||
public String getChunkIndex() { return chunkIndex; }
|
||||
public void setChunkIndex(String chunkIndex) { this.chunkIndex = chunkIndex; }
|
||||
public String getStartTime() { return startTime; }
|
||||
public void setStartTime(String startTime) { this.startTime = startTime; }
|
||||
public String getEndTime() { return endTime; }
|
||||
public void setEndTime(String endTime) { this.endTime = endTime; }
|
||||
public String getUsrNo() { return usrNo; }
|
||||
public void setUsrNo(String usrNo) { this.usrNo = usrNo; }
|
||||
}
|
||||
|
||||
/**
|
||||
* 录音上传响应DTO(内部类)
|
||||
*/
|
||||
@Schema(description = "录音上传响应")
|
||||
public static class SoundRecordingUploadResponse {
|
||||
@Schema(description = "状态码,200或204表示成功", example = "200")
|
||||
private Integer code;
|
||||
|
||||
@Schema(description = "上传状态,0表示成功", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "是否成功", example = "true")
|
||||
private Boolean success;
|
||||
|
||||
@Schema(description = "消息", example = "请求成功")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "时间戳(单位:秒)", example = "1704268800")
|
||||
private Long timestamp;
|
||||
|
||||
// Getters and Setters
|
||||
public Integer getCode() { return code; }
|
||||
public void setCode(Integer code) { this.code = code; }
|
||||
public Integer getStatus() { return status; }
|
||||
public void setStatus(Integer status) { this.status = status; }
|
||||
public Boolean getSuccess() { return success; }
|
||||
public void setSuccess(Boolean success) { this.success = success; }
|
||||
public String getMessage() { return message; }
|
||||
public void setMessage(String message) { this.message = message; }
|
||||
public Long getTimestamp() { return timestamp; }
|
||||
public void setTimestamp(Long timestamp) { this.timestamp = timestamp; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.rj.service;
|
||||
|
||||
import com.rj.service.impl.SoundRecordingUploadServiceImpl;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 录音上传服务接口
|
||||
*
|
||||
* @author rj
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
public interface ISoundRecordingUploadService {
|
||||
|
||||
/**
|
||||
* 上传录音文件
|
||||
*
|
||||
* @param deviceNo 设备序列号
|
||||
* @param deviceType 设备类型
|
||||
* @param soundRecording 录音文件
|
||||
* @param fileName 文件名称
|
||||
* @param chunkIndex 分片索引
|
||||
* @param startTime 录音开始时间
|
||||
* @param endTime 录音结束时间
|
||||
* @param usrNo 自定义编号
|
||||
* @return 上传结果响应对象
|
||||
*/
|
||||
SoundRecordingUploadServiceImpl.ResponseData uploadRecording(String deviceNo, String deviceType,
|
||||
MultipartFile soundRecording,
|
||||
String fileName, String chunkIndex,
|
||||
String startTime, String endTime, String usrNo);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package com.rj.service.impl;
|
||||
|
||||
import com.rj.service.ISoundRecordingUploadService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 录音上传服务实现类
|
||||
*
|
||||
* @author rj
|
||||
* @date 2025-01-03
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SoundRecordingUploadServiceImpl implements ISoundRecordingUploadService {
|
||||
|
||||
/**
|
||||
* 分片索引正则表达式
|
||||
* A开头:启动/运行段,如A001, A005
|
||||
* Z开头:停止段,Z后面数字表示总段数,如Z999表示总共999段,Z001表示只有一段
|
||||
*/
|
||||
private static final Pattern CHUNK_INDEX_PATTERN = Pattern.compile("^[AZ]\\d{3}$");
|
||||
|
||||
/**
|
||||
* 时间格式正则表达式(yyMMddHHmmss)
|
||||
*/
|
||||
private static final Pattern TIME_PATTERN = Pattern.compile("^\\d{12}$");
|
||||
|
||||
@Override
|
||||
public ResponseData uploadRecording(String deviceNo, String deviceType, MultipartFile soundRecording,
|
||||
String fileName, String chunkIndex, String startTime, String endTime, String usrNo) {
|
||||
|
||||
log.info("开始处理录音上传 - deviceNo: {}, fileName: {}, chunkIndex: {}, startTime: {}, endTime: {}",
|
||||
deviceNo, fileName, chunkIndex, startTime, endTime);
|
||||
|
||||
try {
|
||||
// 参数验证
|
||||
String validationError = validateParameters(deviceNo, soundRecording, fileName, chunkIndex, startTime, endTime);
|
||||
if (validationError != null) {
|
||||
log.warn("参数验证失败 - deviceNo: {}, error: {}", deviceNo, validationError);
|
||||
return createErrorResponse(400, 1, validationError);
|
||||
}
|
||||
|
||||
// 解析分片信息
|
||||
ChunkInfo chunkInfo = parseChunkIndex(chunkIndex);
|
||||
log.info("分片信息解析 - prefix: {}, chunkNumber: {}, isLastChunk: {}, totalChunks: {}",
|
||||
chunkInfo.prefix, chunkInfo.chunkNumber, chunkInfo.isLastChunk, chunkInfo.totalChunks);
|
||||
|
||||
// TODO: 这里可以添加文件保存逻辑
|
||||
// 1. 保存文件到指定目录
|
||||
// 2. 如果是分片上传,需要管理分片状态
|
||||
// 3. 如果是最后一片,需要合并分片或标记完成
|
||||
|
||||
log.info("录音文件上传成功 - deviceNo: {}, fileName: {}, fileSize: {} bytes",
|
||||
deviceNo, fileName, soundRecording.getSize());
|
||||
|
||||
// 返回成功响应
|
||||
return createSuccessResponse();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("录音上传处理异常 - deviceNo: {}, fileName: {}, error: {}",
|
||||
deviceNo, fileName, e.getMessage(), e);
|
||||
return createErrorResponse(500, 1, "服务器内部错误:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证参数
|
||||
*/
|
||||
private String validateParameters(String deviceNo, MultipartFile soundRecording, String fileName,
|
||||
String chunkIndex, String startTime, String endTime) {
|
||||
|
||||
if (deviceNo == null || deviceNo.trim().isEmpty()) {
|
||||
return "设备序列号不能为空";
|
||||
}
|
||||
|
||||
if (soundRecording == null || soundRecording.isEmpty()) {
|
||||
return "录音文件不能为空";
|
||||
}
|
||||
|
||||
if (fileName == null || fileName.trim().isEmpty()) {
|
||||
return "文件名称不能为空";
|
||||
}
|
||||
|
||||
if (chunkIndex == null || chunkIndex.trim().isEmpty()) {
|
||||
return "分片索引不能为空";
|
||||
}
|
||||
|
||||
// 验证分片索引格式
|
||||
if (!CHUNK_INDEX_PATTERN.matcher(chunkIndex).matches()) {
|
||||
return "分片索引格式错误,应为A开头或Z开头后跟3位数字,如A001或Z999";
|
||||
}
|
||||
|
||||
if (startTime == null || startTime.trim().isEmpty()) {
|
||||
return "录音开始时间不能为空";
|
||||
}
|
||||
|
||||
if (!TIME_PATTERN.matcher(startTime).matches()) {
|
||||
return "录音开始时间格式错误,应为yyMMddHHmmss,如240510130000";
|
||||
}
|
||||
|
||||
if (endTime == null || endTime.trim().isEmpty()) {
|
||||
return "录音结束时间不能为空";
|
||||
}
|
||||
|
||||
if (!TIME_PATTERN.matcher(endTime).matches()) {
|
||||
return "录音结束时间格式错误,应为yyMMddHHmmss,如240510135959";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析分片索引
|
||||
*/
|
||||
private ChunkInfo parseChunkIndex(String chunkIndex) {
|
||||
char prefix = chunkIndex.charAt(0);
|
||||
int chunkNumber = Integer.parseInt(chunkIndex.substring(1));
|
||||
boolean isLastChunk = prefix == 'Z';
|
||||
int totalChunks = isLastChunk ? chunkNumber : 0;
|
||||
|
||||
return new ChunkInfo(prefix, chunkNumber, isLastChunk, totalChunks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建成功响应
|
||||
*/
|
||||
private ResponseData createSuccessResponse() {
|
||||
ResponseData response = new ResponseData();
|
||||
response.code = 200;
|
||||
response.status = 0;
|
||||
response.success = true;
|
||||
response.message = "请求成功";
|
||||
response.timestamp = Instant.now().getEpochSecond();
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建错误响应
|
||||
*/
|
||||
private ResponseData createErrorResponse(int code, int status, String message) {
|
||||
ResponseData response = new ResponseData();
|
||||
response.code = code;
|
||||
response.status = status;
|
||||
response.success = false;
|
||||
response.message = message;
|
||||
response.timestamp = Instant.now().getEpochSecond();
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分片信息内部类
|
||||
*/
|
||||
private static class ChunkInfo {
|
||||
char prefix;
|
||||
int chunkNumber;
|
||||
boolean isLastChunk;
|
||||
int totalChunks;
|
||||
|
||||
ChunkInfo(char prefix, int chunkNumber, boolean isLastChunk, int totalChunks) {
|
||||
this.prefix = prefix;
|
||||
this.chunkNumber = chunkNumber;
|
||||
this.isLastChunk = isLastChunk;
|
||||
this.totalChunks = totalChunks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应数据内部类
|
||||
*/
|
||||
public static class ResponseData {
|
||||
public Integer code;
|
||||
public Integer status;
|
||||
public Boolean success;
|
||||
public String message;
|
||||
public Long timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user