initial commit
This commit is contained in:
203
web-server/pom.xml
Normal file
203
web-server/pom.xml
Normal file
@@ -0,0 +1,203 @@
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>rainyhon-xzt</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>web-server</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>yelink-redis-starter</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>yelink-doc-starter</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>yelink-security-starter</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yelink.example</groupId>
|
||||
<artifactId>yelink-minio-starter</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
<version>1.19</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
<version>1.19</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!--打包jar-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<!--不打包资源文件,exclude的目录不是src下面的,是以编译结果classes为根目录计算-->
|
||||
<excludes>
|
||||
<exclude>*.properties</exclude>
|
||||
<exclude>*.yml</exclude>
|
||||
<exclude>*.xml</exclude>
|
||||
</excludes>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<!--MANIFEST.MF 中 Class-Path 加入前缀-->
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
<!--jar包不包含唯一版本标识-->
|
||||
<useUniqueVersions>false</useUniqueVersions>
|
||||
<!--指定入口类-->
|
||||
<mainClass>com.rainyhon.swput3.web.WebServerApplication</mainClass>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<!--MANIFEST.MF 中 Class-Path 加入资源文件目录-->
|
||||
<Class-Path>./resources/</Class-Path>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!--拷贝依赖 copy-dependencies-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>
|
||||
${project.build.directory}/lib/
|
||||
</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!--拷贝资源文件 copy-resources-->
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-resources</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<outputDirectory>${project.build.directory}/resources</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!--spring boot repackage,依赖 maven-jar-plugin 打包的jar包 重新打包成 spring boot 的jar包-->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<!--重写包含依赖,包含不存在的依赖,jar里没有pom里的依赖-->
|
||||
<includes>
|
||||
<include>
|
||||
<groupId>null</groupId>
|
||||
<artifactId>null</artifactId>
|
||||
</include>
|
||||
</includes>
|
||||
<layout>ZIP</layout>
|
||||
<!--使用外部配置文件,jar包里没有资源文件-->
|
||||
<addResources>true</addResources>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<!--配置jar包特殊标识 配置后,保留原文件,生成新文件 *-run.jar -->
|
||||
<!--配置jar包特殊标识 不配置,原文件命名为 *.jar.original,生成新文件 *.jar -->
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>dockerfile-maven-plugin</artifactId>
|
||||
<version>1.3.6</version>
|
||||
<configuration>
|
||||
<repository>${project.artifactId}</repository>
|
||||
<buildArgs>
|
||||
<JAR_FILE>target/</JAR_FILE>
|
||||
</buildArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.rainyhon.swput3.web;
|
||||
|
||||
import com.yelink.security.annotation.AppEnableResourceServer;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 15:31
|
||||
*/
|
||||
@AppEnableResourceServer
|
||||
@SpringBootApplication
|
||||
@EnableFeignClients(basePackages = "com.rainyhon.swput3.web.client")
|
||||
public class WebServerApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WebServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.rainyhon.swput3.web.client;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.openapi.openfeign.EdgeFeignConfig;
|
||||
import com.rainyhon.swput3.web.client.factory.DfsClientFallbackFactory;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@FeignClient(
|
||||
name = "dfsClient",
|
||||
url = "${openapi.edge-gateway-url}",
|
||||
// url = "https://192.168.102.180:8301",
|
||||
path = "/openApi/dfs",
|
||||
fallbackFactory = DfsClientFallbackFactory.class,
|
||||
configuration = EdgeFeignConfig.class)
|
||||
public interface DfsClient {
|
||||
|
||||
/**
|
||||
* 查询设备的历史数据
|
||||
*
|
||||
* @param page 页数
|
||||
* @param size 每页大小
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/api/v1/open/sensors/records")
|
||||
Result querySensorsRecords(@RequestParam(value = "current") Integer page, @RequestParam(value = "size") Integer size);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.rainyhon.swput3.web.client.factory;
|
||||
|
||||
import com.rainyhon.swput3.web.client.DfsClient;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-22 15:28
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DfsClientFallbackFactory implements FallbackFactory<DfsClient> {
|
||||
@Override
|
||||
public DfsClient create(Throwable throwable) {
|
||||
log.error("DFS服务调用失败:{}", throwable.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.rainyhon.swput3.web.controller;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.dto.req.SaveDemoModelReq;
|
||||
import com.rainyhon.swput3.web.dto.req.SendKafkaMessageReq;
|
||||
import com.rainyhon.swput3.web.dto.req.UploadFileReq;
|
||||
import com.rainyhon.swput3.web.service.IDemoService;
|
||||
import com.rainyhon.swput3.web.utils.ResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 15:41
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping()
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Api(tags = "样例控制器")
|
||||
public class DemoController {
|
||||
|
||||
private final IDemoService demoService;
|
||||
|
||||
|
||||
@PostMapping("/kafka/send")
|
||||
@ApiOperation(
|
||||
value = "发送kafka消息"
|
||||
)
|
||||
public Result send(@Valid @RequestBody SendKafkaMessageReq req) {
|
||||
demoService.sendKafka(req);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/mysql/save")
|
||||
@ApiOperation(
|
||||
value = "mysql保存数据样例"
|
||||
)
|
||||
public Result mysqlSaveDemo(@Valid @RequestBody SaveDemoModelReq req) {
|
||||
demoService.mysqlSaveDemo(req);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/redis/save")
|
||||
@ApiOperation(
|
||||
value = "redis保存数据样例"
|
||||
)
|
||||
public Result redisSave(@Valid @RequestBody SaveDemoModelReq req) {
|
||||
demoService.redisSave(req);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/redis/test/single")
|
||||
@ApiOperation(
|
||||
value = "redis压测"
|
||||
)
|
||||
public Result redisTestSingle(@RequestParam(value = "count") int count) {
|
||||
demoService.redistTestSingle(count);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PostMapping("/redis/test/batch")
|
||||
@ApiOperation(
|
||||
value = "redis压测"
|
||||
)
|
||||
public Result redisTestBatch(@RequestParam(value = "count") int count) {
|
||||
demoService.redisTestBatch(count);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
@PostMapping("/minio/upload")
|
||||
@ApiOperation(
|
||||
value = "minio上传文件样例",
|
||||
notes = "返回的是minio文件的相对路径。\n" +
|
||||
"为了支持多网络访问,推荐用法是前端使用该相对路径地址请求,后端的nginx已经默认配置路由规则到文件系统服务。"
|
||||
)
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "file", value = "上传文件", paramType = "form", dataType = "__file", required = true),
|
||||
})
|
||||
public Result<String> minioUpload(@Valid UploadFileReq req) {
|
||||
return ResultUtil.successJsonStr(demoService.minioUpload(req));
|
||||
}
|
||||
|
||||
@PostMapping("/call/service/api")
|
||||
@ApiOperation(
|
||||
value = "调用其他服务接口样例",
|
||||
notes = "调用其他服务需要经过网关。\n" +
|
||||
"调用此接口如果携带了token的请求头,则采用token的认证方式调用其他服务,否则采用AK/SK的认证方式。"
|
||||
)
|
||||
public Result callServiceApi() {
|
||||
Result res = demoService.callServiceApi();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.rainyhon.swput3.web.controller;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.dto.req.ProcessesTaskModelReq;
|
||||
import com.rainyhon.swput3.web.service.IProcessesTaskService;
|
||||
import com.rainyhon.swput3.web.utils.ResultUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2024-08-21 15:41
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/task")
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
@Api(tags = "工序任务")
|
||||
public class ProcessesTaskController {
|
||||
|
||||
private final IProcessesTaskService taskService;
|
||||
|
||||
@PostMapping("/add")
|
||||
@ApiOperation(value = "新增任务")
|
||||
public Result add(@Valid @RequestBody ProcessesTaskModelReq data) {
|
||||
taskService.add(data);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@PutMapping("/edit")
|
||||
@ApiOperation(value = "修改任务")
|
||||
public Result edit(@Valid @RequestBody ProcessesTaskModelReq data) {
|
||||
taskService.edit(data);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/del")
|
||||
@ApiOperation(value = "删除任务")
|
||||
public Result del(Long taskId) {
|
||||
taskService.deletes(taskId);
|
||||
return ResultUtil.success();
|
||||
}
|
||||
|
||||
@GetMapping("/detail")
|
||||
@ApiOperation(value = "详情任务")
|
||||
public Result detail(Long taskId) {
|
||||
return ResultUtil.success(taskService.detail(taskId));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@ApiOperation(value = "任务列表")
|
||||
public Result list(@RequestParam(required = false) Long proId,
|
||||
@RequestParam(required = false) String taskName,
|
||||
@RequestParam(required = false) String productCode) {
|
||||
return ResultUtil.success(taskService.list(proId, taskName,productCode));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.rainyhon.swput3.web.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-01-09 12:20
|
||||
*/
|
||||
@Data
|
||||
public class Result<T> {
|
||||
private Integer code;
|
||||
private String message;
|
||||
private T data;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.rainyhon.swput3.web.dto.req;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2025-06-26 17:08
|
||||
*/
|
||||
@Data
|
||||
public class ProcessesTaskModelReq {
|
||||
@ApiModelProperty(value = "产品编码")
|
||||
private String productCode;
|
||||
@ApiModelProperty(value = "任务id")
|
||||
private Long taskId;
|
||||
@ApiModelProperty(value = "任务名称")
|
||||
private String taskName;
|
||||
@ApiModelProperty(value = "任务内容")
|
||||
private String taskContent;
|
||||
@ApiModelProperty(value = "工序id")
|
||||
private Long proId;
|
||||
@ApiModelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.rainyhon.swput3.web.dto.req;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@Data
|
||||
public class SaveDemoModelReq {
|
||||
|
||||
@Pattern(
|
||||
regexp = "^[A-Za-z0-9][A-Za-z0-9_.]{1,18}[A-Za-z0-9]$",
|
||||
message = "用户名长度必须为3-20个字符,以字母或数字开头和结尾,可以包含字母、数字、下划线和句点"
|
||||
)
|
||||
@ApiModelProperty(value = "用户名称", notes = "用户名长度必须为3-20个字符,以字母或数字开头和结尾,可以包含字母、数字、下划线和句点")
|
||||
private String username;
|
||||
@ApiModelProperty(value = "用户地址")
|
||||
private String address;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.rainyhon.swput3.web.dto.req;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-01-09 12:14
|
||||
*/
|
||||
@Data
|
||||
public class SendKafkaMessageReq {
|
||||
|
||||
@Pattern(regexp = "^[a-z0-9]+(_[a-z0-9]+)*(_v[0-9]+)?$", message = "主题名称必须以小写字母或数字开始,主题名称可以包含多个由下划线分隔的部分,每个部分由小写字母或数字组成。")
|
||||
@ApiModelProperty(value = "主题名称", notes = "主题名称必须以小写字母或数字开始,主题名称可以包含多个由下划线分隔的部分,每个部分由小写字母或数字组成。")
|
||||
private String topic;
|
||||
@ApiModelProperty(value = "主题主键")
|
||||
private String key;
|
||||
@ApiModelProperty(value = "消息内容")
|
||||
private String message;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.rainyhon.swput3.web.dto.req;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-22 11:51
|
||||
*/
|
||||
@Data
|
||||
public class UploadFileReq {
|
||||
@NotNull
|
||||
private MultipartFile file;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.rainyhon.swput3.web.handler;
|
||||
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.utils.ResultUtil;
|
||||
import com.rainyhon.swput3.web.handler.exception.BusinessException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import java.nio.file.AccessDeniedException;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @Description 全局异常处理
|
||||
* @auther cwp
|
||||
* @create 2019-07-15 13:35
|
||||
*/
|
||||
@Slf4j
|
||||
public class BaseExceptionHandler {
|
||||
|
||||
|
||||
@ExceptionHandler(value = Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public void handleException(Exception e) {
|
||||
log.error("系统内部异常,异常信息", e);
|
||||
}
|
||||
|
||||
|
||||
@ExceptionHandler(value = BusinessException.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public Result handleFebsException(BusinessException e) {
|
||||
log.error("BusinessException {} ", e.getSnapshot());
|
||||
return ResultUtil.error(e.getCode(), e.getSnapshot());
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一处理请求参数校验(实体对象传参)
|
||||
*
|
||||
* @param e BindException
|
||||
* @return FebsResponse
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Result handleBindException(BindException e) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
|
||||
for (FieldError error : fieldErrors) {
|
||||
message.append(error.getField()).append(error.getDefaultMessage()).append(",");
|
||||
}
|
||||
message = new StringBuilder(message.substring(0, message.length() - 1));
|
||||
return ResultUtil.error(40001, message.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一处理请求参数校验(json)
|
||||
*
|
||||
* @param e ConstraintViolationException
|
||||
* @return FebsResponse
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Result handlerMethodArgumentNotValidException(MethodArgumentNotValidException e) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
for (FieldError error : e.getBindingResult().getFieldErrors()) {
|
||||
message.append(error.getField()).append(error.getDefaultMessage()).append(",");
|
||||
}
|
||||
message = new StringBuilder(message.substring(0, message.length() - 1));
|
||||
log.error(message.toString(), e);
|
||||
return ResultUtil.error(40003, message.toString());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = AccessDeniedException.class)
|
||||
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||
public Result handleAccessDeniedException() {
|
||||
return ResultUtil.error(10407, "没有权限访问该资源");
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
|
||||
String msg = "该方法不支持" + StringUtils.substringBetween(e.getMessage(), "'", "'") + "媒体类型";
|
||||
return ResultUtil.error(100801, msg);
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
|
||||
String msg = "该方法不支持" + StringUtils.substringBetween(e.getMessage(), "'", "'") + "请求方法";
|
||||
return ResultUtil.error(100802, msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.rainyhon.swput3.web.handler;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@Order(value = Ordered.HIGHEST_PRECEDENCE)
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler extends BaseExceptionHandler {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.rainyhon.swput3.web.handler.exception;
|
||||
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 9215327422367136022L;
|
||||
private int code;
|
||||
private String snapshot;
|
||||
|
||||
public BusinessException(ErrorCode errorCode) {
|
||||
super(errorCode.getMessage());
|
||||
this.code = errorCode.getCode();
|
||||
this.snapshot = errorCode.getMessage();
|
||||
}
|
||||
|
||||
public BusinessException(ErrorCode errorCode, String snapshotFormat, Object... argArray) {
|
||||
super(errorCode.getMessage());
|
||||
this.code = errorCode.getCode();
|
||||
this.snapshot = errorCode.getMessage() + MessageFormatter.arrayFormat(snapshotFormat, argArray).getMessage();
|
||||
}
|
||||
public BusinessException(int code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.snapshot = message;
|
||||
}
|
||||
public BusinessException(int code, String message, String snapshotFormat, Object... argArray) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.snapshot = MessageFormatter.arrayFormat(snapshotFormat, argArray).getMessage();
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getSnapshot() {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.rainyhon.swput3.web.handler.exception;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-03-14 11:39
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@ToString
|
||||
public enum ErrorCode {
|
||||
|
||||
/**
|
||||
* code 错误码
|
||||
* message 详细的错误信息
|
||||
*/
|
||||
KAFKA_SEND_FAIL(10000, "kafka消息发送异常"),
|
||||
MINIO_UPLOAD_FAIL(10001, "上传文件失败"),
|
||||
|
||||
END(99999,"END");
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private final int code;
|
||||
|
||||
/**
|
||||
* 详细的错误信息
|
||||
*/
|
||||
private final String message;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.rainyhon.swput3.web.kafka;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
||||
import org.springframework.kafka.annotation.KafkaListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class KafkaListenerDemo {
|
||||
public static final String MIDDLE_PRIORITY_POST = "middle_priority_post";
|
||||
|
||||
@KafkaListener(topics = {MIDDLE_PRIORITY_POST})
|
||||
public void kafkaRealTimeMsgListener(ConsumerRecord<?, ?> record) {
|
||||
String msg = record.value().toString();
|
||||
log.info("接收到kafka消息 {}", msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.rainyhon.swput3.web.model;
|
||||
|
||||
import com.rainyhon.swput3.web.model.audit.DateAudit;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 17:08
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "t_demo")
|
||||
@Data
|
||||
public class DemoModel extends DateAudit {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
private String username;
|
||||
private String address;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.rainyhon.swput3.web.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2025-06-26 17:08
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "processes_task")
|
||||
@Data
|
||||
public class ProcessesTaskModel {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long taskId;
|
||||
|
||||
private String productCode;
|
||||
|
||||
private String taskName;
|
||||
|
||||
private String taskContent;
|
||||
|
||||
private Long proId;
|
||||
|
||||
private String remark;
|
||||
|
||||
private String createBy;
|
||||
|
||||
private String updateBy;
|
||||
|
||||
@CreatedDate
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date updateTime;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.rainyhon.swput3.web.model.audit;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import javax.persistence.EntityListeners;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
|
||||
@MappedSuperclass
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@Data
|
||||
public abstract class DateAudit implements Serializable {
|
||||
|
||||
@CreatedDate
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Instant createdAt;
|
||||
|
||||
@LastModifiedDate
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Instant updatedAt;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.rainyhon.swput3.web.openapi;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@Data
|
||||
public class OpenApiParams {
|
||||
public static final String SIGNATURE_KEY = "Signature";
|
||||
public static final String SIGNATURE_NONCE_KEY = "SignatureNonce";
|
||||
public static final String TIMESTAMP_KEY = "Timestamp";
|
||||
public static final String ACCESSKEY_ID_KEY = "AccessKeyId";
|
||||
|
||||
public static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
|
||||
private String signature;
|
||||
private String signatureMethod;
|
||||
private String signatureNonce;
|
||||
private Instant timestamp;
|
||||
private String accessKeyId;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.rainyhon.swput3.web.openapi.openfeign;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "app.security")
|
||||
@Data
|
||||
public class AccessKeyProperties {
|
||||
@JsonProperty("access-key-id")
|
||||
private String accessKeyId;
|
||||
|
||||
@JsonProperty("access-key-secret")
|
||||
private String accessKeySecret;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.rainyhon.swput3.web.openapi.openfeign;
|
||||
|
||||
import com.rainyhon.swput3.web.utils.RequestInterceptorUtil;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import com.rainyhon.swput3.web.utils.SecurityUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class AccessKeyRequestInterceptor implements RequestInterceptor {
|
||||
|
||||
private AccessKeyProperties properties;
|
||||
|
||||
@Override
|
||||
public void apply(RequestTemplate template) {
|
||||
try {
|
||||
String token = SecurityUtils.getCurrentTokenValue();
|
||||
if (StringUtils.isNotEmpty(token)) {
|
||||
template.header("Authorization", "Bearer " + token);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// do nothing
|
||||
}
|
||||
Collection<String> authorization = template.headers().get("Authorization");
|
||||
if (ObjectUtils.isNotEmpty(authorization)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成ak请求头
|
||||
RequestInterceptorUtil.generateAccessKeyHeader(properties, template);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.rainyhon.swput3.web.openapi.openfeign;
|
||||
|
||||
import feign.Client;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
|
||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class EdgeFeignConfig {
|
||||
|
||||
private final AccessKeyProperties properties;
|
||||
|
||||
/**
|
||||
* 自定义feign拦截器
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public AccessKeyRequestInterceptor customFeignInterceptor() {
|
||||
return new AccessKeyRequestInterceptor(properties);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
|
||||
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
||||
SpringClientFactory clientFactory) throws NoSuchAlgorithmException, KeyManagementException {
|
||||
SSLContext ctx = SSLContext.getInstance("SSL");
|
||||
X509TrustManager tm = new X509TrustManager() {
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) {
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
//如果这里后续报空指针,就return new X509Certificate[0]
|
||||
return null;
|
||||
}
|
||||
};
|
||||
ctx.init(null, new TrustManager[]{tm}, null);
|
||||
return new LoadBalancerFeignClient(new Client.Default(ctx.getSocketFactory(),
|
||||
(hostname, session) -> true),
|
||||
cachingFactory, clientFactory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.rainyhon.swput3.web.repository;
|
||||
|
||||
import com.rainyhon.swput3.web.model.DemoModel;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 17:11
|
||||
*/
|
||||
@Repository
|
||||
public interface DemoModelRepository extends JpaRepository<DemoModel, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.rainyhon.swput3.web.repository;
|
||||
|
||||
import com.rainyhon.swput3.web.model.ProcessesTaskModel;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2024-08-21 17:11
|
||||
*/
|
||||
@Repository
|
||||
public interface ProcessesTaskRepository extends JpaRepository<ProcessesTaskModel, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package com.rainyhon.swput3.web.service;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.dto.req.SaveDemoModelReq;
|
||||
import com.rainyhon.swput3.web.dto.req.SendKafkaMessageReq;
|
||||
import com.rainyhon.swput3.web.dto.req.UploadFileReq;
|
||||
import com.rainyhon.swput3.web.model.DemoModel;
|
||||
import com.yelink.common.redis.service.RedisService;
|
||||
import com.rainyhon.swput3.web.client.DfsClient;
|
||||
import com.rainyhon.swput3.web.handler.exception.BusinessException;
|
||||
import com.rainyhon.swput3.web.handler.exception.ErrorCode;
|
||||
import com.rainyhon.swput3.web.repository.DemoModelRepository;
|
||||
import com.rainyhon.swput3.web.utils.RetryTimerTask;
|
||||
import com.yelink.minio.service.MinioService;
|
||||
import io.netty.util.HashedWheelTimer;
|
||||
import io.netty.util.Timer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.SessionCallback;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.kafka.core.KafkaTemplate;
|
||||
import org.springframework.kafka.support.SendResult;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 17:30
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class DemoServiceImpl implements IDemoService {
|
||||
private Timer failTimer = null;
|
||||
|
||||
private final KafkaTemplate<String, String> kafkaTemplate;
|
||||
private final DemoModelRepository demoModelRepository;
|
||||
|
||||
private final RedisService redisService;
|
||||
private final RedissonClient redissonClient;
|
||||
private final MinioService minioService;
|
||||
private final DfsClient dfsClient;
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
private static final String LOCK_KEY = "REDIS_LOCK";
|
||||
|
||||
@Override
|
||||
public void sendKafka(SendKafkaMessageReq req) {
|
||||
try {
|
||||
SendResult<String, String> result = kafkaTemplate.send(req.getTopic(), req.getKey(), req.getMessage()).get(5, TimeUnit.SECONDS);
|
||||
log.info(result.toString());
|
||||
} catch (ExecutionException | TimeoutException e) {
|
||||
throw new BusinessException(ErrorCode.KAFKA_SEND_FAIL);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("发送消息失败 InterruptedException", e);
|
||||
Thread.currentThread().interrupt();
|
||||
throw new BusinessException(ErrorCode.KAFKA_SEND_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mysqlSaveDemo(SaveDemoModelReq req) {
|
||||
DemoModel demoModel = new DemoModel();
|
||||
BeanUtils.copyProperties(req, demoModel);
|
||||
demoModelRepository.save(demoModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redisSave(SaveDemoModelReq req) {
|
||||
RLock rLock = redissonClient.getLock(LOCK_KEY);
|
||||
try {
|
||||
boolean isLock = rLock.tryLock(10, TimeUnit.MINUTES);
|
||||
if (isLock) {
|
||||
try {
|
||||
doInvoke(req);
|
||||
} catch (Throwable e) {
|
||||
log.error("调用 doInvoke 方法失败,5s 后将进入后台的自动重试,异常信息: ", e);
|
||||
addFailed(() -> doInvoke(req));
|
||||
}
|
||||
} else {
|
||||
log.error("未拿到锁");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
log.error("InterruptedException", e);
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
rLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String minioUpload(UploadFileReq req) {
|
||||
|
||||
try {
|
||||
return minioService.upload(req.getFile(), UUID.randomUUID().toString());
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException(ErrorCode.MINIO_UPLOAD_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result callServiceApi() {
|
||||
return dfsClient.querySensorsRecords(1, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redistTestSingle(int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
redisService.set("test" + i, i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redisTestBatch(int count) {
|
||||
redisTemplate.executePipelined(new SessionCallback<Object>() {
|
||||
@Override
|
||||
public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
|
||||
ValueOperations<String, Object> valueOperations = (ValueOperations<String, Object>) operations.opsForValue();
|
||||
for (int i = 0; i < count; i++) {
|
||||
valueOperations.set("batch-test" + i, i);
|
||||
}
|
||||
// 返回null即可,因为返回值会被管道的返回值覆盖,外层取不到这里的返回值
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addFailed(Runnable task) {
|
||||
getInstance();
|
||||
// 每30s重试一次
|
||||
RetryTimerTask retryTimerTask = new RetryTimerTask(task, 30, 3);
|
||||
try {
|
||||
// 5s 后执行第一次重试
|
||||
failTimer.newTimeout(retryTimerTask, 5, TimeUnit.SECONDS);
|
||||
} catch (Throwable e) {
|
||||
log.error("提交定时任务失败,exception: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void doInvoke(SaveDemoModelReq req) {
|
||||
Boolean res = redisService.set(req.getUsername(), req.getAddress());
|
||||
if (res) {
|
||||
log.info("redis数据保存成功");
|
||||
} else {
|
||||
log.error("redis数据保存失败");
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void getInstance() {
|
||||
if (failTimer == null) {
|
||||
failTimer = new HashedWheelTimer();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<String> authOrganizationIds = Arrays.asList("1", "2", "3","5");
|
||||
// List<Long> authOrganizationIds = Arrays.asList(1L, 2L, 3L);
|
||||
List<String> organizationIds = Arrays.asList("1", "2", "3","4");
|
||||
|
||||
boolean match = authOrganizationIds.stream().anyMatch(v -> !organizationIds.stream().anyMatch(v1 -> Objects.equals(v, v1)));
|
||||
authOrganizationIds.stream().anyMatch(v -> organizationIds.stream().anyMatch(v1 -> !Objects.equals(v, v1)));
|
||||
System.out.println("=====");
|
||||
System.out.println(match);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.rainyhon.swput3.web.service;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
import com.rainyhon.swput3.web.dto.req.SaveDemoModelReq;
|
||||
import com.rainyhon.swput3.web.dto.req.SendKafkaMessageReq;
|
||||
import com.rainyhon.swput3.web.dto.req.UploadFileReq;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-08-21 17:30
|
||||
*/
|
||||
public interface IDemoService {
|
||||
/**
|
||||
* 发送kafka消息
|
||||
*
|
||||
* @param req
|
||||
*/
|
||||
void sendKafka(SendKafkaMessageReq req);
|
||||
|
||||
void mysqlSaveDemo(SaveDemoModelReq req);
|
||||
|
||||
void redisSave(SaveDemoModelReq req);
|
||||
|
||||
String minioUpload(UploadFileReq req);
|
||||
|
||||
Result callServiceApi();
|
||||
|
||||
void redistTestSingle(int count);
|
||||
|
||||
void redisTestBatch(int count);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.rainyhon.swput3.web.service;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.req.ProcessesTaskModelReq;
|
||||
import com.rainyhon.swput3.web.model.ProcessesTaskModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2025-06-26 17:30
|
||||
*/
|
||||
public interface IProcessesTaskService {
|
||||
/**
|
||||
* 新增任务
|
||||
*
|
||||
* @param data
|
||||
* @return null
|
||||
* @Date 2025/6/26
|
||||
*/
|
||||
|
||||
void add(ProcessesTaskModelReq data);
|
||||
|
||||
/**
|
||||
* 修改任务
|
||||
*
|
||||
* @param data
|
||||
* @return null
|
||||
* @Date 2025/6/26
|
||||
*/
|
||||
|
||||
void edit(ProcessesTaskModelReq data);
|
||||
|
||||
/**
|
||||
* 删除任务
|
||||
*
|
||||
* @param taskIds
|
||||
* @return null
|
||||
* @Date 2025/6/26
|
||||
*/
|
||||
|
||||
void deletes(Long taskIds);
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*
|
||||
* @param taskId
|
||||
* @return null
|
||||
* @Date 2025/6/26
|
||||
*/
|
||||
|
||||
ProcessesTaskModel detail(Long taskId);
|
||||
|
||||
/**
|
||||
* 列表
|
||||
*
|
||||
* @param proId
|
||||
* @param taskName
|
||||
* @param productCode 产品编号
|
||||
* @return null
|
||||
* @Date 2025/6/26
|
||||
*/
|
||||
|
||||
List<ProcessesTaskModel> list(Long proId, String taskName,String productCode);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.rainyhon.swput3.web.service;
|
||||
|
||||
import com.rainyhon.swput3.web.dto.req.ProcessesTaskModelReq;
|
||||
import com.rainyhon.swput3.web.model.ProcessesTaskModel;
|
||||
import com.rainyhon.swput3.web.repository.ProcessesTaskRepository;
|
||||
import com.rainyhon.swput3.web.utils.SecurityUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.domain.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author leim
|
||||
* @date 2025-06-26 17:30
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ProcessesTaskServiceImpl implements IProcessesTaskService {
|
||||
|
||||
private final ProcessesTaskRepository taskRepository;
|
||||
|
||||
@Override
|
||||
public void add(ProcessesTaskModelReq data) {
|
||||
ProcessesTaskModel processesTaskModel = new ProcessesTaskModel();
|
||||
BeanUtils.copyProperties(data, processesTaskModel);
|
||||
processesTaskModel.setCreateBy(SecurityUtils.getUserName());
|
||||
processesTaskModel.setCreateTime(new Date());
|
||||
taskRepository.save(processesTaskModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void edit(ProcessesTaskModelReq data) {
|
||||
ProcessesTaskModel taskModel = taskRepository.findById(data.getTaskId()).orElseThrow(() -> new RuntimeException("任务数据不存在!"));
|
||||
taskModel.setTaskName(data.getTaskName());
|
||||
taskModel.setProductCode(data.getProductCode());
|
||||
taskModel.setTaskContent(data.getTaskContent());
|
||||
taskModel.setRemark(data.getRemark());
|
||||
taskModel.setProId(data.getProId());
|
||||
taskModel.setUpdateBy(SecurityUtils.getUserName());
|
||||
taskModel.setUpdateTime(new Date());
|
||||
// save方法在数据存在时更新
|
||||
taskRepository.save(taskModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletes(Long taskId) {
|
||||
taskRepository.deleteById(taskId);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessesTaskModel detail(Long taskId) {
|
||||
Optional<ProcessesTaskModel> taskModel = taskRepository.findById(taskId);
|
||||
return taskModel.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProcessesTaskModel> list(Long proId, String taskName,String productCode) {
|
||||
ProcessesTaskModel probe = new ProcessesTaskModel();
|
||||
if (!ObjectUtils.isEmpty(taskName)) {
|
||||
probe.setTaskName(taskName);
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(proId)) {
|
||||
probe.setProId(proId);
|
||||
}
|
||||
if(!ObjectUtils.isEmpty(productCode)){
|
||||
probe.setProductCode(productCode);
|
||||
}
|
||||
// 2. 创建匹配器 - 关键配置
|
||||
ExampleMatcher matcher = ExampleMatcher.matching()
|
||||
.withMatcher("taskName", match ->
|
||||
match.contains() // 使用包含匹配 (LIKE %value%)
|
||||
.ignoreCase() // 忽略大小写
|
||||
).withIgnoreNullValues(); // 忽略null值
|
||||
Example<ProcessesTaskModel> example = Example.of(probe, matcher);
|
||||
return taskRepository.findAll(example);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.rainyhon.swput3.web.openapi.openfeign.AccessKeyProperties;
|
||||
import com.rainyhon.swput3.web.openapi.OpenApiParams;
|
||||
import feign.RequestTemplate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
*/
|
||||
@Slf4j
|
||||
public class RequestInterceptorUtil {
|
||||
|
||||
public static void generateAccessKeyHeader(AccessKeyProperties properties, RequestTemplate template) {
|
||||
Map<String, String> queries = initQueryParams(template);
|
||||
|
||||
int randomInt = RandomUtil.randomInt(1000, 1000000);
|
||||
Map<String, String> headerMap = new HashMap<>();
|
||||
// 公共参数
|
||||
headerMap.put(OpenApiParams.ACCESSKEY_ID_KEY, properties.getAccessKeyId());
|
||||
headerMap.put(OpenApiParams.TIMESTAMP_KEY, getTimestamp());
|
||||
headerMap.put(OpenApiParams.SIGNATURE_NONCE_KEY, String.valueOf(randomInt));
|
||||
// 请求参数
|
||||
HashMap<String, String> map = new HashMap<>(headerMap);
|
||||
map.putAll(queries);
|
||||
String signature;
|
||||
try {
|
||||
signature = SignatureUtils.generate(template.method(), map, properties.getAccessKeySecret());
|
||||
headerMap.put(OpenApiParams.SIGNATURE_KEY, signature);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Map<String, Collection<String>> headers = toCollectionMap(headerMap);
|
||||
template.headers(headers);
|
||||
}
|
||||
|
||||
|
||||
public static String getTimestamp() {
|
||||
// 获取当前时间
|
||||
OffsetDateTime utcTime = OffsetDateTime.now(ZoneOffset.UTC);
|
||||
// 将时间格式化为指定格式的字符串
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(OpenApiParams.TIMESTAMP_FORMAT);
|
||||
return utcTime.format(formatter);
|
||||
}
|
||||
|
||||
public static Map<String, Collection<String>> toCollectionMap(Map<String, String> map) {
|
||||
Map<String, Collection<String>> result = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||
result.put(entry.getKey(), Collections.singletonList(entry.getValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Map<String, String> toMap(Map<String, Collection<String>> map) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
for (Map.Entry<String, Collection<String>> entry : map.entrySet()) {
|
||||
result.put(entry.getKey(), entry.getValue().iterator().next());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Map<String, String> initQueryParams(RequestTemplate template) {
|
||||
Map<String, Collection<String>> queries = template.queries();
|
||||
Map<String, String> decodedQueryParams = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, Collection<String>> entry : queries.entrySet()) {
|
||||
String encodedKey = entry.getKey();
|
||||
for (String encodedValue : entry.getValue()) {
|
||||
String decodedKey = decodeQueryParam(encodedKey);
|
||||
String decodedValue = decodeQueryParam(encodedValue);
|
||||
decodedQueryParams.put(decodedKey, decodedValue);
|
||||
}
|
||||
}
|
||||
|
||||
return decodedQueryParams;
|
||||
}
|
||||
private static String decodeQueryParam(String value) {
|
||||
try {
|
||||
return URLDecoder.decode(value, "UTF-8");
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
log.warn("Could not decode query value [" + value + "] as 'UTF-8'. " +
|
||||
"Falling back on default encoding: " + ex.getMessage());
|
||||
return URLDecoder.decode(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
|
||||
import com.rainyhon.swput3.web.dto.Result;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @create 2020-04-15 17:21
|
||||
*/
|
||||
public class ResultUtil {
|
||||
public static Result success() {
|
||||
return success("");
|
||||
}
|
||||
|
||||
public static Result success(String msg) {
|
||||
Result result = new Result();
|
||||
result.setCode(0);
|
||||
result.setMessage(msg);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result success(Object data) {
|
||||
Result result = new Result();
|
||||
result.setCode(0);
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result successJsonStr(String data) {
|
||||
Result result = new Result();
|
||||
result.setCode(0);
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result error(int code, String message) {
|
||||
return error(code, message, null);
|
||||
}
|
||||
|
||||
public static Result error(int code, String message, Object data) {
|
||||
Result result = new Result();
|
||||
result.setCode(code);
|
||||
result.setMessage(message);
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
import io.netty.util.Timeout;
|
||||
import io.netty.util.Timer;
|
||||
import io.netty.util.TimerTask;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author cwp
|
||||
* @date 2024-01-24 10:56
|
||||
*/
|
||||
@Slf4j
|
||||
public class RetryTimerTask implements TimerTask {
|
||||
/**
|
||||
* 每隔几秒执行一次
|
||||
*/
|
||||
private final long tick;
|
||||
|
||||
/**
|
||||
* 最大重试次数
|
||||
*/
|
||||
private final int retries;
|
||||
|
||||
private int retryTimes = 0;
|
||||
|
||||
private final Runnable task;
|
||||
|
||||
public RetryTimerTask(Runnable task, long tick, int retries) {
|
||||
this.tick = tick;
|
||||
this.retries = retries;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(Timeout timeout) {
|
||||
try {
|
||||
task.run();
|
||||
} catch (Throwable e) {
|
||||
if ((++retryTimes) >= retries) {
|
||||
// 重试次数超过了设置的值
|
||||
log.error("失败重试次数超过阈值: {},不再重试", retries);
|
||||
} else {
|
||||
log.error("重试失败,继续重试");
|
||||
rePut(timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 timeout 拿到 timer 实例,重新提交一个定时任务
|
||||
* @param timeout
|
||||
*/
|
||||
private void rePut(Timeout timeout) {
|
||||
if (timeout == null) {
|
||||
return;
|
||||
}
|
||||
Timer timer = timeout.timer();
|
||||
if (timeout.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
timer.newTimeout(timeout.task(), tick, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.keycloak.adapters.springsecurity.account.SimpleKeycloakAccount;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
public class SecurityUtils {
|
||||
|
||||
|
||||
private SecurityUtils() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token of the current user.
|
||||
*
|
||||
* @return the login of the current user organizationId.
|
||||
*/
|
||||
public static KeycloakAuthenticationToken getPrincipe() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
return (KeycloakAuthenticationToken) authentication;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token of the current user Id.
|
||||
*
|
||||
* @return the login of the current user organizationId.
|
||||
*/
|
||||
public static String getUserId() {
|
||||
SimpleKeycloakAccount account = getAccount();
|
||||
return account.getKeycloakSecurityContext().getToken().getSubject();
|
||||
}
|
||||
|
||||
public static String getUserName() {
|
||||
SimpleKeycloakAccount account = getAccount();
|
||||
return account.getKeycloakSecurityContext().getToken().getPreferredUsername();
|
||||
}
|
||||
|
||||
|
||||
public static SimpleKeycloakAccount getAccount() {
|
||||
KeycloakAuthenticationToken principe = getPrincipe();
|
||||
return (SimpleKeycloakAccount) principe.getDetails();
|
||||
}
|
||||
|
||||
public static boolean isAdmin() {
|
||||
return getAccount().getRoles().contains("admin");
|
||||
}
|
||||
|
||||
|
||||
public static String getClient() {
|
||||
SimpleKeycloakAccount account = getAccount();
|
||||
String client = account.getKeycloakSecurityContext().getToken().getIssuedFor();
|
||||
return client;
|
||||
}
|
||||
|
||||
public static Set<String> getRealmRoles() {
|
||||
SimpleKeycloakAccount account = getAccount();
|
||||
Set<String> roles = account.getKeycloakSecurityContext().getToken().getRealmAccess().getRoles();
|
||||
return roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前令牌内容
|
||||
*
|
||||
* @return String 令牌内容
|
||||
*/
|
||||
public static String getCurrentTokenValue() {
|
||||
String token = null;
|
||||
try {
|
||||
token = getAccount().getKeycloakSecurityContext().getTokenString();
|
||||
} catch (Exception e) {
|
||||
log.warn("can not get OAuth2AuthenticationDetails;{}", e.getMessage());
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
|
||||
/**
|
||||
* 服务端API签名
|
||||
*
|
||||
* @author cwp
|
||||
*/
|
||||
public class SignatureUtils {
|
||||
|
||||
private final static String CHARSET_UTF8 = "utf8";
|
||||
private final static String ALGORITHM = "HmacSHA1";
|
||||
private final static String SEPARATOR = "&";
|
||||
|
||||
public static Map<String, String> splitQueryString(String url)
|
||||
throws URISyntaxException, UnsupportedEncodingException {
|
||||
URI uri = new URI(url);
|
||||
String query = uri.getQuery();
|
||||
final String[] pairs = query.split("&");
|
||||
TreeMap<String, String> queryMap = new TreeMap<String, String>();
|
||||
for (String pair : pairs) {
|
||||
final int idx = pair.indexOf("=");
|
||||
final String key = idx > 0 ? pair.substring(0, idx) : pair;
|
||||
if (!queryMap.containsKey(key)) {
|
||||
queryMap.put(key, URLDecoder.decode(pair.substring(idx + 1), CHARSET_UTF8));
|
||||
}
|
||||
}
|
||||
return queryMap;
|
||||
}
|
||||
|
||||
public static String generate(String method, Map<String, String> parameter, String accessKeySecret)
|
||||
throws Exception {
|
||||
String signString = generateSignString(method, parameter);
|
||||
byte[] signBytes = hmacSHA1Signature(accessKeySecret + "&", signString);
|
||||
String signature = newStringByBase64(signBytes);
|
||||
if ("POST".equals(method)) {
|
||||
return signature;
|
||||
}
|
||||
return URLEncoder.encode(signature, "UTF-8");
|
||||
}
|
||||
|
||||
public static String generateSignString(String httpMethod, Map<String, String> parameter) throws IOException {
|
||||
TreeMap<String, String> sortParameter = new TreeMap<String, String>();
|
||||
sortParameter.putAll(parameter);
|
||||
String canonicalizedQueryString = UrlUtil.generateQueryString(sortParameter, true);
|
||||
if (null == httpMethod) {
|
||||
throw new RuntimeException("httpMethod can not be empty");
|
||||
}
|
||||
StringBuilder stringToSign = new StringBuilder();
|
||||
stringToSign.append(httpMethod).append(SEPARATOR);
|
||||
stringToSign.append(percentEncode("/")).append(SEPARATOR);
|
||||
stringToSign.append(percentEncode(canonicalizedQueryString));
|
||||
return stringToSign.toString();
|
||||
}
|
||||
|
||||
public static String percentEncode(String value) {
|
||||
try {
|
||||
return value == null ? null
|
||||
: URLEncoder.encode(value, CHARSET_UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E",
|
||||
"~");
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static byte[] hmacSHA1Signature(String secret, String baseString) throws Exception {
|
||||
if (StringUtils.isEmpty(secret)) {
|
||||
throw new IOException("secret can not be empty");
|
||||
}
|
||||
if (StringUtils.isEmpty(baseString)) {
|
||||
return null;
|
||||
}
|
||||
Mac mac = Mac.getInstance(ALGORITHM);
|
||||
SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(CHARSET_UTF8), ALGORITHM);
|
||||
mac.init(keySpec);
|
||||
return mac.doFinal(baseString.getBytes(CHARSET_UTF8));
|
||||
}
|
||||
|
||||
public static String newStringByBase64(byte[] bytes) throws UnsupportedEncodingException {
|
||||
if (bytes == null || bytes.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return new String(Base64.encodeBase64(bytes, false), CHARSET_UTF8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.rainyhon.swput3.web.utils;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* URL处理类
|
||||
*
|
||||
* @author cwp
|
||||
*/
|
||||
public class UrlUtil {
|
||||
|
||||
private final static String CHARSET_UTF8 = "utf8";
|
||||
|
||||
public static String urlEncode(String url) {
|
||||
if (!StringUtils.isEmpty(url)) {
|
||||
try {
|
||||
url = URLEncoder.encode(url, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
System.out.println("Url encode error:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public static String generateQueryString(Map<String, String> params, boolean isEncodeKV) {
|
||||
StringBuilder canonicalizedQueryString = new StringBuilder();
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
if (isEncodeKV) {
|
||||
canonicalizedQueryString.append(percentEncode(entry.getKey())).append("=")
|
||||
.append(percentEncode(entry.getValue())).append("&");
|
||||
} else {
|
||||
canonicalizedQueryString.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
|
||||
}
|
||||
}
|
||||
if (canonicalizedQueryString.length() > 1) {
|
||||
canonicalizedQueryString.setLength(canonicalizedQueryString.length() - 1);
|
||||
}
|
||||
return canonicalizedQueryString.toString();
|
||||
}
|
||||
|
||||
public static String percentEncode(String value) {
|
||||
try {
|
||||
// 使用URLEncoder.encode编码后,将"+","*","%7E"做替换即满足API规定的编码规范。
|
||||
return value == null ? null
|
||||
: URLEncoder.encode(value, CHARSET_UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E",
|
||||
"~");
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
123
web-server/src/main/resources/application.yml
Normal file
123
web-server/src/main/resources/application.yml
Normal file
@@ -0,0 +1,123 @@
|
||||
server:
|
||||
port: 38104
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://${MYSQL_URL:172.23.255.62:3306}/rainyhon?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false
|
||||
username: ${MYSQL_USER:root}
|
||||
password: ${MYSQL_PASSWORD:yelink123}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
hikari:
|
||||
max-lifetime: 600000
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: classpath:db/migration
|
||||
out-of-order: true
|
||||
|
||||
redis:
|
||||
database: 12
|
||||
host: ${REDIS_URL:172.23.255.62}
|
||||
port: 6379
|
||||
jedis:
|
||||
pool:
|
||||
min-idle: 8
|
||||
max-idle: 500
|
||||
max-active: 2000
|
||||
max-wait: 10000
|
||||
timeout: 5000
|
||||
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
jackson:
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
time-zone: GMT+8
|
||||
default-property-inclusion: non_null
|
||||
jpa:
|
||||
open-in-view: true
|
||||
hibernate:
|
||||
ddl-auto:jpa: validate
|
||||
|
||||
kafka:
|
||||
listener:
|
||||
concurrency: 1
|
||||
missing-topics-fatal: false
|
||||
# 设置批量消费
|
||||
# type=batch
|
||||
bootstrap-servers: ${KAFKACLOUD_URL:172.23.255.62:9092}
|
||||
producer:
|
||||
retries: 3
|
||||
# 应答级别:多少个分区副本备份完成时向生产者发送ack确认(可选0、1、all/-1)
|
||||
acks: 1
|
||||
# 批量大小
|
||||
batch-size: 16384
|
||||
# 提交延时
|
||||
properties:
|
||||
linger:
|
||||
ms: 0
|
||||
# 当生产端积累的消息达到batch-size或接收到消息linger.ms后,生产者就会将消息提交给kafka
|
||||
# linger.ms为0表示每接收到一条消息就提交给kafka,这时候batch-size其实就没用了
|
||||
# 生产端缓冲区大小
|
||||
buffer-memory: 33554432
|
||||
# Kafka提供的序列化和反序列化类
|
||||
key-serializer: org.apache.kafka.common.serialization.StringSerializer
|
||||
value-serializer: org.apache.kafka.common.serialization.StringSerializer
|
||||
###########【初始化消费者配置】###########
|
||||
# 默认的消费组ID
|
||||
consumer:
|
||||
# 是否自动提交offset
|
||||
enable-auto-commit: true
|
||||
# 提交offset延时(接收到消息后多久提交offset)
|
||||
auto-commit-interval: 1000
|
||||
auto-offset-reset: latest
|
||||
properties:
|
||||
group.id: consumer-group-example
|
||||
# 消费会话超时时间(超过这个时间consumer没有发送心跳,就会触发rebalance操作)
|
||||
session:
|
||||
timeout.ms: 120000
|
||||
# 消费请求超时时间
|
||||
request:
|
||||
timeout.ms: 180000
|
||||
# Kafka提供的序列化和反序列化类
|
||||
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||
# 批量消费每次最多消费多少条消息
|
||||
# max-poll-records=50
|
||||
|
||||
doc:
|
||||
enable: true
|
||||
title: ${spring.application.name}文档
|
||||
base-package: com.rainyhon.swput3.web.controller
|
||||
description: swput3 doc
|
||||
name: swput3
|
||||
email: swput3@example.com
|
||||
url: https://example.com
|
||||
version: 1.0-SNAPSHOT
|
||||
|
||||
app:
|
||||
security:
|
||||
enable: true
|
||||
anon-uris: /v2/api-docs-ext,/actuator/**,/swagger-ui.html,/webjars/**,/swagger-resources/**,/v2/api-docs/**,/call/service/api
|
||||
access-key-id: ${ACCESS_KEY:DJPE3QJ07OXPAFFJGORO}
|
||||
access-key-secret: ${ACCESS_KEY_SECRET:sFjISWRruGQjJLCZYiEwomqEpBCNiE3HBaYixZFl}
|
||||
|
||||
|
||||
keycloak:
|
||||
auth-server-url: ${AUTH_URL:http://172.23.255.62:8411/auth}
|
||||
realm: ${KEYCLOAK_REALM:edge}
|
||||
principal-attribute: preferred_username
|
||||
use-resource-role-mappings: false
|
||||
ssl-required: none
|
||||
enable-basic-auth: true
|
||||
resource: ${CLIENT_ID:iot}
|
||||
credentials:
|
||||
secret: ${CLIENT_SECRET:de512cc3-1208-4c3c-a389-8637774eee56}
|
||||
enabled: ${app.security.enable}
|
||||
minio:
|
||||
bucket: ${MINIO_BUCKET:oss-public}
|
||||
access-key: ${MINIO_ACCESS_KEY:miniouser}
|
||||
secret-key: ${MINIO_SECRET_KEY:yelink123}
|
||||
endpoint: ${MINIO_ENDPOINT:http://172.23.255.62:28301}
|
||||
application-name: ${MINIO_PREFIX_NAME:example}
|
||||
|
||||
openapi:
|
||||
edge-gateway-url: ${EDGE_GATEWAY_URL:https://172.23.255.62:8301}
|
||||
15
web-server/src/main/resources/bootstrap.yml
Normal file
15
web-server/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
spring:
|
||||
application:
|
||||
name: factory-digital-app-swput3
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: ${NACOS_URL:172.23.255.62:8001}
|
||||
metadata:
|
||||
version: ${VERSION}
|
||||
ip: ${LOCAL_IP:172.23.226.250}
|
||||
port: ${server.port}
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.alibaba.nacos: warn
|
||||
10
web-server/src/main/resources/db.migration/V1.0.0__init.sql
Normal file
10
web-server/src/main/resources/db.migration/V1.0.0__init.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
create table t_demo
|
||||
(
|
||||
id int auto_increment,
|
||||
username varchar(64) not null,
|
||||
address varchar(128) null,
|
||||
created_at timestamp default CURRENT_TIMESTAMP null,
|
||||
updated_at timestamp default CURRENT_TIMESTAMP null,
|
||||
constraint t_demo_pk primary key (id)
|
||||
);
|
||||
|
||||
52
web-server/src/main/resources/logback.xml
Normal file
52
web-server/src/main/resources/logback.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration debug="false">
|
||||
|
||||
<!--设置存储路径变量-->
|
||||
<property name="LOG_HOME" value="./logs/"/>
|
||||
|
||||
<!-- ConsoleAppender:把日志输出到控制台 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<!--设置输出格式-->
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} %line - %msg%n</pattern>
|
||||
<!--设置编码-->
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!--文件输出,时间窗口滚动-->
|
||||
<appender name="timeFileOutput" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
|
||||
<!--日志名,指定最新的文件名,其他文件名使用FileNamePattern -->
|
||||
<File>${LOG_HOME}/rainyhon-xzt.log</File>
|
||||
<append>true</append>
|
||||
<!--文件滚动模式-->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!--日志文件输出的文件名,可设置文件类型为gz,开启文件压缩-->
|
||||
<FileNamePattern>${LOG_HOME}/timeFile/rainyhon-xzt.%d{yyyy-MM-dd}.%i.gz</FileNamePattern>
|
||||
<!--日志文件保留天数-->
|
||||
<MaxHistory>30</MaxHistory>
|
||||
<!--按大小分割同一天的-->
|
||||
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>100MB</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<totalSizeCap>2048MB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
|
||||
<!--输出格式-->
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
||||
<!--设置编码-->
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
<!-- 控制台输出日志级别 -->
|
||||
<root level="info">
|
||||
<appender-ref ref="console"/>
|
||||
<appender-ref ref="timeFileOutput"/>
|
||||
</root>
|
||||
|
||||
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user