3 Commits

Author SHA1 Message Date
4042fd297d 订单详情接口调整 2025-10-27 09:10:03 +08:00
xuelijun
3497962627 Merge branch 'feature/order' into xlj 2025-10-24 17:30:30 +08:00
xuelijun
7fdf546585 Merge remote-tracking branch 'origin/feature/order' into xlj 2025-10-16 15:30:47 +08:00
12 changed files with 182 additions and 93 deletions

View File

@@ -6,6 +6,7 @@ import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateReqVo;
import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateRespVo;
import com.tashow.cloud.ai.service.dialog.AiDialogService;
import com.tashow.cloud.common.pojo.CommonResult;
import com.tashow.cloud.common.pojo.PageParam;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import lombok.extern.slf4j.Slf4j;
@@ -15,8 +16,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.tashow.cloud.common.pojo.CommonResult.success;
/**
@@ -28,8 +27,6 @@ import static com.tashow.cloud.common.pojo.CommonResult.success;
@Slf4j
public class DialogController {
List<String> message = List.of("渴了", "饿了", "想睡觉", "想出去玩", "想溜达", "情绪低落", "很开心", "很伤心", "想哭");
@Resource
private AiDialogService aiDialogService;
@@ -39,10 +36,10 @@ public class DialogController {
*/
@GetMapping("/getDialog")
@PermitAll
public CommonResult<DialogResp> msList() {
public CommonResult<DialogResp> msList(PageParam pageParam) {
//获取当前登录用户
Long userId = 1L;
return success(aiDialogService.getDialog(userId));
return success(aiDialogService.getDialog(userId,pageParam));
}

View File

@@ -1,14 +1,12 @@
package com.tashow.cloud.ai.controller.app.dialog.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.tashow.cloud.common.util.json.databind.StringLocalDateTimeSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* ai-对话消息 DO
*
@@ -71,7 +69,13 @@ public class AiDialogMessageRespVo {
/**
* 创建时间
*/
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
//发送时间
@JsonSerialize(using = StringLocalDateTimeSerializer.class)
private LocalDateTime createTime;
private Integer emotion;
private String terminal;
private String fileType;
private String fileName;
}

View File

@@ -1,10 +1,9 @@
package com.tashow.cloud.ai.controller.app.dialog.vo;
import com.tashow.cloud.common.pojo.PageResult;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "api - 对话 Response VO")
@Data
@@ -17,5 +16,5 @@ public class DialogResp {
@Schema(description = "对话状态")
private String dialogStatus;
@Schema(description = "对话消息内容")
private List<AiDialogMessageRespVo> messages;
private PageResult<AiDialogMessageRespVo> messages;
}

View File

@@ -14,11 +14,13 @@ public class TranslateReqVo {
//消息id
private Long msgId;
private String terminal;
/** 文件附件 */
private MultipartFile file;
//文件时长
private String contentDuration;
private Long contentDuration;
}

View File

@@ -39,6 +39,7 @@ public class TranslateRespVo {
private String transResult;
//文件时长
private Long contentDuration;
private String terminal;
//发送时间
@JsonSerialize(using = StringLocalDateTimeSerializer.class)
private LocalDateTime createTime;

View File

@@ -97,4 +97,7 @@ public class AiDialogMessageDO {
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime createTime;
private Integer emotion;
private String terminal;
}

View File

@@ -0,0 +1,27 @@
package com.tashow.cloud.ai.dal.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum EmotionEnums {
XF(1,"兴奋/玩耍","兴奋"),
PJ(2,"平静/放松","平静"),
je(3,"乞求/饥饿/口渴","饥饿"),
yk(4,"愉快/满足","愉快"),
tt(5,"疼痛/沮丧","疼痛"),
kj(6,"恐惧/不安","恐惧"),
hq(7,"问候/好奇","好奇"),
ng(8,"难过/焦虑","难过"),
fn(9,"愤怒/警告","愤怒"),
;
private Integer code;
private String name;
private String aiName;
}

View File

@@ -5,6 +5,7 @@ import com.tashow.cloud.ai.controller.app.dialog.vo.DialogResp;
import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateReqVo;
import com.tashow.cloud.ai.controller.app.dialog.vo.TranslateRespVo;
import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogDO;
import com.tashow.cloud.common.pojo.PageParam;
/**
* ai-对话 Service 接口
@@ -14,7 +15,7 @@ import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogDO;
public interface AiDialogService extends IService<AiDialogDO> {
DialogResp getDialog(Long userId);
DialogResp getDialog(Long userId, PageParam pageParam);
TranslateRespVo translate(TranslateReqVo fileReqVo);
}

View File

@@ -1,6 +1,7 @@
package com.tashow.cloud.ai.service.dialog;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
@@ -16,6 +17,8 @@ import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogDO;
import com.tashow.cloud.ai.dal.dataobject.dialog.AiDialogMessageDO;
import com.tashow.cloud.ai.dal.mysql.dialog.AiDialogMapper;
import com.tashow.cloud.ai.dal.mysql.dialog.AiDialogMessageMapper;
import com.tashow.cloud.common.pojo.PageParam;
import com.tashow.cloud.common.pojo.PageResult;
import com.tashow.cloud.common.util.object.BeanUtils;
import com.tashow.cloud.infraapi.api.file.FileApi;
import jakarta.annotation.Resource;
@@ -28,9 +31,6 @@ import org.springframework.web.multipart.MultipartFile;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -55,10 +55,56 @@ public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO>
@Resource
private FileApi fileApi;
private List<String> failedStr =List.of(
"刚刚的音符太独特了,我没听清呢~可以再对我说一次吗?",
"哇,这是哪来的小可爱?声音太有魅力了,让我一时走了神。请靠近一点,慢慢再说一遍好?",
"背景音有点热闹我有点分心啦能在一个更安静的地方让我专心听听TA的声音吗",
"景音有点热闹我有点分心啦能在一个更安静的地方让我专心听听TA的声音吗",
"报告主人!声波受到不明干扰(可能是零食袋的声音?),翻译任务失败,请求二次输入!",
"我的‘物种雷达’刚才打了个盹儿~快让我再听一次这美妙的声音!",
"这声音太迷人了让我CPU过载了请简化一下环境音我们再来一次"
);
private Map<String,List<String>> failedMap =new HashMap<>(){{
put("cat",List.of(
"听得出是猫猫在说话但TA似乎在表达一种很深邃的情绪..也许是个小秘密呢?",
"喵星人的这段‘加密通话’等级太高了!翻译官正在努力学习中...你能通过TA的尾巴和眼神猜猜看吗",
"警报!警报!接收到喵星最高机密指令,我的权限不足,无法破译!建议提供小鱼干以获取更多线索"
));
put("dog",List.of(
"听得出是狗狗在说话但TA似乎在表达一种很深邃的情绪...也许是个小秘密呢?",
"汪语十级考试失败!这句大概是“今天的我比昨天更爱你了'之类的复杂情感吧",
"汪星人的这段‘加密通话’等级太高了!翻译官正在努力学习中..你能通过TA的尾巴和眼神猜猜看吗"
));
put("common",List.of(
"TA刚才可能用了某种古老的咒语或者...只是在打一个很有思想的哈欠?",
"这句的哲学深度超出了我的理解范围TA可能是一位诗人"
));
}};
private Map<String,List<String>> successMap =new HashMap<>(){{
put("舒服",List.of(
"TA 现在全身心都放松下来啦,正被满满的安全感包围着呢~ ",
"听这均匀的呼吸TA 的心正像云朵一样软绵绵、轻飘飘的哦。",
"这是幸福的声音此刻的TA觉得自己是全世界最被爱的小宝贝",
"环境很安心主人很贴心TA 正在小声说:好舒服呀~…"
));
put("等待喂食",List.of(
"小肚肚发出‘咕咕’警报啦!美食雷达正在全面启动,搜寻好吃的!",
"来自TA的紧急通讯报告能量储备严重不足请求立刻补充",
"你听到了吗那是小饭碗在思念食物的声音TA的眼神正在发出渴望的光波~",
"“开饭铃”已经摇响!再不开饭,小委屈就要溢出来啦!"
));
put("生气",List.of(
"TA 现在有点小紧张哦周围可能有让TA不安的东西快给TA一点安慰吧",
"检测到心跳加速模式TA需要一点空间和一个温柔的声音来平静下来",
"TA 现在有点小紧张哦周围可能有让TA不安的东西快给TA一点安慰吧"
));
}};
private Map<String, String> petAvatars = new HashMap<>() {{
put("cat", "https://petshy.tashowz.com/admin-api/infra/file/29/get/89f4982de19aadae3248ffde37132857df255b3276de484fdf12e603e5e29a7e.png");
put("dog", "https://petshy.tashowz.com/admin-api/infra/file/29/get/175ba9f58dc812786bfe0598ca9c4a7f422fe138c1889e08d8228aebc953c1b3.png");
put("cat", "https://petshy.tashowz.com/admin-api/infra/file/29/get/默认的猫.png");
put("dog", "https://petshy.tashowz.com/admin-api/infra/file/29/get/默认的狗.png");
}};
private Map<String, String> petName = new HashMap<>() {{
@@ -68,7 +114,7 @@ public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO>
@Override
public DialogResp getDialog(Long userId) {
public DialogResp getDialog(Long userId, PageParam pageParam) {
AiDialogDO aiDialogDO = this.getOne(new LambdaQueryWrapper<AiDialogDO>().eq(AiDialogDO::getUserId, userId));
if (aiDialogDO == null) {
aiDialogDO = new AiDialogDO();
@@ -77,7 +123,12 @@ public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO>
aiDialogDO.setUserId(userId);
this.save(aiDialogDO);
}
List<AiDialogMessageDO> messageDOS = aiDialogMessageMapper.selectList(new LambdaQueryWrapper<AiDialogMessageDO>().eq(AiDialogMessageDO::getDialogId, aiDialogDO.getId()));
PageResult<AiDialogMessageDO> messageDOS = aiDialogMessageMapper.selectPage(pageParam,
new LambdaQueryWrapper<AiDialogMessageDO>().eq(AiDialogMessageDO::getDialogId, aiDialogDO.getId())
.orderByDesc(AiDialogMessageDO::getCreateTime)
);
DialogResp resp = new DialogResp();
resp.setDialogId(aiDialogDO.getId());
resp.setTitle(aiDialogDO.getTitle());
@@ -89,43 +140,23 @@ public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO>
@Override
@SneakyThrows
public TranslateRespVo translate(TranslateReqVo fileReqVo) {
AiDialogMessageDO messageDO = aiDialogMessageMapper.selectById(fileReqVo.getMsgId());
if (messageDO == null) {
messageDO = new AiDialogMessageDO();
}
String fileName = StrUtil.isBlank(messageDO.getFileName()) ? fileReqVo.getFile().getOriginalFilename() : messageDO.getFileName();
AiDialogMessageDO messageDO = new AiDialogMessageDO();
String fileName = fileReqVo.getFile().getOriginalFilename();
//上传文件获取文件地址
String fileUrl = StrUtil.isBlank(messageDO.getContentText()) ? fileServer + fileApi.createFile(fileName, "", fileReqVo.getFile().getBytes()) : messageDO.getContentText();
String fileUrl = fileServer + fileApi.createFile(fileName, "", fileReqVo.getFile().getBytes());
//翻译结果
translate(messageDO, fileUrl, fileName);
//创建消息 持久化消息
if (messageDO.getId() == null) {
messageDO.setFileName(fileName);
messageDO.setFileType(fileReqVo.getFile().getContentType());
messageDO.setDialogId(fileReqVo.getDialogId());
//前端无法转换时 后端进行转
messageDO.setContentDuration(
StrUtil.isBlank(fileReqVo.getContentDuration()) || "Infinity".equals(fileReqVo.getContentDuration())
? 0 : Long.parseLong(fileReqVo.getContentDuration())
);
messageDO.setContentText(fileUrl);
messageDO.setContentType(2);
//获取当前最后的排序
AiDialogMessageDO aiDialogMessageDO = aiDialogMessageMapper.selectOne(new LambdaQueryWrapper<AiDialogMessageDO>()
.eq(AiDialogMessageDO::getDialogId, fileReqVo.getDialogId())
.orderByDesc(AiDialogMessageDO::getMessageOrder)
.last("limit 1")
);
messageDO.setMessageOrder(aiDialogMessageDO == null ? 1 : aiDialogMessageDO.getMessageOrder() + 1);
messageDO.setCreateTime(LocalDateTimeUtil.now());
}
int i = messageDO.getId() == null ? aiDialogMessageMapper.insert(messageDO) : aiDialogMessageMapper.updateById(messageDO);
TranslateRespVo bean = BeanUtils.toBean(messageDO, TranslateRespVo.class);
bean.setId(messageDO.getId());
return bean;
//创建消息
messageDO.setFileName(fileName);
messageDO.setFileType(fileReqVo.getFile().getContentType());
messageDO.setDialogId(fileReqVo.getDialogId());
//前端无法转换时 后端进行转
messageDO.setContentDuration(fileReqVo.getContentDuration());
messageDO.setContentText(fileUrl);
messageDO.setContentType(2);
messageDO.setCreateTime(LocalDateTimeUtil.now());
return BeanUtils.toBean(messageDO, TranslateRespVo.class);
}
@SneakyThrows
@@ -148,51 +179,54 @@ public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO>
//数据解析
JSONObject translateResult = JSON.parseObject(result);
//翻译失败
if (translateResult.isEmpty()
|| !translateResult.containsKey("intent_result")
|| !translateResult.getBoolean("is_species_sound")
|| translateResult.getInteger("confidence") < 0.7
|| translateResult.getDouble("confidence") < 0.7
) {
messageDO.setTransStatus(0);
messageDO.setTransResult("");
messageDO.setTransResult(failedStr.get(RandomUtil.randomInt(failedStr.size())));
return messageDO;
}
//标签 如 cat dog
String speciesLabels = translateResult.getString("species_labels");
//解析翻译结果
JSONObject probabilities = translateResult.getJSONObject("intent_result")
.getJSONObject("probabilities");
JSONObject intentResult = translateResult.getJSONObject("intent_result");
String resultKey = probabilities.entrySet().stream()
.filter(entry -> entry.getValue() instanceof Number && ((Number) entry.getValue()).doubleValue() > 0.7)
.max(Comparator.comparingDouble(entry -> ((Number) entry.getValue()).doubleValue()))
.map(Map.Entry::getKey)
.orElse(null);
//返回结果
if (StrUtil.isBlank(resultKey)) {
//识别物种,无法翻译音频
if (intentResult.getDouble("confidence")<0.7){
messageDO.setTransStatus(0);
messageDO.setTransResult("");
List<String> petFailResult = failedMap.get(speciesLabels);
if (petFailResult == null) {
petFailResult = failedMap.get("common");
}
messageDO.setTransResult(petFailResult.get(RandomUtil.randomInt(petFailResult.size())));
return messageDO;
}
//都识别成功后 也有不同的翻译结果
JSONObject probabilities = intentResult.getJSONObject("probabilities");
String resultKey = probabilities.entrySet().stream()
.max(Comparator.comparingDouble(entry -> ((Number) entry.getValue()).doubleValue()))
.map(Map.Entry::getKey)
.orElse(null);
//根据key解析结果
String emo = StrUtil.isBlank(resultKey) ? "" : resultKey.split(cn.hutool.core.util.StrUtil.UNDERLINE)[1];
List<String> emoList = successMap.get(emo);
messageDO.setTransStatus(1);
messageDO.setTransResult(emoList.get(RandomUtil.randomInt(emoList.size())));
//宠物档案 todo
messageDO.setPetId(1l);
messageDO.setPetName(petName.get(speciesLabels));
https:
//qcloud.dpfile.com/pc/qw4HqeQN5Af9tLaw0mx8pXQhxKUCvwHbCXmSRI-nKiW1NpX6wMdvSN80YpcTbMKw.jpg
messageDO.setPetAvatar(petAvatars.get(speciesLabels));
messageDO.setPetType(speciesLabels);
messageDO.setTransStatus(1);
messageDO.setTransResult(StrUtil.isBlank(resultKey) ? "" : resultKey.split(StrUtil.UNDERLINE)[1]);
return messageDO;
}
public static void main(String[] args) throws Exception {
InputStream inputStream = getInputStreamFromUrl("https://petshy.tashowz.com/admin-api/infra/file/29/get/857def513547ec33a105f71108c8ece329cb64dacc3a4779c94b0fcc3398cc32.webm");
AudioInputStream audioStream = AudioSystem.getAudioInputStream(inputStream);
System.out.println((long) (audioStream.getFrameLength() / audioStream.getFormat().getFrameRate()));
// byte[] bytes = HttpUtil.downloadBytes("http://192.168.1.231:48080/admin-api/infra/file/29/get/c7351abf780f18600c104ec5662d843783ed8c309c01fb427046565217f51102.wav");
//
//// File file = new File("http://192.168.1.231:48080/admin-api/infra/file/29/get/c7351abf780f18600c104ec5662d843783ed8c309c01fb427046565217f51102.wav");
@@ -202,19 +236,4 @@ public class AiDialogServiceImpl extends ServiceImpl<AiDialogMapper, AiDialogDO>
}
public static InputStream getInputStreamFromUrl(String urlString) throws Exception {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET"); // 可以是GET, POST等
connection.setConnectTimeout(5000); // 设置连接超时时间
connection.setReadTimeout(5000); // 设置读取超时时间
int responseCode = connection.getResponseCode(); // 获取响应码
if (responseCode == HttpURLConnection.HTTP_OK) { // 状态码200表示成功
return connection.getInputStream();
} else {
// 处理错误情况例如抛出异常或返回null等
throw new RuntimeException("Failed : HTTP error code : " + responseCode);
}
}
}

View File

@@ -25,6 +25,14 @@
<artifactId>tashow-framework-env</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jaudiotagger/jaudiotagger -->
<dependency>
<groupId>org.jaudiotagger</groupId>
<artifactId>jaudiotagger</artifactId>
<version>2.0.1</version>
</dependency>
<!-- 依赖服务 -->
<dependency>
<groupId>com.tashow.cloud</groupId>

View File

@@ -87,7 +87,6 @@ public class FileController {
response.setStatus(HttpStatus.NOT_FOUND.value());
return;
}
response.setContentLength(content.length);
writeAttachment(response, path, content);
}

View File

@@ -6,8 +6,20 @@ import com.alibaba.ttl.TransmittableThreadLocal;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.apache.tika.Tika;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.CannotWriteException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.TagException;
import org.jaudiotagger.tag.id3.AbstractID3v2Tag;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
/**
@@ -64,7 +76,7 @@ public class FileTypeUtils {
String contentType = getMineType(content, filename);
response.setContentType(contentType);
// 针对 video 的特殊处理,解决视频地址在移动端播放的兼容性问题
if (StrUtil.containsIgnoreCase(contentType, "video")) {
if (StrUtil.containsIgnoreCase(contentType, "video")||StrUtil.containsIgnoreCase(contentType, "audio")) {
response.setHeader("Content-Length", String.valueOf(content.length - 1));
response.setHeader("Content-Range", String.valueOf(content.length - 1));
response.setHeader("Accept-Ranges", "bytes");
@@ -73,4 +85,21 @@ public class FileTypeUtils {
IoUtil.write(response.getOutputStream(), false, content);
}
public static void main(String[] args) throws CannotReadException, TagException, InvalidAudioFrameException, ReadOnlyFileException, IOException, CannotWriteException, URISyntaxException {
URL url = new URL("https://petshy.tashowz.com/admin-api/infra/file/29/get/jna2-雪球-难过焦虑.wav");
File file = new File(url.getFile());
System.out.println(file.exists());
AudioFile audioFile = AudioFileIO.read(file);
Tag tag = audioFile.getTag();
if (tag instanceof AbstractID3v2Tag) {
AbstractID3v2Tag id3v2Tag = (AbstractID3v2Tag) tag;
// id3v2Tag.delete(); // 删除所有ID3标签
} else {
System.out.println("The file does not contain ID3v2 tags.");
}
AudioFileIO.write(audioFile); // 保存更改
System.out.println("ID3 tags removed successfully.");
}
}