初始化

master
tangwenkang 2023-11-06 19:03:35 +08:00
commit f27d8cdb7d
32 changed files with 2301 additions and 0 deletions

39
.gitignore vendored 100644
View File

@ -0,0 +1,39 @@
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
logs
.idea
### 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

18
Dockerfile 100644
View File

@ -0,0 +1,18 @@
#起始镜像
FROM anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/openjdk:17-8.6
#暴露端口号
EXPOSE 10010
#挂载目录的位置
VOLUME /home/logs/health-video
#构建复制外部文件到docker
COPY health-video-server/target/health-video-server.jar /home/app.jar
#工作目录 exec -it 进入容器内部后的默认的起始目录
WORKDIR /home
ENV TIME_ZONE Asia/Shanghai
#指定东八区
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
#启动java 程序
ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-jar","/home/app.jar"]

View File

@ -0,0 +1,49 @@
<?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>com.health</groupId>
<artifactId>health-video</artifactId>
<version>3.6.3</version>
</parent>
<version>3.6.3</version>
<artifactId>health-video-common</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.health</groupId>
<artifactId>health-common-core</artifactId>
</dependency>
<dependency>
<groupId>com.health</groupId>
<artifactId>health-common-security</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<repositories>
<repository>
<id>dragon-public</id>
<name>dragon-maven</name>
<url>http://10.100.1.7:8081/repository/maven-public/</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>dragon-release</id>
<name>dragon-releases</name>
<url>http://10.100.1.7:8081/repository/maven-releases/</url>
</repository>
</distributionManagement>
</project>

View File

@ -0,0 +1,37 @@
package com.health.video.common.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* @author Wenkang Tang
* @date 2023/10/20 21:22
*/
@Data
public class Comments {
/**
*
*/
private Integer commentsId;
/**
*
*/
private String commentContent;
/**
* ID
*/
private Integer commentsUserId;
/**
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date createTime;
/**
*
*/
private Integer videoId;
}

View File

@ -0,0 +1,54 @@
package com.health.video.common.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author Wenkang Tang
* @date 2023/10/20 21:17
*/
@Data
public class Video {
/**
*
*/
private Integer videoId;
/**
*
*/
private String title;
/**
*
*/
private String details;
/**
*
*/
private String fullVideo;
/**
*
*/
private Integer purchaseNumber;
/**
*
*/
private Integer videoTypeId;
/**
*
*/
private Integer price;
/**
*
*/
private Integer founder;
/**
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date releaseTime;
}

View File

@ -0,0 +1,41 @@
package com.health.video.common.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* @author Wenkang Tang
* @date 2023/10/24 20:03
*/
@Data
public class VideoBuy {
/**
*
*/
private Integer videoBuyId;
/**
*
*/
private Integer videoId;
/**
*
*/
private Integer buyUserId;
/**
* 1-
*/
private Integer isBuy;
/**
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date buyTime;
/**
* 1- 2-
*/
private Integer isDeleteBuy;
}

View File

@ -0,0 +1,41 @@
package com.health.video.common.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* @author Wenkang Tang
* @date 2023/10/24 20:05
*/
@Data
public class VideoCollection {
/**
*
*/
private Integer videoCollectionId;
/**
*
*/
private Integer videoId;
/**
*
*/
private Integer collectionUserId;
/**
* 1-
*/
private Integer isCollection;
/**
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date collectTime;
/**
* 1- 2-
*/
private Integer isDeleteCollection;
}

View File

@ -0,0 +1,19 @@
package com.health.video.common.domain;
import lombok.Data;
/**
* @author Wenkang Tang
* @date 2023/10/20 21:18
*/
@Data
public class VideoType {
/**
*
*/
private Integer videoTypeId;
/**
*
*/
private String videoTypeName;
}

View File

@ -0,0 +1,20 @@
package com.health.video.common.domain.request;
import lombok.Data;
/**
* @author Wenkang Tang
* @date 2023/10/21 11:25
*
*/
@Data
public class RequestInsertComments {
/**
*
*/
private Integer videoId;
/**
*
*/
private String commentContent;
}

View File

@ -0,0 +1,51 @@
package com.health.video.common.domain.request;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author Wenkang Tang
* @date 2023/10/31 13:27
*
*/
@Data
public class RequestPostVideos {
/**
*
*/
private String title;
/**
*
*/
private String details;
/**
*
*/
private String fullVideo;
/**
*
*/
private Integer purchaseNumber;
/**
*
*/
private Integer videoTypeId;
/**
*
*/
private BigDecimal price;
/**
*
*/
private Integer founder;
/**
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date releaseTime;
}

View File

@ -0,0 +1,103 @@
package com.health.video.common.domain.response;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author Wenkang Tang
* @date 2023/10/20 21:45
*
*/
@Data
public class ResponseVideo {
/**
*
*/
private Integer videoId;
/**
*
*/
private String title;
/**
*
*/
private String details;
/**
*
*/
private String fullVideo;
/**
*
*/
private Integer purchaseNumber;
/**
*
*/
private Integer videoTypeId;
/**
*
*/
private String videoTypeName;
/**
*
*/
private BigDecimal price;
/**
*
*/
private Integer videoBuyId;
/**
* 1-
*/
private Integer isBuy;
/**
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date buyTime;
/**
* 1- 2-
*/
private Integer isDeleteBuy;
/**
*
*/
private Integer videoCollectionId;
/**
* 1-
*/
private Integer isCollection;
/**
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date collectTime;
/**
* 1- 2-
*/
private Integer isDeleteCollection;
/**
*
*/
private Integer founder;
/**
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date releaseTime;
/**
*
*/
private Integer collectionUserId;
/**
*
*/
private Integer buyUserId;
}

View File

@ -0,0 +1,20 @@
<?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>com.health</groupId>
<artifactId>health-video</artifactId>
<version>3.6.3</version>
</parent>
<version>3.6.3</version>
<artifactId>health-video-remote</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@ -0,0 +1,197 @@
<?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>com.health</groupId>
<artifactId>health-video</artifactId>
<version>3.6.3</version>
</parent>
<version>3.6.3</version>
<artifactId>health-video-server</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.health</groupId>
<artifactId>health-video-common</artifactId>
<version>3.6.3</version>
</dependency>
<dependency>
<groupId>com.health</groupId>
<artifactId>health-wallet-common</artifactId>
<version>3.6.5</version>
</dependency>
<dependency>
<groupId>com.health</groupId>
<artifactId>health-wallet-remote</artifactId>
<version>3.6.5</version>
</dependency>
<dependency>
<groupId>com.health</groupId>
<artifactId>health-common-security</artifactId>
<version>3.6.3</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.16</version>
</dependency>
<dependency>
<groupId>com.health</groupId>
<artifactId>base-file-remote</artifactId>
</dependency>
<dependency>
<groupId>com.health</groupId>
<artifactId>base-system-common</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- SpringBoot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Swagger UI -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.fox.version}</version>
</dependency>
<!-- Mysql Connector -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- RuoYi Common DataSource -->
<dependency>
<groupId>com.health</groupId>
<artifactId>health-common-datasource</artifactId>
</dependency>
<!-- RuoYi Common DataScope -->
<dependency>
<groupId>com.health</groupId>
<artifactId>health-common-datascope</artifactId>
</dependency>
<dependency>
<groupId>com.health</groupId>
<artifactId>health-common-security</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-core</artifactId>
<version>1.11.813</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.18.30</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.813</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.0</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>dragon-public</id>
<name>dragon-maven</name>
<url>http://10.100.1.7:8081/repository/maven-public/</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>dragon-release</id>
<name>dragon-releases</name>
<url>http://10.100.1.7:8081/repository/maven-releases/</url>
</repository>
</distributionManagement>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!--当deploy时候忽略此model-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,18 @@
package com.health.video.server;
import com.health.common.security.annotation.EnableRyFeignClients;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Wenkang Tang
* @date 2023/10/20 20:01
*/
@SpringBootApplication
@EnableRyFeignClients
public class VideoApplication {
public static void main(String[] args) {
SpringApplication.run(VideoApplication.class);
}
}

View File

@ -0,0 +1,50 @@
package com.health.video.server.config;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
*
*
* @author
*/
@Component
public class ConfirmCallbackConfig implements RabbitTemplate.ConfirmCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* @PostContructspringspring
* @PostConstruct bean
* @PreDestory bean
*/
@PostConstruct
public void init() {
rabbitTemplate.setConfirmCallback(this);
}
/**
*
*
* @param correlationData
* @param ack
* @param cause
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
// 消息投递到 broker 的状态true表示成功
System.out.println("消息发送成功!");
} else {
// 发送异常
System.out.println("发送异常原因 = " + cause);
// TODO 可以将消息 内容 以及 失败的原因 记录到 日志表中
}
}
}

View File

@ -0,0 +1,60 @@
package com.health.video.server.config;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* RabbitAdminRabbitMQJavaRabbitMQRabbitMQ
*
* @author
*/
@Configuration
public class RabbitAdminConfig {
@Value("${spring.rabbitmq.host}")
private String host;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Value("${spring.rabbitmq.virtualhost}")
private String virtualhost;
/**
* RabbitMQ
*
* @return
*/
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(host);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualhost);//虚拟主机
// 配置发送确认回调时次配置必须配置否则即使在RabbitTemplate配置了ConfirmCallback也不会生效
connectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);//开启confirm机制 消息到达MQ的确认机制
connectionFactory.setPublisherReturns(true);//开启消息返回机制 消息无法送到Exchange时返回到生产者
return connectionFactory;
}
/**
* RabbitAdmin
*
* @param connectionFactory
* @return
*/
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
}

View File

@ -0,0 +1,18 @@
package com.health.video.server.config;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author
*/
@Configuration
public class RabbitmqConfig {
// 消息转换配置
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}

View File

@ -0,0 +1,29 @@
package com.health.video.server.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
* @author
*/
@Configuration
@Component
public class RedissonConfig {
@Bean(destroyMethod = "shutdown") // 服务停止后调用 shutdown 方法。
public RedissonClient redisson() {
System.out.println("Redisson配置类初始加载......");
// 1.创建配置
Config config = new Config();
// 集群模式
// config.useClusterServers().addNodeAddress("127.0.0.1:6379", "127.0.0.1:6378");
// 2.根据 Config 创建出 RedissonClient 实例。
config.useSingleServer().setAddress("redis://10.100.1.2:6379");
return Redisson.create(config);
}
}

View File

@ -0,0 +1,41 @@
package com.health.video.server.config;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
*
*
* @author
*/
@Component
public class ReturnCallbackConfig implements RabbitTemplate.ReturnsCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* @PostContructspringspring
*/
@PostConstruct
public void init() {
rabbitTemplate.setReturnsCallback(this);
}
/**
*
*
* @param returnedMessage the returned message and metadata.
*/
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("消息" + returnedMessage.getMessage().toString() + "被交换机" + returnedMessage.getExchange() + "回退!"
+ "退回原因为:" + returnedMessage.getReplyText());
// TODO 回退了所有的信息,可做补偿机制 记录日志
}
}

View File

@ -0,0 +1,151 @@
package com.health.video.server.controller;
import com.health.common.core.domain.Result;
import com.health.video.common.domain.Comments;
import com.health.video.common.domain.VideoType;
import com.health.video.common.domain.request.RequestInsertComments;
import com.health.video.common.domain.request.RequestPostVideos;
import com.health.video.common.domain.response.ResponseVideo;
import com.health.video.server.service.VideoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
/**
* @author Wenkang Tang
* @date 2023/10/20 20:02
*/
@RestController
@RequestMapping("/video")
public class VideoController {
@Autowired
private VideoService videoService;
/**
*
*
* @return
*/
@GetMapping("/listVideoType")
public Result<List<VideoType>> listVideoType() {
List<VideoType> videoTypes = videoService.listVideoType();
return Result.success(videoTypes, "操作成功!");
}
/**
*
*
* @param videoTypeId
* @return
*/
@PostMapping("/listVideo/{videoTypeId}")
public Result<List<ResponseVideo>> listVideo(@PathVariable Integer videoTypeId) {
List<ResponseVideo> responseVideos = videoService.listVideo(videoTypeId);
return Result.success(responseVideos, "操作成功!");
}
/**
*
*
* @param requestInsertComments
*/
@PostMapping("/insertComments")
public Result insertComments(@RequestBody RequestInsertComments requestInsertComments) {
videoService.insertComments(requestInsertComments);
return Result.success(null, "操作成功!");
}
/**
*
*
* @param videoId
*/
@PostMapping("/insertVideoCollection/{videoId}")
public Result insertVideoCollection(@PathVariable Integer videoId) {
videoService.insertVideoCollection(videoId);
return Result.success(null, "操作成功!");
}
/**
*
*
* @param videoId
*/
@PostMapping("/insertVideoBuy/{videoId}")
public void insertVideoBuy(@PathVariable Integer videoId) {
videoService.insertVideoBuy(videoId);
}
/**
*
*
* @param collectionUserId
* @return
*/
@PostMapping("/listVideoCollection/{collectionUserId}")
public Result<List<ResponseVideo>> listVideoCollection(@PathVariable Integer collectionUserId) {
List<ResponseVideo> responseVideos = videoService.listVideoCollection(collectionUserId);
return Result.success(responseVideos, "操作成功!");
}
/**
*
*
* @param videoCollectionId
*/
@PostMapping("/deleteVideoCollection/{videoCollectionId}")
public Result deleteVideoCollection(@PathVariable Integer videoCollectionId) {
videoService.deleteVideoCollection(videoCollectionId);
return Result.success(null, "操作成功!");
}
/**
*
*
* @param buyUserId
* @return
*/
@PostMapping("/listVideoBuy/{buyUserId}")
public Result<List<ResponseVideo>> listVideoBuy(@PathVariable Integer buyUserId) {
List<ResponseVideo> responseVideos = videoService.listVideoBuy(buyUserId);
return Result.success(responseVideos, "操作成功!");
}
/**
*
*
* @param file
* @return
*/
@PostMapping("/uploadVideo")
public Result uploadVideo(@RequestParam MultipartFile file) throws IOException, InterruptedException {
return videoService.uploadVideo(file);
}
/**
*
*
* @param requestPostVideos
*/
@PostMapping("/PostVideos")
public Result postVideos(@RequestBody RequestPostVideos requestPostVideos) {
return videoService.postVideos(requestPostVideos);
}
/**
*
*
* @param videoId
* @return
*/
@PostMapping("/listComments/{videoId}")
public Result<List<Comments>> listComments(@PathVariable Integer videoId) {
List<Comments> commentsList = videoService.listComments(videoId);
return Result.success(commentsList, "操作成功!");
}
}

View File

@ -0,0 +1,111 @@
package com.health.video.server.mapper;
import com.health.video.common.domain.*;
import com.health.video.common.domain.request.RequestPostVideos;
import com.health.video.common.domain.response.ResponseVideo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author Wenkang Tang
* @date 2023/10/20 20:03
*/
@Mapper
public interface VideoMapper {
/**
*
*
* @return
*/
List<VideoType> listVideoType();
/**
*
*
* @return
*/
List<ResponseVideo> listVideo(@Param("videoTypeId") Integer videoTypeId);
/**
*
*
* @param comments
*/
void insertComments(Comments comments);
/**
* videoId
*
* @param videoId
*/
Video getVideoByVideoId(@Param("videoId") Integer videoId);
/**
*
*
* @param videoCollection
*/
void insertVideoCollection(VideoCollection videoCollection);
/**
*
*
* @param videoBuy
*/
void insertVideoBuy(VideoBuy videoBuy);
/**
*
*
* @param collectionUserId
* @return
*/
List<ResponseVideo> listVideoCollection(@Param("collectionUserId") Integer collectionUserId);
/**
*
*
* @param videoCollection
*/
void deleteVideoCollection(VideoCollection videoCollection);
/**
*
*
* @param buyUserId
* @return
*/
List<ResponseVideo> listVideoBuy(@Param("buyUserId") Integer buyUserId);
/**
*
*
* @param requestPostVideos
*/
void postVideos(RequestPostVideos requestPostVideos);
/**
*
*
* @param videoId
* @return
*/
List<Comments> listComments(@Param("videoId") Integer videoId);
/**
*
*
* @param videoId
*/
void updateVideo(@Param("videoId") Integer videoId);
/**
*
* @param videoId
* @return
*/
Video getVideoPrice(@Param("videoId") Integer videoId);
}

View File

@ -0,0 +1,102 @@
package com.health.video.server.service;
import com.health.common.core.domain.Result;
import com.health.video.common.domain.Comments;
import com.health.video.common.domain.VideoType;
import com.health.video.common.domain.request.RequestInsertComments;
import com.health.video.common.domain.request.RequestPostVideos;
import com.health.video.common.domain.response.ResponseVideo;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
/**
* @author Wenkang Tang
* @date 2023/10/20 20:03
*/
public interface VideoService {
/**
*
*
* @return
*/
List<VideoType> listVideoType();
/**
*
*
* @param videoTypeId
* @return
*/
List<ResponseVideo> listVideo(Integer videoTypeId);
/**
*
*
* @param requestInsertComments
*/
void insertComments(RequestInsertComments requestInsertComments);
/**
*
*
* @param videoId
*/
void insertVideoCollection(Integer videoId);
/**
*
*
* @param videoId
*/
void insertVideoBuy(Integer videoId);
/**
*
*
* @param collectionUserId
* @return
*/
List<ResponseVideo> listVideoCollection(Integer collectionUserId);
/**
*
*
* @param videoCollectionId
*/
void deleteVideoCollection(Integer videoCollectionId);
/**
*
*
* @param buyUserId
* @return
*/
List<ResponseVideo> listVideoBuy(Integer buyUserId);
/**
*
*
* @param file
* @return
*/
Result uploadVideo(MultipartFile file) throws InterruptedException, IOException;
/**
*
*
* @param requestPostVideos
* @return
*/
Result postVideos(RequestPostVideos requestPostVideos);
/**
*
*
* @param videoId
* @return
*/
List<Comments> listComments(Integer videoId);
}

View File

@ -0,0 +1,48 @@
package com.health.video.server.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.health.video.common.domain.VideoBuy;
import com.health.video.server.mapper.VideoMapper;
import com.rabbitmq.client.Channel;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
/**
* @author Wenkang Tang
* @date 2023/10/24 10:51
*/
@Log4j2
@Service
public class InsertUserVideoQueue {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private VideoMapper videoMapper;
@RabbitListener(queuesToDeclare = {@Queue("insert_video_buy")})
public void insertUserVideo(String msg, Message message, Channel channel) {
try {
log.info("消费者接收到消息,消息是:{}", msg);
String messageId = message.getMessageProperties().getMessageId();
Long insertUserVideo = redisTemplate.opsForSet().add("insert_video_buy", messageId);//消息不重复
if (null != insertUserVideo && 0 < insertUserVideo) {
VideoBuy videoBuy = JSONObject.parseObject(msg, VideoBuy.class);
videoMapper.insertVideoBuy(videoBuy);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);//消息成功消费后发送消息给生产者
log.info("消费者成功消费消息,消息是:{}", msg);
}
} catch (Exception e) {
log.error("消费者消费消息异常,消息内容是:{},异常信息是:{}", msg, e);
try {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);//消费失败 消息回退到队列 ,其他消费者继续消费
} catch (Exception ex) {
log.error("消费者回退消息异常,消息内容是:{},异常信息是:{}", msg, ex);
}
}
}
}

View File

@ -0,0 +1,334 @@
package com.health.video.server.service.impl;
import com.health.common.core.constant.SecurityConstants;
import com.health.wallet.common.domain.req.MoneyChangeRecordReq;
import com.alibaba.fastjson.JSONObject;
import com.amazonaws.AmazonClientException;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicSessionCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.event.ProgressEventType;
import com.amazonaws.event.ProgressListener;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.TransferManagerBuilder;
import com.amazonaws.services.s3.transfer.Upload;
import com.health.common.core.domain.Result;
import com.health.common.security.utils.SecurityUtils;
import com.health.video.common.domain.*;
import com.health.video.common.domain.request.RequestInsertComments;
import com.health.video.common.domain.request.RequestPostVideos;
import com.health.video.common.domain.response.ResponseVideo;
import com.health.video.server.mapper.VideoMapper;
import com.health.video.server.service.VideoService;
import com.health.wallet.remote.RemoteWalletService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import com.health.video.server.utils.DogeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
* @author Wenkang Tang
* @date 2023/10/20 20:03
*/
@Service
@SuppressWarnings("ALL")
public class VideoServiceImpl implements VideoService {
@Autowired
private VideoMapper videoMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private RedissonClient redissonClient;
@Autowired
private RemoteWalletService remoteWalletService;
/**
*
*
* @return
*/
@Override
public List<VideoType> listVideoType() {
return videoMapper.listVideoType();
}
/**
*
*
* @return
*/
@Override
public List<ResponseVideo> listVideo(Integer videoTypeId) {
return videoMapper.listVideo(videoTypeId);
}
/**
*
*
* @param requestInsertComments
*/
@Override
public void insertComments(RequestInsertComments requestInsertComments) {
Comments comments = new Comments();//创建Comments对象
//赋值
comments.setCommentsUserId(Math.toIntExact(SecurityUtils.getUserId()));
comments.setCommentContent(requestInsertComments.getCommentContent());
comments.setVideoId(requestInsertComments.getVideoId());
videoMapper.insertComments(comments);
}
/**
*
*
* @param videoId
*/
@Override
public void insertVideoCollection(Integer videoId) {
VideoCollection videoCollection = new VideoCollection();//创建VideoCollection对象
videoCollection.setVideoId(videoId);
videoCollection.setCollectionUserId(Math.toIntExact(SecurityUtils.getUserId()));//获取当前登录人ID给用户ID赋值
videoCollection.setIsCollection(1);
videoCollection.setIsDeleteCollection(2);
videoMapper.insertVideoCollection(videoCollection);
}
/**
*
*
* @param videoId
*/
@Override
@Transactional
public void insertVideoBuy(Integer videoId) {
Video video = videoMapper.getVideoPrice(videoId);//查询视频价格
//创建分布式锁对象
RLock myLock = redissonClient.getLock("myLock");
try {
//加锁 最多等待5秒 10秒后自动释放
boolean tryLock = myLock.tryLock(5, 10, TimeUnit.SECONDS);
if (Boolean.TRUE.equals(tryLock)) { //获取锁成功
//创建VideoBuy对象
VideoBuy videoBuy = new VideoBuy();
videoBuy.setVideoId(videoId);
videoBuy.setIsBuy(1);
videoBuy.setIsDeleteBuy(2);
videoBuy.setBuyUserId(Math.toIntExact(SecurityUtils.getUserId()));
//rabbitMq发送消息
rabbitTemplate.convertAndSend("insert_video_buy", JSONObject.toJSONString(videoBuy), msg -> {
msg.getMessageProperties().setMessageId(UUID.randomUUID().toString().replaceAll("-", ""));
msg.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);//生产者开启消息持久化
return msg;
});
//修改用户余额
MoneyChangeRecordReq moneyChangeRecordReq = new MoneyChangeRecordReq();
moneyChangeRecordReq.setChangeAmount(video.getPrice());
moneyChangeRecordReq.setChangeOrigin("视频购买");
moneyChangeRecordReq.setChangeType(0);
moneyChangeRecordReq.setCreateUser(SecurityUtils.getUserId());
remoteWalletService.moneyChangeByOtherOperate(moneyChangeRecordReq, SecurityConstants.INNER);
//异步修改购买后的视频购买总人数
CompletableFuture.runAsync(()->{
videoMapper.updateVideo(videoId);
});
} else {//获取锁失败
throw new RuntimeException("等待时间过长,请重试!");
}
} catch (Exception e) {
throw new RuntimeException("异常内容:"+e);
} finally {
//释放锁
myLock.unlock();
}
}
/**
*
*
* @param collectionUserId
* @return
*/
@Override
public List<ResponseVideo> listVideoCollection(Integer collectionUserId) {
return videoMapper.listVideoCollection(collectionUserId);
}
/**
*
*
* @param videoCollectionId
*/
@Override
public void deleteVideoCollection(Integer videoCollectionId) {
VideoCollection videoCollection = new VideoCollection();//创建VideoCollection对象
videoCollection.setVideoCollectionId(videoCollectionId);
videoCollection.setIsCollection(2);
videoCollection.setIsDeleteCollection(1);
videoMapper.deleteVideoCollection(videoCollection);
}
/**
*
*
* @param buyUserId
* @return
*/
@Override
public List<ResponseVideo> listVideoBuy(Integer buyUserId) {
return videoMapper.listVideoBuy(buyUserId);
}
/**
*
*
* @param file
* @return
*/
@Override
public Result uploadVideo(MultipartFile file) {
// 初始化JSONObject对象
JSONObject vodConfig = new JSONObject();
// 获取文件名称
String originalFilename = file.getOriginalFilename();
int dotIndex = originalFilename.lastIndexOf(".");
String result = originalFilename.substring(0, dotIndex);
// 获取文件后缀,因此此后端代码可接收一切文件,上传格式前端限定
String fileExt = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1).toLowerCase();
// 重构文件名称
String newVideoName = UUID.randomUUID().toString().replaceAll("-", "") + "." + fileExt;// 新的文件名
vodConfig.put("filename", originalFilename + fileExt); // 此参数不重要,只要能保留文件后缀名如 a.mp4 即可
vodConfig.put("vn", newVideoName); // .. . 分组配置、回调参数
JSONObject body = new JSONObject();// 构建body对象
body.put("channel", "VOD_UPLOAD");
body.put("vodConfig", vodConfig);
JSONObject api = DogeUtil.dogeAPIGet("/auth/tmp_token.json", body);//获取临时密码和上传信息
JSONObject credentials = api.getJSONObject("Credentials"); // 包含本次上传视频使用的临时密钥,下面初始化 S3 实例时会用到
JSONObject uploadInfo = api.getJSONObject("VodUploadInfo"); // 包含本次上传视频的目标信息s3Bucket、s3Endpoint、key系统提供无需修改以及本次上传的唯一 IDdid ,下面上传时会用到
System.out.println(uploadInfo);
// 使用临时密钥信息初始化 BasicSessionCredentials 对象
BasicSessionCredentials awsCredentials = new BasicSessionCredentials(
credentials.getString("accessKeyId"),
credentials.getString("secretAccessKey"),
credentials.getString("sessionToken"));
// 使用上传信息中的S3配置信息和认证信息初始化 AmazonS3 对象
AmazonS3 s3 = AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(awsCredentials)).
withEndpointConfiguration(new AwsClientBuilder.
EndpointConfiguration(uploadInfo.getString("s3Endpoint"), "automatic")).build();
try {
// 利用 s3 初始化,并配置上传进度回调
TransferManager tm = TransferManagerBuilder
.standard()
.withS3Client(s3)
.build();
// 获取上传的文件名
String filename = file.getOriginalFilename();
// 设置本地存储路径
String localPath = "/tmp/tomcat.10010.12973406643068623510/work/Tomcat/localhost/ROOT";
// 拼接本地文件路径,使用了文件分隔符来保证路径的正确性
String filePath = localPath + File.separator + "D:\\uploadVideo" + File.separator + filename;
// 创建本地文件对象
File localFile = new File(filePath);
// 检查目录是否存在,如果不存在则创建
File localDirectory = localFile.getParentFile();
if (!localDirectory.exists()) {
localDirectory.mkdirs();
}
// 将上传的文件保存到本地文件
file.transferTo(localFile);
// 获取本地文件的大小
long fileSize = localFile.length();
System.out.println("开始分片上传");
// 指定上传的目标空间路径、目标文件名和本地文件
PutObjectRequest req = new PutObjectRequest(
uploadInfo.getString("s3Bucket"),
uploadInfo.getString("key"),
localFile);
// 目标空间路径,由上面获取到的 VodUploadInfo 指定,不可更改
// 设置各过程进度回调
req.setGeneralProgressListener(new ProgressListener() {
Long totalByteRead = 0L;
@Override
public void progressChanged(com.amazonaws.event.ProgressEvent progressEvent) {
if (progressEvent.getEventType() == ProgressEventType.REQUEST_BYTE_TRANSFER_EVENT && totalByteRead < fileSize) {
totalByteRead += progressEvent.getBytesTransferred();
System.out.println("传输中:" + totalByteRead + "/" + fileSize + " (" + ((totalByteRead * 100.0F / fileSize)) + "%)");
} else if (progressEvent.getEventType() == ProgressEventType.TRANSFER_COMPLETED_EVENT) {
System.out.println("传输完成");
} else if (progressEvent.getEventType() == ProgressEventType.TRANSFER_FAILED_EVENT) {
System.out.println("传输失败");
}
}
});
// TransferManager 的处理默认是异步的,下面的 upload 方法调用完成后不会等待
// 调用 tm.upload(req) 方法开始上传文件
Upload upload = tm.upload(req);
// 不过,你也可以使用 waitForCompletion 来等待上传完成
upload.waitForCompletion();
System.out.println("分片上传完成");
// 向多吉云汇报此次上传已完成并获得本次上传的视频 IDdid 参数来自上面获取临时密钥时获取到的 VodUploadInfo 。
JSONObject cbapi = DogeUtil.dogeAPIGet("/callback/upload.json?did=" + uploadInfo.getString("did"));
System.out.println(cbapi);
Integer vid = cbapi.getInteger("vid");
System.out.println("上传完成,视频 ID" + vid);
// 获取视频的详细信息
JSONObject msg = DogeUtil.dogeAPIGet("/video/info.json?vid=" + vid);
System.out.println("视频详细信息:" + msg);
return Result.success(msg.get("vcode"), "操作成功!");
} catch (AmazonClientException | InterruptedException | IOException e) {
throw new RuntimeException(e);
}
}
/**
*
*
* @param requestPostVideos
* @return
*/
@Override
public Result postVideos(RequestPostVideos requestPostVideos) {
Video video = new Video();// 创建Video对象
video.setFounder(Math.toIntExact(SecurityUtils.getUserId()));
//发布视频
videoMapper.postVideos(requestPostVideos);
return Result.success(null, "成功发布视频!");
}
/**
*
*
* @param videoId
* @return
*/
@Override
public List<Comments> listComments(Integer videoId) {
return videoMapper.listComments(videoId);
}
}

View File

@ -0,0 +1,77 @@
package com.health.video.server.utils;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@Component
public class DLXQueue {
// routingKey
private static final String DEAD_ROUTING_KEY = "dead.routingkey";
private static final String ROUTING_KEY = "routingkey";
private static final String DEAD_EXCHANGE = "dead.exchange";
private static final String EXCHANGE = "common.exchange";
@Autowired
RabbitTemplate rabbitTemplate;
@Resource
RabbitAdmin rabbitAdmin;
/**
*
*
* @param queueName
* @param deadQueueName
* @param params
* @param expiration
*/
public void sendDLXQueue(String queueName, String deadQueueName, Object params, Integer expiration) {
/**
* ----------------------------------ttl--------------------------------------------
*/
Map<String, Object> map = new HashMap<>();
// 队列设置存活时间单位ms, 必须是整形数据。
map.put("x-message-ttl", expiration);
// 设置死信交换机
map.put("x-dead-letter-exchange", DEAD_EXCHANGE);
// 设置死信交换器路由
map.put("x-dead-letter-routing-key", DEAD_ROUTING_KEY);
/*参数1队列名称 参数2持久化 参数3是否排他 参数4自动删除队列 参数5队列参数*/
Queue queue = new Queue(queueName, true, false, false, map);
rabbitAdmin.declareQueue(queue);
/**
* ------------------------------------------------------------------------------
*/
DirectExchange directExchange = new DirectExchange(EXCHANGE, true, false);
rabbitAdmin.declareExchange(directExchange);
/**
* ------------------------------------------------------------------------------
*/
Binding binding = BindingBuilder.bind(queue).to(directExchange).with(ROUTING_KEY);
rabbitAdmin.declareBinding(binding);
/**
* ------------------------------------------------------------------------------
*/
DirectExchange deadExchange = new DirectExchange(DEAD_EXCHANGE, true, false);
rabbitAdmin.declareExchange(deadExchange);
Queue deadQueue = new Queue(deadQueueName, true, false, false);
rabbitAdmin.declareQueue(deadQueue);
/**
* ------------------------------------------------------------------------------
*/
// 将队列和交换机绑定
Binding deadbinding = BindingBuilder.bind(deadQueue).to(deadExchange).with(DEAD_ROUTING_KEY);
rabbitAdmin.declareBinding(deadbinding);
// 发送消息
rabbitTemplate.convertAndSend(EXCHANGE, ROUTING_KEY, params);
}
}

View File

@ -0,0 +1,80 @@
package com.health.video.server.utils;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
*
* @author
*/
@Component
public class DelayedQueue {
// routingKey
private static final String DELAYED_ROUTING_KEY = "delayed.routingkey";
// 延迟队列交换机
private static final String DELAYED_EXCHANGE = "delayed.exchange";
@Autowired
RabbitTemplate rabbitTemplate;
@Resource
RabbitAdmin rabbitAdmin;
/**
*
*
* @param queueName
* @param params
* @param expiration
*/
public void sendDelayedQueue(String queueName, Object params, Integer expiration) {
// 先创建一个队列
Queue queue = new Queue(queueName);
rabbitAdmin.declareQueue(queue);
// 创建延迟队列交换机
CustomExchange customExchange = createCustomExchange();
rabbitAdmin.declareExchange(customExchange);
// 将队列和交换机绑定
Binding binding = BindingBuilder.bind(queue).to(customExchange).with(DELAYED_ROUTING_KEY).noargs();
rabbitAdmin.declareBinding(binding);
// 发送延迟消息
rabbitTemplate.convertAndSend(DELAYED_EXCHANGE, DELAYED_ROUTING_KEY, params, msg -> {
// 发送消息的时候 延迟时长
msg.getMessageProperties().setMessageId(UUID.randomUUID().toString().replaceAll("-", ""));
msg.getMessageProperties().setDelay(expiration);
return msg;
});
}
private CustomExchange createCustomExchange() {
Map<String, Object> arguments = new HashMap<>();
/**
*
* 1.
* 2.
* 3.
* 4.
* 5.
*/
arguments.put("x-delayed-type", "direct");
return new CustomExchange(DELAYED_EXCHANGE, "x-delayed-message", true, false, arguments);
}
}

View File

@ -0,0 +1,114 @@
package com.health.video.server.utils;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Hex;
import org.apache.http.impl.io.ChunkedOutputStream;
import org.apache.http.io.SessionOutputBuffer;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
/**
* @author Wenkang Tang
* @date 2023/10/27 20:33
*/
@Configuration
@Component
public class DogeUtil {
// 普通 API 请使用这个方法
public static JSONObject dogeAPIGet(String apiPath, Map<String, String> params) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> hm : params.entrySet()) {
try {
sb.append(URLEncoder.encode(hm.getKey(), String.valueOf(StandardCharsets.UTF_8))).append('=').append(URLEncoder.encode(hm.getValue(), String.valueOf(StandardCharsets.UTF_8))).append("&");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
String bodyText = sb.toString().replace("&$", "");
try {
return dogeAPIGet(apiPath, bodyText, false);
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
// 要求请求内容 Body 是一个 JSON 的 API请使用这个方法
public static JSONObject dogeAPIGet(String apiPath, JSONObject params) {
String bodyText = params.toString();
try {
return dogeAPIGet(apiPath, bodyText, true);
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
// 无参数 API
public static JSONObject dogeAPIGet(String apiPath) {
try {
return dogeAPIGet(apiPath, "", true);
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
public static JSONObject dogeAPIGet(String apiPath, String paramsText, Boolean jsonMode) throws IOException {
// 这里返回值类型是 JSONObject你也可以根据你的具体情况使用不同的 JSON 库并修改最下方 JSON 处理代码
// 这里替换为你的多吉云永久 AccessKey 和 SecretKey可在用户中心 - 密钥管理中查看
// 请勿在客户端暴露 AccessKey 和 SecretKey那样恶意用户将获得账号完全控制权
String accessKey = "02a4810cb73129c4";
String secretKey = "4d9a1342fcee91d96698ce9425d5a23a";
String signStr = apiPath + "\n" + paramsText;
String sign = "";
try {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(secretKey.getBytes(), "HmacSHA1"));
sign = new String(new Hex().encode(mac.doFinal(signStr.getBytes())), StandardCharsets.UTF_8); // 这里 Hex 来自 org.apache.commons.codec.binary.Hex
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e.getMessage());
}
String authorization = "TOKEN " + accessKey + ':' + sign;
URL u = new URL("https://api.dogecloud.com" + apiPath);
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", jsonMode ? "application/json" : "application/x-www-form-urlencoded");
conn.setRequestProperty("Authorization", authorization);
conn.setRequestProperty("Content-Length", String.valueOf(paramsText.length()));
OutputStream os = conn.getOutputStream();
os.write(paramsText.getBytes());
os.flush();
os.close();
StringBuilder retJSON = new StringBuilder();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
String readLine = "";
try (BufferedReader responseReader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
while ((readLine = responseReader.readLine()) != null) {
retJSON.append(readLine).append("\n");
}
}
JSONObject ret = JSONObject.parseObject(retJSON.toString());
if (ret.getInteger("code") != 200) {
System.err.println("{\"error\":\"API 返回错误:" + ret.getString("msg") + "\"}");
} else {
return ret.getJSONObject("data");
}
} else {
System.err.println("{\"error\":\"网络错误:" + conn.getResponseCode() + "\"}");
}
return null;
}
}

View File

@ -0,0 +1,66 @@
package com.health.video.server.utils;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* TTL
*
*/
@Component
public class TtlQueue {
// routingKey
private static final String TTL_KEY = "ttl.routingkey";
private static final String TTL_EXCHANGE = "ttl.exchange";
@Autowired
RabbitTemplate rabbitTemplate;
@Resource
RabbitAdmin rabbitAdmin;
/**
* TTL
*
* @param queueName
* @param params
* @param expiration
*/
public void sendTtlQueue(String queueName, Object params, Integer expiration) {
/**
* ----------------------------------ttl--------------------------------------------
*/
Map<String, Object> map = new HashMap<>();
// 队列设置存活时间单位ms,必须是整形数据。
map.put("x-message-ttl", expiration);
/*参数1队列名称 参数2持久化 参数3是否排他 参数4自动删除队列 参数5队列参数*/
Queue queue = new Queue(queueName, true, false, false, map);
rabbitAdmin.declareQueue(queue);
/**
* ------------------------------------------------------------------------------
*/
DirectExchange directExchange = new DirectExchange(TTL_EXCHANGE, true, false);
rabbitAdmin.declareExchange(directExchange);
/**
* ------------------------------------------------------------------------------
*/
// 将队列和交换机绑定
Binding binding = BindingBuilder.bind(queue).to(directExchange).with(TTL_KEY);
rabbitAdmin.declareBinding(binding);
// 发送消息
rabbitTemplate.convertAndSend(TTL_EXCHANGE, TTL_KEY, params);
}
}

View File

@ -0,0 +1,57 @@
# Tomcat
server:
port: 10010
# Spring
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
servlet:
multipart:
max-file-size: 50MB
max-request-size: 300MB
application:
# 应用名称
name: health-video
profiles:
# 环境配置
active: dev
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 10.100.1.5:8848
config:
# 配置中心地址
server-addr: 10.100.1.5:8848
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
rabbitmq:
host: 10.100.1.3
port: 5072
username: guest
password: guest
virtual-host: /
publisher-confirm-type: correlated #确认消息已发送到交换机(Exchange)
publisher-returns: true #确认消息已发送到队列(Queue)
listener:
simple:
prefetch: 1 # 每次只能获取一条,处理完成才能获取下一条
acknowledge-mode: manual # 设置消费端手动ack确认
retry:
enabled: true # 是否支持重试
template:
# 只要消息抵达Queue就会异步发送优先回调return firm
mandatory: true
redis:
host: 10.100.1.2
# 关闭健康检查
management:
health:
rabbit:
enabled: false

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="./logs/health-video" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.health" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
</configuration>

View File

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.health.video.server.mapper.VideoMapper">
<!-- 发送评论 -->
<insert id="insertComments">
INSERT INTO `health-video`.`t_comments` (`comments_id`, `comment_content`, `comments_user_id`, `create_time`,
`video_id`)
VALUES (0, #{commentContent}, #{commentsUserId}, NOW(), #{videoId});
</insert>
<!-- 收藏视频 -->
<insert id="insertVideoCollection">
INSERT INTO `health-video`.`t_video_collection` (`video_collection_id`, `video_id`, `collection_user_id`,
`is_collection`, `collect_time`, `is_delete_collection`)
VALUES (0, #{videoId}, #{collectionUserId}, #{isCollection}, NOW(), #{isDeleteCollection});
</insert>
<!-- 发布视频 -->
<insert id="postVideos" useGeneratedKeys="true" keyProperty="purchaseNumber">
INSERT INTO `health-video`.`t_video` (`video_id`, `title`, `details`, `full_video`, `purchase_number`,
`video_type_id`, `price`, `founder`, `release_time`)
VALUES (0, #{title}, #{details}, #{fullVideo}, 0, #{videoTypeId}, #{price}, #{founder}, NOW());
</insert>
<!-- 视频添加视频购买列表中 -->
<insert id="insertVideoBuy">
INSERT INTO `health-video`.`t_video_buy` (`video_buy_id`, `video_id`, `buy_user_id`, `is_buy`, `buy_time`,
`is_delete_buy`)
VALUES (0, #{videoId}, #{buyUserId}, #{isBuy}, now(), #{isDeleteBuy});
</insert>
<!-- 删除我收藏的视频 -->
<update id="deleteVideoCollection">
UPDATE `health-video`.`t_video_collection`
SET `is_collection` = 2,
`is_delete_collection` = 1
WHERE `video_collection_id` = #{videoCollectionId};
</update>
<!-- 修改购买后的视频购买总人数 -->
<update id="updateVideo">
UPDATE `health-video`.`t_video`
SET `purchase_number` = purchase_number + 1
WHERE `video_id` = #{videoId};
</update>
<!-- 查询视频类型列表 -->
<select id="listVideoType" resultType="com.health.video.common.domain.VideoType">
select *
from t_video_type
</select>
<!-- 视频列表 -->
<select id="listVideo" resultType="com.health.video.common.domain.response.ResponseVideo">
select
v.*,
vb.video_buy_id,
vb.is_buy,
vb.buy_time,
vb.is_delete_buy,
vb.buy_user_id,
vc.video_collection_id,
vc.is_collection,
vc.collect_time,
vc.is_delete_collection,
vc.collection_user_id,
vt.video_type_name
from t_video v
left join t_video_buy vb on vb.video_id = v.video_id
left join t_video_collection vc on vc.video_id = v.video_id
left join t_video_type vt on vt.video_type_id = v.video_type_id
<where>
<if test="null != videoTypeId">
and v.video_type_id = #{videoTypeId}
</if>
</where>
</select>
<!-- 根据videoId查询视频表 -->
<select id="getVideoByVideoId" resultType="com.health.video.common.domain.Video">
select *
from t_video
where video_id = #{videoId}
</select>
<!-- 查询我收藏的视频 -->
<select id="listVideoCollection" resultType="com.health.video.common.domain.response.ResponseVideo">
select v.*,
vc.video_collection_id,
vc.is_collection,
vc.collect_time,
vc.is_delete_collection,
vc.collection_user_id,
vt.video_type_name,
tvb.video_buy_id,
tvb.is_buy,
tvb.buy_time,
tvb.is_delete_buy
from t_video_collection vc
left join t_video v on v.video_id = vc.video_id
left join t_video_type vt on vt.video_type_id = v.video_type_id
left join t_video_buy tvb on tvb.video_id = v.video_id
where vc.collection_user_id = #{collectionUserId}
and vc.is_delete_collection = 2
and vc.is_collection = 1
</select>
<!-- 查询我购买的视频 -->
<select id="listVideoBuy" resultType="com.health.video.common.domain.response.ResponseVideo">
select tv.*,
tvb.video_buy_id,
tvb.buy_user_id,
tvb.is_buy,
tvb.buy_time,
tvb.is_delete_buy,
tvt.video_type_name
from t_video_buy tvb
left join t_video tv on tv.video_id = tvb.video_id
left join t_video_type tvt on tvt.video_type_id = tv.video_type_id
where tvb.buy_user_id = #{buyUserId}
and tvb.is_buy = 1
and tvb.is_delete_buy = 2
</select>
<!-- 查询评论列表 -->
<select id="listComments" resultType="com.health.video.common.domain.Comments">
select *
from t_comments
where video_id = #{videoId}
</select>
<!-- 查询视频价格 -->
<select id="getVideoPrice" resultType="com.health.video.common.domain.Video">
select * from t_video where video_id = #{videoId}
</select>
</mapper>

44
pom.xml 100644
View File

@ -0,0 +1,44 @@
<?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>com.health</groupId>
<artifactId>health-modules</artifactId>
<version>3.6.3</version>
</parent>
<artifactId>health-video</artifactId>
<version>3.6.3</version>
<packaging>pom</packaging>
<modules>
<module>health-video-common</module>
<module>health-video-remote</module>
<module>health-video-server</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>dragon-public</id>
<name>dragon-maven</name>
<url>http://10.100.1.7:8081/repository/maven-public/</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>dragon-release</id>
<name>dragon-releases</name>
<url>http://10.100.1.7:8081/repository/maven-releases/</url>
</repository>
</distributionManagement>
</project>