feat:把bt服务单独拿出来
parent
a4f2096c5d
commit
c3c8f11939
|
@ -0,0 +1,152 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.0</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>com.unisinsight</groupId>
|
||||
<artifactId>torrent-be</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>torrent-be</name>
|
||||
<description>bt服务</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<protobuf.version>3.23.4</protobuf.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot Web Starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Configuration Processor (可选) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.18.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.24</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>2.0.49</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.dampcake</groupId>
|
||||
<artifactId>bencode</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-boot-starter</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- Apache HttpClient 依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.14</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.turn</groupId>
|
||||
<artifactId>ttorrent-core</artifactId>
|
||||
<version>1.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>${protobuf.version}</version>
|
||||
</dependency>
|
||||
<!-- grpc server和spring-boot集成框架 -->
|
||||
<dependency>
|
||||
<groupId>net.devh</groupId>
|
||||
<artifactId>grpc-server-spring-boot-starter</artifactId>
|
||||
<version>2.14.0.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<extensions>
|
||||
<extension>
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
<version>1.6.2</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.xolstice.maven.plugins</groupId>
|
||||
<artifactId>protobuf-maven-plugin</artifactId>
|
||||
<version>0.6.1</version>
|
||||
<configuration>
|
||||
<protocArtifact>com.google.protobuf:protoc:3.19.2:exe:${os.detected.classifier}</protocArtifact>
|
||||
<pluginId>grpc-java</pluginId>
|
||||
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.50.0:exe:${os.detected.classifier}</pluginArtifact>
|
||||
|
||||
<!-- proto文件目录 -->
|
||||
<protoSourceRoot>${project.basedir}/src/main/java/com/unisinsight/project/grpc/proto</protoSourceRoot>
|
||||
<!-- 生成的Java文件目录,这里指定到这一级就可以咯,proto文件有基于package指定 -->
|
||||
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
|
||||
<clearOutputDirectory>false</clearOutputDirectory>
|
||||
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
<goal>compile-custom</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,15 @@
|
|||
package com.unisinsight.torrent;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 分片上传应用启动类
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.unisinsight.torrent.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.service.Contact;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
@Import(BeanValidatorPluginsConfiguration.class)
|
||||
@EnableWebMvc
|
||||
public class Knife4jConfig implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
public Docket defaultApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(apiInfo())
|
||||
.groupName("默认分组")
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage("com.unisinsight.torrent.controller")) // 修改为你的controller包路径
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
.title("种子服务API")
|
||||
.description("# 制作服务接口文档\n\n"
|
||||
+ "制作种子文件")
|
||||
.termsOfServiceUrl("http://localhost:8080/")
|
||||
.contact(new Contact("API开发者", "https://example.com", "developer@example.com"))
|
||||
.version("1.0.0")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
// 配置Swagger UI和WebJars资源的访问路径,使得这些静态资源可以通过特定的URL路径在Web应用中被访问
|
||||
registry.addResourceHandler("doc.html")
|
||||
.addResourceLocations("classpath:/META-INF/resources/");
|
||||
|
||||
registry.addResourceHandler("swagger-ui.html")
|
||||
.addResourceLocations("classpath:/META-INF/resources/");
|
||||
registry.addResourceHandler("/webjars/**")
|
||||
.addResourceLocations("classpath:/META-INF/resources/webjars/");
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.unisinsight.torrent.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;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
* @author: rdpnr_puzhi
|
||||
* @create: 2025/08/12
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
|
||||
factory.setConnectTimeout(600000);
|
||||
factory.setReadTimeout(600000);
|
||||
factory.setConnectionRequestTimeout(600000);
|
||||
return new RestTemplate(factory);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.unisinsight.torrent.config;
|
||||
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author : ch
|
||||
* @version : 1.0
|
||||
* @ClassName : ThreadConfig
|
||||
* @Description :
|
||||
* @DATE : Created in 16:22 2025/8/21
|
||||
* <pre> Copyright: Copyright(c) 2025 </pre>
|
||||
* <pre> Company : 紫光汇智信息技术有限公司 </pre>
|
||||
* Modification History:
|
||||
* Date Author Version Discription
|
||||
* --------------------------------------------------------------------------
|
||||
* 2025/08/21 ch 1.0 Why & What is modified: <修改原因描述> *
|
||||
*/
|
||||
@Configuration
|
||||
public class ThreadConfig {
|
||||
/**
|
||||
* 创建用于gRPC连接监听的线程池
|
||||
* 用于处理客户端连接/断开连接等事件
|
||||
*/
|
||||
@Bean("grpcConnectionListenerExecutor")
|
||||
public ExecutorService grpcConnectionListenerExecutor() {
|
||||
// 获取系统可用处理器核心数
|
||||
int corePoolSize =1;
|
||||
int maximumPoolSize = corePoolSize * 2;
|
||||
|
||||
// 设置线程空闲时间
|
||||
long keepAliveTime = 60L;
|
||||
|
||||
// 设置队列大小
|
||||
int queueCapacity = 1000;
|
||||
|
||||
// 创建线程池
|
||||
return new ThreadPoolExecutor(
|
||||
// 核心线程数
|
||||
corePoolSize,
|
||||
// 最大线程数
|
||||
maximumPoolSize,
|
||||
// 空闲线程存活时间
|
||||
keepAliveTime,
|
||||
// 时间单位
|
||||
TimeUnit.SECONDS,
|
||||
// 任务队列
|
||||
new LinkedBlockingQueue<>(queueCapacity),
|
||||
new ThreadFactory() {
|
||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable runable) {
|
||||
Thread thread = new Thread(runable, "grpc-connection-listener-" + threadNumber.getAndIncrement());
|
||||
thread.setDaemon(false); // 设置为非守护线程
|
||||
return thread;
|
||||
}
|
||||
},
|
||||
// 拒绝策略:由调用线程执行任务
|
||||
new ThreadPoolExecutor.CallerRunsPolicy()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.unisinsight.torrent.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
WebMvcConfigurer.super.addResourceHandlers(registry);
|
||||
registry.addResourceHandler("/api/vdi/file/down/**").addResourceLocations("file:/var/lib/vdi/test/");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.unisinsight.torrent.controller;
|
||||
|
||||
import com.unisinsight.torrent.util.BtTorrentUtils;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
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;
|
||||
|
||||
@RestController
|
||||
@Slf4j
|
||||
@RequestMapping("/vdi")
|
||||
@Api("种子服务")
|
||||
public class TestController {
|
||||
|
||||
@GetMapping("/start")
|
||||
@ApiOperation(value = "开始做种")
|
||||
public boolean start(@RequestParam("sourceFile") String sourceFile,
|
||||
@RequestParam("torrentFile") String torrentFile) {
|
||||
System.out.println("开始做种...");
|
||||
boolean seedResult = BtTorrentUtils.createAndSeed(sourceFile, torrentFile);
|
||||
System.out.println("做种结果: " + (seedResult ? "成功" : "失败"));
|
||||
return seedResult;
|
||||
}
|
||||
|
||||
@GetMapping("/stop")
|
||||
@ApiOperation(value = "停止做种")
|
||||
public boolean stop(@RequestParam("sourceFile") String sourceFile) {
|
||||
// 测试停止
|
||||
System.out.println("停止做种...");
|
||||
boolean stopResult = BtTorrentUtils.stopSeeding(sourceFile);
|
||||
System.out.println("停止结果: " + (stopResult ? "成功" : "失败"));
|
||||
return stopResult;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
package com.unisinsight.torrent.exception;
|
||||
|
||||
/**
|
||||
* @author zhangmengfei [yf_zhang.mengfei@unisinsight.com]
|
||||
* @description 通用错误码定义
|
||||
* @date 2020/8/10 19:53
|
||||
* @since 1.0
|
||||
*/
|
||||
public enum BaseErrorCode implements ErrorCode {
|
||||
INTERNAL_EXCEPTION("00001", "系统内部异常"),
|
||||
INIT_EXCEPTION("00002", "系统初始化异常"),
|
||||
UNKNOWN_ERROR("00003", "未知错误"),
|
||||
MEMORY_ALLOCA_ERROR("00004", "内存分配失败"),
|
||||
OUT_OF_MEMORY("00005", "内存溢出"),
|
||||
REQUEST_TIMEOUT("00006", "请求超时"),
|
||||
ILLEGAL_HANDLE("00007", "非法句柄"),
|
||||
ENVIRONMENT_GET_FAIL("00008", "环境变量获取失败"),
|
||||
FREQUENT_SYSTEM("00009", "系统繁忙"),
|
||||
ILLEGAL_DATASOURCE("00010", "数据源异常"),
|
||||
FREQUENT_OPERATION("00011", "当前操作频繁"),
|
||||
UNKNOWN_REQUEST("00012", "未知的请求源"),
|
||||
REQUEST_EXCEEDED("00013", "请求超过次数限制"),
|
||||
CONFIG_GET_ERROR("00014", "获取配置错误"),
|
||||
CONFIG_SET_ERROR("00015", "设置配置错误"),
|
||||
PROGRAM_NEED_RESTART("00016", "需要重启程序"),
|
||||
SYSTEM_NEED_RESTART("00017", "需要重启系统"),
|
||||
CHARACTERISTICS_NOT_SUPPORT("00018", "特性不支持"),
|
||||
VERIFY_FAILURE("00019", "验证失败"),
|
||||
CALL_FAILURE("00020", "调用失败"),
|
||||
MESSAGE_COMPONENT_EXCEPTION("00021", "消息组件异常"),
|
||||
INSUFFICIENT_STORAGE_CAPACITY("00022", "存储容量不足"),
|
||||
STORAGE_SERVICE_ERROR("00023", "存储服务错误"),
|
||||
NETWORK_ERROR("00024", "网络错误"),
|
||||
IP_ILLEGAL_ADDRESS("00025", "非法IP错误"),
|
||||
IP_FORBIDDEN("00026", "被禁止的IP"),
|
||||
IP_RESOLVE_DOMAIN("00027", "解析域名获取IP错误"),
|
||||
PORT_ERROR("00028", "端口错误"),
|
||||
API_DOMAIN_ERROR("00029", "域名错误"),
|
||||
IP_ACCESS_EXCEEDED("00030", "当前IP请求超过限制"),
|
||||
IP_NULL_ERROR("00031", "IP地址不能为空"),
|
||||
CONNECTION_FAILURE("00032", "连接失败"),
|
||||
DATA_SEND_FAILURE("00033", "数据发送失败"),
|
||||
DATA_RECE_FAILURE("00034", "数据接收失败"),
|
||||
HTTP_CLIENT_INIT_FAILURE("00035", "HTTP客户端初始化失败"),
|
||||
HTTP_REQUEST_FAILURE("00036", "HTTP请求失败"),
|
||||
HTTP_REQUEST_TIME_OUT("00037", "HTTP请求超时"),
|
||||
HTTP_ERROR_CODE_400("00038", "HTTP错误码400"),
|
||||
HTTP_ERROR_CODE_404("00039", "HTTP错误码404"),
|
||||
HTTP_ERROR_CODE_500("500", "HTTP错误码500"),
|
||||
KAFKA_TOPICE_INVALID("00041", "kafka的topic无效"),
|
||||
KAFKA_MESSAGE_SEND_FAILURE("00042", " kafka消息发送失败"),
|
||||
DATABASE_ERROR("00043", "数据库错误"),
|
||||
DATABASE_OPERATION_FAILED("00044", "数据库操作失败"),
|
||||
QUERIER_EMPTY_RESULT("00045", "查询无结果"),
|
||||
ID_REQUIRE("00046", "ID不能为空"),
|
||||
DATA_DUPLICATED("00047", "数据重复"),
|
||||
DATA_IN_UPDATING("00048", "数据更新中"),
|
||||
QUERY_RESULT_NOT_UNIQURE("00049", "查询结果不唯一"),
|
||||
DICTIONARY_GET_FAILED("00050", "字典获取失败"),
|
||||
INTERFACE_NOT_EXISTS("00051", "接口不存在"),
|
||||
PARAMS_CHK_ERROR("00052", "参数校验错误"),
|
||||
PARAMETERS_INVALID("00053", "无效入参"),
|
||||
NECESSARY_PARAMS_REQURIED("00054", "必填参数缺失,核对后重试"),
|
||||
TYPE_NOT_SUPPORTED("00055", "类型不支持"),
|
||||
NAME_REPEATED("00056", "名称重复"),
|
||||
PARAMETERS_EMPTY("00057", "参数为空"),
|
||||
LANGUAGE_NOT_SUPPORT("00058", "不支持该语言"),
|
||||
API_ABANDONED("00059", "接口停用"),
|
||||
API_MAINTENANCE("00060", "接口维护"),
|
||||
API_VER_ERROR("00061", "版本号错误"),
|
||||
VERIFICATION_CODE_WRONG("00062", "验证码错误"),
|
||||
FORMAT_NOT_EMPTY("00063", "%s不能为空"),
|
||||
MOBILE_FORMAT_ERROR("00064", "手机号码格式错误"),
|
||||
IDCARD_FORMAT_ERROR("00065", "身份证错误"),
|
||||
EMAIL_FORMAT_ERROR("00066", "邮箱错误"),
|
||||
LONGITUDE_LATITUDE_ERROR("00067", "经纬度错误"),
|
||||
REQUEST_PATH_ERROR("00068", "请求路径错误"),
|
||||
DATA_FORMAT_ERROR("00069", "数据格式错误"),
|
||||
TIME_PARAMS_ERROR("00070", "时间参数错误"),
|
||||
CAR_LICENSE_ERROR("00071", "车牌号错误"),
|
||||
CAMERA_NUMBER_ERROR("00072", "摄像头编号错误"),
|
||||
CALLBACK_ADDR_ERROR("00073", "回调地址URL错误"),
|
||||
TIME_FORMAT_ERROR("00074", "时间格式必须是ISO-8601"),
|
||||
PAGE_NUM_OVERLIMIT("00075", "页码超出限制"),
|
||||
PAGE_SIZE_OVERLIMIT("00076", "分页大小超出限制"),
|
||||
THRID_PARTY_INTERFACE_FAILURE("00077", "调用第三方接口失败"),
|
||||
JSON_PARSE_ERROR("00078", "json解析错误"),
|
||||
JSON_MESSAGE_FIELD_TYPE_WRONG("00079", "json消息字段类型错误"),
|
||||
JSON_MESSAGE_FIELD_VALUE_WRONG("00080", "json消息字段取值错误"),
|
||||
JSON_MESSAGE_FIELD_MISSING("00081", "json消息字段缺失"),
|
||||
CREATING_JSON_OBJECT_FAILED("00082", "创建json对象失败"),
|
||||
CREATING_JSON_ARRAY_FAILED("00083", "创建json数组失败"),
|
||||
JSON_FORMAT_CONTENT_WRONG("00084", "json格式或内容错误"),
|
||||
FILENAME_ERROR("00085", "文件名错误"),
|
||||
FILE_ADDRESS_ERROR("00086", "文件地址不存在"),
|
||||
FILE_ID_NOT_EXIST("00087", "文件ID不存在"),
|
||||
FILE_ID_LONG("00088", "文件ID过长"),
|
||||
FILE_TYPE_WRONG("00089", "文件类型错误"),
|
||||
FILE_CREATE_FAILED("00090", "创建文件失败"),
|
||||
FILE_OPEN_FAILED("00091", "打开文件失败"),
|
||||
FILE_DOWNLOAD_FAILED("00092", "下载文件失败"),
|
||||
FILE_READ_FAILED("00093", "读取文件失败"),
|
||||
FILE_WRITE_FAILED("00094", "写入文件失败"),
|
||||
FILE_FORMAT_ERROR("00095", "文件格式错误"),
|
||||
FILE_ENCRYPTED("00096", "文件被加密"),
|
||||
EXEL_GENERATE_FAILED("00097", "EXEL生成失败"),
|
||||
EXEL_IMPORT_FAILED("00098", "EXEL导入失败"),
|
||||
TEXT_ENCODING_CONVERSION_FAILED("00099", "文本编码转换失败"),
|
||||
FILE_NOT_RECOGNIZED("00100", "未识别文件"),
|
||||
FILE_SIZE_EXCEEDS_LIMIT("00101", "文件大小超过限制"),
|
||||
FILE_UPLOAD_FAILED("00102", "文件上传失败"),
|
||||
OTHER_USER_UPLOAD_FILE("00103", "其他用户正在上传该文件"),
|
||||
USER_NOT_FOUND("00104", "用户不存在"),
|
||||
USER_NOT_LOGIN("00105", "用户未登录"),
|
||||
USER_NOT_PERMISSON("00106", "用户没权限"),
|
||||
PASSWORD_ERROR("00107", "密码错误"),
|
||||
USER_NAME_EXIST("00108", "用户名已存在"),
|
||||
USER_NAME_WRONG_LENGTH("00109", "用户名长度错误"),
|
||||
PASSWORD_WRONG_LENGTH("00110", "密码长度错误"),
|
||||
USER_NAME_OR_PASSWORD_WRONG_FORMAT("00111", "用户名或密码格式错误"),
|
||||
USER_NAME_OR_PASSWORD_EMPTY("00112", "用户名或密码为空"),
|
||||
USER_LOGIN_EXPIRED("00113", "用户登录已过期"),
|
||||
SECURITY_AUTHENTICATION_FAILED("00114", "安全认证失败"),
|
||||
ACCOUNT_NUMBER_LOCKED("00115", "账号被锁定"),
|
||||
USER_ADD_FAILED("00116", "添加用户失败"),
|
||||
USER_DELETE_FAILED("00117", "删除用户失败"),
|
||||
USER_UPDATE_FAILED("00118", "修改用户失败"),
|
||||
USER_QUERY_FAILED("00119", "查询用户失败"),
|
||||
PASSWORD_UPDATE_FAILED("00120", "修改密码失败"),
|
||||
PASSWORD_NEW_WRONG("00121", "新密码错误"),
|
||||
PASSWORD_NEW_WRONG_LENGTH("00122", "新密码长度错误"),
|
||||
PASSWORD_OLD_WRONG_LENGTH("00123", "原密码长度错误"),
|
||||
PASSWORD_OLD_WRONG("00124", "原密码错误"),
|
||||
PASSWORD_OLD_NEW_SAME("00125", "新密码和原密码相同"),
|
||||
USER_NAME_OR_PASSWORD_INCORRECT("00126", "用户名或密码错误,需重新配置"),
|
||||
DEVICE_GET_FAILED("00127", "设备获取失败"),
|
||||
CHANNEL_GET_FAILED("00128", "通道获取失败"),
|
||||
DEVICE_OFFLINE("00129", "设备离线"),
|
||||
RESOURCES_TREE_GET_FAILED("00130", "资源树获取失败"),
|
||||
RESOURCES_EXIST("00131", "资源已经存在"),
|
||||
DEVICE_NOT_EXIST("00132", "设备不存在"),
|
||||
DEVICE_EXIST("00133", "设备已经存在"),
|
||||
DEVICE_NUMBER_REACHE_MAX("00134", "设备数达到上限"),
|
||||
CHANNEL_NUMBER_REACHE_MAX("00135", "设备数达到上限"),
|
||||
CAMERA_ID_WRONG("00136", "相机ID错误"),
|
||||
CAMERA_NAME_WRONG("00137", "相机名错误"),
|
||||
IPC_DEVICE_CODE_EXCEEDS_LONG("00138", "IPC设备编码超长"),
|
||||
IPC_DEVICE_NAME_EXCEEDS_LONG("00139", "IPC设备名称超长"),
|
||||
IPC_TYPE_GET_FAILED("00140", "获取IPC款型失败"),
|
||||
FORMAT_VALIDATE_ERROR("00141", "%s"),
|
||||
EXPORT_ERROR("00142", "导出EXCEL失败"),
|
||||
SUCCESS("200", "处理成功");
|
||||
|
||||
private String code;
|
||||
private String message;
|
||||
|
||||
BaseErrorCode(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.unisinsight.torrent.exception;
|
||||
|
||||
/**
|
||||
* @author zhangmengfei [yf_zhang.mengfei@unisinsight.com]
|
||||
* @description 错误码接口
|
||||
* @date 2020/8/10 19:53
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface ErrorCode {
|
||||
String getCode();
|
||||
|
||||
String getMessage();
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package com.unisinsight.torrent.exception;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author zhangmengfei [yf_zhang.mengfei@unisinsight.com]
|
||||
* @description 返回结果类
|
||||
* @date 2020/8/10 19:53
|
||||
* @since 1.0
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("返回结果类")
|
||||
public class Result<T> {
|
||||
|
||||
/**
|
||||
* 返回码
|
||||
*/
|
||||
@ApiModelProperty("返回码")
|
||||
@JsonProperty(value = "code")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 返回信息
|
||||
*/
|
||||
@ApiModelProperty("返回信息")
|
||||
@JsonProperty(value = "message")
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 返回数据
|
||||
*/
|
||||
@ApiModelProperty("返回数据")
|
||||
@JsonProperty(value = "data")
|
||||
private T data;
|
||||
|
||||
public Result() {
|
||||
}
|
||||
|
||||
public Result(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Result(String code, String message, T data) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static <T> Result<T> successResult(T data) {
|
||||
return new Result<>(BaseErrorCode.SUCCESS.getCode(), BaseErrorCode.SUCCESS.getMessage(), data);
|
||||
}
|
||||
|
||||
public static <T> Result<T> successResult() {
|
||||
return new Result<>(BaseErrorCode.SUCCESS.getCode(), BaseErrorCode.SUCCESS.getMessage());
|
||||
}
|
||||
|
||||
public static <T> Result<T> errorResult(ErrorCode errorCode) {
|
||||
return new Result<>(errorCode.getCode(), errorCode.getMessage());
|
||||
}
|
||||
|
||||
public static <T> Result<T> errorResult(ErrorCode errorCode, T data) {
|
||||
return new Result<>(errorCode.getCode(), errorCode.getMessage(), data);
|
||||
}
|
||||
|
||||
public static <T> Result<T> errorResultMessage(ErrorCode errorCode, String message) {
|
||||
return new Result<>(errorCode.getCode(), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前返回是否成功
|
||||
*/
|
||||
public boolean success() {
|
||||
return BaseErrorCode.SUCCESS.getCode().equals(this.code);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package com.unisinsight.torrent.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* 种子文件工具类
|
||||
*/
|
||||
public class BtTorrentUtils {
|
||||
|
||||
private static final String BT_SCRIPT_PATH = "/var/lib/vdi/nodejs/bttorrent.sh";
|
||||
|
||||
private static final Long WAIT_START_TIME = 8000L;
|
||||
|
||||
/**
|
||||
* 制作种子并开始做种
|
||||
*
|
||||
* @param sourceFile 源文件路径(如 /data/file.iso)
|
||||
* @param torrentFile 生成的种子文件路径(如 /data/file.torrent)
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean createAndSeed(String sourceFile, String torrentFile) {
|
||||
// 验证文件是否存在
|
||||
if (!new File(sourceFile).exists()) {
|
||||
System.err.println("源文件不存在: " + sourceFile);
|
||||
return false;
|
||||
}
|
||||
// 确保目标目录存在
|
||||
new File(torrentFile).getParentFile().mkdirs();
|
||||
return executeCommand("start", sourceFile, torrentFile);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 停止指定文件的做种
|
||||
*
|
||||
* @param sourceFile 源文件路径(如 /data/file.iso)
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean stopSeeding(String sourceFile) {
|
||||
return executeCommand("stop_path", sourceFile, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 执行 bttorrent.sh 脚本
|
||||
*/
|
||||
private static boolean executeCommand(String command, String arg1, String arg2) {
|
||||
try {
|
||||
// 构造命令
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
"bash",
|
||||
BT_SCRIPT_PATH,
|
||||
command,
|
||||
arg1,
|
||||
arg2 != null ? arg2 : "" // 处理可选参数
|
||||
).redirectErrorStream(true);
|
||||
// 启动进程
|
||||
Process process = pb.start();
|
||||
// 启动新线程读取输出,避免阻塞
|
||||
new Thread(() -> {
|
||||
try {
|
||||
logProcessOutput(process);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
// 对于start命令,只要进程启动成功就返回true
|
||||
if ("start".equals(command)) {
|
||||
// 稍微等待一下看初始输出是否有错误
|
||||
Thread.sleep(WAIT_START_TIME);
|
||||
return process.isAlive(); // 如果进程还在运行,认为启动成功
|
||||
}
|
||||
// 对于stop命令,仍然等待完成
|
||||
return process.waitFor() == 0;
|
||||
} catch (IOException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印进程输出
|
||||
*/
|
||||
private static void logProcessOutput(Process process) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
System.out.println("[BT输出] " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
# application.yml
|
||||
server:
|
||||
port: 8114
|
||||
|
||||
spring:
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 25MB
|
||||
max-request-size: 25MB
|
||||
|
||||
knife4j:
|
||||
production: false
|
||||
basic:
|
||||
enable: false
|
Loading…
Reference in New Issue