feat: 移植工程

This commit is contained in:
2025-06-10 09:54:52 +08:00
commit a2de21b092
277 changed files with 11116 additions and 0 deletions

35
.gitignore vendored Normal file
View File

@@ -0,0 +1,35 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
!**/logs/**

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

18
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@@ -0,0 +1,18 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

52
README.md Normal file
View File

@@ -0,0 +1,52 @@
Ubuntu安装ftp服务器
1. 安装
sudo apt-get install vsftpd
2. 查看是否成功
sudo systemctl status vsftpd
3. 编辑配置文件
sudo gedit /etc/vsftpd.conf
启用本地用户local_enable=YES
允许上传文件write_enable=YES
4. 重启vsftpd
sudo systemctl restart vsftpd
5. 如果需要 可以新增FTP用户如ftp-user
sudo adduser ftp-user
6. 设置用户的访问目录权限
// 是否设置用户的访问目录权限
chroot_local_user=YES
// 是否指定用户列表文件
chroot_list_enable=YES
// 用户列表文件
chroot_list_file=/etc/vsftpd.chroot_list
例如设置了YES YES则除了vsftpd.chroot_list文件里面的用户都不能
切换目录访问其他文件。
7. 设置/home/ftp-user的权限为555 这里不能有写的权限,否则登录失败。
8. 在/home/ftp-user下新建文件夹注意要改成777的权限否则会写失败。
中文乱码问题:
utf8_filesystem=YES
dirmessage_enable=YES
use_localtime=YES
xferlog_enable=YES
connect_from_port_20=YES
https://blog.csdn.net/u013345780/article/details/134325550?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-134325550-blog-51419630.235^v43^pc_blog_bottom_relevance_base5&spm=1001.2101.3001.4242.1&utm_relevant_index=3
Logback、Fluency 和 OpenObserve 之间的关系如下:
Logback这是一个 Java 日志框架,负责在 Spring Boot 应用中生成和管理日志。它提供了灵活的配置选项,可以定义日志格式和输出目标。
Fluency这是一个轻量级的 Fluentd 客户端库,能够将日志数据从 Logback 发送到 Fluentd。它充当了中间层将 Logback 生成的日志收集并转发给 Fluentd。
OpenObserve这是一个日志监控和分析平台接收来自 Fluentd 的日志数据。OpenObserve 提供可视化界面,用于查看和分析这些日志。
工作流程
Logback 生成日志。
Fluency 将这些日志发送到 Fluency并将其格式化为适合传输的形式。
Fluency 将日志转发到 OpenObserve允许用户在 OpenObserve 界面上查看和分析日志数据。
这种组合提供了一个强大且灵活的日志管理和监控解决方案。
安装在服务器的软件:
FTP文件服务器vsftpd

38
admin-service/.gitignore vendored Normal file
View File

@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

6
admin-service/Dockerfile Normal file
View File

@@ -0,0 +1,6 @@
FROM docker-0.unsee.tech/openjdk:11-jdk-slim
ARG JAR_FILE=target/*.jar
COPY $JAR_FILE app.jar
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone
ENTRYPOINT ["java","-jar","/app.jar"]

58
admin-service/pom.xml Normal file
View File

@@ -0,0 +1,58 @@
<?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.cxx</groupId>
<artifactId>sweet-hut-service</artifactId>
<version>2.0.0</version>
</parent>
<artifactId>admin-service</artifactId>
<version>2.0.0</version>
<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.cxx</groupId>
<artifactId>common</artifactId>
<version>2.0.0</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.checkerframework</groupId>-->
<!-- <artifactId>checker-qual</artifactId>-->
<!-- <version>2.0.0</version>-->
<!-- <scope>compile</scope>-->
<!-- </dependency>-->
<!-- 服务器监控 -->
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>6.4.0</version>
</dependency>
<!-- influxdb -->
<dependency>
<groupId>com.influxdb</groupId>
<artifactId>influxdb-client-java</artifactId>
<version>7.2.0</version>
</dependency>
</dependencies>
<!-- <build>-->
<!-- <plugins>-->
<!-- <plugin>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
<!-- </plugin>-->
<!-- </plugins>-->
<!-- </build>-->
</project>

View File

@@ -0,0 +1,15 @@
package com.cxx.admin;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@MapperScan(basePackages = {"com.cxx.common.mapper", "com.cxx.admin.mapper", "com.cxx.admin.dao"})
@EnableScheduling
public class AdminServiceApplication {
public static void main(String[] args) {
SpringApplication.run(AdminServiceApplication.class, args);
}
}

View File

@@ -0,0 +1,31 @@
package com.cxx.admin.config;
import com.cxx.admin.constant.FileConstant;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Author: Cxx
* @Date: 2024/9/22 10:57
* @Description:
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Value("${file.path}")
private String filePath;
@Value("${file.log-path}")
private String logFilePath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/" + FileConstant.FILE_URL_PREFIX + "/**")
.addResourceLocations("file:" + filePath);
registry.addResourceHandler("/" + FileConstant.FILE_LOG_PREFIX + "/**")
.addResourceLocations("file:" + logFilePath);
}
}

View File

@@ -0,0 +1,7 @@
package com.cxx.admin.constant;
public interface FileConstant {
String FILE_NAME_SPLIT = "_";
String FILE_URL_PREFIX = "files";
String FILE_LOG_PREFIX = "logs";
}

View File

@@ -0,0 +1,48 @@
package com.cxx.admin.controller;
import com.cxx.admin.service.AccountService;
import com.cxx.common.ReadView;
import com.cxx.common.WriteView;
import com.cxx.admin.dto.user.AccountDto;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/account")
@Tag(name = "账户管理")
public class AccountController {
@Resource
private AccountService accountService;
@Operation(summary = "查询用户账号")
@GetMapping("")
public @JsonView(ReadView.class) List<AccountDto> queryAccountList() {
return accountService.queryAccountList();
}
@Operation(summary = "注册账户")
@PostMapping("")
public Boolean registerAccount(@JsonView(WriteView.class) @Validated @RequestBody AccountDto accountDto) {
return accountService.registerAccount(accountDto);
}
@Operation(summary = "注销账户")
@DeleteMapping("/{id}")
public Boolean deleteAccount(@PathVariable("id") Long id) {
return accountService.deleteAccount(id);
}
@Operation(summary = "更改账户密码")
@PutMapping("/password/{id}")
public Boolean updateAccountPassword(@PathVariable("id") Long id,
@RequestParam String oldPassword,
@RequestParam String newPassword) {
return Boolean.TRUE;
}
}

View File

@@ -0,0 +1,58 @@
package com.cxx.admin.controller;
import com.cxx.admin.service.BillManageService;
import com.cxx.common.WriteView;
import com.cxx.common.dto.bill.BillCategoryDto;
import com.cxx.common.dto.bill.BillItemDto;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/bill-manage")
@Tag(name = "账单管理")
public class BillManageController {
@Resource
private BillManageService billManageService;
@Operation(summary = "新增支付账户")
@PostMapping("/pay")
public Boolean addBillPay(@JsonView(WriteView.class) @RequestBody BillItemDto billItemDto) {
return billManageService.addBillPay(billItemDto);
}
@Operation(summary = "更新支付账户")
@PutMapping("/pay/{id}")
public Boolean updateBillPay(@PathVariable("id") Long id,
@JsonView(WriteView.class) @RequestBody BillItemDto billItemDto) {
return billManageService.updateBillPay(id, billItemDto);
}
@Operation(summary = "新增账本类型")
@PostMapping("/book")
public Boolean addBillBook(@JsonView(WriteView.class) @RequestBody BillItemDto billItemDto) {
return billManageService.addBillBook(billItemDto);
}
@Operation(summary = "更新账本类型")
@PutMapping("/book/{id}")
public Boolean updateBillBook(@PathVariable("id") Long id,
@JsonView(WriteView.class) @RequestBody BillItemDto billItemDto) {
return billManageService.updateBillBook(id, billItemDto);
}
@Operation(summary = "新增账单类型")
@PostMapping("/category")
public Boolean addBillCategory(@RequestBody BillCategoryDto billCategoryDto) {
return billManageService.addBillCategory(billCategoryDto);
}
@Operation(summary = "更新账单类型")
@PutMapping("/category/{id}")
public Boolean updateBillCategory(@PathVariable("id") Long id,
@RequestBody BillCategoryDto billCategoryDto) {
return billManageService.updateBillCategory(id, billCategoryDto);
}
}

View File

@@ -0,0 +1,69 @@
package com.cxx.admin.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cxx.admin.service.BlogManageService;
import com.cxx.common.ReadView;
import com.cxx.common.WriteView;
import com.cxx.common.dto.blog.BlogCategoryDto;
import com.cxx.common.dto.blog.BlogDto;
import com.cxx.common.dto.blog.BlogVisitDto;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/blog-manage")
@Tag(name = "博客管理")
public class BlogManageController {
@Resource
private BlogManageService blogManageService;
@Operation(summary = "分页查询博客")
@GetMapping("/page")
public IPage<BlogDto> queryBlogByPage(@RequestParam("currentPage") Integer currentPage,
@RequestParam("pageSize") Integer pageSize) {
return blogManageService.queryBlogByPage(currentPage, pageSize);
}
@Operation(summary = "查询博客内容")
@GetMapping("/content/{id}")
public @JsonView(ReadView.class) BlogDto queryBlogById(@PathVariable("id") long id) {
return blogManageService.queryBlogById(id);
}
@Operation(summary = "查询博客分类")
@GetMapping("/category")
public List<BlogCategoryDto> queryBlogCategory() {
return blogManageService.queryBlogCategory();
}
@Operation(summary = "增加博客")
@PostMapping("")
public Boolean addBlog(@JsonView(WriteView.class) @RequestBody BlogDto blog) {
return blogManageService.addBlog(blog);
}
@Operation(summary = "更新博客")
@PutMapping("/{id}")
public Boolean updateBlog(@PathVariable("id") long id,
@JsonView(WriteView.class) @RequestBody BlogDto blog) {
return blogManageService.updateBlog(id, blog);
}
@Operation(summary = "删除博客")
@DeleteMapping("/{id}")
public Boolean deleteBlog(@PathVariable("id") long id) {
return blogManageService.deleteBlog(id);
}
@Operation(summary = "查询博客访问")
@GetMapping("/visit")
public IPage<BlogVisitDto> queryBlogVisit(@RequestParam("currentPage") Integer currentPage,
@RequestParam("pageSize") Integer pageSize) {
return blogManageService.queryBlogVisit(currentPage, pageSize);
}
}

View File

@@ -0,0 +1,27 @@
package com.cxx.admin.controller;
import com.cxx.admin.service.BudgetManageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/budget-manage")
@Tag(name = "预算管理")
public class BudgetManageController {
@Resource
private BudgetManageService budgetManageService;
@Operation(summary = "新增预算类别")
@PostMapping("/category")
public Boolean addBudgetCategory(@RequestParam String category) {
return budgetManageService.addBudgetCategory(category);
}
@Operation(summary = "更新预算类别")
@PutMapping("/category/{id}")
public Boolean updateBudgetCategory(@PathVariable("id") Long id, @RequestParam String category) {
return budgetManageService.updateBudgetCategory(id, category);
}
}

View File

@@ -0,0 +1,65 @@
package com.cxx.admin.controller;
import com.cxx.admin.dto.file.ChunkInfoDto;
import com.cxx.admin.dto.file.ChunkResultDto;
import com.cxx.admin.dto.file.FileInfoDto;
import com.cxx.admin.service.FileService;
import com.cxx.admin.dto.file.FileRecordDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@RestController
@RequestMapping("/file")
@Tag(name = "文件管理")
public class FileController {
@Resource
private FileService fileService;
@Operation(summary = "上传文件")
@PostMapping(value = "", headers = "content-type=multipart/form-data")
public FileRecordDto uploadFile(@RequestPart("file") MultipartFile file,
@RequestParam("path") String path,
@RequestParam("md5") String md5) {
return fileService.uploadFile(file, path, md5);
}
@Operation(summary = "上传文件块")
@PostMapping("/chunk")
public Boolean uploadChunk(ChunkInfoDto chunkInfo,
String path) {
return fileService.uploadChunk(chunkInfo, path);
}
@Operation(summary = "验证当前文件块是否上传")
@GetMapping("/chunk")
public ChunkResultDto checkChunk(@RequestParam String identifier,
@RequestParam String filename,
@RequestParam String path) {
return fileService.checkChunk(identifier, filename, path);
}
@Operation(summary = "删除当前已上传的文件块")
@DeleteMapping("/chunk")
public Boolean deleteChunk(@RequestParam String identifier,
@RequestParam String path) {
return fileService.deleteChunk(identifier, path);
}
@Operation(summary = "合并文件")
@PostMapping("/chunk/merge")
public FileRecordDto mergeFile(@RequestParam String filename,
@RequestParam String path) {
return fileService.mergeFile(filename, path);
}
@Operation(summary = "获取文件夹目录")
@GetMapping("/catalog")
public List<FileInfoDto> getFolderInfo(@RequestParam String folderName) {
return fileService.getFolderInfo(folderName);
}
}

View File

@@ -0,0 +1,30 @@
package com.cxx.admin.controller;
import com.cxx.admin.dto.server.*;
import com.cxx.admin.service.ServerMonitorService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/server-monitor")
@Tag(name = "服务器监控")
public class ServerMonitorController {
@Resource
private ServerMonitorService serverMonitorService;
@Operation(summary = "系统信息")
@GetMapping("/system/info")
public SystemInfoDto getSystemInfo() {
return serverMonitorService.getSystemInfo();
}
@Operation(summary = "系统指标")
@GetMapping("/system/metrics")
public List<SystemMetricsDto> querySystemMetrics(@RequestParam String range) {
return serverMonitorService.querySystemMetrics(range);
}
}

View File

@@ -0,0 +1,23 @@
package com.cxx.admin.controller;
import com.cxx.common.dto.user.SessionDto;
import com.cxx.admin.service.SessionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/session")
@Tag(name = "会话管理")
public class SessionController {
@Resource
private SessionService sessionService;
@Operation(summary = "创建会话")
@PostMapping("")
public SessionDto createSession(@RequestParam String name,
@RequestParam String password) {
return sessionService.create(name, password);
}
}

View File

@@ -0,0 +1,33 @@
package com.cxx.admin.controller;
import com.cxx.admin.service.UserService;
import com.cxx.common.ReadView;
import com.cxx.common.WriteView;
import com.cxx.common.dto.user.UserDto;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
@Tag(name = "用户管理")
public class UserController {
@Resource
private UserService userService;
@Operation(summary = "查询用户")
@GetMapping("/{id}")
public @JsonView(ReadView.class) UserDto queryUser(@PathVariable("id") Long id) {
return userService.queryUser(id);
}
@Operation(summary = "更新用户")
@PutMapping("/{id}")
public Boolean updateUser(@PathVariable("id") Long id,
@JsonView(WriteView.class) @Validated @RequestBody UserDto userDto) {
return userService.updateUser(id, userDto);
}
}

View File

@@ -0,0 +1,19 @@
package com.cxx.admin.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/wechat")
@Tag(name = "微信管理")
public class WeChatController {
@Operation(summary = "获取code")
@GetMapping("/code")
public Boolean getWeChatAuthCode(@RequestParam("code") String code) {
System.out.println(code);
return Boolean.TRUE;
}
}

View File

@@ -0,0 +1,12 @@
package com.cxx.admin.dao;
import com.cxx.admin.dto.user.AccountDto;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface AccountDao {
List<AccountDto> queryAccount();
}

View File

@@ -0,0 +1,24 @@
package com.cxx.admin.dao;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cxx.common.dto.blog.BlogCategoryDto;
import com.cxx.common.dto.blog.BlogDto;
import com.cxx.common.dto.blog.BlogVisitDto;
import com.cxx.common.entity.Blog;
import com.cxx.common.vo.blog.BlogQueryVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface BlogDao {
IPage<BlogDto> queryBlogByPage(IPage<Blog> page);
BlogDto queryBlogById(@Param("id") Long id);
List<BlogCategoryDto> queryBlogCategory();
IPage<BlogVisitDto> queryBlogVisit(IPage<Blog> page);
}

View File

@@ -0,0 +1,23 @@
package com.cxx.admin.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author: Cxx
* @Date: 2024/9/24 22:41
* @Description:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FtpInfo {
private String ftpIp;
private Integer ftpPort;
private String ftpUsername;
private String ftpPassword;
}

View File

@@ -0,0 +1,22 @@
package com.cxx.admin.dto.file;
import lombok.Getter;
import lombok.Setter;
import org.springframework.web.multipart.MultipartFile;
@Getter
@Setter
public class ChunkInfoDto {
/**
* 当前文件块从1开始
*/
private Integer chunkNumber;
/**
* 文件名
*/
private String filename;
/**
* 块内容
*/
private transient MultipartFile multipartFile;
}

View File

@@ -0,0 +1,20 @@
package com.cxx.admin.dto.file;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class ChunkResultDto {
/**
* 是否跳过上传(已上传的可以直接跳过,达到秒传的效果)
*/
private boolean isSkipUpload;
/**
* 已经上传的文件块编号,可以跳过,断点续传
*/
private List<Integer> uploadedChunkList;
}

View File

@@ -0,0 +1,17 @@
package com.cxx.admin.dto.file;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
public class FileInfoDto {
private String name;
private String type;
private String size;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createDate;
}

View File

@@ -0,0 +1,14 @@
package com.cxx.admin.dto.file;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class FileRecordDto {
private Long id;
private String name;
private String md5;
private String path;
private String url;
}

View File

@@ -0,0 +1,10 @@
package com.cxx.admin.dto.server;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class CpuDto {
private double usagePercent;
}

View File

@@ -0,0 +1,13 @@
package com.cxx.admin.dto.server;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class DiskDto {
private double total;
private double used;
private double free;
private double usagePercent;
}

View File

@@ -0,0 +1,13 @@
package com.cxx.admin.dto.server;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class MemoryDto {
private double total;
private double used;
private double free;
private double usagePercent;
}

View File

@@ -0,0 +1,24 @@
package com.cxx.admin.dto.server;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
public class SystemInfoDto {
// CPU信息
private String cpuName;
private String cpuVendor;
private int cpuLogicalCores;
private int cpuPhysicalCores;
private double cpuSystemLoad;
// 操作系统信息
private String osFamily;
private String osVersion;
private String osManufacturer;
// 系统运行时间
private long systemUptime;
}

View File

@@ -0,0 +1,24 @@
package com.cxx.admin.dto.server;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
public class SystemMetricsDto {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date time;
private double cpuUsage;
private double memoryTotal;
private double memoryUsed;
private double memoryFree;
private double memoryUsage;
private double diskTotal;
private double diskUsed;
private double diskFree;
private double diskUsage;
}

View File

@@ -0,0 +1,42 @@
package com.cxx.admin.dto.user;
import com.cxx.common.PlainView;
import com.cxx.common.ReadView;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
@JsonView(PlainView.class)
public class AccountDto {
@Schema(description = "id")
@JsonView(ReadView.class)
private Long id;
@Schema(description = "账户名称")
@NotBlank(message = "账户名称不能为空")
private String accountName;
@Schema(description = "密码")
@NotBlank(message = "密码不能为空")
private String password;
@Schema(description = "用户名称")
@NotBlank(message = "用户名称不能为空")
private String username;
@Schema(description = "账户状态")
@JsonView(ReadView.class)
private Integer state;
@Schema(description = "创建日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@JsonView(ReadView.class)
private Date date;
}

View File

@@ -0,0 +1,36 @@
package com.cxx.admin.dto.user;
import com.cxx.common.PlainView;
import com.cxx.common.ReadView;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
@JsonView(PlainView.class)
public class UserSummaryDto {
@Schema(description = "id")
@JsonView(ReadView.class)
private Long id;
@Schema(description = "id")
@JsonView(ReadView.class)
private String accountName;
@Schema(description = "用户名称")
@NotBlank(message = "用户名称不能为空")
private String username;
@Schema(description = "账户状态")
private Integer state;
@Schema(description = "创建日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDateTime date;
}

View File

@@ -0,0 +1,21 @@
package com.cxx.admin.schedule;
import com.cxx.admin.service.ServerMonitorService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class ServerMonitor {
@Resource
private ServerMonitorService serverMonitorService;
// @Scheduled(fixedRate = 60000)
public void collectAndStoreMetrics() {
log.info("开始存储服务器状态");
serverMonitorService.storeSystemMetrics();
log.info("结束存储服务器状态");
}
}

View File

@@ -0,0 +1,17 @@
package com.cxx.admin.service;
import com.cxx.admin.dto.user.AccountDto;
import com.cxx.common.entity.Account;
import java.util.List;
public interface AccountService {
List<AccountDto> queryAccountList();
Account queryAccountByUserId(Long userId);
Boolean registerAccount(AccountDto accountDto);
Boolean deleteAccount(Long id);
}

View File

@@ -0,0 +1,18 @@
package com.cxx.admin.service;
import com.cxx.common.dto.bill.BillCategoryDto;
import com.cxx.common.dto.bill.BillItemDto;
public interface BillManageService {
Boolean addBillPay(BillItemDto billItemDto);
Boolean updateBillPay(Long id, BillItemDto billItemDto);
Boolean addBillBook(BillItemDto billItemDto);
Boolean updateBillBook(Long id,BillItemDto billItemDto);
Boolean addBillCategory(BillCategoryDto billCategoryDto);
Boolean updateBillCategory(Long id, BillCategoryDto billCategoryDto);
}

View File

@@ -0,0 +1,25 @@
package com.cxx.admin.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cxx.common.dto.blog.BlogCategoryDto;
import com.cxx.common.dto.blog.BlogDto;
import com.cxx.common.dto.blog.BlogVisitDto;
import com.cxx.common.vo.blog.BlogQueryVo;
import java.util.List;
public interface BlogManageService {
IPage<BlogDto> queryBlogByPage(Integer currentPage, Integer pageSize);
BlogDto queryBlogById(Long id);
List<BlogCategoryDto> queryBlogCategory();
Boolean addBlog(BlogDto blog);
Boolean updateBlog(Long id, BlogDto blog);
Boolean deleteBlog(Long id);
IPage<BlogVisitDto> queryBlogVisit(Integer currentPage, Integer pageSize);
}

View File

@@ -0,0 +1,8 @@
package com.cxx.admin.service;
public interface BudgetManageService {
Boolean addBudgetCategory(String category);
Boolean updateBudgetCategory(Long id, String category);
}

View File

@@ -0,0 +1,48 @@
package com.cxx.admin.service;
import com.cxx.admin.dto.file.ChunkInfoDto;
import com.cxx.admin.dto.file.ChunkResultDto;
import com.cxx.admin.dto.file.FileInfoDto;
import com.cxx.admin.dto.file.FileRecordDto;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
public interface FileService {
FileRecordDto uploadFile(MultipartFile file, String path, String md5);
/**
* 上传文件块
* @param chunkInfo 文件块信息
* @param path 上传路径
* @return 是否上传成功
*/
Boolean uploadChunk(ChunkInfoDto chunkInfo, String path);
/**
* 检查文件块信息
* @param identifier 唯一标识
* @param filename 文件名
* @param path 上传文件夹路径
* @return 检查结果
*/
ChunkResultDto checkChunk(String identifier, String filename, String path);
/**
* 删除文件块
* @param identifier 唯一标识
* @param path 上传文件夹路径
* @return 是否删除成功
*/
Boolean deleteChunk(String identifier, String path);
/**
* 合并文件
* @param filename 文件名
* @param path 上传文件夹路径
* @return 是否合并成功
*/
FileRecordDto mergeFile(String filename, String path);
List<FileInfoDto> getFolderInfo(String folderName);
}

View File

@@ -0,0 +1,19 @@
package com.cxx.admin.service;
import com.cxx.admin.dto.server.*;
import java.util.List;
public interface ServerMonitorService {
CpuDto getCpuInfo();
MemoryDto getMemoryInfo();
DiskDto getDiskInfo();
SystemInfoDto getSystemInfo();
void storeSystemMetrics();
List<SystemMetricsDto> querySystemMetrics(String range) ;
}

View File

@@ -0,0 +1,7 @@
package com.cxx.admin.service;
import com.cxx.common.dto.user.SessionDto;
public interface SessionService {
SessionDto create(String name, String password);
}

View File

@@ -0,0 +1,10 @@
package com.cxx.admin.service;
import com.cxx.common.dto.user.UserDto;
public interface UserService {
UserDto queryUser(Long id);
Boolean updateUser(Long id, UserDto userDto);
}

View File

@@ -0,0 +1,84 @@
package com.cxx.admin.service.impl;
import cn.dev33.satoken.secure.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cxx.admin.dao.AccountDao;
import com.cxx.admin.service.AccountService;
import com.cxx.admin.dto.user.AccountDto;
import com.cxx.common.entity.Account;
import com.cxx.common.entity.User;
import com.cxx.common.mapper.AccountMapper;
import com.cxx.common.mapper.UserMapper;
import com.cxx.framework.web.CustomException;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
public class AccountServiceImpl implements AccountService {
@Resource
private AccountDao accountDao;
@Resource
private AccountMapper accountMapper;
@Resource
private UserMapper userMapper;
@Override
public List<AccountDto> queryAccountList() {
return accountDao.queryAccount();
}
@Override
public Account queryAccountByUserId(Long userId) {
LambdaQueryWrapper<Account> queryWrapper = new LambdaQueryWrapper<>();
return accountMapper.selectOne(queryWrapper.eq(Account::getUserId, userId));
}
@Transactional
@Override
public Boolean registerAccount(AccountDto accountDto) {
LambdaQueryWrapper<Account> queryWrapper = new LambdaQueryWrapper<>();
if (accountMapper.exists(queryWrapper.eq(Account::getAccountName, accountDto.getAccountName()))) {
throw new CustomException("该账户已经存在");
}
LambdaQueryWrapper<User> userQueryWrapper = new LambdaQueryWrapper<>();
if (userMapper.exists(userQueryWrapper.eq(User::getUsername, accountDto.getUsername()))) {
throw new CustomException("该用户已注册");
}
List<Integer> results = new ArrayList<>();
User user = new User();
BeanUtils.copyProperties(accountDto, user);
results.add(userMapper.insert(user));
Account account = new Account();
account.setAccountName(accountDto.getAccountName());
account.setPassword(BCrypt.hashpw(accountDto.getPassword(), BCrypt.gensalt()));
account.setUserId(user.getId());
account.setState(0);
results.add(accountMapper.insert(account));
return results.stream().allMatch(x -> x > 0);
}
@Override
public Boolean deleteAccount(Long id) {
Account account = accountMapper.selectById(id);
if (account == null) {
throw new CustomException("该账号不存在");
}
accountMapper.deleteById(id);
return userMapper.deleteById(account.getUserId()) == 1;
}
}

View File

@@ -0,0 +1,134 @@
package com.cxx.admin.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cxx.admin.service.BillManageService;
import com.cxx.common.dto.bill.BillCategoryDto;
import com.cxx.common.dto.bill.BillItemDto;
import com.cxx.common.entity.BillBook;
import com.cxx.common.entity.BillCategory;
import com.cxx.common.entity.BillPay;
import com.cxx.common.mapper.BillBookMapper;
import com.cxx.common.mapper.BillCategoryMapper;
import com.cxx.common.mapper.BillPayMapper;
import com.cxx.framework.web.CustomException;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.Objects;
@Service
public class BillManageServiceImpl implements BillManageService {
@Resource
private BillPayMapper billPayMapper;
@Resource
private BillBookMapper billBookMapper;
@Resource
private BillCategoryMapper billCategoryMapper;
@Override
public Boolean addBillPay(BillItemDto billItemDto) {
// 1. 校验是否存在
LambdaQueryWrapper<BillPay> queryWrapper = new LambdaQueryWrapper<>();
if (billPayMapper.exists(queryWrapper.eq(BillPay::getName, billItemDto.getName()))) {
throw new CustomException("该账户已经存在");
}
// 2. 新增账户
BillPay newbillPay = new BillPay();
BeanUtils.copyProperties(billItemDto, newbillPay);
return billPayMapper.insert(newbillPay) == 1;
}
@Override
public Boolean updateBillPay(Long id, BillItemDto billItemDto) {
// 1. 校验是否存在
if (Objects.isNull(billPayMapper.selectById(id))) {
throw new RuntimeException("该账户不存在");
}
// 2. 更新账户
BillPay billPay = new BillPay();
BeanUtils.copyProperties(billItemDto, billPay);
billPay.setId(id);
return billPayMapper.updateById(billPay) == 1;
}
@Override
public Boolean addBillBook(BillItemDto billItemDto) {
// 1. 校验是否存在
LambdaQueryWrapper<BillBook> queryWrapper = new LambdaQueryWrapper<>();
if (billBookMapper.exists(queryWrapper.eq(BillBook::getName, billItemDto.getName()))) {
throw new CustomException("该账本已经存在");
}
// 2. 新增账本
BillBook billBook = new BillBook();
BeanUtils.copyProperties(billItemDto, billBook);
return billBookMapper.insert(billBook) == 1;
}
@Override
public Boolean updateBillBook(Long id, BillItemDto billItemDto) {
// 1. 校验是否存在
if (Objects.isNull(billBookMapper.selectById(id))) {
throw new CustomException("该账单不存在");
}
// 2. 更新账本
BillBook billBook = new BillBook();
BeanUtils.copyProperties(billItemDto, billBook);
billBook.setId(id);
return billBookMapper.updateById(billBook) == 1;
}
private Long getBillBook(String name) {
LambdaQueryWrapper<BillBook> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(BillBook::getName, name);
BillBook billBook = billBookMapper.selectOne(queryWrapper);
if (Objects.isNull(billBook)) {
throw new CustomException("该账本类型不存在");
}
return billBook.getId();
}
@Override
public Boolean addBillCategory(BillCategoryDto billCategoryDto) {
// 1. 校验是否存在
LambdaQueryWrapper<BillCategory> queryWrapper = new LambdaQueryWrapper<>();
if (billCategoryMapper.exists(queryWrapper.eq(BillCategory::getName, billCategoryDto.getName()))) {
throw new CustomException("该账单类型已经存在");
}
// 2. 新增账单类型
BillCategory newBillCategory = new BillCategory();
BeanUtils.copyProperties(billCategoryDto, newBillCategory);
newBillCategory.setBookId(getBillBook(billCategoryDto.getBookName()));
return billCategoryMapper.insert(newBillCategory) == 1;
}
@Override
public Boolean updateBillCategory(Long id, BillCategoryDto billCategoryDto) {
// 1. 校验是否存在
if (Objects.isNull(billCategoryMapper.selectById(id))) {
throw new CustomException("该账单类型不存在");
}
// 2. 更新账单类型
BillCategory billCategory = new BillCategory();
BeanUtils.copyProperties(billCategoryDto, billCategory);
billCategory.setBookId(getBillBook(billCategoryDto.getBookName()));
billCategory.setId(id);
return billCategoryMapper.updateById(billCategory) == 1;
}
}

View File

@@ -0,0 +1,193 @@
package com.cxx.admin.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cxx.admin.dao.BlogDao;
import com.cxx.admin.service.BlogManageService;
import com.cxx.admin.util.BlogUtils;
import com.cxx.common.dto.blog.BlogCategoryDto;
import com.cxx.common.dto.blog.BlogDto;
import com.cxx.common.dto.blog.BlogVisitDto;
import com.cxx.common.entity.Blog;
import com.cxx.common.entity.BlogCategory;
import com.cxx.common.entity.BlogContent;
import com.cxx.common.mapper.BlogCategoryMapper;
import com.cxx.common.mapper.BlogContentMapper;
import com.cxx.common.mapper.BlogMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
@Service
public class BlogManageServiceImpl implements BlogManageService {
@Resource
private BlogDao blogDao;
@Resource
private BlogMapper blogMapper;
@Resource
private BlogCategoryMapper categoryMapper;
@Resource
private BlogContentMapper contentMapper;
@Override
public IPage<BlogDto> queryBlogByPage(Integer currentPage, Integer pageSize) {
return blogDao.queryBlogByPage(new Page<>(currentPage, pageSize));
}
@Override
public BlogDto queryBlogById(Long id) {
return blogDao.queryBlogById(id);
}
@Override
public List<BlogCategoryDto> queryBlogCategory() {
return blogDao.queryBlogCategory();
}
@Transactional
@Override
public Boolean addBlog(BlogDto blogDto) {
// 1. 新增博客基本信息
Blog blog = saveOrUpdateNewBlog(blogDto);
// 2. 新增博客内容信息
blog.setContentId(addBlogContent(blogDto.getContent()));
// 3. 插入数据库
return blogMapper.insert(blog) == 1;
}
@Transactional
@Override
public Boolean updateBlog(Long id, BlogDto blogDto) {
Blog blog = saveOrUpdateNewBlog(blogDto);
blog.setId(id);
// 1. 更新博客内容信息
updateBlogContent(getBlogById(id).getContentId(), blogDto.getContent());
// 2. 校验是否删除博客类别
deleteBlogCategory(getBlogById(id).getCategoryId());
// 3. 更新数据库
return blogMapper.updateById(blog) == 1;
}
@Transactional
@Override
public Boolean deleteBlog(Long id) {
// 1. 删除博客类别
deleteBlogCategory(getBlogById(id).getCategoryId());
// 2. 删除博客内容
contentMapper.deleteById(getBlogById(id).getContentId());
// 3. 删除博客
return blogMapper.deleteById(id) == 1;
}
@Override
public IPage<BlogVisitDto> queryBlogVisit(Integer currentPage, Integer pageSize) {
return blogDao.queryBlogVisit(new Page<>(currentPage, pageSize));
}
/**
* 通过博客id获取博客
* @param id id
* @return 博客
*/
private Blog getBlogById(Long id) {
Blog blog = blogMapper.selectById(id);
if (Objects.isNull(blog)) {
throw new RuntimeException("暂无该博客");
}
return blog;
}
/**
* 增加/更新 博客
* @param blogDto 前端传入的博客内容
* @return 博客
*/
private Blog saveOrUpdateNewBlog(BlogDto blogDto) {
Blog newBlog = new Blog();
// 设置博客基础信息
newBlog.setTitle(blogDto.getTitle());
newBlog.setTopValue(blogDto.getTopValue());
newBlog.setIsGreat(blogDto.getIsGreat());
// 设置博客类别
newBlog.setCategoryId(saveOrUpdateBlogCategory(blogDto.getCategory()));
// 设置博客内容
newBlog.setSummary(BlogUtils.getBlogSummary(blogDto.getContent()));
// 设置博客字数统计
Integer wordCount = BlogUtils.getWordCount(blogDto.getContent());
newBlog.setWordCount(wordCount);
newBlog.setReadDuration(BlogUtils.getReadDuration(wordCount));
return newBlog;
}
/**
* 设置博客类别
* @param category 类别
* @return 类别id
*/
private Long saveOrUpdateBlogCategory(String category) {
// 1. 查找类别是否存在
LambdaQueryWrapper<BlogCategory> queryWrapper = new LambdaQueryWrapper<>();
BlogCategory blogCategory = categoryMapper.selectOne(queryWrapper.eq(BlogCategory::getName, category));
// 2. 如果存在则返回id 否则需要新增类别
if (Objects.isNull(blogCategory)) {
BlogCategory newBlogCategory = new BlogCategory();
newBlogCategory.setName(category);
categoryMapper.insert(newBlogCategory);
return newBlogCategory.getId();
} else {
return blogCategory.getId();
}
}
/**
* 判断是否需要删除博客类别
* @param categoryId 博客类别id
*/
private void deleteBlogCategory(Long categoryId) {
LambdaQueryWrapper<Blog> queryWrapper = new LambdaQueryWrapper<>();
if (!blogMapper.exists(queryWrapper.eq(Blog::getCategoryId, categoryId))) {
// 如果没有该类别的博客 则删除博客类别
categoryMapper.deleteById(categoryId);
}
}
/**
* 新增博客内容
* @param content 博客内容
* @return 博客内容id
*/
private Long addBlogContent(String content) {
BlogContent blogContent = new BlogContent();
blogContent.setContent(content);
contentMapper.insert(blogContent);
return blogContent.getId();
}
/**
* 更新博客内容
* @param contentId 博客内容id
* @param content 博客内容
*/
private void updateBlogContent(Long contentId, String content) {
BlogContent blogContent = new BlogContent();
blogContent.setContent(content);
blogContent.setId(contentId);
contentMapper.updateById(blogContent);
}
}

View File

@@ -0,0 +1,43 @@
package com.cxx.admin.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cxx.admin.service.BudgetManageService;
import com.cxx.common.entity.BudgetCategory;
import com.cxx.common.mapper.BudgetCategoryMapper;
import com.cxx.framework.web.CustomException;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.Objects;
@Service
public class BudgetManageServiceImpl implements BudgetManageService {
@Resource
private BudgetCategoryMapper budgetCategoryMapper;
@Override
public Boolean addBudgetCategory(String category) {
LambdaQueryWrapper<BudgetCategory> queryWrapper = new LambdaQueryWrapper<>();
if (budgetCategoryMapper.exists(queryWrapper.eq(BudgetCategory::getName, category))) {
throw new CustomException("该预算类别已存在");
}
BudgetCategory budgetCategory = new BudgetCategory();
budgetCategory.setName(category);
return budgetCategoryMapper.insert(budgetCategory) == 1;
}
@Override
public Boolean updateBudgetCategory(Long id, String category) {
if (Objects.isNull(budgetCategoryMapper.selectById(id))) {
throw new CustomException("该预算类别不存在");
}
BudgetCategory budgetCategory = new BudgetCategory();
budgetCategory.setId(id);
budgetCategory.setName(category);
return budgetCategoryMapper.updateById(budgetCategory) == 1;
}
}

View File

@@ -0,0 +1,204 @@
package com.cxx.admin.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cxx.admin.constant.FileConstant;
import com.cxx.admin.dto.file.ChunkInfoDto;
import com.cxx.admin.dto.file.ChunkResultDto;
import com.cxx.admin.dto.file.FileInfoDto;
import com.cxx.admin.service.FileService;
import com.cxx.admin.dto.file.FileRecordDto;
import com.cxx.admin.util.FileUtils;
import com.cxx.admin.util.UploadFileUtil;
import com.cxx.common.entity.FileRecord;
import com.cxx.common.mapper.FileRecordMapper;
import com.cxx.framework.web.CustomException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
@Service
@Slf4j
public class FileServiceImpl implements FileService {
@Value("${file.path}")
private String fileRootPath;
@Resource
private FileRecordMapper fileRecordMapper;
@Override
public FileRecordDto uploadFile(MultipartFile multipartFile, String path, String md5) {
UploadFileUtil.checkDirIsExist(getFilePath(path));
String filename = multipartFile.getOriginalFilename();
String newPath = path + File.separator + md5 + "." + FilenameUtils.getExtension(filename);
File newFile = new File(getFilePath(newPath));
saveFile(multipartFile, newFile);
// 这里的url需要加上 WebMvcConfig 中设置的前缀
String url = FileConstant.FILE_URL_PREFIX + File.separator + newPath;
return getFileRecord(filename, md5, path, url);
}
@Override
public Boolean uploadChunk(ChunkInfoDto chunkInfo, String path) {
MultipartFile file = chunkInfo.getMultipartFile();
try {
// 1. 校验路径是否存在
UploadFileUtil.checkDirIsExist(getFilePath(path));
// 2. 获取文件块路径
Path chunkPath = Paths.get(UploadFileUtil.generateChunkPath(getFilePath(path), chunkInfo));
// 3. 将文件块写入指定路径
Files.write(chunkPath, file.getBytes());
} catch (IOException exception) {
log.error("文件上传块失败: {}", exception.getMessage());
throw new CustomException("文件上传块失败: " + exception.getMessage());
}
return true;
}
@Override
public ChunkResultDto checkChunk(String identifier, String filename, String path) {
ChunkResultDto chunkResult = new ChunkResultDto();
List<Integer> uploadedChunkList = new ArrayList<>();
String folder = getFilePath(path) + File.separator + identifier;
String file = folder + File.separator + filename;
// 判断文件夹是否存在
if (UploadFileUtil.fileExists(folder)) {
// 先判断整个文件是否已经上传过了,如果是,则告诉前端跳过上传,实现秒传
if (UploadFileUtil.fileExists(file)) {
chunkResult.setSkipUpload(true);
chunkResult.setUploadedChunkList(uploadedChunkList);
log.info("完整文件已存在,直接跳过上传,实现秒传");
} else {
chunkResult.setSkipUpload(false);
// 获取已经上传的文件块
chunkResult.setUploadedChunkList(UploadFileUtil.getUploadedChunkList(folder, filename));
}
} else {
chunkResult.setSkipUpload(false);
chunkResult.setUploadedChunkList(uploadedChunkList);
}
return chunkResult;
}
@Override
public Boolean deleteChunk(String identifier, String path) {
String folder = getFilePath(path) + File.separator + identifier;
log.info("开始删除文件: " + folder);
// 判断文件夹是否存在
if (UploadFileUtil.fileExists(folder)) {
UploadFileUtil.deleteDirectory(folder);
} else {
throw new CustomException("文件: " + folder + "不存在");
}
return true;
}
@Override
public FileRecordDto mergeFile(String filename, String path) {
String uuid = UUID.randomUUID().toString().replace("-", "");
String newFilename = uuid + "." + FilenameUtils.getExtension(filename);
try {
UploadFileUtil.mergeFile(newFilename, getFilePath(path));
} catch (IOException exception) {
throw new CustomException("合并文件: " + filename + " 失败");
}
String newPath = path + File.separator + newFilename;
String url = FileConstant.FILE_URL_PREFIX + File.separator + newPath;
return getFileRecord(filename, uuid, path, url);
}
@Override
public List<FileInfoDto> getFolderInfo(String folderName) {
File folder = new File(folderName);
List<FileInfoDto> fileInfoList = new ArrayList<>();
if (folder.exists() && folder.isDirectory()) {
File[] files = folder.listFiles();
if (files != null) {
List<FileInfoDto> folderInfoList = new ArrayList<>();
List<FileInfoDto> fileInfoSubList = new ArrayList<>();
for (File file : files) {
FileInfoDto fileInfo = new FileInfoDto();
fileInfo.setName(file.getName());
if (file.isDirectory()) {
fileInfo.setType("文件夹");
fileInfo.setSize("-");
folderInfoList.add(fileInfo);
} else {
fileInfo.setType(FileUtils.getFileType(file.getName()));
fileInfo.setSize(FileUtils.formatSize(file.length()));
fileInfoSubList.add(fileInfo);
}
fileInfo.setCreateDate(new Date(file.lastModified()));
}
fileInfoList.addAll(folderInfoList);
fileInfoList.addAll(fileInfoSubList);
}
}
return fileInfoList;
}
private void saveFile(MultipartFile multipartFile, File file) {
// 如果文件不存在
if (!file.exists()) {
try {
// 3. 如果不存在则创建文件
multipartFile.transferTo(file);
} catch (IOException e) {
throw new CustomException("文件上传失败:{}", e.getMessage());
}
}
}
private FileRecordDto getFileRecord(String filename, String md5, String path, String url) {
FileRecord fileRecord = new FileRecord();
fileRecord.setName(filename);
fileRecord.setMd5(md5);
fileRecord.setPath(path);
fileRecord.setUrl(url);
fileRecord.setId(saveDatabase(fileRecord, md5));
// 4. 返回文件信息
FileRecordDto fileRecordDto = new FileRecordDto();
BeanUtils.copyProperties(fileRecord, fileRecordDto);
return fileRecordDto;
}
private Long saveDatabase(FileRecord fileRecord, String md5) {
LambdaQueryWrapper<FileRecord> queryWrapper = Wrappers.lambdaQuery(FileRecord.class);
FileRecord dbFileRecord = fileRecordMapper.selectOne(queryWrapper.eq(FileRecord::getMd5, md5));
if (Objects.isNull(dbFileRecord)) {
fileRecordMapper.insert(fileRecord);
return fileRecord.getId();
} else {
return dbFileRecord.getId();
}
}
private String getFilePath(String path) {
return fileRootPath + path;
}
}

View File

@@ -0,0 +1,214 @@
package com.cxx.admin.service.impl;
import com.cxx.admin.dto.server.*;
import com.cxx.admin.service.ServerMonitorService;
import com.influxdb.client.InfluxDBClient;
import com.influxdb.client.InfluxDBClientFactory;
import com.influxdb.client.QueryApi;
import com.influxdb.client.WriteApi;
import com.influxdb.client.domain.WritePrecision;
import com.influxdb.client.write.Point;
import com.influxdb.query.FluxTable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HWDiskStore;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OperatingSystem;
import java.io.File;
import java.text.DecimalFormat;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Service
public class ServerMonitorServiceImpl implements ServerMonitorService {
private final SystemInfo systemInfo = new SystemInfo();
private final HardwareAbstractionLayer hardware = systemInfo.getHardware();
private final OperatingSystem os = systemInfo.getOperatingSystem();
private final CentralProcessor processor = hardware.getProcessor();
private final InfluxDBClient client;
private final QueryApi queryApi;
private final String SYSTEM_METRICS = "system_metrics";
private final long GB = 1024 * 1024 * 1024;
private final DecimalFormat df = new DecimalFormat("0.00");
public ServerMonitorServiceImpl(
@Value("${influxdb.url}") String url,
@Value("${influxdb.token}") String token,
@Value("${influxdb.org}") String org,
@Value("${influxdb.bucket}") String bucket) {
this.client = InfluxDBClientFactory.create(url, token.toCharArray(), org, bucket);
this.queryApi = this.client.getQueryApi();
}
@Override
public CpuDto getCpuInfo() {
CpuDto cpuDto = new CpuDto();
cpuDto.setUsagePercent(formatPercent(hardware.getProcessor().getSystemCpuLoad(500) * 100));
return cpuDto;
}
@Override
public MemoryDto getMemoryInfo() {
GlobalMemory memory = hardware.getMemory();
MemoryDto memoryDto = new MemoryDto();
long total = memory.getTotal();
long free = memory.getAvailable();
long used = total - free;
memoryDto.setTotal(bytesToGb(total));
memoryDto.setUsed(bytesToGb(used));
memoryDto.setFree(bytesToGb(free));
memoryDto.setUsagePercent(formatPercent((used * 100.0) / total));
return memoryDto;
}
@Override
public DiskDto getDiskInfo() {
List<HWDiskStore> disks = hardware.getDiskStores();
if (disks.isEmpty()) {
return null;
}
// 获取根分区信息
HWDiskStore rootDisk = disks.get(0);
DiskDto diskInfo = new DiskDto();
diskInfo.setTotal(bytesToGb(rootDisk.getSize()));
// 获取文件系统使用情况
os.getFileSystem().getFileStores().stream()
.filter(fs -> fs.getMount().equals(File.separator))
.findFirst()
.ifPresent(fs -> {
long total = fs.getTotalSpace();
long free = fs.getFreeSpace();
long used = total - free;
diskInfo.setUsed(bytesToGb(used));
diskInfo.setFree(bytesToGb(free));
diskInfo.setUsagePercent(formatPercent((double) (used) * 100.0 / total));
});
return diskInfo;
}
@Override
public SystemInfoDto getSystemInfo() {
SystemInfoDto dto = new SystemInfoDto();
// 获取CPU信息
dto.setCpuName(processor.getProcessorIdentifier().getName());
dto.setCpuVendor(processor.getProcessorIdentifier().getVendor());
dto.setCpuLogicalCores(processor.getLogicalProcessorCount());
dto.setCpuPhysicalCores(processor.getPhysicalProcessorCount());
// 获取CPU负载
double[] loadAverage = processor.getSystemLoadAverage(3);
dto.setCpuSystemLoad(loadAverage[0]); // 1分钟平均负载
// 获取操作系统信息
dto.setOsFamily(os.getFamily());
dto.setOsVersion(os.getVersionInfo().getVersion());
dto.setOsManufacturer(os.getManufacturer());
// 获取系统运行时间(秒)
dto.setSystemUptime(TimeUnit.MILLISECONDS.toSeconds(systemInfo.getOperatingSystem().getSystemUptime()));
return dto;
}
@Override
public void storeSystemMetrics() {
try (WriteApi writeApi = client.getWriteApi()) {
CpuDto cpuDto = getCpuInfo();
MemoryDto memoryDto = getMemoryInfo();
DiskDto diskDto = getDiskInfo();
Point point = Point
.measurement(SYSTEM_METRICS)
.addTag("host", "localhost")
.addField("cpu_usage", cpuDto.getUsagePercent())
.addField("memory_total", memoryDto.getTotal())
.addField("memory_used", memoryDto.getUsed())
.addField("memory_free", memoryDto.getFree())
.addField("memory_usage", memoryDto.getUsagePercent())
.addField("disk_total", diskDto.getTotal())
.addField("disk_used", diskDto.getUsed())
.addField("disk_free", diskDto.getFree())
.addField("disk_usage", diskDto.getUsagePercent())
.time(Instant.now(), WritePrecision.S);
writeApi.writePoint(point);
}
}
@Override
public List<SystemMetricsDto> querySystemMetrics(String range) {
String fluxQuery = "from(bucket: \"server_metrics\")" +
"|> range(start: -" + range + ")" + // range 可以是 "1h", "24h" 等
"|> filter(fn: (r) => r._measurement == \"system_metrics\")" +
"|> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")";
List<Map<String, Object>> results = executeQuery(fluxQuery);
List<SystemMetricsDto> systemMetricsList = new ArrayList<>();
for (Map<String, Object> data : results) {
SystemMetricsDto systemMetrics = new SystemMetricsDto();
systemMetrics.setCpuUsage((Double) data.get("cpu_usage"));
systemMetrics.setMemoryTotal((Double) data.get("memory_total"));
systemMetrics.setMemoryUsed((Double) data.get("memory_used"));
systemMetrics.setMemoryFree((Double) data.get("memory_free"));
systemMetrics.setMemoryUsage((Double) data.get("memory_usage"));
systemMetrics.setDiskTotal((Double) data.get("disk_total"));
systemMetrics.setDiskUsed((Double) data.get("disk_used"));
systemMetrics.setDiskFree((Double) data.get("disk_free"));
systemMetrics.setDiskUsage((Double) data.get("disk_usage"));
systemMetrics.setTime(Date.from((Instant) data.get("time")));
systemMetricsList.add(systemMetrics);
}
return systemMetricsList;
}
private List<Map<String, Object>> executeQuery(String fluxQuery) {
List<FluxTable> tables = queryApi.query(fluxQuery);
List<Map<String, Object>> results = new ArrayList<>();
for (FluxTable table : tables) {
table.getRecords().forEach(record -> {
Map<String, Object> data = new HashMap<>();
data.put("time", record.getTime());
record.getValues().forEach((key, value) -> {
if (!key.startsWith("_")) { // 过滤掉内部字段
data.put(key, value);
}
});
results.add(data);
});
}
return results;
}
private double bytesToGb(long bytes) {
double gb = bytes / (double) GB;
return Double.parseDouble(df.format(gb));
}
public double formatPercent(double percent) {
return Double.parseDouble(df.format(percent));
}
}

View File

@@ -0,0 +1,56 @@
package com.cxx.admin.service.impl;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cxx.common.dto.user.SessionDto;
import com.cxx.admin.service.SessionService;
import com.cxx.common.dto.user.UserDto;
import com.cxx.common.entity.Account;
import com.cxx.common.entity.User;
import com.cxx.common.mapper.AccountMapper;
import com.cxx.common.mapper.UserMapper;
import com.cxx.framework.web.CustomException;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
@Service
public class SessionServiceImpl implements SessionService {
@Resource
private AccountMapper accountMapper;
@Resource
private UserMapper userMapper;
@Override
public SessionDto create(String name, String password) {
LambdaQueryWrapper<Account> queryWrapper = Wrappers.lambdaQuery(Account.class).eq(Account::getAccountName, name);
Account account = accountMapper.selectOne(queryWrapper);
if (account == null) {
throw new CustomException("账户: " + name + " 不存在");
}
if (!BCrypt.checkpw(password, account.getPassword())) {
throw new CustomException("密码不正确");
}
User user = userMapper.selectById(account.getUserId());
UserDto userDto = new UserDto();
BeanUtils.copyProperties(user, userDto);
userDto.setAccountId(account.getId());
userDto.setAccountName(name);
SessionDto sessionDto = new SessionDto();
StpUtil.login(account.getId());
sessionDto.setSaToken(StpUtil.getTokenInfo());
sessionDto.setUserInfo(userDto);
StpUtil.getSession().set("username", name);
return sessionDto;
}
}

View File

@@ -0,0 +1,53 @@
package com.cxx.admin.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cxx.admin.service.AccountService;
import com.cxx.admin.service.UserService;
import com.cxx.common.dto.user.UserDto;
import com.cxx.common.entity.User;
import com.cxx.common.mapper.UserMapper;
import com.cxx.framework.web.CustomException;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Resource
private AccountService accountService;
@Override
public UserDto queryUser(Long id) {
User user = userMapper.selectById(id);
if (user == null) {
throw new CustomException("未查询到该用户");
}
UserDto userDto = new UserDto();
BeanUtils.copyProperties(user, userDto);
userDto.setAccountName(accountService.queryAccountByUserId(id).getAccountName());
return userDto;
}
@Override
public Boolean updateUser(Long id, UserDto userDto) {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
if (!userMapper.exists(queryWrapper.eq(User::getId, id))) {
throw new CustomException("该用户不存在");
}
queryWrapper.clear();
if (userMapper.exists(queryWrapper.eq(User::getUsername, userDto.getUsername()).ne(User::getId, id))) {
throw new CustomException("该用户已经存在");
}
User user = new User();
BeanUtils.copyProperties(userDto, user);
user.setId(id);
return userMapper.updateById(user) == 1;
}
}

View File

@@ -0,0 +1,73 @@
package com.cxx.admin.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BlogUtils {
/**
* 博客概要字数
*/
private static final Integer BLOG_SUMMARY_COUNT = 100;
/**
* 阅读速度 600 字/分钟
*/
private static final Double READ_SPEED = 600.0;
/**
* 博客非统计字符
*/
private static final List<Character> excludeChart = new ArrayList<>(Arrays.asList(' ', '\n', '\t'));
/**
* 获取文本字数
*
* @param str 文本
* @return 字数
*/
public static Integer getWordCount(String str) {
Integer count = 0;
if (null == str || str.isEmpty()) {
return count;
}
for (int i = 0; i < str.length(); i++) {
char tmp = str.charAt(i);
if (!excludeChart.contains(tmp)) {
count++;
}
}
return count;
}
/**
* 获取阅读时长 单位:分钟
*
* @param wordCount 文本字数
* @return 阅读时长
*/
public static Double getReadDuration(Integer wordCount) {
return wordCount / READ_SPEED;
}
/**
* 获取简要信息
*
* @param content 博客内容
* @return 简要信息
*/
public static String getBlogSummary(String content) {
if (content.length() > BLOG_SUMMARY_COUNT) {
return content.substring(0, BLOG_SUMMARY_COUNT);
}
return content;
}
public static String getHexTimeStamp() {
return Long.toHexString(System.currentTimeMillis());
}
}

View File

@@ -0,0 +1,28 @@
package com.cxx.admin.util;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class FileUtils {
public static String getFileType(String fileName) {
int lastIndex = fileName.lastIndexOf('.');
if (lastIndex != -1) {
return fileName.substring(lastIndex + 1);
}
return "未知类型";
}
public static String formatSize(long size) {
if (size < 1024) {
return size + " B";
} else if (size < 1024 * 1024) {
return String.format("%.2f KB", (double) size / 1024);
} else if (size < 1024 * 1024 * 1024) {
return String.format("%.2f MB", (double) size / (1024 * 1024));
} else {
return String.format("%.2f GB", (double) size / (1024 * 1024 * 1024));
}
}
}

View File

@@ -0,0 +1,125 @@
package com.cxx.admin.util;
import com.cxx.admin.dto.FtpInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* @Author: Cxx
* @Date: 2024/9/24 21:49
* @Description:
*/
@Slf4j
public class FtpUtils {
private static FTPClient ftpClient;
/**
* 文件名分隔符
*/
public static String FILE_NAME_SPLIT = "_";
private static boolean connectFtp(FtpInfo ftpInfo) {
try {
ftpClient = new FTPClient();
// 设置utf-8 编码 否则中文文件名会乱码
ftpClient.setControlEncoding("utf-8");
ftpClient.connect(ftpInfo.getFtpIp(), ftpInfo.getFtpPort());
ftpClient.login(ftpInfo.getFtpUsername(), ftpInfo.getFtpPassword());
ftpClient.addProtocolCommandListener(
new PrintCommandListener(
new PrintWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8)), true));
log.info("连接ftp服务器成功");
} catch (IOException e) {
log.error("连接ftp服务器失败", e);
throw new RuntimeException("连接ftp服务器失败");
}
return true;
}
private static void disConnectFtp() {
try {
if (ftpClient != null) {
ftpClient.disconnect();
}
log.info("关闭ftp服务器成功");
} catch (IOException e) {
log.error("关闭ftp服务器失败", e);
throw new RuntimeException("关闭ftp服务器失败");
}
}
private static void closeInputStream(FileInputStream fileInputStream) {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (IOException e) {
log.error("关闭文件流失败:", e);
throw new RuntimeException("关闭文件流失败");
}
}
public static void uploadFile(FtpInfo ftpInfo, String remotePath, String fileName, File file) {
FileInputStream fileInputStream = null;
try {
if (connectFtp(ftpInfo)) {
ftpClient.changeWorkingDirectory(remotePath);
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
fileInputStream = new FileInputStream(file);
boolean uploadResult = ftpClient.storeFile(fileName, fileInputStream);
if (uploadResult) {
log.info("上传文件:{}成功", fileName);
} else {
throw new RuntimeException("上传ftp文件失败");
}
}
} catch (IOException e) {
log.error("上传ftp文件异常", e);
throw new RuntimeException("上传ftp文件异常");
} finally {
disConnectFtp();
closeInputStream(fileInputStream);
}
}
/**
* 校验是否存在文件
*
* @param ftpInfo ftp信息
* @param remotePath 文件路径
* @param md5 md5值
* @return 校验结果
*/
public static boolean checkIsExitFile(FtpInfo ftpInfo, String remotePath, String md5) {
boolean isExist = false;
try {
if (connectFtp(ftpInfo)) {
ftpClient.changeWorkingDirectory(remotePath);
FTPFile[] fileList = ftpClient.listFiles();
if (fileList != null) {
for (FTPFile imageFile : fileList) {
String fileMd5 = imageFile.getName().split(FILE_NAME_SPLIT)[0];
if (fileMd5.equals(md5)) {
log.info("文件{}已经存在", imageFile.getName());
isExist = true;
}
}
}
}
} catch (IOException e) {
log.error("校验ftp文件异常", e);
throw new RuntimeException("校验ftp文件异常");
} finally {
disConnectFtp();
}
return isExist;
}
}

View File

@@ -0,0 +1,173 @@
package com.cxx.admin.util;
import cn.hutool.core.io.file.FileNameUtil;
import com.cxx.admin.dto.file.ChunkInfoDto;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
/**
* @Author: chenxiuxiang
* @Date: 2023/7/24 14:49
* @Description: 上传文件工具类
*/
@Slf4j
public class UploadFileUtil {
private static final String FILE_SEPARATOR = "_";
/**
* 校验文件夹是否存在
*
* @param path 路径
*/
public static void checkDirIsExist(String path) {
File dir = new File(path);
if (!dir.exists() && !dir.isDirectory()) {
boolean mkdir = dir.mkdirs();
}
}
/**
* 获取文件路径
*
* @param path 上传文件夹路径
* @param chunkInfo 分片文件信息
* @return 文件路径
*/
public static String generateChunkPath(String path, ChunkInfoDto chunkInfo) {
return path + File.separator + chunkInfo.getFilename() + FILE_SEPARATOR + chunkInfo.getChunkNumber();
}
public static List<Integer> getUploadedChunkList(String folder, String filename) {
List<Integer> uploadedChunkList = new ArrayList<>();
// 获取文件夹下所有的文件
try (Stream<Path> list = Files.list(Paths.get(folder))) {
// 去除需要合并的文件
list.filter(path -> !path.getFileName().toString().equals(filename))
// 循环遍历文件 将已经上传的文件序号添加至列表中
.forEach(path -> {
String chunkPath = path.getFileName().toString();
int index = chunkPath.lastIndexOf(FILE_SEPARATOR);
uploadedChunkList.add(Integer.valueOf(chunkPath.substring(index + 1)));
});
} catch (IOException e) {
throw new RuntimeException(e);
}
return uploadedChunkList;
}
/**
* 文件合并
*/
public static void mergeFile(String filename, String path) throws IOException {
// 判断文件是否存在
String file = path + File.separator + filename;
if (fileExists(file)) {
deleteFile(file);
}
// 不存在的话,进行合并
Files.createFile(Paths.get(file));
// 获取文件夹下所有的文件
try (Stream<Path> list = Files.list(Paths.get(path))) {
// 保留后缀包含分隔符的
list.filter(chunkPath -> FileNameUtil.getSuffix(chunkPath.getFileName().toString()).contains(FILE_SEPARATOR))
// 按照文件名排序
.sorted((o1, o2) -> {
String p1 = o1.getFileName().toString();
String p2 = o2.getFileName().toString();
int i1 = p1.lastIndexOf(FILE_SEPARATOR);
int i2 = p2.lastIndexOf(FILE_SEPARATOR);
return Integer.valueOf(p1.substring(i1 + 1))
.compareTo(Integer.valueOf(p2.substring(i2 + 1)));
})
// 循环写入到文件中
.forEach(chunkPath -> {
try {
// 以追加的形式写入文件
Files.write(Paths.get(file), Files.readAllBytes(chunkPath), StandardOpenOption.APPEND);
// 合并后删除该块
Files.delete(chunkPath);
} catch (IOException exception) {
log.error("写入文件失败: " + exception.getMessage());
throw new RuntimeException(exception);
}
});
}
}
/**
* 根据文件的全路径名判断文件是否存在
*/
public static boolean fileExists(String file) {
Path path = Paths.get(file);
return Files.exists(path, LinkOption.NOFOLLOW_LINKS);
}
/**
* 删除目录(文件夹)以及目录下的文件
*
* @param sPath 被删除目录的文件路径
* @return 目录删除成功返回true否则返回false
*/
public static boolean deleteDirectory(String sPath) {
// 如果sPath不以文件分隔符结尾自动添加文件分隔符
if (!sPath.endsWith(File.separator)) {
sPath = sPath + File.separator;
}
File dirFile = new File(sPath);
// 如果dir对应的文件不存在或者不是一个目录则退出
if (!dirFile.exists() || !dirFile.isDirectory()) {
return false;
}
boolean flag = true;
// 删除文件夹下的所有文件(包括子目录)
File[] files = dirFile.listFiles();
for (File file : Objects.requireNonNull(files)) {
// 删除子文件
if (file.isFile()) {
flag = deleteFile(file.getAbsolutePath());
}
// 删除子目录
else {
flag = deleteDirectory(file.getAbsolutePath());
}
if (!flag) {
break;
}
}
if (!flag) {
return false;
}
log.info("文件删除成功");
// 删除当前目录
return dirFile.delete();
}
/**
* 删除单个文件
*
* @param sPath 被删除文件的文件名
* @return 单个文件删除成功返回true否则返回false
*/
public static boolean deleteFile(String sPath) {
boolean flag = false;
File file = new File(sPath);
// 路径为文件且不为空则进行删除
if (file.isFile() && file.exists()) {
flag = file.delete();
}
return flag;
}
}

View File

@@ -0,0 +1,15 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/sweet_hut?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: 123456
logging:
level:
root: info
config:
file:
path: D:\\temp\\
log-path: D:\\temp\\

View File

@@ -0,0 +1,16 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://mysql:3306/sweet_hut?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: docker
password: 19940822Cxx
logging:
fluentd:
host: fluent-bit
port: 24224
level:
root: info
file:
path: /upload-file/

View File

@@ -0,0 +1,16 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/sweet_hut?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: cxx
password: 19940822Cxx@1213
logging:
level:
root: info
config:
classpath:logback-spring-drop.xml
file:
path: /root/docker/upload-file/
log-path: /root/service/logs/

View File

@@ -0,0 +1,36 @@
server:
port: 8080
spring:
application:
name: @project.artifactId@
version: @project.version@
profiles:
active: dev
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
# mybatis plus mapper路径
mybatis-plus:
# mybatis plus
mapper-locations: classpath*:/mapper/**/*.xml
configuration:
log-impl:
org.apache.ibatis.logging.stdout.StdOutImpl
ftp:
ip: 192.168.31.54
port: 21
username: ftp-cxx
password: 19940822cxx
influxdb:
url: http://localhost:8086
token: i3eja-5jMQBxxF29pvKBialZ_j4y3p8iAMI86Ht3YcMTXhitIeUfx30vAv79xfhckC5YExTKj91AribNwBZBOQ==
org: cxx
bucket: server_metrics
web-starter:
base-package: com.cxx.service

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback</contextName>
<property name="log.path" value="logs/adminService"/>
<!--控制台日志格式:彩色日志-->
<!-- magenta:洋红 -->
<!-- boldMagenta:粗红-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋红 -->
<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
<!--文件日志格式-->
<property name="FILE_LOG_PATTERN"
value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n"/>
<!--编码-->
<property name="ENCODING"
value="UTF-8"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!--日志级别-->
<level>DEBUG</level>
</filter>
<encoder>
<!--日志格式-->
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!--日志字符集-->
<charset>${ENCODING}</charset>
</encoder>
</appender>
<!--输出到文件-->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志过滤器此日志文件只记录INFO级别的-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_info.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志过滤器此日志文件只记录WARN级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_warn.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志过滤器此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_error.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</configuration>

View File

@@ -0,0 +1,13 @@
<?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.cxx.admin.dao.AccountDao">
<select id="queryAccount" resultType="com.cxx.admin.dto.user.AccountDto">
SELECT b.id AS id,
b.account_name AS accountName,
a.username AS username,
b.state AS state,
b.create_time AS date
FROM user as a
LEFT JOIN account as b on a.id = b.user_id
</select>
</mapper>

View File

@@ -0,0 +1,64 @@
<?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.cxx.admin.dao.BlogDao">
<select id="queryBlogByPage" resultType="com.cxx.common.dto.blog.BlogDto">
SELECT a.id AS id,
a.title AS title,
a.top_value AS topValue,
a.is_great AS isGreat,
b.NAME AS category,
a.summary AS summary,
a.word_count AS wordCount,
a.read_duration AS readDuration,
COUNT(c.id) AS visitCount,
a.create_time AS createTime,
a.update_time AS updateTime
FROM blog a
LEFT JOIN blog_category b ON b.id = a.category_id
LEFT JOIN blog_visit c ON c.blog_id = a.id
GROUP BY a.id
ORDER BY a.top_value DESC,
a.update_time DESC
</select>
<select id="queryBlogById" resultType="com.cxx.common.dto.blog.BlogDto">
SELECT a.id AS id,
a.title AS title,
a.top_value AS topValue,
a.is_great AS isGreat,
b.NAME AS category,
a.summary AS summary,
c.content AS content,
a.word_count AS wordCount,
a.read_duration AS readDuration,
COUNT(d.id) AS visitCount,
a.create_time AS createTime,
a.update_time AS updateTime
FROM blog a
LEFT JOIN blog_category b ON b.id = a.category_id
LEFT JOIN blog_content c ON c.id = a.content_id
LEFT JOIN blog_visit d ON d.blog_id = a.id
WHERE a.id = #{id} LIMIT 1
</select>
<select id="queryBlogCategory" resultType="com.cxx.common.dto.blog.BlogCategoryDto">
SELECT b.name AS name,
count(b.name) AS count
FROM blog a
LEFT JOIN blog_category b
ON a.category_id = b.id
GROUP BY b.name
</select>
<select id="queryBlogVisit" resultType="com.cxx.common.dto.blog.BlogVisitDto">
SELECT a.ip AS ip,
a.os AS os,
a.browser AS browser,
a.uri AS uri,
b.title AS title,
a.create_time AS visitTime
FROM blog_visit AS a
LEFT JOIN blog AS b on a.blog_id = b.id
ORDER BY a.create_time DESC
</select>
</mapper>

View File

@@ -0,0 +1,21 @@
package com.cxx.admin;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
@SpringBootTest
public class AdminServiceApplicationTests {
@Test
public void contextLoads() {
}
@Test
public void TestEmail() {
}
}

6
blog-service/Dockerfile Normal file
View File

@@ -0,0 +1,6 @@
FROM docker-0.unsee.tech/openjdk:11-jdk-slim
ARG JAR_FILE=target/*.jar
COPY $JAR_FILE app.jar
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone
ENTRYPOINT ["java","-jar","/app.jar"]

1
blog-service/pom.xml Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?>

View File

@@ -0,0 +1,13 @@
package com.cxx.blog;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan(basePackages = {"com.cxx.common.mapper", "com.cxx.blog.dao"})
public class BlogServiceApplication {
public static void main(String[] args) {
SpringApplication.run(BlogServiceApplication.class, args);
}
}

View File

@@ -0,0 +1,71 @@
package com.cxx.blog.config;
import com.cxx.blog.service.BlogService;
import com.cxx.common.entity.BlogVisit;
import com.cxx.blog.util.IpUtils;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 利用AOP监控博客访问
* 1. @Before前置通知在方法执行之前执行
* 2. @After后置通知在方法执行之后执行
* 3. @AfterRunning返回通知在方法返回结果之后执行
* 4. @AfterThrowing异常通知在方法抛出异常之后执行
* 5. @Around环绕通知围绕着方法执行
*/
@Component
@Aspect
@Slf4j
public class BlogVisitMonitor {
@Resource
private BlogService blogService;
/**
* 定义切面
* 指定需要统计的包
*/
@Pointcut("execution(* com.cxx.blog.controller.BlogController.*(..))")
public void pointCut() {
}
/**
* 只有正常返回才会执行此方法
* 如果程序执行失败,则不执行此方法
*/
@AfterReturning(returning = "returnVal", pointcut = "pointCut()")
public void doAfterReturning(JoinPoint joinPoint, Object returnVal) {
// 获取接收到的HTTP请求信息
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String agent = request.getHeader("User-Agent");
UserAgent userAgent = UserAgent.parseUserAgentString(agent);
OperatingSystem os = userAgent.getOperatingSystem();
Browser browser = userAgent.getBrowser();
String uri = request.getRequestURI();
BlogVisit blogVisit = new BlogVisit();
blogVisit.setIp(IpUtils.getIpAddress(request));
blogVisit.setOs(os.getName());
blogVisit.setBrowser(browser.getName() + "-" + userAgent.getBrowserVersion());
blogVisit.setUri(uri);
blogVisit.setBlogId(0L);
if (blogVisit.getUri().contains("content")) {
blogVisit.setBlogId(Long.valueOf(uri.split("/")[3]));
}
blogService.addBlogVisit(blogVisit);
}
}

View File

@@ -0,0 +1,62 @@
package com.cxx.blog.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cxx.blog.service.BlogService;
import com.cxx.common.ReadView;
import com.cxx.common.dto.blog.BlogCategoryDto;
import com.cxx.common.dto.blog.BlogDto;
import com.cxx.common.dto.blog.BlogLatestDto;
import com.cxx.common.dto.blog.BlogStatsDto;
import com.cxx.common.vo.blog.BlogQueryVo;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/blog")
@Tag(name = "查看博客")
public class BlogController {
@Resource
private BlogService blogService;
@Operation(summary = "分页查询博客")
@GetMapping("/page")
public IPage<BlogDto> queryBlogByPage(@RequestParam("currentPage") Integer currentPage,
@RequestParam("pageSize") Integer pageSize) {
return blogService.queryBlogByPage(currentPage, pageSize);
}
@Operation(summary = "条件查询博客")
@GetMapping("/condition")
public @JsonView(ReadView.class) List<BlogDto> queryBlogByCondition(BlogQueryVo query) {
return blogService.queryBlogByCondition(query);
}
@Operation(summary = "查询博客内容")
@GetMapping("/content/{id}")
public @JsonView(ReadView.class) BlogDto queryBlogById(@PathVariable("id") long id) {
return blogService.queryBlogById(id);
}
@Operation(summary = "查询博客分类")
@GetMapping("/category")
public List<BlogCategoryDto> queryBlogCategory() {
return blogService.queryBlogCategory();
}
@Operation(summary = "查询博客统计信息")
@GetMapping("/stats")
public BlogStatsDto queryBlogStats() {
return blogService.queryBlogStats();
}
@Operation(summary = "查询近期博客")
@GetMapping("/latest")
public List<BlogLatestDto> queryLatestBlog() {
return blogService.queryLatestBlog();
}
}

View File

@@ -0,0 +1,25 @@
package com.cxx.blog.dao;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cxx.common.dto.blog.BlogCategoryDto;
import com.cxx.common.dto.blog.BlogDto;
import com.cxx.common.dto.blog.BlogLatestDto;
import com.cxx.common.entity.Blog;
import com.cxx.common.vo.blog.BlogQueryVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface BlogDao {
IPage<BlogDto> queryBlogByPage(IPage<Blog> page);
List<BlogDto> queryBlogByCondition(@Param("query") BlogQueryVo query);
BlogDto queryBlogById(@Param("id") Long id);
List<BlogCategoryDto> queryBlogCategory();
List<BlogLatestDto> queryLatestBlog();
}

View File

@@ -0,0 +1,28 @@
package com.cxx.blog.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cxx.common.dto.blog.BlogCategoryDto;
import com.cxx.common.dto.blog.BlogDto;
import com.cxx.common.dto.blog.BlogLatestDto;
import com.cxx.common.dto.blog.BlogStatsDto;
import com.cxx.common.entity.BlogVisit;
import com.cxx.common.vo.blog.BlogQueryVo;
import java.util.List;
public interface BlogService {
IPage<BlogDto> queryBlogByPage(Integer currentPage, Integer pageSize);
List<BlogDto> queryBlogByCondition(BlogQueryVo query);
BlogDto queryBlogById(Long id);
List<BlogCategoryDto> queryBlogCategory();
BlogStatsDto queryBlogStats();
List<BlogLatestDto> queryLatestBlog();
void addBlogVisit(BlogVisit blogVisit);
}

View File

@@ -0,0 +1,73 @@
package com.cxx.blog.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cxx.blog.dao.BlogDao;
import com.cxx.blog.service.BlogService;
import com.cxx.common.dto.blog.BlogCategoryDto;
import com.cxx.common.dto.blog.BlogDto;
import com.cxx.common.dto.blog.BlogLatestDto;
import com.cxx.common.dto.blog.BlogStatsDto;
import com.cxx.common.entity.BlogVisit;
import com.cxx.common.mapper.BlogCategoryMapper;
import com.cxx.common.mapper.BlogMapper;
import com.cxx.common.mapper.BlogVisitMapper;
import com.cxx.common.vo.blog.BlogQueryVo;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BlogServiceImpl implements BlogService {
@Resource
private BlogDao blogDao;
@Resource
private BlogMapper blogMapper;
@Resource
private BlogCategoryMapper categoryMapper;
@Resource
private BlogVisitMapper visitMapper;
@Override
public IPage<BlogDto> queryBlogByPage(Integer currentPage, Integer pageSize) {
return blogDao.queryBlogByPage(new Page<>(currentPage, pageSize));
}
@Override
public List<BlogDto> queryBlogByCondition(BlogQueryVo query) {
return blogDao.queryBlogByCondition(query);
}
@Override
public BlogDto queryBlogById(Long id) {
return blogDao.queryBlogById(id);
}
@Override
public List<BlogCategoryDto> queryBlogCategory() {
return blogDao.queryBlogCategory();
}
@Override
public BlogStatsDto queryBlogStats() {
BlogStatsDto blogStats = new BlogStatsDto();
blogStats.setBlogCount(blogMapper.selectCount(null));
blogStats.setCategoryCount(categoryMapper.selectCount(null));
return blogStats;
}
@Override
public List<BlogLatestDto> queryLatestBlog() {
return blogDao.queryLatestBlog();
}
@Override
public void addBlogVisit(BlogVisit blogVisit) {
visitMapper.insert(blogVisit);
}
}

View File

@@ -0,0 +1,36 @@
package com.cxx.blog.util;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BlogUtils {
public static File multipartFileToFile(MultipartFile multipartFile) {
if (multipartFile.getOriginalFilename() == null) {
return null;
}
File file = new File(multipartFile.getOriginalFilename());
try {
InputStream ins = multipartFile.getInputStream();
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
} catch (Exception e) {
e.printStackTrace();
}
return file;
}
}

View File

@@ -0,0 +1,59 @@
package com.cxx.blog.util;
import jakarta.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IpUtils {
private static final String UNKNOWN = "unknown";
private static final String LOCALHOST_IP = "127.0.0.1";
// 客户端与服务器同为一台机器,获取的 ip 有时候是 ipv6 格式
private static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1";
private static final String SEPARATOR = ",";
/**
* 根据 HttpServletRequest 获取 IP
* @param request 请求
* @return ip
*/
public static String getIpAddress(HttpServletRequest request) {
if (request == null) {
return "unknown";
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
if (LOCALHOST_IP.equalsIgnoreCase(ip) || LOCALHOST_IPV6.equalsIgnoreCase(ip)) {
// 根据网卡取本机配置的 IP
InetAddress iNet = null;
try {
iNet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
if (iNet != null)
ip = iNet.getHostAddress();
}
}
// 对于通过多个代理的情况,分割出第一个 IP
if (ip != null && ip.length() > 15) {
if (ip.indexOf(SEPARATOR) > 0) {
ip = ip.substring(0, ip.indexOf(SEPARATOR));
}
}
return LOCALHOST_IPV6.equals(ip) ? LOCALHOST_IP : ip;
}
}

View File

@@ -0,0 +1,11 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/sweet_hut?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: 123456
logging:
level:
root: info
config:

View File

@@ -0,0 +1,13 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://mysql:3306/sweet_hut?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: docker
password: 19940822Cxx
logging:
fluentd:
host: fluent-bit
port: 24224
level:
root: info

View File

@@ -0,0 +1,12 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/sweet_hut?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: cxx
password: 19940822Cxx@1213
logging:
level:
root: info
config:
classpath:logback-spring-drop.xml

View File

@@ -0,0 +1,20 @@
server:
port: 8082
spring:
application:
name: @project.artifactId@
version: @project.version@
profiles:
active: dev
# mybatis plus mapper路径
mybatis-plus:
# mybatis plus
mapper-locations: classpath*:/mapper/**/*.xml
configuration:
log-impl:
org.apache.ibatis.logging.stdout.StdOutImpl
web-starter:
base-package: com.cxx.blog

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback</contextName>
<property name="log.path" value="logs/blogService"/>
<!--控制台日志格式:彩色日志-->
<!-- magenta:洋红 -->
<!-- boldMagenta:粗红-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋红 -->
<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
<!--文件日志格式-->
<property name="FILE_LOG_PATTERN"
value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n"/>
<!--编码-->
<property name="ENCODING"
value="UTF-8"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!--日志级别-->
<level>DEBUG</level>
</filter>
<encoder>
<!--日志格式-->
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!--日志字符集-->
<charset>${ENCODING}</charset>
</encoder>
</appender>
<!--输出到文件-->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志过滤器此日志文件只记录INFO级别的-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_info.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志过滤器此日志文件只记录WARN级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_warn.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志过滤器此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_error.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</configuration>

View File

@@ -0,0 +1,90 @@
<?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.cxx.blog.dao.BlogDao">
<select id="queryBlogByPage" resultType="com.cxx.common.dto.blog.BlogDto">
SELECT a.id AS id,
a.title AS title,
a.top_value AS topValue,
a.is_great AS isGreat,
b.NAME AS category,
a.summary AS summary,
a.word_count AS wordCount,
a.read_duration AS readDuration,
COUNT(c.id) AS visitCount,
a.create_time AS createTime,
a.update_time AS updateTime
FROM blog a
LEFT JOIN blog_category b ON b.id = a.category_id
LEFT JOIN blog_visit c ON c.blog_id = a.id
GROUP BY a.id
ORDER BY a.top_value DESC,
a.update_time DESC
</select>
<select id="queryBlogByCondition" resultType="com.cxx.common.dto.blog.BlogDto">
SELECT a.id AS id,
a.title AS title,
a.top_value AS topValue,
a.is_great AS isGreat,
b.name AS category,
a.summary AS summary,
c.content AS content,
a.word_count AS wordCount,
a.read_duration AS readDuration,
a.create_time AS createTime,
a.update_time AS updateTime
FROM blog a
LEFT JOIN blog_category b on a.category_id = b.id
LEFT JOIN blog_content c on a.content_id = c.id
<where>
<if test="query.category != null and query.category != ''">
b.name = #{query.category}
</if>
<if test="query.title != null and query.title != ''">
AND a.title LIKE CONCAT('%',#{query.title},'%')
</if>
<if test="query.year != null">
AND YEAR(a.create_time) = #{query.year}
</if>
</where>
ORDER BY a.update_time DESC
</select>
<select id="queryBlogById" resultType="com.cxx.common.dto.blog.BlogDto">
SELECT a.id AS id,
a.title AS title,
a.top_value AS topValue,
a.is_great AS isGreat,
b.NAME AS category,
a.summary AS summary,
c.content AS content,
a.word_count AS wordCount,
a.read_duration AS readDuration,
COUNT(d.id) AS visitCount,
a.create_time AS createTime,
a.update_time AS updateTime
FROM blog a
LEFT JOIN blog_category b ON b.id = a.category_id
LEFT JOIN blog_content c ON c.id = a.content_id
LEFT JOIN blog_visit d ON d.blog_id = a.id
WHERE a.id = #{id}
LIMIT 1
</select>
<select id="queryBlogCategory" resultType="com.cxx.common.dto.blog.BlogCategoryDto">
SELECT b.name AS name,
count(b.name) AS count
FROM blog a
LEFT JOIN blog_category b
ON a.category_id = b.id
GROUP BY b.name
</select>
<select id="queryLatestBlog" resultType="com.cxx.common.dto.blog.BlogLatestDto">
SELECT id AS id,
title AS title
FROM blog
ORDER BY update_time DESC
LIMIT 5
</select>
</mapper>

View File

@@ -0,0 +1,18 @@
package com.cxx.blog;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @Author: Cxx
* @Date: 2024/9/22 0:17
* @Description:
*/
@SpringBootTest
public class BlogServiceTest {
@Test
public void test() {
}
}

38
common/.gitignore vendored Normal file
View File

@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

40
common/pom.xml Normal file
View File

@@ -0,0 +1,40 @@
<?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.cxx</groupId>
<artifactId>sweet-hut-service</artifactId>
<version>2.0.0</version>
</parent>
<artifactId>common</artifactId>
<version>2.0.0</version>
<packaging>jar</packaging>
<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.cxx</groupId>
<artifactId>starter-web</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.cxx</groupId>
<artifactId>starter-jdbc</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.cxx</groupId>
<artifactId>starter-logging</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,4 @@
package com.cxx.common;
public interface PlainView {
}

View File

@@ -0,0 +1,4 @@
package com.cxx.common;
public interface ReadView extends PlainView {
}

View File

@@ -0,0 +1,4 @@
package com.cxx.common;
public interface WriteView extends PlainView {
}

View File

@@ -0,0 +1,11 @@
package com.cxx.common.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class StatsChartDto {
private String name;
private Double value;
}

View File

@@ -0,0 +1,18 @@
package com.cxx.common.dto.bill;
import com.cxx.common.PlainView;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@JsonView(PlainView.class)
public class BillCategoryDto extends BillItemDto{
@Schema(description = "账本名称")
private String bookName;
@Schema(description = "账本类型")
private String type;
}

View File

@@ -0,0 +1,26 @@
package com.cxx.common.dto.bill;
import com.cxx.common.PlainView;
import com.cxx.common.ReadView;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@JsonView(PlainView.class)
public class BillItemDto {
@Schema(description = "id")
@JsonView(ReadView.class)
private Long id;
@Schema(description = "名称")
private String name;
@Schema(description = "图标")
private String icon;
@Schema(description = "颜色")
private String color;
}

View File

@@ -0,0 +1,52 @@
package com.cxx.common.dto.bill;
import com.cxx.common.PlainView;
import com.cxx.common.ReadView;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.List;
@Getter
@Setter
@JsonView(PlainView.class)
public class BillRecordDto {
@Schema(description = "id")
@JsonView(ReadView.class)
private Long id;
@Schema(description = "账本名称")
private String bookName;
@Schema(description = "账单类型")
private String type;
@Schema(description = "账单类别")
private String category;
@Schema(description = "账单地点")
private String location;
@Schema(description = "账单账户")
private String payAccount;
@Schema(description = "账单金额")
private Double amount;
@Schema(description = "账单内容")
private String content;
@Schema(description = "账单备注")
private String remark;
@Schema(description = "账单图片")
private List<String> imageList;
@Schema(description = "账单日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date date;
}

View File

@@ -0,0 +1,49 @@
package com.cxx.common.dto.bill;
import com.cxx.common.PlainView;
import com.cxx.common.ReadView;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.List;
@Getter
@Setter
@JsonView(PlainView.class)
public class BillSummaryDto {
@Schema(description = "id")
@JsonView(ReadView.class)
private Long id;
@Schema(description = "账本名称")
private String bookName;
@Schema(description = "账单类型")
private String type;
@Schema(description = "账单类别")
private String category;
@Schema(description = "账单地点")
private String location;
@Schema(description = "账单账户")
private String payAccount;
@Schema(description = "账单金额")
private Double amount;
@Schema(description = "账单内容")
private String content;
@Schema(description = "账单备注")
private String remark;
@Schema(description = "账单日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date date;
}

View File

@@ -0,0 +1,15 @@
package com.cxx.common.dto.blog;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class BlogCategoryDto {
@Schema(description = "类别名称")
private String name;
@Schema(description = "类别数量")
private Integer count;
}

View File

@@ -0,0 +1 @@
package com.cxx.common.dto.blog;

View File

@@ -0,0 +1,20 @@
package com.cxx.common.dto.blog;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @Author: Cxx
* @Date: 2024/9/16 19:47
* @Description:
*/
@Getter
@Setter
public class BlogLatestDto {
@Schema(description = "id")
private Long id;
@Schema(description = "博客标题")
private String title;
}

View File

@@ -0,0 +1,20 @@
package com.cxx.common.dto.blog;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @Author: Cxx
* @Date: 2024/9/16 19:47
* @Description:
*/
@Getter
@Setter
public class BlogStatsDto {
@Schema(description = "博客数量")
private Long blogCount;
@Schema(description = "博客类别数量")
private Long categoryCount;
}

View File

@@ -0,0 +1,31 @@
package com.cxx.common.dto.blog;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
public class BlogVisitDto {
@Schema(description = "访问ip")
private String ip;
@Schema(description = "访问系统")
private String os;
@Schema(description = "访问浏览器")
private String browser;
@Schema(description = "访问路径")
private String uri;
@Schema(description = "博客标题")
private String title;
@Schema(description = "访问时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date visitTime;
}

View File

@@ -0,0 +1,51 @@
package com.cxx.common.dto.budget;
import com.cxx.common.PlainView;
import com.cxx.common.ReadView;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
@JsonView(PlainView.class)
public class BudgetDto {
@Schema(description = "id")
@JsonView(ReadView.class)
private Long id;
@Schema(description = "预算类别")
@NotBlank(message = "类别不能为空")
private String category;
@Schema(description = "预算项目")
@NotBlank(message = "项目不能为空")
private String project;
@Schema(description = "预算内容")
private String content;
@Schema(description = "供应商")
private String vendor;
@Schema(description = "预算支出")
@PositiveOrZero(message = "预算支出支出必须大于等于0")
private Double budget;
@Schema(description = "实际支出")
@PositiveOrZero(message = "实际支出支出必须大于等于0")
private Double actual;
@Schema(description = "支付时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date payTime;
@Schema(description = "备注")
private String remake;
}

View File

@@ -0,0 +1,10 @@
package com.cxx.common.dto.message;
import lombok.Data;
@Data
public class MailMessageDto {
private String to;
private String subject;
private String body;
}

View File

@@ -0,0 +1,62 @@
package com.cxx.common.dto.plan;
import com.cxx.common.PlainView;
import com.cxx.common.ReadView;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
@JsonView(PlainView.class)
public class PlanDto {
@Schema(description = "id")
@JsonView(ReadView.class)
private Long id;
@Schema(description = "标题")
@NotBlank(message = "标题不能为空")
private String title;
@Schema(description = "内容")
private String content;
@Schema(description = "日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date date;
@Schema(description = "开始时间")
@JsonFormat(pattern = "HH:mm", timezone = "GMT+8")
private Date startTime;
@Schema(description = "结束时间")
@JsonFormat(pattern = "HH:mm", timezone = "GMT+8")
private Date endTime;
@Schema(description = "提醒时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date alterTime;
@Schema(description = "邮箱地址")
private String email;
@Schema(description = "任务id")
private Long taskId;
@Schema(description = "标签")
private String tag;
@Schema(description = "是否置顶")
private Integer isTop;
@Schema(description = "优先级")
private Integer priority;
@Schema(description = "是否完成")
private Integer completed;
}

View File

@@ -0,0 +1,21 @@
package com.cxx.common.dto.plan;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PlanStatsDto {
@Schema(description = "计划总数")
private Integer totalCount;
@Schema(description = "完成总数")
private Integer completedCount;
@Schema(description = "当前计划")
private Integer currentCount;
@Schema(description = "过期计划")
private Integer remainCount;
}

View File

@@ -0,0 +1,25 @@
package com.cxx.common.dto.task;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
public class TaskDto {
@Schema(description = "任务类型")
private String type;
@Schema(description = "任务内容")
private String content;
@Schema(description = "执行时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date executeTime;
@Schema(description = "定时任务表达式")
private String cronExpression;
}

View File

@@ -0,0 +1,17 @@
package com.cxx.common.dto.user;
import cn.dev33.satoken.stp.SaTokenInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class SessionDto {
@Schema(description = "令牌")
private SaTokenInfo saToken;
@Schema(description = "用户信息")
private UserDto userInfo;
}

Some files were not shown because too many files have changed in this diff Show More