commit b4a35efbd112766174b25db0ce170373cb7e4f18 Author: N1KO Date: Wed Dec 11 18:33:15 2024 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..a3aa9db --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,73 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..13b62e0 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9c8ab89 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# 第一阶段:使用包含 Maven 和 JDK 11 的镜像进行构建 +FROM maven:3.8.4-openjdk-11-slim AS builder + +WORKDIR /app +COPY . . +# 使用 Maven 在容器中进行编译和打包(根据需要决定是否跳过测试) +RUN mvn clean package -DskipTests + +# 第二阶段:使用精简的 OpenJDK 11 镜像作为运行时环境 +FROM openjdk:11-jre-slim + +WORKDIR /app +# 从构建阶段复制打包好的 jar 文件到运行阶段 +COPY --from=builder /app/target/baogutang-music-1.0-SNAPSHOT.jar /app/app.jar + +# 若应用在 8080 端口监听,这里进行暴露 +EXPOSE 8105 + +# 运行应用 +CMD ["java", "-jar", "app.jar"] diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..63ca34f --- /dev/null +++ b/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + com.baogutang.music + baogutang-music + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.3.2.RELEASE + + + + 11 + 11 + UTF-8 + + + + + org.projectlombok + lombok + + + + org.springframework.boot + spring-boot-starter-web + + + + mysql + mysql-connector-java + + + + com.baomidou + mybatis-plus-boot-starter + 3.5.3.1 + + + + org.apache.commons + commons-lang3 + + + + com.squareup.okhttp3 + okhttp + 4.9.3 + + + + org.aspectj + aspectjweaver + 1.9.6 + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + + + + src/main/resources + true + + templates/fonts/** + + + + src/main/resources + false + + templates/fonts/** + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.19 + + true + + + + + + + \ No newline at end of file diff --git a/src/main/java/top/baogutang/music/BaogutangMusicApplication.java b/src/main/java/top/baogutang/music/BaogutangMusicApplication.java new file mode 100644 index 0000000..c3b55ba --- /dev/null +++ b/src/main/java/top/baogutang/music/BaogutangMusicApplication.java @@ -0,0 +1,26 @@ +package top.baogutang.music; + +import lombok.extern.slf4j.Slf4j; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableAsync; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 18:20 + */ +@Slf4j +@SpringBootApplication(scanBasePackages = {"top.baogutang.music.*"}) +@MapperScan(basePackages = {"top.baogutang.music.dao.mapper"}) +@EnableAsync +public class BaogutangMusicApplication { + + public static void main(String[] args) { + SpringApplication.run(BaogutangMusicApplication.class, args); + log.info("==============音乐服务启动成功!!================="); + } +} diff --git a/src/main/java/top/baogutang/music/annos/ChannelInfo.java b/src/main/java/top/baogutang/music/annos/ChannelInfo.java new file mode 100644 index 0000000..d49e230 --- /dev/null +++ b/src/main/java/top/baogutang/music/annos/ChannelInfo.java @@ -0,0 +1,22 @@ +package top.baogutang.music.annos; + + +import top.baogutang.music.enums.ChannelEnum; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 17:47 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ChannelInfo { + ChannelEnum value(); +} diff --git a/src/main/java/top/baogutang/music/aspect/LogAspect.java b/src/main/java/top/baogutang/music/aspect/LogAspect.java new file mode 100644 index 0000000..8dc852a --- /dev/null +++ b/src/main/java/top/baogutang/music/aspect/LogAspect.java @@ -0,0 +1,64 @@ +package top.baogutang.music.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import top.baogutang.music.domain.Results; +import top.baogutang.music.utils.JacksonUtil; + +import javax.servlet.http.HttpServletRequest; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 12:16 + */ +@Slf4j +@Aspect +@Component +public class LogAspect { + + + @Pointcut("@within(org.springframework.web.bind.annotation.RestController)") + public void logPointCut() { + //pointCut + + } + + @Around("logPointCut()") + public Object doAround(ProceedingJoinPoint pjp) throws Throwable { + Object result; + RequestAttributes ra = RequestContextHolder.getRequestAttributes(); + ServletRequestAttributes sra = (ServletRequestAttributes) ra; + assert sra != null; + HttpServletRequest request = sra.getRequest(); + String requestId = MDC.get("X-Request-Id"); + long startMills = System.currentTimeMillis(); + long cost = 0; + try { + result = pjp.proceed(); + cost = System.currentTimeMillis() - startMills; + log.info("请求结束!本次请求耗时:{},url: {}, method: {}, params: {},user:{}, token: {}, 响应结果:{}", + cost, request.getRequestURL().toString(), + request.getMethod(), pjp.getArgs(), JacksonUtil.toJson(request.getAttribute("user")), request.getHeader("authorization"), + JacksonUtil.toJson(result)); + if (result instanceof Results) { + Results r = (Results) result; + r.setRid(requestId); + } + } catch (Exception e) { + log.error("请求异常!!!本次请求耗时:{},error:{},url: {}, method: {}, params: {},user:{}, token: {}", cost, e, request.getRequestURL().toString(), + request.getMethod(), pjp.getArgs(), JacksonUtil.toJson(request.getAttribute("user")), request.getHeader("token")); + throw e; + } + return result; + } + +} diff --git a/src/main/java/top/baogutang/music/client/ChannelClient.java b/src/main/java/top/baogutang/music/client/ChannelClient.java new file mode 100644 index 0000000..e0d019f --- /dev/null +++ b/src/main/java/top/baogutang/music/client/ChannelClient.java @@ -0,0 +1,35 @@ +package top.baogutang.music.client; + +import top.baogutang.music.domain.req.AbstractMusicReq; +import top.baogutang.music.domain.req.search.MusicSearchReq; +import top.baogutang.music.domain.res.AbstractMusicRes; +import top.baogutang.music.domain.res.download.MusicDownloadRes; +import top.baogutang.music.domain.res.search.*; + +import java.io.IOException; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 17:47 + */ +public interface ChannelClient { + + MusicSearchRes search(MusicSearchReq req); + + MusicPlaylistRes playlist(Long id); + + MusicDetailRes detail(Long id); + + MusicAlbumRes album(Long id); + + MusicArtistRes artist(Long id); + + MusicDownloadRes download(Long id); + + void saveMusic(MusicDownloadRes res); + + void processFile(MusicDownloadRes res) throws IOException; +} diff --git a/src/main/java/top/baogutang/music/client/NetEaseMusicClient.java b/src/main/java/top/baogutang/music/client/NetEaseMusicClient.java new file mode 100644 index 0000000..94e58ad --- /dev/null +++ b/src/main/java/top/baogutang/music/client/NetEaseMusicClient.java @@ -0,0 +1,120 @@ +package top.baogutang.music.client; + +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import top.baogutang.music.annos.ChannelInfo; +import top.baogutang.music.domain.req.search.MusicSearchReq; +import top.baogutang.music.domain.res.download.MusicDownloadRes; +import top.baogutang.music.domain.res.search.*; +import top.baogutang.music.enums.ChannelEnum; +import top.baogutang.music.properties.NetEaseMusicProperties; +import top.baogutang.music.service.IMusicRecordService; +import top.baogutang.music.utils.OkHttpUtil; + +import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Objects; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 17:57 + */ +@Slf4j +@Component +@ChannelInfo(ChannelEnum.NET_EASE_MUSIC) +public class NetEaseMusicClient implements ChannelClient { + + + @Resource + private NetEaseMusicProperties netEaseMusicProperties; + + @Resource + private IMusicRecordService musicRecordService; + + @Override + public MusicSearchRes search(MusicSearchReq req) { + String searchUrl = String.format(netEaseMusicProperties.getQueryBaseUrl(), req.getKeywords(), req.getLimit(), req.getOffset(), req.getType()); + return OkHttpUtil.get(searchUrl, null, null, new TypeReference<>() { + }); + } + + @Override + public MusicPlaylistRes playlist(Long id) { + String playlistUrl = String.format(netEaseMusicProperties.getPlaylistBaseUrl(), id); + return OkHttpUtil.get(playlistUrl, null, null, new TypeReference<>() { + }); + } + + @Override + public MusicDetailRes detail(Long id) { + String detailUrl = String.format(netEaseMusicProperties.getDownloadBaseUrl(), id); + return OkHttpUtil.get(detailUrl, null, null, new TypeReference<>() { + }); + } + + @Override + public MusicAlbumRes album(Long id) { + String albumUrl = String.format(netEaseMusicProperties.getAlbumBaseUrl(), id); + return OkHttpUtil.get(albumUrl, null, null, new TypeReference<>() { + }); + } + + @Override + public MusicArtistRes artist(Long id) { + String albumUrl = String.format(netEaseMusicProperties.getArtistBaseUrl(), id); + return OkHttpUtil.get(albumUrl, null, null, new TypeReference<>() { + }); + } + + @Override + public MusicDownloadRes download(Long id) { + String downloadUrl = String.format(netEaseMusicProperties.getDownloadBaseUrl(), id); + MusicDownloadRes res = OkHttpUtil.get(downloadUrl, null, null, new TypeReference<>() { + }); + if (Objects.nonNull(res)) { + res.setId(id); + } + return res; + } + + @Override + public void saveMusic(MusicDownloadRes res) { + musicRecordService.save(res, ChannelEnum.NET_EASE_MUSIC); + } + + @Override + public void processFile(MusicDownloadRes res) throws IOException { + if (Objects.isNull(res) || Objects.isNull(res.getName()) || Objects.isNull(res.getUrl())) { + return; + } + Path baseDir = Paths.get(netEaseMusicProperties.getDownloadPath(), res.getName()); + if (!Files.exists(baseDir)) { + Files.createDirectories(baseDir); + } + if (res.getPic() != null && !res.getPic().isEmpty()) { + String[] split = res.getPic().split("\\."); + downloadFile(res.getPic(), baseDir.resolve(res.getName() + "." + split[split.length - 1])); + } + if (res.getUrl() != null && !res.getUrl().isEmpty()) { + String[] split = res.getUrl().split("\\."); + downloadFile(res.getUrl(), baseDir.resolve(res.getName() + "." + split[split.length - 1])); + } + } + + private void downloadFile(String fileUrl, Path targetPath) throws IOException { + try (InputStream in = new URL(fileUrl).openStream()) { + Files.copy(in, targetPath, StandardCopyOption.REPLACE_EXISTING); + } + } + +} diff --git a/src/main/java/top/baogutang/music/config/ExecutorConfig.java b/src/main/java/top/baogutang/music/config/ExecutorConfig.java new file mode 100644 index 0000000..44319cc --- /dev/null +++ b/src/main/java/top/baogutang/music/config/ExecutorConfig.java @@ -0,0 +1,49 @@ +package top.baogutang.music.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * @description: 线程池配置 + * @author: nikooh + * @date: 2023/06/15 : 12:18 + */ +@Slf4j +@Configuration +public class ExecutorConfig { + + @Value("${spring.application.name}") + private String appName; + + @Value("${thread.pool.core.pool.size:5}") + private Integer corePoolSize; + + @Value("${thread.pool.max.pool.size:10}") + private Integer maxPoolSize; + + @Value("${thread.pool.keep.alive.second:10}") + private Integer keepAliveSecond; + + @Value("${thread.pool.queue.capacity:200}") + private Integer queueCapacity; + + @Bean("commonExecutor") + public ThreadPoolTaskExecutor commonExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setThreadNamePrefix(appName + "-common-"); + executor.setCorePoolSize(corePoolSize); + executor.setMaxPoolSize(maxPoolSize); + executor.setKeepAliveSeconds(keepAliveSecond); + executor.setQueueCapacity(queueCapacity); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.setWaitForTasksToCompleteOnShutdown(true); + executor.initialize(); + return executor; + } + +} diff --git a/src/main/java/top/baogutang/music/config/GlobalCorsConfig.java b/src/main/java/top/baogutang/music/config/GlobalCorsConfig.java new file mode 100644 index 0000000..e5019f8 --- /dev/null +++ b/src/main/java/top/baogutang/music/config/GlobalCorsConfig.java @@ -0,0 +1,37 @@ +package top.baogutang.music.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @description: 全局跨域配置 + * @author: nikooh + * @date: 2023/06/15 : 12:19 + */ +@Configuration +public class GlobalCorsConfig { + + @Bean + public WebMvcConfigurer corsConfigurer() { + return new WebMvcConfigurer() { + @Override + //重写父类提供的跨域请求处理的接口 + public void addCorsMappings(CorsRegistry registry) { + //添加映射路径 + registry.addMapping("/api/**") + //放行哪些原始域 + .allowedOrigins("*") + //是否发送Cookie信息 + .allowCredentials(true) + //放行哪些原始域(请求方式) + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + //放行哪些原始域(头部信息) + .allowedHeaders("*") + //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息) + .exposedHeaders("token", "Access-Control-Allow-Headers", "X-Access-Token"); + } + }; + } +} diff --git a/src/main/java/top/baogutang/music/config/GlobalMDCTaskDecorator.java b/src/main/java/top/baogutang/music/config/GlobalMDCTaskDecorator.java new file mode 100644 index 0000000..d42d8ed --- /dev/null +++ b/src/main/java/top/baogutang/music/config/GlobalMDCTaskDecorator.java @@ -0,0 +1,45 @@ +package top.baogutang.music.config; + +import org.slf4j.MDC; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.TaskDecorator; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.Map; + +/** + * @description: 多线程链路ID + * @author: nikooh + * @date: 2023/06/15 : 12:20 + */ +@Configuration +public class GlobalMDCTaskDecorator implements TaskDecorator, BeanPostProcessor { + + + @Override + public Runnable decorate(Runnable runnable) { + Map mdcContext = MDC.getCopyOfContextMap(); + return () -> { + try { + if (mdcContext != null) { + MDC.setContextMap(mdcContext); + } + runnable.run(); + } finally { + MDC.clear(); + } + }; + } + + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof ThreadPoolTaskExecutor) { + ((ThreadPoolTaskExecutor) bean).setTaskDecorator(this); + } + return bean; + } + +} diff --git a/src/main/java/top/baogutang/music/config/MdcRequestIdFilter.java b/src/main/java/top/baogutang/music/config/MdcRequestIdFilter.java new file mode 100644 index 0000000..8df50f6 --- /dev/null +++ b/src/main/java/top/baogutang/music/config/MdcRequestIdFilter.java @@ -0,0 +1,44 @@ +package top.baogutang.music.config; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.UUID; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 12:25 + */ +@Slf4j +@Component +@Order(1) +public class MdcRequestIdFilter extends OncePerRequestFilter { + + private static final String REQUEST_ID_KEY = "X-Request-Id"; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + try { + String traceId = request.getHeader(REQUEST_ID_KEY); + if (traceId == null) { + traceId = UUID.randomUUID().toString().replace("-", ""); + log.info("requestId为空,自动生成 {}", traceId); + request.setAttribute(REQUEST_ID_KEY, traceId); + } + MDC.put(REQUEST_ID_KEY, traceId); + filterChain.doFilter(request, response); + } finally { + MDC.remove(REQUEST_ID_KEY); + } + + } +} diff --git a/src/main/java/top/baogutang/music/config/MybatisPlusConfig.java b/src/main/java/top/baogutang/music/config/MybatisPlusConfig.java new file mode 100644 index 0000000..e5cc2df --- /dev/null +++ b/src/main/java/top/baogutang/music/config/MybatisPlusConfig.java @@ -0,0 +1,26 @@ +package top.baogutang.music.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @description: + * @author: developer + * @date: 2022/05/26 : 15:51 + */ +@Configuration +public class MybatisPlusConfig { + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + //分页插件 + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + //乐观锁插件 + interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + return interceptor; + } +} diff --git a/src/main/java/top/baogutang/music/controller/MusicCommonController.java b/src/main/java/top/baogutang/music/controller/MusicCommonController.java new file mode 100644 index 0000000..05dc626 --- /dev/null +++ b/src/main/java/top/baogutang/music/controller/MusicCommonController.java @@ -0,0 +1,56 @@ +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.domain.Results; +import top.baogutang.music.domain.res.EnumRes; +import top.baogutang.music.enums.ChannelEnum; +import top.baogutang.music.enums.SearchTypeEnum; +import top.baogutang.music.exceptions.BusinessException; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 09:51 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/music/common") +public class MusicCommonController { + + + @GetMapping + public Results> getEnumRes(@RequestParam(name = "type") String type) { + switch (type) { + case "channel": + List channelRes = Arrays.stream(ChannelEnum.values()) + .map(channel -> EnumRes.builder() + .type("channel") + .code(String.valueOf(channel.getCode())) + .desc(channel.getDesc()) + .build()) + .collect(Collectors.toList()); + return Results.ok(channelRes); + case "searchType": + List searchTypeRes = Arrays.stream(SearchTypeEnum.values()) + .map(searchType -> EnumRes.builder() + .type("searchType") + .code(String.valueOf(searchType.getCode())) + .desc(searchType.getDesc()) + .build()) + .collect(Collectors.toList()); + return Results.ok(searchTypeRes); + default: + throw new BusinessException("类型异常"); + } + } +} diff --git a/src/main/java/top/baogutang/music/controller/MusicDownloadController.java b/src/main/java/top/baogutang/music/controller/MusicDownloadController.java new file mode 100644 index 0000000..0caff5f --- /dev/null +++ b/src/main/java/top/baogutang/music/controller/MusicDownloadController.java @@ -0,0 +1,38 @@ +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.domain.Results; +import top.baogutang.music.domain.res.download.MusicDownloadRes; +import top.baogutang.music.domain.res.search.MusicPlaylistRes; +import top.baogutang.music.service.IMusicService; + +import javax.annotation.Resource; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 21:57 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/music/download") +public class MusicDownloadController { + + @Resource + private IMusicService musicService; + + @GetMapping + public Results download(@RequestParam(name = "channel") Integer channel, + @RequestParam(name = "id") Long id) { + MusicDownloadRes res = musicService.getMusicService(channel).download(id); + return Results.ok(res); + } + + +} diff --git a/src/main/java/top/baogutang/music/controller/MusicSearchController.java b/src/main/java/top/baogutang/music/controller/MusicSearchController.java new file mode 100644 index 0000000..4aaa5d4 --- /dev/null +++ b/src/main/java/top/baogutang/music/controller/MusicSearchController.java @@ -0,0 +1,63 @@ +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.domain.Results; +import top.baogutang.music.domain.req.search.MusicSearchReq; +import top.baogutang.music.domain.res.search.*; +import top.baogutang.music.service.IMusicService; + +import javax.annotation.Resource; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 21:06 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/music/search") +public class MusicSearchController { + + @Resource + private IMusicService musicService; + + @GetMapping + public Results search(MusicSearchReq req) { + MusicSearchRes res = musicService.getMusicService(req.getChannel()).search(req); + return Results.ok(res); + } + + @GetMapping("/playlist") + public Results playlist(@RequestParam(name = "channel") Integer channel, + @RequestParam(name = "id") Long id) { + MusicPlaylistRes res = musicService.getMusicService(channel).playList(id); + return Results.ok(res); + } + + @GetMapping("/album") + public Results album(@RequestParam(name = "channel") Integer channel, + @RequestParam(name = "id") Long id) { + MusicAlbumRes res = musicService.getMusicService(channel).album(id); + return Results.ok(res); + } + + @GetMapping("/artist") + public Results artist(@RequestParam(name = "channel") Integer channel, + @RequestParam(name = "id") Long id) { + MusicArtistRes res = musicService.getMusicService(channel).artist(id); + return Results.ok(res); + } + + @GetMapping("/detail") + public Results detail(@RequestParam(name = "channel") Integer channel, + @RequestParam(name = "id") Long id) { + MusicDetailRes res = musicService.getMusicService(channel).detail(id); + return Results.ok(res); + } +} diff --git a/src/main/java/top/baogutang/music/controller/MusicStaticController.java b/src/main/java/top/baogutang/music/controller/MusicStaticController.java new file mode 100644 index 0000000..493f7ea --- /dev/null +++ b/src/main/java/top/baogutang/music/controller/MusicStaticController.java @@ -0,0 +1,21 @@ +package top.baogutang.music.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 10:41 + */ +@Controller +public class MusicStaticController { + + @RequestMapping + public String viewJsonParseHtml() { + // 这里返回的字符串是HTML文件名(不包括扩展名) + return "music"; + } +} diff --git a/src/main/java/top/baogutang/music/dao/entity/BaseEntity.java b/src/main/java/top/baogutang/music/dao/entity/BaseEntity.java new file mode 100644 index 0000000..7d7981f --- /dev/null +++ b/src/main/java/top/baogutang/music/dao/entity/BaseEntity.java @@ -0,0 +1,34 @@ +package top.baogutang.music.dao.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * description: base entity + * + * @author N1KO + * date: 2024/12/10 + **/ +@Getter +@Setter +public abstract class BaseEntity implements Serializable { + + private static final long serialVersionUID = -4256387608783413943L; + + @TableId(value = "id", type = IdType.AUTO) + protected Long id; + + @TableField(value = "create_time", fill = FieldFill.INSERT) + protected LocalDateTime createTime; + + @TableField("deleted") + private Boolean deleted; + +} \ No newline at end of file diff --git a/src/main/java/top/baogutang/music/dao/entity/MusicRecordEntity.java b/src/main/java/top/baogutang/music/dao/entity/MusicRecordEntity.java new file mode 100644 index 0000000..f4f78bd --- /dev/null +++ b/src/main/java/top/baogutang/music/dao/entity/MusicRecordEntity.java @@ -0,0 +1,41 @@ +package top.baogutang.music.dao.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Getter; +import lombok.Setter; +import top.baogutang.music.enums.ChannelEnum; +import top.baogutang.music.enums.MusicQualityEnum; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 15:32 + */ +@Getter +@Setter +@TableName("t_music_record") +public class MusicRecordEntity extends BaseEntity { + + private static final long serialVersionUID = 1232270162701099532L; + + private String platformId; + + private ChannelEnum channel; + + private String name; + + private String albumName; + + private String artistName; + + private MusicQualityEnum level; + + private String pic; + + private String size; + + private String url; + +} diff --git a/src/main/java/top/baogutang/music/dao/mapper/MusicRecordMapper.java b/src/main/java/top/baogutang/music/dao/mapper/MusicRecordMapper.java new file mode 100644 index 0000000..a2382c7 --- /dev/null +++ b/src/main/java/top/baogutang/music/dao/mapper/MusicRecordMapper.java @@ -0,0 +1,16 @@ +package top.baogutang.music.dao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import top.baogutang.music.dao.entity.MusicRecordEntity; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 15:37 + */ +@Mapper +public interface MusicRecordMapper extends BaseMapper { +} diff --git a/src/main/java/top/baogutang/music/domain/Results.java b/src/main/java/top/baogutang/music/domain/Results.java new file mode 100644 index 0000000..016f942 --- /dev/null +++ b/src/main/java/top/baogutang/music/domain/Results.java @@ -0,0 +1,198 @@ +package top.baogutang.music.domain; + +import org.slf4j.MDC; + +import java.io.Serializable; + +/** + * @description: + * @author: nikooh + * @date: 2023/06/15 : 11:54 + */ +public class Results implements Serializable { + private static final long serialVersionUID = 1L; + public static final int SUCCESS_CODE = 200; + public static final int FAIL_CODE = 300; + public static final String SUCCESS_MSG = "success"; + public static final int PARAM_ILLEGAL_CODE = 301; + + /** + * 响应码,200为请求成功;其他异常均为业务异常 + */ + private int code; + + /** + * 响应信息 + */ + private String msg; + + /** + * 业务数据 + */ + private T data; + + /** + * 全局链路请求id + */ + private String rid; + + public Results() { + } + + public Results(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public Results(int code, String msg, T data) { + this.code = code; + this.msg = msg; + this.data = data; + } + + public static Results ok() { + return restResult(null, 200, (String) null); + } + + public static Results ok(T data) { + return restResult(data, SUCCESS_CODE, SUCCESS_MSG); + } + + public static Results ok(T data, int i, String msg) { + return restResult(data, 200, (String) null); + } + + + public static Results ok(T data, String msg) { + return restResult(data, 200, msg); + } + + public static Results result(T data, int code, String msg) { + return restResult(data, code, msg); + } + + public static Results failed() { + return restResult(null, 300, null); + } + + public static Results failed(String msg) { + return restResult(null, 300, msg); + } + + public static Results failed(int code, String msg) { + return restResult(null, code, msg); + } + + + public static Results failed(T data) { + return restResult(data, 300, (String) null); + } + + public static Results failed(T data, String msg) { + return restResult(data, 300, msg); + } + + public static Results restResult(T data, int code, String msg) { + Results r = new Results<>(); + r.setCode(code); + r.setData(data); + r.setMsg(msg); + String rid = MDC.get("X-Request-Id"); + r.setRid(rid); + return r; + } + + + public Boolean isSuccess() { + return this.code == SUCCESS_CODE; + } + + public static boolean isSuccess(Results response) { + return response != null && response.code == SUCCESS_CODE; + } + + + public void setErrorCodeEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public static Results.ResultsBuilder builder() { + return new Results.ResultsBuilder(); + } + + public int getCode() { + return this.code; + } + + public String getMsg() { + return this.msg; + } + + public T getData() { + return this.data; + } + + public String getRid() { + return rid; + } + + public void setRid(String rid) { + this.rid = rid; + } + + public void setCode(final int code) { + this.code = code; + } + + public void setMsg(final String msg) { + this.msg = msg; + } + + public void setData(final T data) { + this.data = data; + } + + protected boolean canEqual(final Object other) { + return other instanceof Results; + } + + + @Override + public String toString() { + return "Results(code=" + this.getCode() + ", msg=" + this.getMsg() + ", data=" + this.getData() + ")"; + } + + public static class ResultsBuilder { + private int code; + private String msg; + private T data; + + ResultsBuilder() { + } + + public Results.ResultsBuilder code(final int code) { + this.code = code; + return this; + } + + public Results.ResultsBuilder msg(final String msg) { + this.msg = msg; + return this; + } + + public Results.ResultsBuilder data(final T data) { + this.data = data; + return this; + } + + public Results build() { + return new Results(this.code, this.msg, this.data); + } + + @Override + public String toString() { + return "Results.ResultsBuilder(code=" + this.code + ", msg=" + this.msg + ", data=" + this.data + ")"; + } + } +} diff --git a/src/main/java/top/baogutang/music/domain/req/AbstractMusicReq.java b/src/main/java/top/baogutang/music/domain/req/AbstractMusicReq.java new file mode 100644 index 0000000..b860c7a --- /dev/null +++ b/src/main/java/top/baogutang/music/domain/req/AbstractMusicReq.java @@ -0,0 +1,15 @@ +package top.baogutang.music.domain.req; + +import java.io.Serializable; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 17:40 + */ +public class AbstractMusicReq implements Serializable { + + private static final long serialVersionUID = 9153927395653874994L; +} diff --git a/src/main/java/top/baogutang/music/domain/req/search/MusicSearchReq.java b/src/main/java/top/baogutang/music/domain/req/search/MusicSearchReq.java new file mode 100644 index 0000000..d222a20 --- /dev/null +++ b/src/main/java/top/baogutang/music/domain/req/search/MusicSearchReq.java @@ -0,0 +1,58 @@ +package top.baogutang.music.domain.req.search; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.baogutang.music.domain.req.AbstractMusicReq; +import top.baogutang.music.enums.ChannelEnum; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 17:42 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class MusicSearchReq extends AbstractMusicReq { + + private static final long serialVersionUID = -3287272847248227599L; + + /** + * 渠道 M + */ + private Integer channel; + + /** + * 关键词 M + */ + private String keywords; + + /** + * 返回数量 , 默认为 30 O + */ + private Integer limit = 50; + + /** + * 偏移数量,用于分页 , 如 : 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0 O + */ + private Integer offset = 0; + + /** + * 搜索类型;默认为 1 即单曲 , + * 取值意义 : 1: 单曲, + * 10: 专辑, + * 100: 歌手, + * 1000: 歌单, + * 1002: 用户, + * 1004: MV, + * 1006: 歌词, + * 1009: 电台, + * 1014: 视频, + * 1018:综合, + * 2000:声音(搜索声音返回字段格式会不一样) + */ + private Integer type = 1; + + +} diff --git a/src/main/java/top/baogutang/music/domain/res/AbstractMusicRes.java b/src/main/java/top/baogutang/music/domain/res/AbstractMusicRes.java new file mode 100644 index 0000000..979b2fb --- /dev/null +++ b/src/main/java/top/baogutang/music/domain/res/AbstractMusicRes.java @@ -0,0 +1,31 @@ +package top.baogutang.music.domain.res; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 17:35 + */ +@Data +public class AbstractMusicRes implements Serializable { + + private static final long serialVersionUID = -4002863447773637066L; + + private String response; + + private String requestId; + + private Integer status; + + private String message; + + private Date createTime; + + +} diff --git a/src/main/java/top/baogutang/music/domain/res/EnumRes.java b/src/main/java/top/baogutang/music/domain/res/EnumRes.java new file mode 100644 index 0000000..c31bcc8 --- /dev/null +++ b/src/main/java/top/baogutang/music/domain/res/EnumRes.java @@ -0,0 +1,27 @@ +package top.baogutang.music.domain.res; + +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 09:52 + */ +@Data +@Builder +public class EnumRes implements Serializable { + + private static final long serialVersionUID = -1426730104932991851L; + + private String type; + + private String code; + + private String desc; + +} diff --git a/src/main/java/top/baogutang/music/domain/res/download/MusicDownloadRes.java b/src/main/java/top/baogutang/music/domain/res/download/MusicDownloadRes.java new file mode 100644 index 0000000..fccd5ac --- /dev/null +++ b/src/main/java/top/baogutang/music/domain/res/download/MusicDownloadRes.java @@ -0,0 +1,41 @@ +package top.baogutang.music.domain.res.download; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.baogutang.music.domain.res.AbstractMusicRes; + +import java.io.Serializable; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 15:16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class MusicDownloadRes extends AbstractMusicRes implements Serializable { + + private static final long serialVersionUID = -2542699155122341353L; + + @JsonProperty("al_name") + private String albumName; + + @JsonProperty("ar_name") + private String artistName; + + private String pic; + + private String size; + + private String name; + + private String url; + + private Long id; + + private String level; + +} diff --git a/src/main/java/top/baogutang/music/domain/res/search/MusicAlbumRes.java b/src/main/java/top/baogutang/music/domain/res/search/MusicAlbumRes.java new file mode 100644 index 0000000..4e6587d --- /dev/null +++ b/src/main/java/top/baogutang/music/domain/res/search/MusicAlbumRes.java @@ -0,0 +1,62 @@ +package top.baogutang.music.domain.res.search; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.baogutang.music.domain.res.AbstractMusicRes; + +import java.io.Serializable; +import java.util.List; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 10:18 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class MusicAlbumRes extends AbstractMusicRes implements Serializable { + + private static final long serialVersionUID = 3548382309848506723L; + + private Integer code; + + private List songs; + + @Data + public static class Song implements Serializable { + + private static final long serialVersionUID = -3111291245613339667L; + + private long id; + + private String name; + + private List ar; + + private Album al; + } + + @Data + public static class Album implements Serializable { + + private static final long serialVersionUID = 1103297822656245236L; + + private long id; + + private String name; + + private String picUrl; + } + + @Data + public static class Artist implements Serializable { + + private static final long serialVersionUID = -5403293880708265600L; + + private long id; + + private String name; + } +} diff --git a/src/main/java/top/baogutang/music/domain/res/search/MusicArtistRes.java b/src/main/java/top/baogutang/music/domain/res/search/MusicArtistRes.java new file mode 100644 index 0000000..13ffb53 --- /dev/null +++ b/src/main/java/top/baogutang/music/domain/res/search/MusicArtistRes.java @@ -0,0 +1,76 @@ +package top.baogutang.music.domain.res.search; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.baogutang.music.domain.res.AbstractMusicRes; + +import java.io.Serializable; +import java.util.List; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 10:33 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class MusicArtistRes extends AbstractMusicRes implements Serializable { + + private static final long serialVersionUID = 482425136308129805L; + + private Artists artist; + + @JsonProperty("hotSongs") + private List songs; + + @Data + public static class Song implements Serializable { + + private static final long serialVersionUID = -636513450925661161L; + + private long id; + + private String name; + + private List ar; + + private Album al; + } + + @Data + public static class Album implements Serializable { + + private static final long serialVersionUID = 4531880293928863467L; + + private long id; + + private String name; + + private String picUrl; + } + + @Data + public static class Artist implements Serializable { + + private static final long serialVersionUID = -6686291842590730363L; + + private long id; + + private String name; + } + + @Data + public static class Artists implements Serializable { + + private static final long serialVersionUID = 621224840806664845L; + + private long id; + + private String name; + + private String picUrl; + } +} diff --git a/src/main/java/top/baogutang/music/domain/res/search/MusicDetailRes.java b/src/main/java/top/baogutang/music/domain/res/search/MusicDetailRes.java new file mode 100644 index 0000000..57cea41 --- /dev/null +++ b/src/main/java/top/baogutang/music/domain/res/search/MusicDetailRes.java @@ -0,0 +1,44 @@ +package top.baogutang.music.domain.res.search; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.baogutang.music.domain.res.AbstractMusicRes; + +import java.io.Serializable; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 22:02 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class MusicDetailRes extends AbstractMusicRes implements Serializable { + + private static final long serialVersionUID = -6299429225169483420L; + + private Integer status; + + private String name; + + private String pic; + + @JsonProperty("ar_name") + private String artistsName; + + @JsonProperty("al_name") + private String albumName; + + private String level; + + private String size; + + private String url; + + private String lyric; + + +} diff --git a/src/main/java/top/baogutang/music/domain/res/search/MusicPlaylistRes.java b/src/main/java/top/baogutang/music/domain/res/search/MusicPlaylistRes.java new file mode 100644 index 0000000..af68028 --- /dev/null +++ b/src/main/java/top/baogutang/music/domain/res/search/MusicPlaylistRes.java @@ -0,0 +1,62 @@ +package top.baogutang.music.domain.res.search; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.baogutang.music.domain.res.AbstractMusicRes; + +import java.io.Serializable; +import java.util.List; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 21:50 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class MusicPlaylistRes extends AbstractMusicRes implements Serializable { + + private static final long serialVersionUID = -3439075853886139179L; + + private Integer code; + + private List songs; + + @Data + public static class Song implements Serializable { + + private static final long serialVersionUID = 1854475810777692098L; + + private long id; + + private String name; + + private List ar; + + private Album al; + } + + @Data + public static class Album implements Serializable { + + private static final long serialVersionUID = -8238903321545975920L; + + private long id; + + private String name; + + private String picUrl; + } + + @Data + public static class Artist implements Serializable { + + private static final long serialVersionUID = 7806822401954161309L; + + private long id; + + private String name; + } +} diff --git a/src/main/java/top/baogutang/music/domain/res/search/MusicSearchRes.java b/src/main/java/top/baogutang/music/domain/res/search/MusicSearchRes.java new file mode 100644 index 0000000..15828ab --- /dev/null +++ b/src/main/java/top/baogutang/music/domain/res/search/MusicSearchRes.java @@ -0,0 +1,98 @@ +package top.baogutang.music.domain.res.search; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import top.baogutang.music.domain.res.AbstractMusicRes; + +import java.io.Serializable; +import java.util.List; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 17:55 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class MusicSearchRes extends AbstractMusicRes implements Serializable { + + private static final long serialVersionUID = -5308560815529348574L; + + private Integer code; + + private SearchResult result; + + @Data + public static class SearchResult implements Serializable { + + private static final long serialVersionUID = -7648998111830569007L; + + private Boolean hasMore; + + private List playlists; + + private List songs; + + private List artists; + + private List albums; + + } + + @Data + public static class Song implements Serializable { + + private static final long serialVersionUID = -266782145902343916L; + + private long id; + + private String name; + + private List artists; + + private Album album; + + } + + @Data + public static class Album implements Serializable { + + private static final long serialVersionUID = -4592741953573940059L; + + private long id; + + private String name; + + private String type; + + private String blurPicUrl; + + } + + @Data + public static class Artist implements Serializable { + + private static final long serialVersionUID = -8992566914406324393L; + + private long id; + + private String name; + + private String picUrl; + } + + @Data + public static class PlayList implements Serializable { + + private static final long serialVersionUID = -780326125277012465L; + + private long id; + + private String name; + + private String coverImgUrl; + + } +} diff --git a/src/main/java/top/baogutang/music/enums/ChannelEnum.java b/src/main/java/top/baogutang/music/enums/ChannelEnum.java new file mode 100644 index 0000000..ad0ac93 --- /dev/null +++ b/src/main/java/top/baogutang/music/enums/ChannelEnum.java @@ -0,0 +1,35 @@ +package top.baogutang.music.enums; + +import lombok.Getter; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 17:01 + */ +@Getter +public enum ChannelEnum { + + NET_EASE_MUSIC(0, "网易云音乐"), + + QQ_MUSIC(1, "QQ 音乐"), + + KU_GOU_MUSIC(2, "酷狗音乐"), + + KU_WO_MUSIC(3, "酷我音乐"), + +// CUS_MUSIC(4, "自定义音乐"), + + ; + + private final Integer code; + + private final String desc; + + ChannelEnum(Integer code, String desc) { + this.code = code; + this.desc = desc; + } +} diff --git a/src/main/java/top/baogutang/music/enums/MusicQualityEnum.java b/src/main/java/top/baogutang/music/enums/MusicQualityEnum.java new file mode 100644 index 0000000..23dbb8b --- /dev/null +++ b/src/main/java/top/baogutang/music/enums/MusicQualityEnum.java @@ -0,0 +1,56 @@ +package top.baogutang.music.enums; + +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 15:34 + */ +@Getter +public enum MusicQualityEnum { + + /** + * 音质 standard => 标准, + * higher => 较高, + * exhigh=>极高, + * lossless=>无损, + * hires=>Hi-Res, + * jyeffect => 高清环绕声, + * sky => 沉浸环绕声, + * dolby => 杜比全景声, + * jymaster => 超清母带', + */ + STANDARD("标准"), + HIGHER("较高"), + EXHIGH("极高"), + LOSSLESS("无损"), + HIRES("Hires音质"), + JYEFFECT("高清环绕声"), + SKY("沉浸环绕声"), + DOLBY("杜比全景声"), + JYMASTER("超清母带"), + + ; + + private final String desc; + + MusicQualityEnum(String desc) { + this.desc = desc; + } + + public static MusicQualityEnum parse(String level) { + if (StringUtils.isBlank(level)) { + return null; + } + return Arrays.stream(MusicQualityEnum.values()) + .filter(e -> StringUtils.equalsIgnoreCase(e.name(), level) || StringUtils.equalsIgnoreCase(e.getDesc(), level)) + .findFirst() + .orElse(null); + } +} diff --git a/src/main/java/top/baogutang/music/enums/OperationTypeEnum.java b/src/main/java/top/baogutang/music/enums/OperationTypeEnum.java new file mode 100644 index 0000000..a47707c --- /dev/null +++ b/src/main/java/top/baogutang/music/enums/OperationTypeEnum.java @@ -0,0 +1,14 @@ +package top.baogutang.music.enums; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 18:02 + */ +public enum OperationTypeEnum { + + SEARCH, + ; +} diff --git a/src/main/java/top/baogutang/music/enums/SearchTypeEnum.java b/src/main/java/top/baogutang/music/enums/SearchTypeEnum.java new file mode 100644 index 0000000..dd1c79f --- /dev/null +++ b/src/main/java/top/baogutang/music/enums/SearchTypeEnum.java @@ -0,0 +1,43 @@ +package top.baogutang.music.enums; + +import lombok.Getter; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 10:03 + */ +@Getter +public enum SearchTypeEnum { + /** + * 搜索类型; 1: 单曲, + * 10: 专辑, + * 100: 歌手, + * 1000: 歌单, + * 1002: 用户, + * 1004: MV, + * 1006: 歌词, + * 1009: 电台, + * 1014: 视频, + * 1018:综合, + * 2000:声音(搜索声音返回字段格式会不一样) + */ + SONG(1, "单曲"), + ALBUM(10, "专辑"), + SINGER(100, "歌手"), + PLAYLIST(1000, "歌单"), + + + ; + + private final Integer code; + + private final String desc; + + SearchTypeEnum(Integer code, String desc) { + this.code = code; + this.desc = desc; + } +} diff --git a/src/main/java/top/baogutang/music/enums/Status.java b/src/main/java/top/baogutang/music/enums/Status.java new file mode 100644 index 0000000..db5b4fc --- /dev/null +++ b/src/main/java/top/baogutang/music/enums/Status.java @@ -0,0 +1,29 @@ +package top.baogutang.music.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum Status { + + /** + * common status + */ + WAITING(10), + + PROCESSING(20), + + PROCESSED(30), + + SUCCESS(40), + + FAILED(50), + + ERROR(60), + + CLOSED(70); + + private final int code; + +} \ No newline at end of file diff --git a/src/main/java/top/baogutang/music/exceptions/BusinessException.java b/src/main/java/top/baogutang/music/exceptions/BusinessException.java new file mode 100644 index 0000000..1d2ef69 --- /dev/null +++ b/src/main/java/top/baogutang/music/exceptions/BusinessException.java @@ -0,0 +1,35 @@ +package top.baogutang.music.exceptions; + + +import top.baogutang.music.domain.Results; + +/** + * @description: 业务异常 + * @author: nikooh + * @date: 2023/06/15 : 11:48 + */ +public class BusinessException extends RuntimeException { + + private static final long serialVersionUID = -8485514816508612287L; + + private int code = Results.FAIL_CODE; + + public BusinessException(int code, String message) { + super(message); + this.code = code; + } + + + public BusinessException(String message) { + super(message); + } + + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } +} diff --git a/src/main/java/top/baogutang/music/exceptions/GlobalExceptionHandler.java b/src/main/java/top/baogutang/music/exceptions/GlobalExceptionHandler.java new file mode 100644 index 0000000..3990511 --- /dev/null +++ b/src/main/java/top/baogutang/music/exceptions/GlobalExceptionHandler.java @@ -0,0 +1,75 @@ +package top.baogutang.music.exceptions; + +import lombok.extern.slf4j.Slf4j; +import org.apache.catalina.connector.ClientAbortException; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import top.baogutang.music.domain.Results; + +import java.util.List; + +/** + * @description: 全局异常处理 + * @author: nikooh + * @date: 2023/06/15 : 12:23 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + + public GlobalExceptionHandler() { + // + } + + @ExceptionHandler({Throwable.class}) + public Results handleException(Throwable e) { + log.error("请求发生错误,错误信息:{}", e.getMessage(), e); + return Results.failed("出错啦,请稍后再试哟~"); + } + + + @ExceptionHandler({Exception.class}) + public Results handleException(Exception e) { + log.error("请求发生错误,错误信息:", e); + return Results.failed("服务器出错啦,请稍后再试哟~"); + } + + @ExceptionHandler({ClientAbortException.class}) + public Results handleClientAbortException() { + log.error("ClientAbortException: java.io.IOException: Broken pipe: {}", "客户端链接断开"); + return Results.failed(300, "服务器出错啦,请稍后再试哟~"); + } + + @ExceptionHandler({IllegalArgumentException.class, MissingServletRequestParameterException.class, HttpMediaTypeNotSupportedException.class, HttpRequestMethodNotSupportedException.class}) + public Results handleMultiException(Exception e) { + log.error("请求发生错误,错误信息:{}", e.getMessage(), e); + return Results.failed(300, e.getMessage()); + } + + @ExceptionHandler({BusinessException.class}) + public Results businessException(BusinessException e) { + log.error("请求发生错误,code:{},message:{}", e.getCode(), e.getMessage()); + return Results.failed(e.getCode(), e.getMessage()); + } + + @ExceptionHandler({MethodArgumentNotValidException.class}) + public Results parameterExceptionHandler(MethodArgumentNotValidException e) { + BindingResult exceptions = e.getBindingResult(); + if (exceptions.hasErrors()) { + List errors = exceptions.getAllErrors(); + if (!errors.isEmpty()) { + FieldError fieldError = (FieldError) errors.get(0); + return Results.failed(301, fieldError.getDefaultMessage()); + } + } + return Results.failed(301, e.getMessage()); + } +} diff --git a/src/main/java/top/baogutang/music/factory/ChannelClientFactory.java b/src/main/java/top/baogutang/music/factory/ChannelClientFactory.java new file mode 100644 index 0000000..67f5a75 --- /dev/null +++ b/src/main/java/top/baogutang/music/factory/ChannelClientFactory.java @@ -0,0 +1,58 @@ +package top.baogutang.music.factory; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.stereotype.Component; +import top.baogutang.music.annos.ChannelInfo; +import top.baogutang.music.domain.req.AbstractMusicReq; +import top.baogutang.music.domain.res.AbstractMusicRes; +import top.baogutang.music.enums.ChannelEnum; +import top.baogutang.music.client.ChannelClient; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 17:00 + */ + +@Slf4j +@Component +public class ChannelClientFactory implements BeanPostProcessor { + + private final Map> channelClientMap = new HashMap<>(); + + private Integer getChannelId(ChannelClient channelClient) { + ChannelInfo channelInfo = channelClient.getClass().getAnnotation(ChannelInfo.class); + return channelInfo.value().getCode(); + } + + public ChannelClient getClient(ChannelEnum channelEnum) { + return channelClientMap.get(channelEnum.getCode()); + } + + @Override + @SuppressWarnings("unchecked") + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof ChannelClient) { + channelClientMap.put(getChannelId(((ChannelClient) bean)), (ChannelClient) bean); + } + return bean; + } +} + + + + + + + + + + + diff --git a/src/main/java/top/baogutang/music/properties/NetEaseMusicProperties.java b/src/main/java/top/baogutang/music/properties/NetEaseMusicProperties.java new file mode 100644 index 0000000..0007ca8 --- /dev/null +++ b/src/main/java/top/baogutang/music/properties/NetEaseMusicProperties.java @@ -0,0 +1,31 @@ +package top.baogutang.music.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 16:45 + */ +@Data +@Component +@ConfigurationProperties(prefix = "baogutang.net-ease-music") +public class NetEaseMusicProperties { + + private String queryBaseUrl; + + private String playlistBaseUrl; + + private String albumBaseUrl; + + private String artistBaseUrl; + + private String downloadBaseUrl; + + private String downloadPath; + +} diff --git a/src/main/java/top/baogutang/music/service/AbstractMusicService.java b/src/main/java/top/baogutang/music/service/AbstractMusicService.java new file mode 100644 index 0000000..1b5b917 --- /dev/null +++ b/src/main/java/top/baogutang/music/service/AbstractMusicService.java @@ -0,0 +1,88 @@ +package top.baogutang.music.service; + +import lombok.extern.slf4j.Slf4j; +import top.baogutang.music.client.ChannelClient; +import top.baogutang.music.domain.req.AbstractMusicReq; +import top.baogutang.music.domain.req.search.MusicSearchReq; +import top.baogutang.music.domain.res.AbstractMusicRes; +import top.baogutang.music.domain.res.download.MusicDownloadRes; +import top.baogutang.music.domain.res.search.*; +import top.baogutang.music.enums.ChannelEnum; +import top.baogutang.music.exceptions.BusinessException; +import top.baogutang.music.factory.ChannelClientFactory; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 17:00 + */ +@Slf4j +public abstract class AbstractMusicService { + + @Resource + private ChannelClientFactory channelClientFactory; + + @Resource(name = "commonExecutor") + private Executor commonExecutor; + + public abstract ChannelEnum getChannelEnum(); + + public MusicSearchRes search(MusicSearchReq req) { + ChannelEnum channelEnum = getChannelEnum(); + ChannelClient channelClient = channelClientFactory.getClient(channelEnum); + return channelClient.search(req); + } + + public MusicPlaylistRes playList(Long id) { + ChannelEnum channelEnum = getChannelEnum(); + ChannelClient channelClient = channelClientFactory.getClient(channelEnum); + return channelClient.playlist(id); + } + + public MusicDetailRes detail(Long id) { + ChannelEnum channelEnum = getChannelEnum(); + ChannelClient channelClient = channelClientFactory.getClient(channelEnum); + return channelClient.detail(id); + } + + public MusicAlbumRes album(Long id) { + ChannelEnum channelEnum = getChannelEnum(); + ChannelClient channelClient = channelClientFactory.getClient(channelEnum); + return channelClient.album(id); + } + + public MusicArtistRes artist(Long id) { + ChannelEnum channelEnum = getChannelEnum(); + ChannelClient channelClient = channelClientFactory.getClient(channelEnum); + return channelClient.artist(id); + } + + public MusicDownloadRes download(Long id) { + ChannelEnum channelEnum = getChannelEnum(); + ChannelClient channelClient = channelClientFactory.getClient(channelEnum); + MusicDownloadRes res = channelClient.download(id); + channelClient.saveMusic(res); +// CompletableFuture.runAsync(() -> { +// try { +// channelClient.processFile(res); +// } catch (IOException e) { +// log.error(">>>>>>>>>>download error:{}<<<<<<<<<<", e.getMessage(), e); +// throw new BusinessException("下载异常"); +// } +// }, commonExecutor); + try { + channelClient.processFile(res); + } catch (IOException e) { + log.error(">>>>>>>>>>download error:{}<<<<<<<<<<", e.getMessage(), e); + throw new BusinessException("下载异常"); + } + return res; + } +} diff --git a/src/main/java/top/baogutang/music/service/IMusicRecordService.java b/src/main/java/top/baogutang/music/service/IMusicRecordService.java new file mode 100644 index 0000000..57845c4 --- /dev/null +++ b/src/main/java/top/baogutang/music/service/IMusicRecordService.java @@ -0,0 +1,18 @@ +package top.baogutang.music.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import top.baogutang.music.dao.entity.MusicRecordEntity; +import top.baogutang.music.domain.res.download.MusicDownloadRes; +import top.baogutang.music.enums.ChannelEnum; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 15:24 + */ +public interface IMusicRecordService extends IService { + + void save(MusicDownloadRes res, ChannelEnum channel); +} diff --git a/src/main/java/top/baogutang/music/service/IMusicService.java b/src/main/java/top/baogutang/music/service/IMusicService.java new file mode 100644 index 0000000..d0521a0 --- /dev/null +++ b/src/main/java/top/baogutang/music/service/IMusicService.java @@ -0,0 +1,15 @@ +package top.baogutang.music.service; + +import top.baogutang.music.enums.ChannelEnum; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 21:09 + */ +public interface IMusicService { + + AbstractMusicService getMusicService(Integer channel); +} diff --git a/src/main/java/top/baogutang/music/service/INetEaseMusicService.java b/src/main/java/top/baogutang/music/service/INetEaseMusicService.java new file mode 100644 index 0000000..9aa2030 --- /dev/null +++ b/src/main/java/top/baogutang/music/service/INetEaseMusicService.java @@ -0,0 +1,20 @@ +package top.baogutang.music.service; + +import org.springframework.stereotype.Service; +import top.baogutang.music.enums.ChannelEnum; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 16:59 + */ +@Service +public class INetEaseMusicService extends AbstractMusicService { + + @Override + public ChannelEnum getChannelEnum() { + return ChannelEnum.NET_EASE_MUSIC; + } +} diff --git a/src/main/java/top/baogutang/music/service/impl/MusicRecordServiceImpl.java b/src/main/java/top/baogutang/music/service/impl/MusicRecordServiceImpl.java new file mode 100644 index 0000000..40ab1eb --- /dev/null +++ b/src/main/java/top/baogutang/music/service/impl/MusicRecordServiceImpl.java @@ -0,0 +1,53 @@ +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.springframework.stereotype.Service; +import top.baogutang.music.dao.entity.MusicRecordEntity; +import top.baogutang.music.dao.mapper.MusicRecordMapper; +import top.baogutang.music.domain.res.download.MusicDownloadRes; +import top.baogutang.music.enums.ChannelEnum; +import top.baogutang.music.enums.MusicQualityEnum; +import top.baogutang.music.service.IMusicRecordService; + +import java.time.LocalDateTime; +import java.util.Objects; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/11 : 15:38 + */ +@Slf4j +@Service +public class MusicRecordServiceImpl extends ServiceImpl implements IMusicRecordService { + + @Override + public void save(MusicDownloadRes res, ChannelEnum channel) { + MusicRecordEntity entity = new LambdaQueryChainWrapper<>(baseMapper) + .eq(MusicRecordEntity::getChannel, channel) + .eq(MusicRecordEntity::getPlatformId, String.valueOf(res.getId())) + .eq(MusicRecordEntity::getDeleted, Boolean.FALSE) + .orderByDesc(MusicRecordEntity::getCreateTime) + .last(" limit 1 ") + .one(); + if (Objects.nonNull(entity)) { + return; + } + entity = new MusicRecordEntity(); + entity.setPlatformId(String.valueOf(res.getId())); + entity.setName(res.getName()); + entity.setAlbumName(res.getAlbumName()); + entity.setArtistName(res.getArtistName()); + entity.setLevel(MusicQualityEnum.parse(res.getLevel())); + entity.setPic(res.getPic()); + entity.setSize(res.getSize()); + entity.setUrl(res.getUrl()); + entity.setChannel(channel); + entity.setCreateTime(LocalDateTime.now()); + baseMapper.insert(entity); + } +} diff --git a/src/main/java/top/baogutang/music/service/impl/MusicServiceImpl.java b/src/main/java/top/baogutang/music/service/impl/MusicServiceImpl.java new file mode 100644 index 0000000..0b4ecf6 --- /dev/null +++ b/src/main/java/top/baogutang/music/service/impl/MusicServiceImpl.java @@ -0,0 +1,46 @@ +package top.baogutang.music.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.stereotype.Service; +import top.baogutang.music.domain.req.AbstractMusicReq; +import top.baogutang.music.domain.res.AbstractMusicRes; +import top.baogutang.music.enums.ChannelEnum; +import top.baogutang.music.exceptions.BusinessException; +import top.baogutang.music.service.AbstractMusicService; +import top.baogutang.music.service.IMusicService; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * + * @description: + * + * @author: N1KO + * @date: 2024/12/10 : 21:09 + */ +@Slf4j +@Service +public class MusicServiceImpl implements IMusicService, BeanPostProcessor { + + private final Map> musicServiceMap = new HashMap<>(); + + + @Override + public AbstractMusicService getMusicService(Integer channel) { + return Optional.of(musicServiceMap.get(channel)) + .orElseThrow(() -> new BusinessException("暂不支持该服务")); + } + + @Override + @SuppressWarnings("unchecked") + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof AbstractMusicService) { + musicServiceMap.put((((AbstractMusicService) bean).getChannelEnum().getCode()), (AbstractMusicService) bean); + } + return bean; + } +} diff --git a/src/main/java/top/baogutang/music/utils/JacksonUtil.java b/src/main/java/top/baogutang/music/utils/JacksonUtil.java new file mode 100644 index 0000000..9d18a37 --- /dev/null +++ b/src/main/java/top/baogutang/music/utils/JacksonUtil.java @@ -0,0 +1,189 @@ +package top.baogutang.music.utils; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * @description: JacksonUtil + * @author: nikooh + * @date: 2024/08/06 : 10:50 + */ +@Slf4j +public class JacksonUtil { + + private JacksonUtil() { + // empty private constructor + } + + private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + + static { + + // yyyy-MM-dd HH:mm:ss + OBJECT_MAPPER.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_FORMAT)); + // JavaTimeModule Java 8 + OBJECT_MAPPER.registerModule(new JavaTimeModule()); + // + OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + // ObjectMapper null + OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); + // ObjectMapper Bean () + OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + // ObjectMapper JSON Java + OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + } + + /** + * json + * + * @param content + * @return json + */ + public static String toJson(Object content) { + String result = StringUtils.EMPTY; + if (Objects.isNull(content)) { + return result; + } + try { + result = OBJECT_MAPPER.writeValueAsString(content); + } catch (Exception e) { + log.error(">>>>>>>>>>parse object to json fail:{}<<<<<<<<<<", e.getMessage()); + } + return result; + } + + /** + * + * + * @param content + * @param clazz + * @param + * @return + */ + public static T fromJson(String content, Class clazz) { + T result = null; + if (StringUtils.isBlank(content)) { + return result; + } + try { + result = OBJECT_MAPPER.readValue(content, clazz); + } catch (Exception e) { + log.error(">>>>>>>>>>parse json to object fail:{}<<<<<<<<<<", e.getMessage()); + } + return result; + } + + /** + * + * + * @param content + * @param type + * @param + * @return + */ + public static T fromJson(String content, TypeReference type) { + T result = null; + if (StringUtils.isBlank(content)) { + return result; + } + try { + result = OBJECT_MAPPER.readValue(content, type); + } catch (Exception e) { + log.error(">>>>>>>>>>parse json to object fail:{}<<<<<<<<<<", e.getMessage()); + } + return result; + } + + /** + * jsonList + * + * @param content + * @param clazz + * @param + * @return + */ + public static List jsonToList(String content, Class clazz) { + List list = new ArrayList<>(); + if (StringUtils.isBlank(content) || clazz == null) { + return list; + } + try { + list = OBJECT_MAPPER.readValue(content, new TypeReference>() { + }); + } catch (Exception e) { + log.error(">>>>>>>>>>parse json to list fail:{}<<<<<<<<<<", e.getMessage()); + } + return list; + } + + /** + * jsonMap + * + * @param content + * @param keyType key + * @param valueType value + * @param Class + * @param Class + * @return Map + */ + public static Map jsonToMap(String content, Class keyType, Class valueType) { + Map result = new HashMap<>(); + if (StringUtils.isBlank(content) || Objects.isNull(keyType) || Objects.isNull(valueType)) { + return result; + } + try { + result = OBJECT_MAPPER.readValue(content, new TypeReference<>() { + }); + } catch (Exception e) { + log.error(">>>>>>>>>>parse json to map fail:{}<<<<<<<<<<", e.getMessage()); + } + return result; + } + + public static Map beanToMap(Object content, Class keyType, Class valueType) { + Map result = new HashMap<>(); + if (Objects.isNull(content) || Objects.isNull(keyType) || Objects.isNull(valueType)) { + return result; + } + try { + result = OBJECT_MAPPER.convertValue(content, new TypeReference<>() { + }); + } catch (Exception e) { + log.error(">>>>>>>>>>parse json to map fail:{}<<<<<<<<<<", e.getMessage()); + } + return result; + } + + /** + * jsonJsonNode + * + * @param content + * @return JsonNode + */ + public static JsonNode jsonToNode(String content) { + JsonNode jsonNode = null; + if (StringUtils.isBlank(content)) { + return null; + } + try { + jsonNode = OBJECT_MAPPER.readTree(content); + } catch (Exception e) { + log.error(">>>>>>>>>>parse json to node fail:{}<<<<<<<<<<", e.getMessage()); + } + return jsonNode; + } +} diff --git a/src/main/java/top/baogutang/music/utils/OkHttpUtil.java b/src/main/java/top/baogutang/music/utils/OkHttpUtil.java new file mode 100644 index 0000000..cd78764 --- /dev/null +++ b/src/main/java/top/baogutang/music/utils/OkHttpUtil.java @@ -0,0 +1,127 @@ +package top.baogutang.music.utils; + +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.apache.commons.lang3.StringUtils; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * okhttp连接池单例 + * + * @author developer + **/ +@Slf4j +public class OkHttpUtil { + + private OkHttpUtil() { + } + + public static OkHttpClient getInstance() { + return Singleton.INSTANCE.getInstance(); + } + + private enum Singleton { + /** + * + */ + INSTANCE; + private final OkHttpClient singleton; + + Singleton() { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.connectTimeout(10L, TimeUnit.SECONDS); + builder.readTimeout(60L, TimeUnit.SECONDS); + builder.writeTimeout(60L, TimeUnit.SECONDS); + ConnectionPool connectionPool = new ConnectionPool(50, 60, TimeUnit.SECONDS); + builder.connectionPool(connectionPool); + singleton = builder.build(); + } + + public OkHttpClient getInstance() { + return singleton; + } + } + + + public static T post(String url, Map params, TypeReference type) { + try { + FormBody.Builder builder = new FormBody.Builder(); + params.forEach(builder::add); + RequestBody body = builder.build(); + Request request = new Request.Builder().post(body).url(url).build(); + Response response = OkHttpUtil.getInstance().newCall(request).execute(); + if (response.isSuccessful()) { + String content = Objects.requireNonNull(response.body()).string(); + if (StringUtils.isNotBlank(content)) { + return JacksonUtil.fromJson(content, type); + } + } else { + log.error("postRequest fail ,url:{}, params:{}, res:{}", url, params, response); + } + } catch (Exception e) { + log.error(">>>>>>>>>>request error:{}<<<<<<<<<<", e.getMessage(), e); + } + return null; + } + + public static T post(String url, Map headerMap, Map params, TypeReference type) { + try { + Headers.Builder headerBuilder = new Headers.Builder(); + if (Objects.nonNull(headerMap) && !headerMap.isEmpty()) { + headerMap.forEach(headerBuilder::add); + } + RequestBody body = RequestBody.create(JacksonUtil.toJson(params), MediaType.parse("application/json; charset=utf-8")); + Request request = new Request.Builder().post(body).headers(headerBuilder.build()).url(url).build(); + Response response = OkHttpUtil.getInstance().newCall(request).execute(); + log.info("postJSONRequestReq:{},Res:{}, ", JacksonUtil.toJson(params), JacksonUtil.toJson(response)); + if (response.isSuccessful()) { + String content = Objects.requireNonNull(response.body()).string(); + if (StringUtils.isNotBlank(content)) { + return JacksonUtil.fromJson(content, type); + } + } else { + log.error("postJSONRequest fail ,url:{}, params:{}, res:{}", url, params, response); + } + } catch (Exception e) { + log.error(">>>>>>>>>>request error:{}<<<<<<<<<<", e.getMessage(), e); + } + return null; + } + + + public static T get(String url, Map headerMap, Map params, TypeReference type) { + try { + StringBuilder urlBuilder = new StringBuilder(); + urlBuilder.append(url); + if (Objects.nonNull(params) && !params.isEmpty()) { + urlBuilder.append("?"); + for (Map.Entry m : params.entrySet()) { + urlBuilder.append(m.getKey()).append("="); + urlBuilder.append(m.getValue()).append("&"); + } + } + Headers.Builder headerBuilder = new Headers.Builder(); + if (Objects.nonNull(headerMap) && !headerMap.isEmpty()) { + headerMap.forEach(headerBuilder::add); + } + Request request = new Request.Builder().get().url(urlBuilder.toString()).headers(headerBuilder.build()).build(); + Response response = OkHttpUtil.getInstance().newCall(request).execute(); + if (response.isSuccessful()) { + String content = Objects.requireNonNull(response.body()).string(); + if (StringUtils.isNotBlank(content)) { + return JacksonUtil.fromJson(content, type); + } + } else { + log.error("getRequest fail ,url:{}, params:{}, res:{}", url, params, response); + } + } catch (Exception e) { + log.error(">>>>>>>>>>request error:{}<<<<<<<<<<", e.getMessage(), e); + } + return null; + } + +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..0a65245 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,49 @@ + +server: + port: 8105 + +spring: + profiles: + active: ${SPRING_ACTIVE_PROFILE:local} + application: + name: baogutang-music + datasource: + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://117.72.78.133:3306/baogutang-music?characterEncoding=UTF-8 + username: baogutang-music + password: xCcFfY3zXh8sC28e + hikari: + max-lifetime: 60000 + redis: + host: 117.72.78.133 + port: 6379 + password: admin@pwdpwd + servlet: + multipart: + max-file-size: 2048MB + max-request-size: 32MB + resources: + static-locations: classpath:/templates/ + +thread: + pool: + core: 10 + max: 10 + alive: 10 + capacity: 200 + + + +knife4j: + enable: true + + +baogutang: + net-ease-music: + query-base-url: http://117.72.78.133:5173/search?keywords=%s&limit=%d&offset=%d&type=%d + playlist-base-url: http://117.72.78.133:5173/playlist/track/all?id=%d + album-base-url: http://117.72.78.133:5173/album?id=%d + artist-base-url: http://117.72.78.133:5173/artists?id=%d + download-base-url: https://api.sooooooooooooooooootheby.top/Netease_url/Song_V1?level=jymaster&type=json&ids=%d + download-path: /baogutang-music/download/music + diff --git a/src/main/resources/templates/NIKO.png b/src/main/resources/templates/NIKO.png new file mode 100644 index 0000000..875dd78 Binary files /dev/null and b/src/main/resources/templates/NIKO.png differ diff --git a/src/main/resources/templates/music.html b/src/main/resources/templates/music.html new file mode 100644 index 0000000..6cad104 --- /dev/null +++ b/src/main/resources/templates/music.html @@ -0,0 +1,779 @@ + + + + + + + BAOGUTANG-MUSIC + + + +
+

BAOGUTANG-MUSIC

+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + + +
+ + +
+

下载进度:0%

+ +
+
+ + + +