diff --git a/src/main/java/top/baogutang/music/config/RestTemplateConfig.java b/src/main/java/top/baogutang/music/config/RestTemplateConfig.java new file mode 100644 index 0000000..ceb34d1 --- /dev/null +++ b/src/main/java/top/baogutang/music/config/RestTemplateConfig.java @@ -0,0 +1,18 @@ +package top.baogutang.music.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); + factory.setConnectTimeout(5000); // 连接超时 + factory.setReadTimeout(5000); // 读取超时 + return new RestTemplate(factory); + } +} diff --git a/src/main/java/top/baogutang/music/constants/CacheKey.java b/src/main/java/top/baogutang/music/constants/CacheKey.java index 22c10e2..f30fb69 100644 --- a/src/main/java/top/baogutang/music/constants/CacheKey.java +++ b/src/main/java/top/baogutang/music/constants/CacheKey.java @@ -19,5 +19,7 @@ public class CacheKey { public static final String KEY_USER_LEVEL_PREFIX = "baogutang-music:user:level:id:"; + public static final String KEY_MUSIC_TAG_TOKEN_PREFIX = "baogutang-music:music-tag:token:"; + } diff --git a/src/main/java/top/baogutang/music/controller/MusicInfoController.java b/src/main/java/top/baogutang/music/controller/MusicInfoController.java new file mode 100644 index 0000000..b7ad9f5 --- /dev/null +++ b/src/main/java/top/baogutang/music/controller/MusicInfoController.java @@ -0,0 +1,35 @@ +package top.baogutang.music.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import top.baogutang.music.service.IMusicInfoService; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2025/01/23 : 14:45 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/music/info") +public class MusicInfoController { + + @Resource + private IMusicInfoService musicInfoService; + + @GetMapping("/cover") + public void getMusicCover(@RequestParam(name = "title", required = false) String title, + @RequestParam(name = "artist", required = true) String artist, + @RequestParam(name = "album", required = false) String album, + HttpServletResponse response) { + musicInfoService.getCover(title, artist, album, response); + } +} diff --git a/src/main/java/top/baogutang/music/service/IMusicInfoService.java b/src/main/java/top/baogutang/music/service/IMusicInfoService.java new file mode 100644 index 0000000..e3e3c3c --- /dev/null +++ b/src/main/java/top/baogutang/music/service/IMusicInfoService.java @@ -0,0 +1,16 @@ +package top.baogutang.music.service; + +import javax.servlet.http.HttpServletResponse; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2025/01/23 : 14:53 + */ +public interface IMusicInfoService { + + void getCover(String title, String artist, String album, HttpServletResponse response); + +} diff --git a/src/main/java/top/baogutang/music/service/IMusicRecordService.java b/src/main/java/top/baogutang/music/service/IMusicRecordService.java index 7c2af30..40d56ef 100644 --- a/src/main/java/top/baogutang/music/service/IMusicRecordService.java +++ b/src/main/java/top/baogutang/music/service/IMusicRecordService.java @@ -21,4 +21,6 @@ public interface IMusicRecordService extends IService { MusicRecordEntity queryByChannelAndPlatform(ChannelEnum channel, String platformId); List queryByPlatformIdList(List platformIdList); + + MusicRecordEntity queryByNameOrAlbumOrArtist(String title, String album, String artist); } diff --git a/src/main/java/top/baogutang/music/service/impl/MusicInfoServiceImpl.java b/src/main/java/top/baogutang/music/service/impl/MusicInfoServiceImpl.java new file mode 100644 index 0000000..4fd240e --- /dev/null +++ b/src/main/java/top/baogutang/music/service/impl/MusicInfoServiceImpl.java @@ -0,0 +1,211 @@ +package top.baogutang.music.service.impl; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.web.client.RestTemplate; +import top.baogutang.music.dao.entity.MusicRecordEntity; +import top.baogutang.music.exceptions.BusinessException; +import top.baogutang.music.service.IMusicInfoService; +import top.baogutang.music.service.IMusicRecordService; +import top.baogutang.music.utils.CacheUtil; +import top.baogutang.music.utils.OkHttpUtil; + + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +import static top.baogutang.music.constants.CacheKey.KEY_MUSIC_TAG_TOKEN_PREFIX; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2025/01/23 : 14:53 + */ +@Slf4j +@Service +public class MusicInfoServiceImpl implements IMusicInfoService { + + @Resource + private IMusicRecordService musicRecordService; + + @Resource + private RestTemplate restTemplate; + + @Resource + private RedisTemplate redisTemplate; + + @Resource(name = "commonExecutor") + private Executor commonExecutor; + + @Override + public void getCover(String title, String artist, String album, HttpServletResponse response) { + MusicRecordEntity musicRecord = musicRecordService.queryByNameOrAlbumOrArtist(title, album, artist); + if (Objects.nonNull(musicRecord) && StringUtils.isNotBlank(musicRecord.getPic())) { + this.downloadPicAndResponse(musicRecord.getPic(), response); + return; + } + + // 获取三方token + String token = CacheUtil.cacheOrSupply(KEY_MUSIC_TAG_TOKEN_PREFIX, + 1L, + TimeUnit.DAYS, + redisTemplate, + this::geneToken, + new TypeReference<>() { + }); + Map headers = new HashMap<>(); + headers.put("authorization", "jwt " + token); + headers.put("Content-Type", "application/json"); + Map params = new HashMap<>(); + if (StringUtils.isBlank(title)) { + params.put("title", album); + } else { + params.put("title", title); + } + params.put("artist", artist); + params.put("album", album); + InfoRes> res = this.queryInfo(headers, params); + if (Objects.isNull(res) || !Boolean.TRUE.equals(res.getResult()) || CollectionUtils.isEmpty(res.getData())) { + log.error("<<<<<<<<<>>>>>>>>>"); + return; + } + MusicInfoRes musicInfoRes = res.getData().get(0); + + this.downloadPicAndResponse(musicInfoRes.getAlbumImg(), response); + } + + + private InfoRes> queryInfo(Map headers, Map params) { + InfoRes> res = null; + try { + params.put("resource", "qmusic"); + res = OkHttpUtil.post("http://114.96.87.132:8002/apimt/fetch_id3_by_title/", headers, params, new TypeReference<>() { + }); + if (Objects.isNull(res) || !Boolean.TRUE.equals(res.getResult()) || CollectionUtils.isEmpty(res.getData())) { + params.put("resource", "netease"); + res = OkHttpUtil.post("http://114.96.87.132:8002/apimt/fetch_id3_by_title/", headers, params, new TypeReference<>() { + }); + } + } catch (Exception e) { + log.error("query music info error:{}", e.getMessage(), e); + return null; + } + return res; + } + + private String geneToken() { + Map params = new HashMap<>(); + params.put("username", "admin"); + params.put("password", "admin"); + TokenRes tokenRes = null; + try { + tokenRes = OkHttpUtil.post("http://114.96.87.132:8002/apimt/token/", params, new TypeReference<>() { + }); + } catch (Exception e) { + log.error("<<<<<<<<<<获取token异常:{}>>>>>>>>>>", e.getMessage(), e); + throw new BusinessException("获取token异常!"); + } + if (Objects.nonNull(tokenRes) && Boolean.TRUE.equals(tokenRes.getResult())) { + return tokenRes.getToken(); + } + throw new BusinessException("获取token异常!"); + } + + private void downloadPicAndResponse(String picUrl, HttpServletResponse response) { + + // 获取响应实体 + ResponseEntity responseEntity = restTemplate.exchange( + picUrl, + HttpMethod.GET, + null, + org.springframework.core.io.Resource.class + ); + + // 设置Content-Type + MediaType contentType = responseEntity.getHeaders().getContentType(); + if (contentType != null) { + response.setContentType(contentType.toString()); + } else { + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + } + + // 设置Content-Length(如果可用) + List contentLength = responseEntity.getHeaders().get(HttpHeaders.CONTENT_LENGTH); + if (contentLength != null && !contentLength.isEmpty()) { + response.setHeader(HttpHeaders.CONTENT_LENGTH, contentLength.get(0)); + } + if (Objects.isNull(responseEntity.getBody())) { + log.error("<<<<<<<<<>>>>>>>>>", picUrl); + return; + } + try (InputStream inputStream = responseEntity.getBody().getInputStream(); + OutputStream outputStream = response.getOutputStream()) { + + // 数据传输 + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + outputStream.flush(); + } catch (Exception e) { + log.error("getCover error!picUrl:{},message:{}", picUrl, e.getMessage(), e); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + + } + + @Data + static class TokenRes { + private String token; + private Boolean result; + } + + @Data + static class InfoRes { + private Boolean result; + private String code; + private T data; + } + + @Data + static class MusicInfoRes { + + private String mid; + + private String extra; + + private String notice; + + private String title; + + private String singer; + + private String album; + + @JsonProperty("album_img") + private String albumImg; + + private String artist; + } +} diff --git a/src/main/java/top/baogutang/music/service/impl/MusicRecordServiceImpl.java b/src/main/java/top/baogutang/music/service/impl/MusicRecordServiceImpl.java index 04fd73d..9715b73 100644 --- a/src/main/java/top/baogutang/music/service/impl/MusicRecordServiceImpl.java +++ b/src/main/java/top/baogutang/music/service/impl/MusicRecordServiceImpl.java @@ -3,6 +3,7 @@ package top.baogutang.music.service.impl; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import top.baogutang.music.dao.entity.MusicRecordEntity; import top.baogutang.music.dao.mapper.MusicRecordMapper; @@ -76,4 +77,15 @@ public class MusicRecordServiceImpl extends ServiceImpl(baseMapper) + .like(StringUtils.isNotBlank(title), MusicRecordEntity::getName, title + "%") + .like(StringUtils.isNotBlank(album), MusicRecordEntity::getAlbumName, album + "%") + .like(StringUtils.isNotBlank(artist), MusicRecordEntity::getArtistName, artist + "%") + .orderByDesc(MusicRecordEntity::getId) + .last(" limit 1") + .one(); + } + }