首次提交后端接口
This commit is contained in:
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
*.class
|
||||
*.log
|
||||
*.ctxt
|
||||
.mtj.tmp/
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
hs_err_pid*
|
||||
.classpath
|
||||
.project
|
||||
.settings
|
||||
.target
|
||||
.idea
|
||||
*.iml
|
||||
156
README.md
Normal file
156
README.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Nanxiislet Admin Backend
|
||||
|
||||
南溪小岛管理后台 Spring Boot 后端服务
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **框架**: Spring Boot 3.3.4
|
||||
- **Java 版本**: JDK 21
|
||||
- **数据库**: MySQL 8.0+
|
||||
- **缓存**: Redis
|
||||
- **ORM**: MyBatis-Plus 3.5.7
|
||||
- **认证**: Sa-Token 1.39.0
|
||||
- **API 文档**: Knife4j 4.5.0 (OpenAPI 3)
|
||||
- **工具库**: Hutool、Lombok、EasyExcel
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
src/main/java/com/nanxiislet/admin/
|
||||
├── common/ # 公共模块
|
||||
│ ├── base/ # 基础类
|
||||
│ ├── exception/ # 异常处理
|
||||
│ └── result/ # 响应结果
|
||||
├── config/ # 配置类
|
||||
├── controller/ # 控制器
|
||||
├── dto/ # 数据传输对象
|
||||
│ ├── auth/ # 认证相关
|
||||
│ └── query/ # 查询条件
|
||||
├── entity/ # 实体类
|
||||
├── mapper/ # MyBatis Mapper
|
||||
└── service/ # 服务层
|
||||
└── impl/ # 服务实现
|
||||
```
|
||||
|
||||
## 功能模块
|
||||
|
||||
### 认证管理
|
||||
|
||||
- 用户登录/登出
|
||||
- 验证码
|
||||
- 获取当前用户信息
|
||||
|
||||
### 用户管理
|
||||
|
||||
- 用户 CRUD
|
||||
- 密码重置/修改
|
||||
- 用户状态管理
|
||||
|
||||
### 平台管理
|
||||
|
||||
- 项目管理
|
||||
- 服务器管理
|
||||
- 域名管理
|
||||
|
||||
### 财务管理
|
||||
|
||||
- 收入管理
|
||||
- 支出管理
|
||||
- 预算管理
|
||||
- 报销管理
|
||||
- 发票管理
|
||||
- 结算管理
|
||||
|
||||
### 仪表盘
|
||||
|
||||
- 统计数据
|
||||
- 快速概览
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 环境要求
|
||||
|
||||
- JDK 21+
|
||||
- Maven 3.8+
|
||||
- MySQL 8.0+
|
||||
- Redis 6.0+
|
||||
|
||||
### 2. 数据库初始化
|
||||
|
||||
执行 `src/main/resources/db/init.sql` 脚本创建数据库和表。
|
||||
|
||||
```bash
|
||||
mysql -h 192.168.9.100 -u root -p < src/main/resources/db/init.sql
|
||||
```
|
||||
|
||||
### 3. 配置修改
|
||||
|
||||
编辑 `src/main/resources/application.yml`,修改数据库和 Redis 连接信息。
|
||||
|
||||
### 4. 启动项目
|
||||
|
||||
```bash
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
或打包后运行:
|
||||
|
||||
```bash
|
||||
mvn package -DskipTests
|
||||
java -jar target/nanxiislet-admin-1.0.0-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
### 5. 访问 API 文档
|
||||
|
||||
启动后访问: http://localhost:8080/api/doc.html
|
||||
|
||||
## 默认账户
|
||||
|
||||
- 用户名: `admin`
|
||||
- 密码: `admin123`
|
||||
|
||||
## API 说明
|
||||
|
||||
### 认证接口
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| ---- | --------------- | ---------------- |
|
||||
| GET | /auth/captcha | 获取验证码 |
|
||||
| POST | /auth/login | 用户登录 |
|
||||
| POST | /auth/logout | 用户登出 |
|
||||
| GET | /auth/user-info | 获取当前用户信息 |
|
||||
|
||||
### 用户管理
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| ------ | -------------------------------- | -------- |
|
||||
| GET | /system/user/list | 用户列表 |
|
||||
| GET | /system/user/{id} | 用户详情 |
|
||||
| POST | /system/user | 新增用户 |
|
||||
| PUT | /system/user/{id} | 更新用户 |
|
||||
| DELETE | /system/user/{id} | 删除用户 |
|
||||
| POST | /system/user/{id}/reset-password | 重置密码 |
|
||||
| POST | /system/user/change-password | 修改密码 |
|
||||
|
||||
### 请求头
|
||||
|
||||
需要登录的接口请在请求头中携带 Token:
|
||||
|
||||
```
|
||||
Authorization: Bearer {token}
|
||||
```
|
||||
|
||||
## 响应格式
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "操作成功",
|
||||
"data": {},
|
||||
"timestamp": 1704540000000
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
142
docs/domain_integration_guide.md
Normal file
142
docs/domain_integration_guide.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# 域名管理集成指南
|
||||
|
||||
## 1Panel 接口文档
|
||||
|
||||
1Panel API 文档地址:http://47.109.57.58:42588/1panel/swagger/index.html
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 数据流
|
||||
|
||||
```
|
||||
前端(域名管理页面)
|
||||
↓
|
||||
后端(PlatformDomainController)
|
||||
↓
|
||||
后端(PlatformDomainService)
|
||||
↓ 存储/读取
|
||||
数据库(platform_domain表)
|
||||
↓ 部署时调用
|
||||
1Panel API(创建网站/申请证书/配置HTTPS)
|
||||
```
|
||||
|
||||
### 核心流程
|
||||
|
||||
1. **域名创建**:用户在前端新建域名 → 后端存储到数据库(状态为 `pending`,部署状态为 `not_deployed`)
|
||||
|
||||
2. **项目关联**:用户在项目管理中绑定域名 → 自动生成项目地址和部署路径
|
||||
|
||||
3. **项目部署**(点击"部署"按钮时触发):
|
||||
- 检查/创建 1Panel 网站
|
||||
- 如果启用 HTTPS,检查/申请 SSL 证书
|
||||
- 配置 HTTPS(强制跳转)
|
||||
- 更新域名和项目状态
|
||||
|
||||
## 后端接口
|
||||
|
||||
### 域名管理 API
|
||||
|
||||
| 方法 | 路径 | 描述 |
|
||||
| ------ | ------------------------------- | ---------------------- |
|
||||
| GET | /platform/domain/list | 分页获取域名列表 |
|
||||
| GET | /platform/domain/all | 获取所有域名 |
|
||||
| GET | /platform/domain/{id} | 获取域名详情 |
|
||||
| POST | /platform/domain | 新增域名 |
|
||||
| PUT | /platform/domain/{id} | 更新域名 |
|
||||
| DELETE | /platform/domain/{id} | 删除域名 |
|
||||
| GET | /platform/domain/stats | 域名统计信息 |
|
||||
| POST | /platform/domain/deploy | 部署域名到 1Panel |
|
||||
| POST | /platform/domain/{id}/check-dns | 检查 DNS 解析状态 |
|
||||
| POST | /platform/domain/{id}/sync | 从 1Panel 同步域名信息 |
|
||||
|
||||
### 部署请求参数
|
||||
|
||||
```json
|
||||
{
|
||||
"domainId": 1,
|
||||
"enableHttps": true,
|
||||
"acmeAccountId": 1,
|
||||
"dnsAccountId": 1,
|
||||
"createIfNotExist": true
|
||||
}
|
||||
```
|
||||
|
||||
### 部署结果
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "部署流程完成",
|
||||
"websiteId": 123,
|
||||
"sslCertificateId": 456,
|
||||
"steps": [
|
||||
{
|
||||
"step": "检查网站",
|
||||
"status": "success",
|
||||
"message": "网站已存在,ID: 123"
|
||||
},
|
||||
{
|
||||
"step": "检查SSL证书",
|
||||
"status": "success",
|
||||
"message": "证书已存在,ID: 456"
|
||||
},
|
||||
{ "step": "配置HTTPS", "status": "success", "message": "HTTPS配置成功" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
### platform_domain 表
|
||||
|
||||
新增字段(V6\_\_domain_enhance.sql):
|
||||
|
||||
- `panel_website_id` - 1Panel 网站 ID
|
||||
- `panel_ssl_id` - 1Panel 证书 ID
|
||||
- `site_path` - 网站目录路径
|
||||
- `alias` - 网站别名
|
||||
- `deploy_status` - 部署状态(not_deployed/deploying/deployed/failed)
|
||||
- `last_deploy_time` - 最后部署时间
|
||||
- `last_deploy_message` - 最后部署消息
|
||||
|
||||
## 前端集成
|
||||
|
||||
### API 文件
|
||||
|
||||
`src/api/domain.ts` - 域名管理 API 封装
|
||||
|
||||
### 使用示例
|
||||
|
||||
```typescript
|
||||
import {
|
||||
getDomainList,
|
||||
createDomain,
|
||||
deployDomain,
|
||||
checkDomainDns,
|
||||
} from "@/api/domain";
|
||||
|
||||
// 获取域名列表
|
||||
const { records, total } = await getDomainList({ page: 1, pageSize: 10 });
|
||||
|
||||
// 创建域名
|
||||
await createDomain({
|
||||
domain: "example.com",
|
||||
serverId: 1,
|
||||
port: 80,
|
||||
});
|
||||
|
||||
// 部署域名
|
||||
const result = await deployDomain({
|
||||
domainId: 1,
|
||||
enableHttps: true,
|
||||
acmeAccountId: 1,
|
||||
dnsAccountId: 1,
|
||||
});
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 部署操作是异步的,可能需要等待证书申请完成(最长 120 秒)
|
||||
2. 首次部署会自动创建网站目录,路径为 `/opt/1panel/www/sites/{域名}/index`
|
||||
3. SSL 证书申请需要配置 Acme 账户和 DNS 账户(在 1Panel 中配置)
|
||||
4. 部署完成后,项目的"部署"按钮会自动同步状态
|
||||
49
init-database.bat
Normal file
49
init-database.bat
Normal file
@@ -0,0 +1,49 @@
|
||||
@echo off
|
||||
REM 数据库初始化脚本
|
||||
REM 请先配置好MySQL可执行文件路径
|
||||
|
||||
set MYSQL_PATH=mysql
|
||||
set HOST=192.168.9.100
|
||||
set PORT=3306
|
||||
set USER=root
|
||||
set PASSWORD=mysql_23fw7s
|
||||
set DATABASE=nanxiislet
|
||||
|
||||
echo ================================
|
||||
echo 南溪小岛管理后台 - 数据库初始化
|
||||
echo ================================
|
||||
echo.
|
||||
echo 数据库地址: %HOST%:%PORT%
|
||||
echo 数据库名称: %DATABASE%
|
||||
echo.
|
||||
|
||||
REM 先创建数据库
|
||||
echo 正在创建数据库...
|
||||
%MYSQL_PATH% -h%HOST% -P%PORT% -u%USER% -p%PASSWORD% -e "CREATE DATABASE IF NOT EXISTS %DATABASE% DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
||||
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
echo 创建数据库失败!请检查MySQL连接配置。
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM 导入初始化脚本
|
||||
echo 正在导入数据表...
|
||||
%MYSQL_PATH% -h%HOST% -P%PORT% -u%USER% -p%PASSWORD% %DATABASE% < src\main\resources\db\init.sql
|
||||
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
echo 导入数据失败!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ================================
|
||||
echo 数据库初始化成功!
|
||||
echo ================================
|
||||
echo.
|
||||
echo 默认管理员账号:
|
||||
echo 用户名: admin
|
||||
echo 密码: admin123
|
||||
echo.
|
||||
pause
|
||||
BIN
logs/nanxiislet-admin.log.2026-01-21.0.gz
Normal file
BIN
logs/nanxiislet-admin.log.2026-01-21.0.gz
Normal file
Binary file not shown.
223
pom.xml
Normal file
223
pom.xml
Normal file
@@ -0,0 +1,223 @@
|
||||
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.nanxiislet</groupId>
|
||||
<artifactId>nanxiislet-admin</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>NanxiIslet Admin Backend</name>
|
||||
<description>南溪小岛管理后台 - Spring Boot后端服务</description>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<spring-boot.version>3.3.4</spring-boot.version>
|
||||
<mybatis-plus.version>3.5.7</mybatis-plus.version>
|
||||
<jwt.version>0.12.6</jwt.version>
|
||||
<hutool.version>5.8.31</hutool.version>
|
||||
<knife4j.version>4.5.0</knife4j.version>
|
||||
<sa-token.version>1.39.0</sa-token.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot Starters -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Database -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-3-starter</artifactId>
|
||||
<version>1.2.23</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis-jackson</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Knife4j OpenAPI Documentation -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Hutool 工具类 -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Security Crypto (for BCryptPasswordEncoder) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache Commons -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- File Upload -->
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.17.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Excel 处理 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>4.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 验证码 -->
|
||||
<dependency>
|
||||
<groupId>pro.fessional</groupId>
|
||||
<artifactId>kaptcha</artifactId>
|
||||
<version>2.3.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>aliyun</id>
|
||||
<name>Aliyun Maven Repository</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.nanxiislet.admin;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* 南溪小岛管理后台 - 启动类
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.nanxiislet.admin.mapper")
|
||||
@EnableTransactionManagement
|
||||
@EnableAsync
|
||||
@EnableScheduling
|
||||
public class NanxiisletAdminApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(NanxiisletAdminApplication.class, args);
|
||||
System.out.println("====================================================");
|
||||
System.out.println(" 南溪小岛管理后台服务启动成功!");
|
||||
System.out.println(" 接口文档地址: http://localhost:8080/api/doc.html");
|
||||
System.out.println("====================================================");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.nanxiislet.admin.common.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 实体基类
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
public class BaseEntity implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "主键ID")
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@Schema(description = "创建人ID")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Long createdBy;
|
||||
|
||||
@Schema(description = "更新人ID")
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Long updatedBy;
|
||||
|
||||
@Schema(description = "删除标记 0-未删除 1-已删除")
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.nanxiislet.admin.common.base;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 分页查询基类
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
public class BasePageQuery implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "当前页码", example = "1")
|
||||
private Integer page = 1;
|
||||
|
||||
@Schema(description = "每页条数", example = "10")
|
||||
private Integer pageSize = 10;
|
||||
|
||||
@Schema(description = "搜索关键词")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "排序字段")
|
||||
private String orderBy;
|
||||
|
||||
@Schema(description = "排序方式 asc/desc")
|
||||
private String order = "desc";
|
||||
|
||||
/**
|
||||
* 获取偏移量
|
||||
*/
|
||||
public int getOffset() {
|
||||
return (page - 1) * pageSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.nanxiislet.admin.common.exception;
|
||||
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Getter
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final int code;
|
||||
private final String message;
|
||||
|
||||
public BusinessException(String message) {
|
||||
super(message);
|
||||
this.code = ResultCode.FAILED.getCode();
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public BusinessException(int code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public BusinessException(ResultCode resultCode) {
|
||||
super(resultCode.getMessage());
|
||||
this.code = resultCode.getCode();
|
||||
this.message = resultCode.getMessage();
|
||||
}
|
||||
|
||||
public BusinessException(ResultCode resultCode, String message) {
|
||||
super(message);
|
||||
this.code = resultCode.getCode();
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.nanxiislet.admin.common.exception;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*/
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public R<Void> handleBusinessException(BusinessException e, HttpServletRequest request) {
|
||||
log.warn("业务异常: {} - {}", request.getRequestURI(), e.getMessage());
|
||||
return R.fail(e.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 未登录异常
|
||||
*/
|
||||
@ExceptionHandler(NotLoginException.class)
|
||||
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||
public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
|
||||
log.warn("未登录访问: {} - {}", request.getRequestURI(), e.getMessage());
|
||||
return R.fail(ResultCode.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 无权限异常
|
||||
*/
|
||||
@ExceptionHandler(NotPermissionException.class)
|
||||
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||
public R<Void> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
|
||||
log.warn("无权限访问: {} - {}", request.getRequestURI(), e.getMessage());
|
||||
return R.fail(ResultCode.FORBIDDEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 无角色异常
|
||||
*/
|
||||
@ExceptionHandler(NotRoleException.class)
|
||||
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||
public R<Void> handleNotRoleException(NotRoleException e, HttpServletRequest request) {
|
||||
log.warn("无角色访问: {} - {}", request.getRequestURI(), e.getMessage());
|
||||
return R.fail(ResultCode.FORBIDDEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数校验异常
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R<Void> handleValidException(MethodArgumentNotValidException e) {
|
||||
String message = e.getBindingResult().getFieldErrors().stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.joining("; "));
|
||||
log.warn("参数校验失败: {}", message);
|
||||
return R.fail(ResultCode.VALIDATE_FAILED, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定异常
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R<Void> handleBindException(BindException e) {
|
||||
String message = e.getBindingResult().getFieldErrors().stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.joining("; "));
|
||||
log.warn("参数绑定失败: {}", message);
|
||||
return R.fail(ResultCode.VALIDATE_FAILED, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缺少请求参数异常
|
||||
*/
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R<Void> handleMissingParameterException(MissingServletRequestParameterException e) {
|
||||
log.warn("缺少请求参数: {}", e.getParameterName());
|
||||
return R.fail(ResultCode.BAD_REQUEST, "缺少请求参数: " + e.getParameterName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求方法不支持异常
|
||||
*/
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
public R<Void> handleMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
|
||||
log.warn("不支持的请求方法: {}", e.getMethod());
|
||||
return R.fail(ResultCode.METHOD_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 404异常
|
||||
*/
|
||||
@ExceptionHandler(NoHandlerFoundException.class)
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public R<Void> handleNoHandlerFoundException(NoHandlerFoundException e) {
|
||||
log.warn("资源不存在: {}", e.getRequestURL());
|
||||
return R.fail(ResultCode.NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库唯一键冲突异常
|
||||
*/
|
||||
@ExceptionHandler(org.springframework.dao.DuplicateKeyException.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public R<Void> handleDuplicateKeyException(org.springframework.dao.DuplicateKeyException e) {
|
||||
log.warn("数据重复: {}", e.getMessage());
|
||||
return R.fail(ResultCode.VALIDATE_FAILED, "数据已存在(编码或名称重复),即使是已删除的历史数据也不能重复,请更换后重试");
|
||||
}
|
||||
|
||||
/**
|
||||
* 其他异常
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public R<Void> handleException(Exception e, HttpServletRequest request) {
|
||||
log.error("系统异常: {} - {}", request.getRequestURI(), e.getMessage(), e);
|
||||
return R.fail(ResultCode.INTERNAL_ERROR, "系统繁忙,请稍后重试");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.nanxiislet.admin.common.result;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页结果
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "分页结果")
|
||||
public class PageResult<T> implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "数据列表")
|
||||
private List<T> records;
|
||||
|
||||
@Schema(description = "总条数")
|
||||
private long total;
|
||||
|
||||
@Schema(description = "当前页码")
|
||||
private long page;
|
||||
|
||||
@Schema(description = "每页条数")
|
||||
private long pageSize;
|
||||
|
||||
@Schema(description = "总页数")
|
||||
private long totalPages;
|
||||
|
||||
public PageResult() {
|
||||
}
|
||||
|
||||
public PageResult(List<T> records, long total, long page, long pageSize) {
|
||||
this.records = records;
|
||||
this.total = total;
|
||||
this.page = page;
|
||||
this.pageSize = pageSize;
|
||||
this.totalPages = (total + pageSize - 1) / pageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建分页结果
|
||||
*/
|
||||
public static <T> PageResult<T> of(List<T> records, long total, long page, long pageSize) {
|
||||
return new PageResult<>(records, total, page, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 空分页结果
|
||||
*/
|
||||
public static <T> PageResult<T> empty() {
|
||||
return new PageResult<>(List.of(), 0, 1, 10);
|
||||
}
|
||||
}
|
||||
|
||||
107
src/main/java/com/nanxiislet/admin/common/result/R.java
Normal file
107
src/main/java/com/nanxiislet/admin/common/result/R.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package com.nanxiislet.admin.common.result;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 统一响应结果
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "统一响应结果")
|
||||
public class R<T> implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "响应码", example = "200")
|
||||
private int code;
|
||||
|
||||
@Schema(description = "响应消息", example = "操作成功")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "响应数据")
|
||||
private T data;
|
||||
|
||||
@Schema(description = "时间戳")
|
||||
private long timestamp;
|
||||
|
||||
public R() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public R(int code, String message, T data) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
public static <T> R<T> ok() {
|
||||
return new R<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
public static <T> R<T> ok(T data) {
|
||||
return new R<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
public static <T> R<T> ok(String message, T data) {
|
||||
return new R<>(ResultCode.SUCCESS.getCode(), message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
public static <T> R<T> fail() {
|
||||
return new R<>(ResultCode.FAILED.getCode(), ResultCode.FAILED.getMessage(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
public static <T> R<T> fail(String message) {
|
||||
return new R<>(ResultCode.FAILED.getCode(), message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
public static <T> R<T> fail(int code, String message) {
|
||||
return new R<>(code, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
public static <T> R<T> fail(ResultCode resultCode) {
|
||||
return new R<>(resultCode.getCode(), resultCode.getMessage(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
public static <T> R<T> fail(ResultCode resultCode, String message) {
|
||||
return new R<>(resultCode.getCode(), message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否成功
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return this.code == ResultCode.SUCCESS.getCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.nanxiislet.admin.common.result;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 响应码枚举
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Getter
|
||||
public enum ResultCode {
|
||||
|
||||
// 成功
|
||||
SUCCESS(200, "操作成功"),
|
||||
|
||||
// 客户端错误 4xx
|
||||
BAD_REQUEST(400, "请求参数错误"),
|
||||
UNAUTHORIZED(401, "未登录或登录已过期"),
|
||||
FORBIDDEN(403, "没有操作权限"),
|
||||
NOT_FOUND(404, "资源不存在"),
|
||||
METHOD_NOT_ALLOWED(405, "请求方法不支持"),
|
||||
CONFLICT(409, "资源冲突"),
|
||||
VALIDATE_FAILED(422, "参数校验失败"),
|
||||
TOO_MANY_REQUESTS(429, "请求过于频繁"),
|
||||
|
||||
// 服务端错误 5xx
|
||||
FAILED(500, "操作失败"),
|
||||
INTERNAL_ERROR(500, "服务器内部错误"),
|
||||
SERVICE_UNAVAILABLE(503, "服务暂不可用"),
|
||||
|
||||
// 业务错误 1xxx
|
||||
USER_NOT_EXIST(1001, "用户不存在"),
|
||||
USER_PASSWORD_ERROR(1002, "用户名或密码错误"),
|
||||
USER_DISABLED(1003, "用户已被禁用"),
|
||||
USER_EXISTS(1004, "用户已存在"),
|
||||
CAPTCHA_ERROR(1005, "验证码错误"),
|
||||
CAPTCHA_EXPIRED(1006, "验证码已过期"),
|
||||
TOKEN_INVALID(1007, "Token无效"),
|
||||
TOKEN_EXPIRED(1008, "Token已过期"),
|
||||
|
||||
// 数据相关 2xxx
|
||||
DATA_NOT_EXIST(2001, "数据不存在"),
|
||||
DATA_ALREADY_EXIST(2002, "数据已存在"),
|
||||
DATA_SAVE_ERROR(2003, "数据保存失败"),
|
||||
DATA_UPDATE_ERROR(2004, "数据更新失败"),
|
||||
DATA_DELETE_ERROR(2005, "数据删除失败"),
|
||||
DATA_IMPORT_ERROR(2006, "数据导入失败"),
|
||||
|
||||
// 文件相关 3xxx
|
||||
FILE_NOT_FOUND(3001, "文件不存在"),
|
||||
FILE_UPLOAD_ERROR(3002, "文件上传失败"),
|
||||
FILE_TYPE_NOT_ALLOWED(3003, "不支持的文件类型"),
|
||||
FILE_SIZE_EXCEEDED(3004, "文件大小超限"),
|
||||
|
||||
// 审批相关 4xxx
|
||||
APPROVAL_NOT_FOUND(4001, "审批流程不存在"),
|
||||
APPROVAL_ALREADY_PROCESSED(4002, "审批已处理"),
|
||||
APPROVAL_NO_PERMISSION(4003, "无审批权限");
|
||||
|
||||
private final int code;
|
||||
private final String message;
|
||||
|
||||
ResultCode(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
55
src/main/java/com/nanxiislet/admin/config/CaptchaConfig.java
Normal file
55
src/main/java/com/nanxiislet/admin/config/CaptchaConfig.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package com.nanxiislet.admin.config;
|
||||
|
||||
import com.google.code.kaptcha.Producer;
|
||||
import com.google.code.kaptcha.impl.DefaultKaptcha;
|
||||
import com.google.code.kaptcha.util.Config;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 验证码配置
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Configuration
|
||||
public class CaptchaConfig {
|
||||
|
||||
@Bean
|
||||
public Producer captchaProducer() {
|
||||
DefaultKaptcha kaptcha = new DefaultKaptcha();
|
||||
Properties properties = new Properties();
|
||||
// 验证码宽度
|
||||
properties.setProperty("kaptcha.image.width", "150");
|
||||
// 验证码高度
|
||||
properties.setProperty("kaptcha.image.height", "50");
|
||||
// 验证码字符长度
|
||||
properties.setProperty("kaptcha.textproducer.char.length", "4");
|
||||
// 验证码字符集
|
||||
properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
// 验证码字体大小
|
||||
properties.setProperty("kaptcha.textproducer.font.size", "38");
|
||||
// 验证码字体
|
||||
properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
|
||||
// 验证码字体颜色
|
||||
properties.setProperty("kaptcha.textproducer.font.color", "black");
|
||||
// 验证码背景颜色渐变开始
|
||||
properties.setProperty("kaptcha.background.clear.from", "lightGray");
|
||||
// 验证码背景颜色渐变结束
|
||||
properties.setProperty("kaptcha.background.clear.to", "white");
|
||||
// 验证码干扰
|
||||
properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.DefaultNoise");
|
||||
// 验证码干扰颜色
|
||||
properties.setProperty("kaptcha.noise.color", "gray");
|
||||
// 边框
|
||||
properties.setProperty("kaptcha.border", "yes");
|
||||
properties.setProperty("kaptcha.border.color", "105,179,90");
|
||||
properties.setProperty("kaptcha.border.thickness", "1");
|
||||
|
||||
Config config = new Config(properties);
|
||||
kaptcha.setConfig(config);
|
||||
return kaptcha;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.nanxiislet.admin.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 数据库初始化配置
|
||||
* 在应用启动时检查并初始化数据库
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DatabaseInitializer implements CommandLineRunner {
|
||||
|
||||
@Resource
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Value("${nanxiislet.db.init:false}")
|
||||
private boolean initEnabled;
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
if (!initEnabled) {
|
||||
log.info("数据库初始化已禁用,跳过初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 检查表是否存在
|
||||
Integer count = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = 'sys_user'",
|
||||
Integer.class
|
||||
);
|
||||
|
||||
if (count != null && count > 0) {
|
||||
log.info("数据库表已存在,跳过初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("开始初始化数据库...");
|
||||
|
||||
// 读取SQL文件
|
||||
ClassPathResource resource = new ClassPathResource("db/init.sql");
|
||||
String sql = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
|
||||
|
||||
// 分割并执行SQL语句
|
||||
String[] statements = sql.split(";");
|
||||
int executed = 0;
|
||||
for (String statement : statements) {
|
||||
String trimmed = statement.trim();
|
||||
if (!trimmed.isEmpty() && !trimmed.startsWith("--")) {
|
||||
try {
|
||||
jdbcTemplate.execute(trimmed);
|
||||
executed++;
|
||||
} catch (Exception e) {
|
||||
log.warn("执行SQL失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("数据库初始化完成,执行了 {} 条SQL语句", executed);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("数据库初始化失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.nanxiislet.admin.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* MyBatis-Plus 配置
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
/**
|
||||
* MyBatis-Plus 插件配置
|
||||
*/
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 分页插件
|
||||
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
|
||||
paginationInterceptor.setMaxLimit(500L);
|
||||
paginationInterceptor.setOverflow(true);
|
||||
interceptor.addInnerInterceptor(paginationInterceptor);
|
||||
// 乐观锁插件
|
||||
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
|
||||
// 防止全表更新删除插件
|
||||
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动填充处理器
|
||||
*/
|
||||
@Component
|
||||
public static class AutoFillMetaObjectHandler implements MetaObjectHandler {
|
||||
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
this.strictInsertFill(metaObject, "createdAt", LocalDateTime.class, now);
|
||||
this.strictInsertFill(metaObject, "updatedAt", LocalDateTime.class, now);
|
||||
this.strictInsertFill(metaObject, "deleted", Integer.class, 0);
|
||||
|
||||
// 获取当前登录用户ID
|
||||
Long userId = getCurrentUserId();
|
||||
if (userId != null) {
|
||||
this.strictInsertFill(metaObject, "createdBy", Long.class, userId);
|
||||
this.strictInsertFill(metaObject, "updatedBy", Long.class, userId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
this.strictUpdateFill(metaObject, "updatedAt", LocalDateTime.class, LocalDateTime.now());
|
||||
|
||||
Long userId = getCurrentUserId();
|
||||
if (userId != null) {
|
||||
this.strictUpdateFill(metaObject, "updatedBy", Long.class, userId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户ID
|
||||
*/
|
||||
private Long getCurrentUserId() {
|
||||
try {
|
||||
Object loginId = cn.dev33.satoken.stp.StpUtil.getLoginIdDefaultNull();
|
||||
if (loginId != null) {
|
||||
return Long.parseLong(loginId.toString());
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/main/java/com/nanxiislet/admin/config/RedisConfig.java
Normal file
53
src/main/java/com/nanxiislet/admin/config/RedisConfig.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package com.nanxiislet.admin.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
/**
|
||||
* Redis 配置
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
|
||||
// JSON序列化器
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(),
|
||||
ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
objectMapper.registerModule(new JavaTimeModule());
|
||||
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
|
||||
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
|
||||
new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
|
||||
|
||||
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
|
||||
|
||||
// key使用String序列化
|
||||
template.setKeySerializer(stringRedisSerializer);
|
||||
template.setHashKeySerializer(stringRedisSerializer);
|
||||
|
||||
// value使用JSON序列化
|
||||
template.setValueSerializer(jackson2JsonRedisSerializer);
|
||||
template.setHashValueSerializer(jackson2JsonRedisSerializer);
|
||||
|
||||
template.afterPropertiesSet();
|
||||
return template;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.nanxiislet.admin.config;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.nanxiislet.admin.entity.SysMenu;
|
||||
import com.nanxiislet.admin.entity.SysUser;
|
||||
import com.nanxiislet.admin.service.AuthService;
|
||||
import com.nanxiislet.admin.service.SysMenuService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sa-Token 权限认证接口实现
|
||||
* 用于获取当前用户的角色和权限列表
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-08
|
||||
*/
|
||||
@Component
|
||||
public class StpInterfaceImpl implements StpInterface {
|
||||
|
||||
@Resource
|
||||
private AuthService authService;
|
||||
|
||||
@Resource
|
||||
private SysMenuService menuService;
|
||||
|
||||
/**
|
||||
* 获取用户权限列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
// 获取用户信息
|
||||
Long userId = Long.parseLong(loginId.toString());
|
||||
SysUser user = authService.getById(userId);
|
||||
if (user == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
String roleCode = user.getRole();
|
||||
|
||||
// 超级管理员拥有所有权限
|
||||
if ("super_admin".equals(roleCode)) {
|
||||
return List.of("*");
|
||||
}
|
||||
|
||||
// 根据角色获取菜单权限
|
||||
List<SysMenu> menus = menuService.getMenusByRoleCode(roleCode);
|
||||
if (menus == null || menus.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return menus.stream()
|
||||
.map(SysMenu::getCode)
|
||||
.distinct()
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色列表
|
||||
*/
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
Long userId = Long.parseLong(loginId.toString());
|
||||
SysUser user = authService.getById(userId);
|
||||
if (user == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<String> roles = new ArrayList<>();
|
||||
String roleCode = user.getRole();
|
||||
|
||||
if (roleCode != null && !roleCode.isEmpty()) {
|
||||
roles.add(roleCode);
|
||||
|
||||
// 超级管理员同时拥有 admin 角色
|
||||
if ("super_admin".equals(roleCode)) {
|
||||
roles.add("admin");
|
||||
}
|
||||
}
|
||||
|
||||
return roles;
|
||||
}
|
||||
}
|
||||
46
src/main/java/com/nanxiislet/admin/config/SwaggerConfig.java
Normal file
46
src/main/java/com/nanxiislet/admin/config/SwaggerConfig.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package com.nanxiislet.admin.config;
|
||||
|
||||
import io.swagger.v3.oas.models.ExternalDocumentation;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Swagger/OpenAPI 配置
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Configuration
|
||||
public class SwaggerConfig {
|
||||
|
||||
@Bean
|
||||
public OpenAPI openAPI() {
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("南溪小岛管理后台 API")
|
||||
.description("南溪小岛管理后台 RESTful API 文档")
|
||||
.version("1.0.0")
|
||||
.contact(new Contact()
|
||||
.name("NanxiIslet")
|
||||
.email("admin@nanxiislet.com"))
|
||||
.license(new License()
|
||||
.name("MIT License")
|
||||
.url("https://opensource.org/licenses/MIT")))
|
||||
.externalDocs(new ExternalDocumentation()
|
||||
.description("项目文档")
|
||||
.url("https://doc.nanxiislet.com"))
|
||||
.addSecurityItem(new SecurityRequirement().addList("Authorization"))
|
||||
.schemaRequirement("Authorization", new SecurityScheme()
|
||||
.name("Authorization")
|
||||
.type(SecurityScheme.Type.HTTP)
|
||||
.scheme("bearer")
|
||||
.bearerFormat("JWT")
|
||||
.in(SecurityScheme.In.HEADER));
|
||||
}
|
||||
}
|
||||
84
src/main/java/com/nanxiislet/admin/config/WebMvcConfig.java
Normal file
84
src/main/java/com/nanxiislet/admin/config/WebMvcConfig.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package com.nanxiislet.admin.config;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Web MVC 配置
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 跨域配置
|
||||
*/
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(true)
|
||||
.maxAge(3600);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截器配置
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// Sa-Token 鉴权拦截器
|
||||
registry.addInterceptor(new SaInterceptor(handle -> {
|
||||
// OPTIONS 预检请求不检查登录
|
||||
if ("OPTIONS".equalsIgnoreCase(SaHolder.getRequest().getMethod())) {
|
||||
return;
|
||||
}
|
||||
StpUtil.checkLogin();
|
||||
}))
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns(
|
||||
// 登录认证相关
|
||||
"/auth/login",
|
||||
"/auth/captcha",
|
||||
"/auth/logout",
|
||||
// Swagger文档
|
||||
"/doc.html",
|
||||
"/swagger-ui.html",
|
||||
"/swagger-ui/**",
|
||||
"/swagger-resources/**",
|
||||
"/v3/api-docs/**",
|
||||
"/webjars/**",
|
||||
// 静态资源
|
||||
"/static/**",
|
||||
"/favicon.ico",
|
||||
// 健康检查
|
||||
"/actuator/**",
|
||||
"/health",
|
||||
"/" // 根路径
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态资源配置
|
||||
*/
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
// 上传文件访问
|
||||
registry.addResourceHandler("/uploads/**")
|
||||
.addResourceLocations("file:./uploads/");
|
||||
// Swagger UI
|
||||
registry.addResourceHandler("doc.html")
|
||||
.addResourceLocations("classpath:/META-INF/resources/");
|
||||
registry.addResourceHandler("/webjars/**")
|
||||
.addResourceLocations("classpath:/META-INF/resources/webjars/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.dto.auth.*;
|
||||
import com.nanxiislet.admin.service.AuthService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 认证控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/auth")
|
||||
@Tag(name = "认证管理", description = "登录、登出、验证码等")
|
||||
public class AuthController {
|
||||
|
||||
@Resource
|
||||
private AuthService authService;
|
||||
|
||||
@GetMapping("/captcha")
|
||||
@Operation(summary = "获取验证码")
|
||||
public R<CaptchaResponse> getCaptcha() {
|
||||
return R.ok(authService.generateCaptcha());
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
@Operation(summary = "用户登录")
|
||||
public R<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
|
||||
return R.ok(authService.login(request));
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
@Operation(summary = "用户登出")
|
||||
public R<Void> logout() {
|
||||
authService.logout();
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/user-info")
|
||||
@Operation(summary = "获取当前用户信息")
|
||||
public R<UserInfoVO> getCurrentUser() {
|
||||
return R.ok(authService.getCurrentUser());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.service.*;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 仪表盘控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/dashboard")
|
||||
@Tag(name = "仪表盘", description = "首页统计数据")
|
||||
public class DashboardController {
|
||||
|
||||
@Resource
|
||||
private FinanceIncomeService incomeService;
|
||||
|
||||
@Resource
|
||||
private FinanceExpenseService expenseService;
|
||||
|
||||
@Resource
|
||||
private FinanceReimbursementService reimbursementService;
|
||||
|
||||
@Resource
|
||||
private PlatformProjectService projectService;
|
||||
|
||||
@Resource
|
||||
private PlatformServerService serverService;
|
||||
|
||||
@Resource
|
||||
private PlatformDomainService domainService;
|
||||
|
||||
@GetMapping("/stats")
|
||||
@Operation(summary = "获取统计数据", description = "获取首页仪表盘统计数据")
|
||||
public R<DashboardStats> getStats() {
|
||||
DashboardStats stats = new DashboardStats();
|
||||
|
||||
// 项目统计
|
||||
stats.setProjectCount(projectService.count());
|
||||
|
||||
// 服务器统计
|
||||
stats.setServerCount(serverService.count());
|
||||
|
||||
// 域名统计
|
||||
stats.setDomainCount(domainService.count());
|
||||
|
||||
// 财务统计(收入总额、支出总额、待报销金额)
|
||||
// 这里简化处理,实际应该用聚合查询
|
||||
stats.setIncomeCount(incomeService.count());
|
||||
stats.setExpenseCount(expenseService.count());
|
||||
stats.setReimbursementCount(reimbursementService.count());
|
||||
|
||||
return R.ok(stats);
|
||||
}
|
||||
|
||||
@GetMapping("/overview")
|
||||
@Operation(summary = "获取概览数据", description = "获取财务概览数据")
|
||||
public R<Map<String, Object>> getOverview() {
|
||||
Map<String, Object> overview = new HashMap<>();
|
||||
|
||||
// 统计数量
|
||||
overview.put("totalProjects", projectService.count());
|
||||
overview.put("totalServers", serverService.count());
|
||||
overview.put("totalDomains", domainService.count());
|
||||
overview.put("totalIncomes", incomeService.count());
|
||||
overview.put("totalExpenses", expenseService.count());
|
||||
overview.put("pendingReimbursements", reimbursementService.count());
|
||||
|
||||
return R.ok(overview);
|
||||
}
|
||||
|
||||
@GetMapping("/quick-stats")
|
||||
@Operation(summary = "快速统计", description = "获取快速统计卡片数据")
|
||||
public R<List<QuickStat>> getQuickStats() {
|
||||
List<QuickStat> stats = new ArrayList<>();
|
||||
|
||||
stats.add(new QuickStat("项目总数", projectService.count(), "project", "#1890ff"));
|
||||
stats.add(new QuickStat("服务器", serverService.count(), "server", "#52c41a"));
|
||||
stats.add(new QuickStat("域名", domainService.count(), "domain", "#722ed1"));
|
||||
stats.add(new QuickStat("收入记录", incomeService.count(), "income", "#fa8c16"));
|
||||
|
||||
return R.ok(stats);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class DashboardStats {
|
||||
private long projectCount;
|
||||
private long serverCount;
|
||||
private long domainCount;
|
||||
private long incomeCount;
|
||||
private long expenseCount;
|
||||
private long reimbursementCount;
|
||||
private BigDecimal totalIncome;
|
||||
private BigDecimal totalExpense;
|
||||
private BigDecimal pendingReimbursement;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class QuickStat {
|
||||
private String title;
|
||||
private long value;
|
||||
private String icon;
|
||||
private String color;
|
||||
|
||||
public QuickStat(String title, long value, String icon, String color) {
|
||||
this.title = title;
|
||||
this.value = value;
|
||||
this.icon = icon;
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.service.OnePanelService;
|
||||
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.Map;
|
||||
|
||||
/**
|
||||
* 数据库管理控制器
|
||||
* 通过1Panel API管理数据库
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/platform/database")
|
||||
@Tag(name = "数据库管理", description = "通过1Panel管理数据库")
|
||||
public class DatabaseController {
|
||||
|
||||
@Resource
|
||||
private OnePanelService onePanelService;
|
||||
|
||||
/**
|
||||
* 检查应用安装状态(MySQL/PostgreSQL/Redis等)
|
||||
*/
|
||||
@PostMapping("/app/check")
|
||||
@Operation(summary = "检查应用安装状态")
|
||||
public R<Map<String, Object>> checkAppInstalled(@RequestBody Map<String, Object> params) {
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String key = params.get("key").toString();
|
||||
String name = params.get("name").toString();
|
||||
|
||||
Map<String, Object> result = onePanelService.checkAppInstalled(serverId, key, name);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据库列表
|
||||
*/
|
||||
@PostMapping("/search")
|
||||
@Operation(summary = "查询数据库列表")
|
||||
public R<Map<String, Object>> searchDatabases(@RequestBody Map<String, Object> params) {
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String database = params.get("database").toString();
|
||||
int page = params.containsKey("page") ? Integer.parseInt(params.get("page").toString()) : 1;
|
||||
int pageSize = params.containsKey("pageSize") ? Integer.parseInt(params.get("pageSize").toString()) : 20;
|
||||
|
||||
Map<String, Object> result = onePanelService.searchDatabases(serverId, database, page, pageSize);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据库
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建数据库")
|
||||
public R<Map<String, Object>> createDatabase(@RequestBody Map<String, Object> params) {
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
|
||||
// 移除serverId,只保留1Panel需要的参数
|
||||
params.remove("serverId");
|
||||
|
||||
Map<String, Object> result = onePanelService.createDatabase(serverId, params);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据库
|
||||
*/
|
||||
@PostMapping("/delete")
|
||||
@Operation(summary = "删除数据库")
|
||||
public R<Void> deleteDatabase(@RequestBody Map<String, Object> params) {
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
|
||||
// 移除serverId,只保留1Panel需要的参数
|
||||
params.remove("serverId");
|
||||
|
||||
boolean success = onePanelService.deleteDatabase(serverId, params);
|
||||
|
||||
if (success) {
|
||||
return R.ok();
|
||||
} else {
|
||||
return R.fail("删除数据库失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据库描述
|
||||
*/
|
||||
@PostMapping("/description/update")
|
||||
@Operation(summary = "更新数据库描述")
|
||||
public R<Void> updateDescription(@RequestBody Map<String, Object> params) {
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
|
||||
// 移除serverId,只保留1Panel需要的参数
|
||||
params.remove("serverId");
|
||||
|
||||
boolean success = onePanelService.updateDatabaseDescription(serverId, params);
|
||||
|
||||
if (success) {
|
||||
return R.ok();
|
||||
} else {
|
||||
return R.fail("更新数据库描述失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改数据库密码
|
||||
*/
|
||||
@PostMapping("/password/change")
|
||||
@Operation(summary = "修改数据库密码")
|
||||
public R<Void> changePassword(@RequestBody Map<String, Object> params) {
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
|
||||
// 移除serverId,只保留1Panel需要的参数
|
||||
params.remove("serverId");
|
||||
|
||||
boolean success = onePanelService.changeDatabasePassword(serverId, params);
|
||||
|
||||
if (success) {
|
||||
return R.ok();
|
||||
} else {
|
||||
return R.fail("修改数据库密码失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作应用(启动/停止/重启)
|
||||
*/
|
||||
@PostMapping("/app/operate")
|
||||
@Operation(summary = "操作应用")
|
||||
public R<Void> operateApp(@RequestBody Map<String, Object> params) {
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
|
||||
// 移除serverId,只保留1Panel需要的参数 (installId, operate)
|
||||
params.remove("serverId");
|
||||
|
||||
boolean success = onePanelService.operateApp(serverId, params);
|
||||
|
||||
if (success) {
|
||||
return R.ok();
|
||||
} else {
|
||||
return R.fail("操作失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库字符集排序规则选项
|
||||
*/
|
||||
@PostMapping("/format/options")
|
||||
@Operation(summary = "获取数据库排序规则选项")
|
||||
public R<java.util.List<String>> getFormatOptions(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
|
||||
Object typeObj = params.get("type");
|
||||
String type = typeObj != null ? typeObj.toString() : "mysql";
|
||||
|
||||
Object dbObj = params.get("database");
|
||||
String database = dbObj != null ? dbObj.toString() : type;
|
||||
|
||||
Object formatObj = params.get("format");
|
||||
String format = formatObj != null ? formatObj.toString() : null;
|
||||
|
||||
java.util.List<String> options = onePanelService.getDatabaseFormatOptions(serverId, type, database, format);
|
||||
return R.ok(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用信息(如Redis)
|
||||
*/
|
||||
@PostMapping("/app/info")
|
||||
@Operation(summary = "获取应用信息")
|
||||
public R<Map<String, Object>> getAppInfo(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String appKey = params.get("appKey") != null ? params.get("appKey").toString() : "redis";
|
||||
|
||||
Map<String, Object> result = onePanelService.getAppInfo(serverId, appKey);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用版本详情
|
||||
*/
|
||||
@PostMapping("/app/detail")
|
||||
@Operation(summary = "获取应用版本详情")
|
||||
public R<Map<String, Object>> getAppDetail(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
Long appId = params.get("appId") != null ? Long.parseLong(params.get("appId").toString()) : null;
|
||||
String version = params.get("version") != null ? params.get("version").toString() : null;
|
||||
|
||||
if (appId == null || version == null) {
|
||||
return R.fail("appId和version不能为空");
|
||||
}
|
||||
|
||||
Map<String, Object> result = onePanelService.getAppDetail(serverId, appId, version);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装应用
|
||||
*/
|
||||
@PostMapping("/app/install")
|
||||
@Operation(summary = "安装应用")
|
||||
public R<Map<String, Object>> installApp(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
|
||||
// 移除serverId,只保留1Panel需要的参数
|
||||
params.remove("serverId");
|
||||
|
||||
Map<String, Object> result = onePanelService.installApp(serverId, params);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取任务日志
|
||||
*/
|
||||
@PostMapping("/task/log")
|
||||
@Operation(summary = "读取任务日志")
|
||||
public R<Map<String, Object>> readTaskLog(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String taskId = params.get("taskId") != null ? params.get("taskId").toString() : "";
|
||||
int page = params.get("page") != null ? Integer.parseInt(params.get("page").toString()) : 1;
|
||||
int pageSize = params.get("pageSize") != null ? Integer.parseInt(params.get("pageSize").toString()) : 500;
|
||||
|
||||
if (taskId.isEmpty()) {
|
||||
return R.fail("taskId不能为空");
|
||||
}
|
||||
|
||||
Map<String, Object> result = onePanelService.readTaskLog(serverId, taskId, page, pageSize);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.service.OnePanelService;
|
||||
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.Map;
|
||||
|
||||
/**
|
||||
* 文件管理控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/platform/files")
|
||||
@Tag(name = "文件管理")
|
||||
public class FileController {
|
||||
|
||||
@Resource
|
||||
private OnePanelService onePanelService;
|
||||
|
||||
/**
|
||||
* 获取文件/目录列表
|
||||
*/
|
||||
@PostMapping("/list")
|
||||
@Operation(summary = "获取文件列表")
|
||||
public R<Map<String, Object>> listFiles(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String path = params.get("path") != null ? params.get("path").toString() : "/";
|
||||
|
||||
Map<String, Object> result = onePanelService.listFiles(serverId, path);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录
|
||||
*/
|
||||
@PostMapping("/mkdir")
|
||||
@Operation(summary = "创建目录")
|
||||
public R<Void> mkdir(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
if (params.get("path") == null) {
|
||||
return R.fail("path不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String path = params.get("path").toString();
|
||||
|
||||
Map<String, Object> result = onePanelService.mkdir(serverId, path);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件/目录
|
||||
*/
|
||||
@PostMapping("/delete")
|
||||
@Operation(summary = "删除文件")
|
||||
public R<Void> deleteFile(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
if (params.get("path") == null) {
|
||||
return R.fail("path不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String path = params.get("path").toString();
|
||||
Boolean isDir = params.get("isDir") != null && Boolean.parseBoolean(params.get("isDir").toString());
|
||||
|
||||
Map<String, Object> result = onePanelService.deleteFile(serverId, path, isDir);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建文件")
|
||||
public R<Void> createFile(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
if (params.get("path") == null) {
|
||||
return R.fail("path不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String path = params.get("path").toString();
|
||||
|
||||
Map<String, Object> result = onePanelService.createFile(serverId, path);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
@PostMapping(value = "/upload", consumes = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
@Operation(summary = "上传文件")
|
||||
public R<Void> uploadFile(
|
||||
@RequestParam("serverId") Long serverId,
|
||||
@RequestParam("path") String path,
|
||||
@RequestPart("file") org.springframework.web.multipart.MultipartFile file) {
|
||||
|
||||
if (serverId == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
if (path == null) {
|
||||
return R.fail("path不能为空");
|
||||
}
|
||||
if (file == null || file.isEmpty()) {
|
||||
return R.fail("文件不能为空");
|
||||
}
|
||||
|
||||
Map<String, Object> result = onePanelService.uploadFile(serverId, path, file);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否存在
|
||||
*/
|
||||
@PostMapping("/check")
|
||||
@Operation(summary = "检查文件是否存在")
|
||||
public R<java.util.List<String>> checkUploadFiles(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
java.util.List<String> paths = (java.util.List<String>) params.get("paths");
|
||||
|
||||
java.util.List<String> result = onePanelService.checkFileBatch(serverId, paths);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分片上传文件(旧版,兼容)
|
||||
*/
|
||||
@PostMapping(value = "/upload/chunk", consumes = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
@Operation(summary = "分片上传文件(旧版)")
|
||||
public R<String> uploadChunk(
|
||||
@RequestParam("serverId") Long serverId,
|
||||
@RequestParam("filename") String filename,
|
||||
@RequestParam("path") String path,
|
||||
@RequestParam("chunkIndex") int chunkIndex,
|
||||
@RequestParam("chunkCount") int chunkCount,
|
||||
@RequestPart("chunk") org.springframework.web.multipart.MultipartFile chunk) {
|
||||
|
||||
return R.ok(onePanelService.uploadFileChunk(serverId, filename, path, chunkIndex, chunkCount, chunk));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分片上传文件(新版,支持分片合并)
|
||||
*/
|
||||
@PostMapping(value = "/upload/chunk/v2", consumes = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
@Operation(summary = "分片上传文件(v2)")
|
||||
public R<Map<String, Object>> uploadChunkV2(
|
||||
@RequestParam("serverId") Long serverId,
|
||||
@RequestParam("path") String path,
|
||||
@RequestParam("filename") String filename,
|
||||
@RequestParam("chunkIndex") int chunkIndex,
|
||||
@RequestParam("chunkCount") int chunkCount,
|
||||
@RequestParam("fileSize") long fileSize,
|
||||
@RequestParam("uploadId") String uploadId,
|
||||
@RequestPart("chunk") org.springframework.web.multipart.MultipartFile chunk) {
|
||||
|
||||
Map<String, Object> result = onePanelService.uploadChunk(serverId, path, filename,
|
||||
chunkIndex, chunkCount, fileSize, uploadId, chunk);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并已上传的分片
|
||||
*/
|
||||
@PostMapping("/upload/merge")
|
||||
@Operation(summary = "合并分片")
|
||||
public R<Map<String, Object>> mergeChunks(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String path = params.get("path").toString();
|
||||
String filename = params.get("filename").toString();
|
||||
int chunkCount = Integer.parseInt(params.get("chunkCount").toString());
|
||||
long fileSize = Long.parseLong(params.get("fileSize").toString());
|
||||
String uploadId = params.get("uploadId").toString();
|
||||
|
||||
Map<String, Object> result = onePanelService.mergeChunks(serverId, path, filename,
|
||||
chunkCount, fileSize, uploadId);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.entity.FinanceAccount;
|
||||
import com.nanxiislet.admin.service.FinanceAccountService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 财务账户控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/finance/account")
|
||||
@Tag(name = "账户管理", description = "财务账户的增删改查")
|
||||
public class FinanceAccountController {
|
||||
|
||||
@Resource
|
||||
private FinanceAccountService accountService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "账户列表")
|
||||
public R<List<FinanceAccount>> list() {
|
||||
return R.ok(accountService.list());
|
||||
}
|
||||
|
||||
@GetMapping("/active")
|
||||
@Operation(summary = "活跃账户列表")
|
||||
public R<List<FinanceAccount>> listActive() {
|
||||
return R.ok(accountService.listActive());
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "账户详情")
|
||||
public R<FinanceAccount> getById(@PathVariable Long id) {
|
||||
FinanceAccount account = accountService.getById(id);
|
||||
if (account == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(account);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增账户")
|
||||
public R<FinanceAccount> create(@Valid @RequestBody FinanceAccount account) {
|
||||
accountService.save(account);
|
||||
return R.ok(account);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新账户")
|
||||
public R<Void> update(@PathVariable Long id, @Valid @RequestBody FinanceAccount account) {
|
||||
account.setId(id);
|
||||
accountService.updateById(account);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除账户")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
accountService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.PageResult;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.entity.FinanceBudget;
|
||||
import com.nanxiislet.admin.service.FinanceBudgetService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 预算管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/finance/budget")
|
||||
@Tag(name = "预算管理", description = "预算记录的增删改查")
|
||||
public class FinanceBudgetController {
|
||||
|
||||
@Resource
|
||||
private FinanceBudgetService budgetService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "预算列表", description = "分页查询预算记录")
|
||||
public R<PageResult<FinanceBudget>> list(BasePageQuery query) {
|
||||
Page<FinanceBudget> page = budgetService.listPage(query);
|
||||
return R.ok(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "预算详情", description = "根据ID获取预算记录详情")
|
||||
public R<FinanceBudget> getById(@PathVariable Long id) {
|
||||
FinanceBudget budget = budgetService.getById(id);
|
||||
if (budget == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(budget);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增预算", description = "创建新的预算记录")
|
||||
public R<FinanceBudget> create(@Valid @RequestBody FinanceBudget budget) {
|
||||
// 初始化金额
|
||||
if (budget.getUsedAmount() == null) {
|
||||
budget.setUsedAmount(BigDecimal.ZERO);
|
||||
}
|
||||
if (budget.getRemainingAmount() == null) {
|
||||
budget.setRemainingAmount(budget.getTotalBudget());
|
||||
}
|
||||
if (budget.getUsageRate() == null) {
|
||||
budget.setUsageRate(BigDecimal.ZERO);
|
||||
}
|
||||
if (budget.getStatus() == null) {
|
||||
budget.setStatus("active");
|
||||
}
|
||||
|
||||
budgetService.save(budget);
|
||||
return R.ok(budget);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新预算", description = "更新预算记录")
|
||||
public R<Void> update(@PathVariable Long id, @Valid @RequestBody FinanceBudget budget) {
|
||||
budget.setId(id);
|
||||
budgetService.updateById(budget);
|
||||
budgetService.calculateUsageRate(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除预算", description = "删除预算记录")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
budgetService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/find")
|
||||
@Operation(summary = "查找预算", description = "按条件查找预算")
|
||||
public R<FinanceBudget> findBudget(
|
||||
@RequestParam Integer year,
|
||||
@RequestParam String period,
|
||||
@RequestParam(required = false) Integer quarter,
|
||||
@RequestParam(required = false) Integer month,
|
||||
@RequestParam(required = false) Long departmentId,
|
||||
@RequestParam(required = false) Long projectId) {
|
||||
FinanceBudget budget = budgetService.findBudget(year, period, quarter, month, departmentId, projectId);
|
||||
return R.ok(budget);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.PageResult;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.entity.FinanceExpense;
|
||||
import com.nanxiislet.admin.service.FinanceExpenseService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 支出管理控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/finance/expense")
|
||||
@Tag(name = "支出管理", description = "支出记录的增删改查")
|
||||
public class FinanceExpenseController {
|
||||
|
||||
@Resource
|
||||
private FinanceExpenseService expenseService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "支出列表")
|
||||
public R<PageResult<FinanceExpense>> list(BasePageQuery query) {
|
||||
Page<FinanceExpense> page = expenseService.listPage(query);
|
||||
return R.ok(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "支出详情")
|
||||
public R<FinanceExpense> getById(@PathVariable Long id) {
|
||||
FinanceExpense expense = expenseService.getById(id);
|
||||
if (expense == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(expense);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增支出")
|
||||
public R<FinanceExpense> create(@Valid @RequestBody FinanceExpense expense) {
|
||||
expense.setExpenseNo(expenseService.generateExpenseNo());
|
||||
expenseService.save(expense);
|
||||
return R.ok(expense);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新支出")
|
||||
public R<Void> update(@PathVariable Long id, @Valid @RequestBody FinanceExpense expense) {
|
||||
expense.setId(id);
|
||||
expenseService.updateById(expense);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除支出")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
expenseService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.PageResult;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.entity.FinanceIncome;
|
||||
import com.nanxiislet.admin.service.FinanceIncomeService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 收入管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/finance/income")
|
||||
@Tag(name = "收入管理", description = "收入记录的增删改查")
|
||||
public class FinanceIncomeController {
|
||||
|
||||
@Resource
|
||||
private FinanceIncomeService incomeService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "收入列表", description = "分页查询收入记录")
|
||||
public R<PageResult<FinanceIncome>> list(BasePageQuery query) {
|
||||
Page<FinanceIncome> page = incomeService.listPage(query);
|
||||
return R.ok(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "收入详情", description = "根据ID获取收入记录详情")
|
||||
public R<FinanceIncome> getById(@PathVariable Long id) {
|
||||
FinanceIncome income = incomeService.getById(id);
|
||||
if (income == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(income);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增收入", description = "创建新的收入记录")
|
||||
public R<FinanceIncome> create(@Valid @RequestBody FinanceIncome income) {
|
||||
income.setIncomeNo(incomeService.generateIncomeNo());
|
||||
incomeService.save(income);
|
||||
return R.ok(income);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新收入", description = "更新收入记录")
|
||||
public R<Void> update(@PathVariable Long id, @Valid @RequestBody FinanceIncome income) {
|
||||
income.setId(id);
|
||||
incomeService.updateById(income);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除收入", description = "删除收入记录")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
incomeService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.PageResult;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.entity.FinanceInvoice;
|
||||
import com.nanxiislet.admin.service.FinanceInvoiceService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 发票管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/finance/invoice")
|
||||
@Tag(name = "发票管理", description = "发票记录的增删改查")
|
||||
public class FinanceInvoiceController {
|
||||
|
||||
@Resource
|
||||
private FinanceInvoiceService invoiceService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "发票列表", description = "分页查询发票记录")
|
||||
public R<PageResult<FinanceInvoice>> list(BasePageQuery query) {
|
||||
Page<FinanceInvoice> page = invoiceService.listPage(query);
|
||||
return R.ok(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "发票详情", description = "根据ID获取发票记录详情")
|
||||
public R<FinanceInvoice> getById(@PathVariable Long id) {
|
||||
FinanceInvoice invoice = invoiceService.getById(id);
|
||||
if (invoice == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(invoice);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "申请开票", description = "创建新的发票申请")
|
||||
public R<FinanceInvoice> create(@Valid @RequestBody FinanceInvoice invoice) {
|
||||
invoice.setStatus("pending");
|
||||
invoice.setSubmitTime(LocalDate.now());
|
||||
invoiceService.save(invoice);
|
||||
return R.ok(invoice);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新发票", description = "更新发票信息")
|
||||
public R<Void> update(@PathVariable Long id, @Valid @RequestBody FinanceInvoice invoice) {
|
||||
invoice.setId(id);
|
||||
invoiceService.updateById(invoice);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除发票", description = "删除发票记录")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
invoiceService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/issue")
|
||||
@Operation(summary = "开具发票", description = "开具发票")
|
||||
public R<Void> issue(@PathVariable Long id) {
|
||||
invoiceService.issueInvoice(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/reject")
|
||||
@Operation(summary = "驳回发票", description = "驳回发票申请")
|
||||
public R<Void> reject(@PathVariable Long id, @RequestBody RejectRequest request) {
|
||||
invoiceService.rejectInvoice(id, request.getReason());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class RejectRequest {
|
||||
private String reason;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.PageResult;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.entity.FinanceReimbursement;
|
||||
import com.nanxiislet.admin.service.FinanceReimbursementService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 报销管理控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/finance/reimbursement")
|
||||
@Tag(name = "报销管理", description = "报销记录的增删改查")
|
||||
public class FinanceReimbursementController {
|
||||
|
||||
@Resource
|
||||
private FinanceReimbursementService reimbursementService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "报销列表")
|
||||
public R<PageResult<FinanceReimbursement>> list(BasePageQuery query) {
|
||||
Page<FinanceReimbursement> page = reimbursementService.listPage(query);
|
||||
return R.ok(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "报销详情")
|
||||
public R<FinanceReimbursement> getById(@PathVariable Long id) {
|
||||
FinanceReimbursement reimbursement = reimbursementService.getById(id);
|
||||
if (reimbursement == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(reimbursement);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增报销")
|
||||
public R<FinanceReimbursement> create(@Valid @RequestBody FinanceReimbursement reimbursement) {
|
||||
reimbursement.setReimbursementNo(reimbursementService.generateReimbursementNo());
|
||||
reimbursementService.save(reimbursement);
|
||||
return R.ok(reimbursement);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新报销")
|
||||
public R<Void> update(@PathVariable Long id, @Valid @RequestBody FinanceReimbursement reimbursement) {
|
||||
reimbursement.setId(id);
|
||||
reimbursementService.updateById(reimbursement);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除报销")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
reimbursementService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/submit")
|
||||
@Operation(summary = "提交审批")
|
||||
public R<Void> submit(@PathVariable Long id) {
|
||||
FinanceReimbursement reimbursement = reimbursementService.getById(id);
|
||||
if (reimbursement == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
reimbursement.setStatus("pending");
|
||||
reimbursementService.updateById(reimbursement);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.PageResult;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.entity.FinanceSettlement;
|
||||
import com.nanxiislet.admin.service.FinanceSettlementService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 结算管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/finance/settlement")
|
||||
@Tag(name = "结算管理", description = "结算记录的增删改查")
|
||||
public class FinanceSettlementController {
|
||||
|
||||
@Resource
|
||||
private FinanceSettlementService settlementService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "结算列表", description = "分页查询结算记录")
|
||||
public R<PageResult<FinanceSettlement>> list(BasePageQuery query) {
|
||||
Page<FinanceSettlement> page = settlementService.listPage(query);
|
||||
return R.ok(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "结算详情", description = "根据ID获取结算记录详情")
|
||||
public R<FinanceSettlement> getById(@PathVariable Long id) {
|
||||
FinanceSettlement settlement = settlementService.getById(id);
|
||||
if (settlement == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(settlement);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增结算", description = "创建新的结算记录")
|
||||
public R<FinanceSettlement> create(@Valid @RequestBody FinanceSettlement settlement) {
|
||||
// 计算金额
|
||||
settlementService.calculateAmounts(settlement);
|
||||
|
||||
// 默认状态
|
||||
if (settlement.getStatus() == null) {
|
||||
settlement.setStatus("pending");
|
||||
}
|
||||
if (settlement.getInvoiceStatus() == null) {
|
||||
settlement.setInvoiceStatus("none");
|
||||
}
|
||||
|
||||
settlementService.save(settlement);
|
||||
return R.ok(settlement);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新结算", description = "更新结算记录")
|
||||
public R<Void> update(@PathVariable Long id, @Valid @RequestBody FinanceSettlement settlement) {
|
||||
settlement.setId(id);
|
||||
// 重新计算金额
|
||||
settlementService.calculateAmounts(settlement);
|
||||
settlementService.updateById(settlement);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除结算", description = "删除结算记录")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
settlementService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/approve")
|
||||
@Operation(summary = "审核通过", description = "审核通过结算单")
|
||||
public R<Void> approve(@PathVariable Long id) {
|
||||
settlementService.approve(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/reject")
|
||||
@Operation(summary = "驳回", description = "驳回结算单")
|
||||
public R<Void> reject(@PathVariable Long id, @RequestBody RejectRequest request) {
|
||||
settlementService.reject(id, request.getReason());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/confirm-payment")
|
||||
@Operation(summary = "确认打款", description = "确认打款完成")
|
||||
public R<Void> confirmPayment(@PathVariable Long id) {
|
||||
settlementService.confirmPayment(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class RejectRequest {
|
||||
private String reason;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 健康检查控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@RestController
|
||||
@Tag(name = "系统", description = "系统相关接口")
|
||||
public class HealthController {
|
||||
|
||||
@Value("${spring.application.name:nanxiislet-admin}")
|
||||
private String applicationName;
|
||||
|
||||
@Value("${server.port:8080}")
|
||||
private String port;
|
||||
|
||||
@GetMapping("/health")
|
||||
@Operation(summary = "健康检查", description = "检查服务健康状态")
|
||||
public R<Map<String, Object>> health() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("status", "UP");
|
||||
result.put("application", applicationName);
|
||||
result.put("port", port);
|
||||
result.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
@Operation(summary = "首页", description = "API首页")
|
||||
public R<Map<String, String>> index() {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("name", "Nanxiislet Admin API");
|
||||
result.put("version", "1.0.0");
|
||||
result.put("docs", "/doc.html");
|
||||
return R.ok(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.PageResult;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.dto.CertificateApplyRequest;
|
||||
import com.nanxiislet.admin.dto.CertificateApplyResult;
|
||||
import com.nanxiislet.admin.entity.PlatformCertificate;
|
||||
import com.nanxiislet.admin.service.PlatformCertificateService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 证书管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-13
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/platform/certificate")
|
||||
@Tag(name = "证书管理", description = "管理SSL/TLS证书,整合1Panel证书功能")
|
||||
public class PlatformCertificateController {
|
||||
|
||||
@Resource
|
||||
private PlatformCertificateService certificateService;
|
||||
|
||||
@Resource
|
||||
private com.nanxiislet.admin.service.PlatformServerService serverService;
|
||||
|
||||
// ==================== 证书 CRUD ====================
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "证书列表", description = "获取指定服务器的证书列表")
|
||||
public R<PageResult<PlatformCertificate>> list(
|
||||
@RequestParam(required = false) Long serverId,
|
||||
BasePageQuery query) {
|
||||
Page<PlatformCertificate> page = certificateService.listPage(serverId, query);
|
||||
return R.ok(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "证书详情", description = "获取证书详情,包含证书内容和私钥")
|
||||
public R<PlatformCertificate> getDetail(@PathVariable Long id) {
|
||||
PlatformCertificate cert = certificateService.getCertificateDetail(id);
|
||||
if (cert == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(cert);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除证书", description = "删除证书,同时从1Panel删除")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
boolean success = certificateService.deleteCertificate(id);
|
||||
if (!success) {
|
||||
throw new BusinessException("删除失败");
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/settings")
|
||||
@Operation(summary = "更新证书设置", description = "更新自动续签和备注")
|
||||
public R<Void> updateSettings(
|
||||
@PathVariable Long id,
|
||||
@RequestParam(required = false) Boolean autoRenew,
|
||||
@RequestParam(required = false) String description) {
|
||||
certificateService.updateCertificateSettings(id, autoRenew, description);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ==================== 证书申请 ====================
|
||||
|
||||
@PostMapping("/apply")
|
||||
@Operation(summary = "申请证书", description = "调用1Panel API申请SSL证书")
|
||||
public R<CertificateApplyResult> apply(@Valid @RequestBody CertificateApplyRequest request) {
|
||||
CertificateApplyResult result = certificateService.applyCertificate(request);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
// ==================== 证书同步 ====================
|
||||
|
||||
@PostMapping("/sync/{serverId}")
|
||||
@Operation(summary = "同步证书", description = "从1Panel同步证书列表到本地数据库")
|
||||
public R<Map<String, Object>> sync(@PathVariable Long serverId) {
|
||||
int count = certificateService.syncCertificatesFromPanel(serverId);
|
||||
return R.ok(Map.of("syncCount", count, "message", "同步完成,共 " + count + " 个证书"));
|
||||
}
|
||||
|
||||
// ==================== 1Panel 账户查询 ====================
|
||||
|
||||
@GetMapping("/acme-accounts/{serverId}")
|
||||
@Operation(summary = "获取Acme账户列表", description = "从1Panel获取Acme账户列表")
|
||||
public R<List<Map<String, Object>>> getAcmeAccounts(@PathVariable Long serverId) {
|
||||
return R.ok(certificateService.getAcmeAccounts(serverId));
|
||||
}
|
||||
|
||||
@GetMapping("/dns-accounts/{serverId}")
|
||||
@Operation(summary = "获取DNS账户列表", description = "从1Panel获取DNS账户列表")
|
||||
public R<List<Map<String, Object>>> getDnsAccounts(@PathVariable Long serverId) {
|
||||
return R.ok(certificateService.getDnsAccounts(serverId));
|
||||
}
|
||||
|
||||
@GetMapping("/websites/{serverId}")
|
||||
@Operation(summary = "获取网站列表", description = "从1Panel获取网站列表")
|
||||
public R<List<Map<String, Object>>> getWebsites(@PathVariable Long serverId) {
|
||||
return R.ok(certificateService.getWebsites(serverId));
|
||||
}
|
||||
|
||||
// ==================== 调试接口 ====================
|
||||
|
||||
@GetMapping("/debug/server/{serverId}")
|
||||
@Operation(summary = "检查服务器1Panel配置", description = "用于调试:检查服务器是否正确配置了1Panel API")
|
||||
public R<Map<String, Object>> debugServerConfig(@PathVariable Long serverId) {
|
||||
var server = serverService.getById(serverId);
|
||||
if (server == null) {
|
||||
return R.ok(Map.of("error", "服务器不存在", "serverId", serverId));
|
||||
}
|
||||
|
||||
return R.ok(Map.of(
|
||||
"serverId", serverId,
|
||||
"serverName", server.getName() != null ? server.getName() : "",
|
||||
"ip", server.getIp() != null ? server.getIp() : "",
|
||||
"panelUrl", server.getPanelUrl() != null ? server.getPanelUrl() : "(未配置)",
|
||||
"panelPort", server.getPanelPort() != null ? server.getPanelPort() : 42588,
|
||||
"panelApiKeyConfigured", server.getPanelApiKey() != null && !server.getPanelApiKey().isEmpty(),
|
||||
"panelApiKeyLength", server.getPanelApiKey() != null ? server.getPanelApiKey().length() : 0
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.PageResult;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.dto.DomainDeployRequest;
|
||||
import com.nanxiislet.admin.dto.DomainDeployResult;
|
||||
import com.nanxiislet.admin.dto.DomainStatsDTO;
|
||||
import com.nanxiislet.admin.entity.PlatformDomain;
|
||||
import com.nanxiislet.admin.entity.PlatformServer;
|
||||
import com.nanxiislet.admin.service.PlatformDomainService;
|
||||
import com.nanxiislet.admin.service.PlatformServerService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 域名管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-13
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/platform/domain")
|
||||
@Tag(name = "域名管理", description = "域名的增删改查及部署")
|
||||
public class PlatformDomainController {
|
||||
|
||||
@Resource
|
||||
private PlatformDomainService domainService;
|
||||
|
||||
@Resource
|
||||
private PlatformServerService serverService;
|
||||
|
||||
// ==================== 基础 CRUD ====================
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "域名列表")
|
||||
public R<PageResult<PlatformDomain>> list(com.nanxiislet.admin.dto.DomainQueryDTO query) {
|
||||
Page<PlatformDomain> page = domainService.listPage(query);
|
||||
return R.ok(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
||||
}
|
||||
|
||||
@GetMapping("/all")
|
||||
@Operation(summary = "所有域名")
|
||||
public R<List<PlatformDomain>> listAll() {
|
||||
return R.ok(domainService.list());
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "域名详情")
|
||||
public R<PlatformDomain> getById(@PathVariable Long id) {
|
||||
PlatformDomain domain = domainService.getById(id);
|
||||
if (domain == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(domain);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增域名")
|
||||
public R<PlatformDomain> create(@Valid @RequestBody PlatformDomain domain) {
|
||||
// 检查域名是否已存在
|
||||
PlatformDomain existing = domainService.getByDomain(domain.getDomain());
|
||||
if (existing != null) {
|
||||
throw new BusinessException("域名已存在");
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if (domain.getStatus() == null) {
|
||||
domain.setStatus("pending");
|
||||
}
|
||||
if (domain.getDnsStatus() == null) {
|
||||
domain.setDnsStatus("checking");
|
||||
}
|
||||
if (domain.getSslStatus() == null) {
|
||||
domain.setSslStatus("none");
|
||||
}
|
||||
if (domain.getDeployStatus() == null) {
|
||||
domain.setDeployStatus("not_deployed");
|
||||
}
|
||||
if (domain.getPort() == null) {
|
||||
domain.setPort(80);
|
||||
}
|
||||
|
||||
// 自动填充服务器信息
|
||||
if (domain.getServerId() != null) {
|
||||
PlatformServer server = serverService.getById(domain.getServerId());
|
||||
if (server != null) {
|
||||
domain.setServerName(server.getName());
|
||||
domain.setServerIp(server.getIp());
|
||||
}
|
||||
}
|
||||
|
||||
// 自动生成别名和站点路径
|
||||
if (!StringUtils.hasText(domain.getAlias())) {
|
||||
domain.setAlias(domain.getDomain().replace(".", "_"));
|
||||
}
|
||||
if (!StringUtils.hasText(domain.getSitePath())) {
|
||||
domain.setSitePath("/opt/1panel/www/sites/" + domain.getDomain() + "/index");
|
||||
}
|
||||
|
||||
// 如果绑定了SSL证书,自动设置SSL状态和开启HTTPS
|
||||
if (domain.getCertificateId() != null) {
|
||||
domain.setSslStatus("valid");
|
||||
domain.setEnableHttps(true);
|
||||
}
|
||||
|
||||
domainService.save(domain);
|
||||
return R.ok(domain);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新域名")
|
||||
public R<Void> update(@PathVariable Long id, @Valid @RequestBody PlatformDomain domain) {
|
||||
PlatformDomain existing = domainService.getById(id);
|
||||
if (existing == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
|
||||
domain.setId(id);
|
||||
|
||||
// 自动填充服务器信息
|
||||
if (domain.getServerId() != null && !domain.getServerId().equals(existing.getServerId())) {
|
||||
PlatformServer server = serverService.getById(domain.getServerId());
|
||||
if (server != null) {
|
||||
domain.setServerName(server.getName());
|
||||
domain.setServerIp(server.getIp());
|
||||
}
|
||||
}
|
||||
|
||||
// 处理SSL和HTTPS状态
|
||||
// 只有在用户未明确设置 enableHttps 时,才自动根据证书设置
|
||||
if (domain.getEnableHttps() == null) {
|
||||
// 用户未传入 enableHttps,根据证书自动判断
|
||||
if (domain.getCertificateId() != null) {
|
||||
domain.setSslStatus("valid");
|
||||
domain.setEnableHttps(true);
|
||||
}
|
||||
} else if (Boolean.FALSE.equals(domain.getEnableHttps())) {
|
||||
// 用户明确关闭HTTPS
|
||||
domain.setSslStatus("none");
|
||||
} else if (Boolean.TRUE.equals(domain.getEnableHttps()) && domain.getCertificateId() != null) {
|
||||
// 用户开启HTTPS且有证书
|
||||
domain.setSslStatus("valid");
|
||||
}
|
||||
|
||||
domainService.updateById(domain);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除域名")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
domainService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ==================== 统计接口 ====================
|
||||
|
||||
@GetMapping("/stats")
|
||||
@Operation(summary = "域名统计信息")
|
||||
public R<DomainStatsDTO> getStats() {
|
||||
return R.ok(domainService.getDomainStats());
|
||||
}
|
||||
|
||||
// ==================== 部署相关接口 ====================
|
||||
|
||||
@PostMapping("/deploy")
|
||||
@Operation(summary = "部署域名到1Panel", description = "包含创建网站、申请证书、配置HTTPS")
|
||||
public R<DomainDeployResult> deploy(@Valid @RequestBody DomainDeployRequest request) {
|
||||
DomainDeployResult result = domainService.deployDomain(request);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
@PostMapping("/undeploy/{id}")
|
||||
@Operation(summary = "从1Panel删除部署")
|
||||
public R<Void> undeploy(@PathVariable Long id) {
|
||||
domainService.undeployDomain(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/check-dns")
|
||||
@Operation(summary = "检查域名DNS解析状态")
|
||||
public R<Map<String, Object>> checkDns(@PathVariable Long id) {
|
||||
Map<String, Object> result = domainService.checkDomainDns(id);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/sync")
|
||||
@Operation(summary = "从1Panel同步域名信息")
|
||||
public R<PlatformDomain> syncFromPanel(@PathVariable Long id) {
|
||||
PlatformDomain domain = domainService.syncDomainFromPanel(id);
|
||||
return R.ok(domain);
|
||||
}
|
||||
|
||||
@PostMapping("/sync-from-certificates/{serverId}")
|
||||
@Operation(summary = "从证书同步域名", description = "将证书表中的域名同步到域名表")
|
||||
public R<Map<String, Object>> syncFromCertificates(@PathVariable Long serverId) {
|
||||
int count = domainService.syncDomainsFromCertificates(serverId);
|
||||
return R.ok(Map.of("syncCount", count, "message", "同步完成,共 " + count + " 个域名"));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/deploy-runtime")
|
||||
@Operation(summary = "部署运行环境到1Panel", description = "创建运行时类型的网站")
|
||||
public R<DomainDeployResult> deployRuntime(@PathVariable Long id) {
|
||||
DomainDeployResult result = domainService.deployRuntime(id);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
// ==================== Nginx配置相关 ====================
|
||||
|
||||
@Resource
|
||||
private com.nanxiislet.admin.service.OnePanelService onePanelService;
|
||||
|
||||
@GetMapping("/{id}/nginx-config")
|
||||
@Operation(summary = "获取域名Nginx配置")
|
||||
public R<Map<String, Object>> getNginxConfig(@PathVariable Long id) {
|
||||
PlatformDomain domain = domainService.getById(id);
|
||||
if (domain == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
if (domain.getServerId() == null || domain.getPanelWebsiteId() == null) {
|
||||
throw new BusinessException("域名未部署或未绑定服务器");
|
||||
}
|
||||
|
||||
Map<String, Object> config = onePanelService.getWebsiteNginxConfig(domain.getServerId(), domain.getPanelWebsiteId());
|
||||
// 添加域名信息
|
||||
config.put("domain", domain.getDomain());
|
||||
return R.ok(config);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/nginx-config")
|
||||
@Operation(summary = "保存域名Nginx配置")
|
||||
public R<Boolean> saveNginxConfig(@PathVariable Long id, @RequestBody Map<String, Object> params) {
|
||||
PlatformDomain domain = domainService.getById(id);
|
||||
if (domain == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
if (domain.getServerId() == null || domain.getPanelWebsiteId() == null) {
|
||||
throw new BusinessException("域名未部署或未绑定服务器");
|
||||
}
|
||||
|
||||
String content = (String) params.get("content");
|
||||
if (content == null || content.isEmpty()) {
|
||||
throw new BusinessException("配置内容不能为空");
|
||||
}
|
||||
|
||||
boolean success = onePanelService.saveWebsiteNginxConfig(domain.getServerId(), domain.getPanelWebsiteId(), content);
|
||||
return success ? R.ok(true) : R.fail("保存失败");
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/nginx-reload")
|
||||
@Operation(summary = "重载Nginx")
|
||||
public R<Boolean> reloadNginx(@PathVariable Long id) {
|
||||
PlatformDomain domain = domainService.getById(id);
|
||||
if (domain == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
if (domain.getServerId() == null) {
|
||||
throw new BusinessException("域名未绑定服务器");
|
||||
}
|
||||
|
||||
boolean success = onePanelService.reloadNginx(domain.getServerId());
|
||||
return success ? R.ok(true) : R.fail("重载失败");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.PageResult;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.dto.DeployRequest;
|
||||
import com.nanxiislet.admin.dto.DeployResult;
|
||||
import com.nanxiislet.admin.entity.PlatformProject;
|
||||
import com.nanxiislet.admin.service.OnePanelService;
|
||||
import com.nanxiislet.admin.service.PlatformProjectService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 项目管理控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/platform/project")
|
||||
@Tag(name = "项目管理", description = "项目的增删改查和部署")
|
||||
public class PlatformProjectController {
|
||||
|
||||
@Resource
|
||||
private PlatformProjectService projectService;
|
||||
|
||||
@Resource
|
||||
private OnePanelService onePanelService;
|
||||
|
||||
@Resource
|
||||
private com.nanxiislet.admin.service.PlatformDomainService domainService;
|
||||
|
||||
@Resource
|
||||
private com.nanxiislet.admin.service.SysMenuService menuService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "项目列表")
|
||||
public R<PageResult<PlatformProject>> list(BasePageQuery query) {
|
||||
Page<PlatformProject> page = projectService.listPage(query);
|
||||
// 填充菜单数量
|
||||
fillMenuCount(page.getRecords());
|
||||
return R.ok(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
||||
}
|
||||
|
||||
@GetMapping("/all")
|
||||
@Operation(summary = "所有项目")
|
||||
public R<List<PlatformProject>> listAll() {
|
||||
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<PlatformProject> wrapper =
|
||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>();
|
||||
// 过滤掉门户类项目
|
||||
wrapper.ne(PlatformProject::getSystemType, "portal");
|
||||
// 确保按照排序字段排序
|
||||
wrapper.orderByAsc(PlatformProject::getSort);
|
||||
|
||||
List<PlatformProject> list = projectService.list(wrapper);
|
||||
// 填充菜单数量
|
||||
fillMenuCount(list);
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
@GetMapping("/integrated")
|
||||
@Operation(summary = "获取集成到框架的业务项目", description = "返回 systemType=admin 且 integrateToFramework=true 的项目")
|
||||
public R<List<PlatformProject>> listIntegratedProjects() {
|
||||
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<PlatformProject> wrapper =
|
||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>();
|
||||
wrapper.eq(PlatformProject::getSystemType, "admin")
|
||||
.eq(PlatformProject::getIntegrateToFramework, true)
|
||||
.eq(PlatformProject::getStatus, "active")
|
||||
.orderByAsc(PlatformProject::getSort);
|
||||
List<PlatformProject> list = projectService.list(wrapper);
|
||||
|
||||
// 填充菜单数量
|
||||
fillMenuCount(list);
|
||||
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充菜单数量
|
||||
*/
|
||||
private void fillMenuCount(List<PlatformProject> projectList) {
|
||||
if (projectList == null || projectList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (PlatformProject project : projectList) {
|
||||
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<com.nanxiislet.admin.entity.SysMenu> menuWrapper =
|
||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>();
|
||||
|
||||
// 判定是否为主框架 NanxiAdmin
|
||||
// 逻辑:简称或Code匹配(忽略大小写和空格),或者 ID 为 9 (从截图看是9,作为兜底)
|
||||
String shortName = project.getShortName() != null ? project.getShortName().trim() : "";
|
||||
String code = project.getCode() != null ? project.getCode().trim() : "";
|
||||
|
||||
boolean isMainProject = "NanxiAdmin".equalsIgnoreCase(shortName)
|
||||
|| "NanxiAdmin".equalsIgnoreCase(code)
|
||||
|| Long.valueOf(9).equals(project.getId());
|
||||
|
||||
if (isMainProject) {
|
||||
// 主框架:统计 project_id 为 null 或等于其 id
|
||||
menuWrapper.and(w -> w.eq(com.nanxiislet.admin.entity.SysMenu::getProjectId, project.getId())
|
||||
.or()
|
||||
.isNull(com.nanxiislet.admin.entity.SysMenu::getProjectId));
|
||||
} else {
|
||||
// 其他项目,只统计其 id
|
||||
menuWrapper.eq(com.nanxiislet.admin.entity.SysMenu::getProjectId, project.getId());
|
||||
}
|
||||
// 只统计菜单和目录,不统计按钮
|
||||
menuWrapper.in(com.nanxiislet.admin.entity.SysMenu::getType, "directory", "menu");
|
||||
|
||||
long count = menuService.count(menuWrapper);
|
||||
project.setMenuCount(count);
|
||||
|
||||
log.info("Project: {}, ID: {}, IsMain: {}, MenuCount: {}", project.getShortName(), project.getId(), isMainProject, count);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "项目详情")
|
||||
public R<PlatformProject> getById(@PathVariable Long id) {
|
||||
PlatformProject project = projectService.getById(id);
|
||||
if (project == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(project);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增项目")
|
||||
public R<PlatformProject> create(@Valid @RequestBody PlatformProject project) {
|
||||
projectService.save(project);
|
||||
return R.ok(project);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新项目")
|
||||
public R<Void> update(@PathVariable Long id, @Valid @RequestBody PlatformProject project) {
|
||||
project.setId(id);
|
||||
|
||||
// 如果状态发生改变,尝试同步更新 1Panel 网站状态
|
||||
if (project.getStatus() != null) {
|
||||
PlatformProject existingProject = projectService.getById(id);
|
||||
if (existingProject != null && !project.getStatus().equals(existingProject.getStatus())) {
|
||||
// 判断是否已部署且绑定了 1Panel 网站
|
||||
if (existingProject.getPanelWebsiteId() != null && existingProject.getServerId() != null) {
|
||||
String operate = "active".equals(project.getStatus()) ? "start" : "stop";
|
||||
onePanelService.operateWebsite(existingProject.getServerId(), existingProject.getPanelWebsiteId(), operate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
projectService.updateById(project);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除项目")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
PlatformProject project = projectService.getById(id);
|
||||
if (project != null && project.getDomainId() != null) {
|
||||
// 解除域名绑定并重置状态
|
||||
com.nanxiislet.admin.entity.PlatformDomain domain = domainService.getById(project.getDomainId());
|
||||
if (domain != null) {
|
||||
// 如果已部署且绑定了 1Panel 网站,则调用 1Panel 接口删除网站
|
||||
if (domain.getPanelWebsiteId() != null && domain.getServerId() != null) {
|
||||
try {
|
||||
onePanelService.deleteWebsite(domain.getServerId(), domain.getPanelWebsiteId());
|
||||
} catch (Exception e) {
|
||||
// 忽略删除网站失败,继续执行解绑逻辑
|
||||
}
|
||||
}
|
||||
|
||||
domain.setProjectId(null);
|
||||
domain.setProjectName(null);
|
||||
domain.setStatus("pending");
|
||||
domain.setDeployStatus("pending");
|
||||
domain.setPanelWebsiteId(null);
|
||||
domain.setPanelSslId(null);
|
||||
domain.setDnsStatus("pending");
|
||||
domain.setSslStatus("pending");
|
||||
domain.setEnableHttps(false);
|
||||
domainService.updateById(domain);
|
||||
|
||||
// 尝试重新检测 DNS
|
||||
try {
|
||||
domainService.checkDomainDns(domain.getId());
|
||||
} catch (Exception e) {
|
||||
// 忽略 DNS 检测错误
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
projectService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ==================== 部署相关接口 ====================
|
||||
|
||||
@PostMapping("/deploy")
|
||||
@Operation(summary = "部署项目到服务器", description = "通过1Panel API将项目部署到服务器")
|
||||
public R<DeployResult> deploy(@Valid @RequestBody DeployRequest request) {
|
||||
DeployResult result = onePanelService.deployProject(request);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
@GetMapping("/{serverId}/websites")
|
||||
@Operation(summary = "获取服务器网站列表", description = "从1Panel获取指定服务器的网站列表")
|
||||
public R<String> getWebsites(@PathVariable Long serverId) {
|
||||
String websites = onePanelService.getWebsites(serverId);
|
||||
return R.ok(websites);
|
||||
}
|
||||
|
||||
@GetMapping("/{serverId}/certificates")
|
||||
@Operation(summary = "获取服务器证书列表", description = "从1Panel获取指定服务器的SSL证书列表")
|
||||
public R<String> getCertificates(@PathVariable Long serverId) {
|
||||
String certificates = onePanelService.getCertificates(serverId);
|
||||
return R.ok(certificates);
|
||||
}
|
||||
|
||||
@GetMapping("/check-deploy/{projectId}")
|
||||
@Operation(summary = "检查项目部署状态", description = "检查项目对应的网站是否已在1Panel中存在")
|
||||
public R<Map<String, Object>> checkDeployStatus(@PathVariable Long projectId) {
|
||||
PlatformProject project = projectService.getById(projectId);
|
||||
if (project == null) {
|
||||
return R.fail("项目不存在");
|
||||
}
|
||||
|
||||
Map<String, Object> result = new java.util.HashMap<>();
|
||||
result.put("projectId", projectId);
|
||||
result.put("domain", project.getDomain());
|
||||
|
||||
if (project.getServerId() == null || project.getDomain() == null || project.getDomain().isEmpty()) {
|
||||
result.put("deployed", false);
|
||||
result.put("websiteId", null);
|
||||
result.put("message", "项目未配置服务器或域名");
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
// 获取网站详细状态
|
||||
Map<String, Object> websiteStatus = onePanelService.checkWebsiteStatus(project.getServerId(), project.getDomain());
|
||||
boolean exists = Boolean.TRUE.equals(websiteStatus.get("exists"));
|
||||
|
||||
result.put("deployed", exists);
|
||||
result.put("websiteId", websiteStatus.get("id"));
|
||||
result.put("websiteStatus", websiteStatus.get("status"));
|
||||
result.put("sslStatus", websiteStatus.get("sslStatus"));
|
||||
result.put("protocol", websiteStatus.get("protocol"));
|
||||
result.put("message", exists ? "已部署" : "未部署");
|
||||
|
||||
// 更新项目状态到数据库
|
||||
if (exists) {
|
||||
Long websiteId = (Long) websiteStatus.get("id");
|
||||
String status = (String) websiteStatus.get("status");
|
||||
String sslStatus = (String) websiteStatus.get("sslStatus");
|
||||
String protocol = (String) websiteStatus.get("protocol");
|
||||
|
||||
boolean needUpdate = false;
|
||||
|
||||
// 更新 panelWebsiteId
|
||||
if (websiteId != null && (project.getPanelWebsiteId() == null || !project.getPanelWebsiteId().equals(websiteId))) {
|
||||
project.setPanelWebsiteId(websiteId);
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
// 更新部署状态
|
||||
if (!"success".equals(project.getLastDeployStatus())) {
|
||||
project.setLastDeployStatus("success");
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
// 更新项目状态(Running -> active/启用)
|
||||
if ("Running".equals(status) && !"active".equals(project.getStatus())) {
|
||||
project.setStatus("active");
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
// 更新 HTTPS 状态
|
||||
if ("HTTPS".equals(protocol) && !Boolean.TRUE.equals(project.getEnableHttps())) {
|
||||
project.setEnableHttps(true);
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
// 更新部署路径
|
||||
String sitePath = (String) websiteStatus.get("sitePath");
|
||||
if (sitePath != null) {
|
||||
// 强制追加 /index 目录
|
||||
if (!sitePath.endsWith("/index")) {
|
||||
if (sitePath.endsWith("/")) {
|
||||
sitePath = sitePath + "index";
|
||||
} else {
|
||||
sitePath = sitePath + "/index";
|
||||
}
|
||||
}
|
||||
if (!sitePath.equals(project.getDeployPath())) {
|
||||
log.info("Updating Project {} deployPath to {}", project.getId(), sitePath); // Log
|
||||
project.setDeployPath(sitePath);
|
||||
needUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needUpdate) {
|
||||
projectService.updateById(project);
|
||||
}
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
// ==================== 文件上传相关接口 ====================
|
||||
|
||||
@PostMapping("/upload/check")
|
||||
@Operation(summary = "检查文件是否存在")
|
||||
public R<List<String>> checkUploadFiles(@RequestBody Map<String, Object> params) {
|
||||
Long projectId = Long.valueOf(params.get("projectId").toString());
|
||||
List<String> paths = (List<String>) params.get("paths");
|
||||
|
||||
PlatformProject project = projectService.getById(projectId);
|
||||
if (project == null || project.getServerId() == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
|
||||
log.info("Checking files for Project {}: {}", projectId, paths); // Log input
|
||||
List<String> result = onePanelService.checkFileBatch(project.getServerId(), paths);
|
||||
log.info("Check result: {}", result); // Log output
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
@PostMapping("/upload/prepare")
|
||||
@Operation(summary = "准备上传(已废弃,保留向后兼容)", description = "由于 1Panel upload 接口支持 overwrite 参数,此接口已不再需要预先清理文件")
|
||||
@Deprecated
|
||||
public R<Boolean> prepareUpload(@RequestBody Map<String, Object> params) {
|
||||
// upload 接口支持 overwrite=true,不再需要预先清理文件
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/upload/chunk", consumes = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
@Operation(summary = "分片上传文件")
|
||||
public R<String> uploadChunk(
|
||||
@RequestParam("projectId") Long projectId,
|
||||
@RequestParam("filename") String filename,
|
||||
@RequestParam("path") String path,
|
||||
@RequestParam("chunkIndex") int chunkIndex,
|
||||
@RequestParam("chunkCount") int chunkCount,
|
||||
@RequestPart("chunk") org.springframework.web.multipart.MultipartFile chunk) {
|
||||
|
||||
PlatformProject project = projectService.getById(projectId);
|
||||
if (project == null || project.getServerId() == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
|
||||
return R.ok(onePanelService.uploadFileChunk(project.getServerId(), filename, path, chunkIndex, chunkCount, chunk));
|
||||
}
|
||||
|
||||
// ==================== Nginx配置相关 ====================
|
||||
|
||||
@GetMapping("/{projectId}/nginx-config")
|
||||
@Operation(summary = "获取项目Nginx配置")
|
||||
public R<Map<String, Object>> getNginxConfig(@PathVariable Long projectId) {
|
||||
PlatformProject project = projectService.getById(projectId);
|
||||
if (project == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
if (project.getServerId() == null || project.getPanelWebsiteId() == null) {
|
||||
throw new BusinessException("项目未部署或未绑定服务器");
|
||||
}
|
||||
|
||||
Map<String, Object> config = onePanelService.getWebsiteNginxConfig(project.getServerId(), project.getPanelWebsiteId());
|
||||
// 添加域名信息
|
||||
config.put("domain", project.getDomain());
|
||||
return R.ok(config);
|
||||
}
|
||||
|
||||
@PutMapping("/{projectId}/nginx-config")
|
||||
@Operation(summary = "保存项目Nginx配置")
|
||||
public R<Boolean> saveNginxConfig(@PathVariable Long projectId, @RequestBody Map<String, Object> params) {
|
||||
PlatformProject project = projectService.getById(projectId);
|
||||
if (project == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
if (project.getServerId() == null || project.getPanelWebsiteId() == null) {
|
||||
throw new BusinessException("项目未部署或未绑定服务器");
|
||||
}
|
||||
|
||||
String content = (String) params.get("content");
|
||||
if (content == null || content.isEmpty()) {
|
||||
throw new BusinessException("配置内容不能为空");
|
||||
}
|
||||
|
||||
boolean success = onePanelService.saveWebsiteNginxConfig(project.getServerId(), project.getPanelWebsiteId(), content);
|
||||
return success ? R.ok(true) : R.fail("保存失败");
|
||||
}
|
||||
|
||||
@PostMapping("/{projectId}/nginx-reload")
|
||||
@Operation(summary = "重载Nginx")
|
||||
public R<Boolean> reloadNginx(@PathVariable Long projectId) {
|
||||
PlatformProject project = projectService.getById(projectId);
|
||||
if (project == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
if (project.getServerId() == null) {
|
||||
throw new BusinessException("项目未绑定服务器");
|
||||
}
|
||||
|
||||
boolean success = onePanelService.reloadNginx(project.getServerId());
|
||||
return success ? R.ok(true) : R.fail("重载失败");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.PageResult;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.dto.ServerInfoDto;
|
||||
import com.nanxiislet.admin.entity.PlatformServer;
|
||||
import com.nanxiislet.admin.service.PlatformServerService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 服务器管理控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/platform/server")
|
||||
@Tag(name = "服务器管理", description = "服务器的增删改查")
|
||||
public class PlatformServerController {
|
||||
|
||||
@Resource
|
||||
private PlatformServerService serverService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "服务器列表(分页)")
|
||||
public R<PageResult<PlatformServer>> list(BasePageQuery query) {
|
||||
Page<PlatformServer> page = serverService.listPage(query);
|
||||
return R.ok(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
||||
}
|
||||
|
||||
@GetMapping("/all")
|
||||
@Operation(summary = "所有服务器(基础数据,支持筛选)")
|
||||
public R<List<PlatformServer>> listAll(
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String type
|
||||
) {
|
||||
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<PlatformServer> wrapper = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>();
|
||||
|
||||
if (cn.hutool.core.util.StrUtil.isNotBlank(keyword)) {
|
||||
wrapper.and(w -> w
|
||||
.like(PlatformServer::getName, keyword)
|
||||
.or().like(PlatformServer::getIp, keyword)
|
||||
);
|
||||
}
|
||||
|
||||
if (cn.hutool.core.util.StrUtil.isNotBlank(status)) {
|
||||
wrapper.eq(PlatformServer::getStatus, status);
|
||||
}
|
||||
|
||||
if (cn.hutool.core.util.StrUtil.isNotBlank(type)) {
|
||||
wrapper.eq(PlatformServer::getType, type);
|
||||
}
|
||||
|
||||
wrapper.orderByDesc(PlatformServer::getCreatedAt);
|
||||
|
||||
return R.ok(serverService.list(wrapper));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "服务器详情(基础数据)")
|
||||
public R<PlatformServer> getById(@PathVariable Long id) {
|
||||
PlatformServer server = serverService.getById(id);
|
||||
if (server == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(server);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/status")
|
||||
@Operation(summary = "获取服务器实时状态(从1Panel获取CPU/内存/磁盘使用情况)")
|
||||
public R<ServerInfoDto> getServerStatus(@PathVariable Long id) {
|
||||
ServerInfoDto dto = serverService.getServerStatus(id);
|
||||
if (dto == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(dto);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/refresh")
|
||||
@Operation(summary = "刷新服务器状态(同 /{id}/status)")
|
||||
public R<ServerInfoDto> refreshStatus(@PathVariable Long id) {
|
||||
ServerInfoDto dto = serverService.getServerStatus(id);
|
||||
if (dto == null) {
|
||||
throw new BusinessException(ResultCode.DATA_NOT_EXIST);
|
||||
}
|
||||
return R.ok(dto);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增服务器")
|
||||
public R<PlatformServer> create(@Valid @RequestBody PlatformServer server) {
|
||||
serverService.save(server);
|
||||
return R.ok(server);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新服务器")
|
||||
public R<Void> update(@PathVariable Long id, @Valid @RequestBody PlatformServer server) {
|
||||
server.setId(id);
|
||||
serverService.updateById(server);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除服务器")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
// 检查是否有绑定的项目
|
||||
List<String> bindingProjects = serverService.getBindingProjects(id);
|
||||
if (!bindingProjects.isEmpty()) {
|
||||
String projectNames = String.join("、", bindingProjects);
|
||||
return R.fail("无法删除服务器,以下项目正在使用该服务器:" + projectNames + ",请先删除或修改这些项目的服务器配置");
|
||||
}
|
||||
|
||||
// 删除该服务器绑定的域名
|
||||
serverService.deleteBindingDomains(id);
|
||||
|
||||
// 删除该服务器绑定的证书
|
||||
serverService.deleteBindingCertificates(id);
|
||||
|
||||
// 删除服务器
|
||||
serverService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.service.OnePanelService;
|
||||
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;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 运行时管理控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/platform/runtime")
|
||||
@Tag(name = "运行时管理")
|
||||
public class RuntimeController {
|
||||
|
||||
@Resource
|
||||
private OnePanelService onePanelService;
|
||||
|
||||
|
||||
/**
|
||||
* 搜索运行时列表
|
||||
*/
|
||||
@PostMapping("/search")
|
||||
@Operation(summary = "搜索运行时列表")
|
||||
public R<Map<String, Object>> searchRuntimes(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String type = params.get("type") != null ? params.get("type").toString() : "node";
|
||||
int page = params.get("page") != null ? Integer.parseInt(params.get("page").toString()) : 1;
|
||||
int pageSize = params.get("pageSize") != null ? Integer.parseInt(params.get("pageSize").toString()) : 20;
|
||||
|
||||
Map<String, Object> result = onePanelService.searchRuntimes(serverId, type, page, pageSize);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步运行时状态
|
||||
*/
|
||||
@PostMapping("/sync")
|
||||
@Operation(summary = "同步运行时状态")
|
||||
public R<Void> syncRuntimes(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
|
||||
Map<String, Object> result = onePanelService.syncRuntimes(serverId);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作运行时(启动/停止/重启)
|
||||
*/
|
||||
@PostMapping("/operate")
|
||||
@Operation(summary = "操作运行时")
|
||||
public R<Void> operateRuntime(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
if (params.get("id") == null) {
|
||||
return R.fail("id不能为空");
|
||||
}
|
||||
if (params.get("operate") == null) {
|
||||
return R.fail("operate不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
Long id = Long.parseLong(params.get("id").toString());
|
||||
String operate = params.get("operate").toString();
|
||||
|
||||
Map<String, Object> result = onePanelService.operateRuntime(serverId, id, operate);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除运行时
|
||||
*/
|
||||
@PostMapping("/delete")
|
||||
@Operation(summary = "删除运行时")
|
||||
public R<Void> deleteRuntime(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
if (params.get("id") == null) {
|
||||
return R.fail("id不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
Long id = Long.parseLong(params.get("id").toString());
|
||||
boolean forceDelete = params.get("forceDelete") != null && Boolean.parseBoolean(params.get("forceDelete").toString());
|
||||
boolean deleteFolder = params.get("deleteFolder") != null && Boolean.parseBoolean(params.get("deleteFolder").toString());
|
||||
String codeDir = params.get("codeDir") != null ? params.get("codeDir").toString() : null;
|
||||
|
||||
Map<String, Object> result = onePanelService.deleteRuntime(serverId, id, forceDelete, deleteFolder, codeDir);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索运行时应用列表
|
||||
*/
|
||||
@PostMapping("/apps/search")
|
||||
@Operation(summary = "搜索运行时应用列表")
|
||||
public R<Map<String, Object>> searchRuntimeApps(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String type = params.get("type") != null ? params.get("type").toString() : "node";
|
||||
int page = params.get("page") != null ? Integer.parseInt(params.get("page").toString()) : 1;
|
||||
int pageSize = params.get("pageSize") != null ? Integer.parseInt(params.get("pageSize").toString()) : 20;
|
||||
|
||||
Map<String, Object> result = onePanelService.searchRuntimeApps(serverId, type, page, pageSize);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取运行时版本详情
|
||||
*/
|
||||
@PostMapping("/detail")
|
||||
@Operation(summary = "获取运行时版本详情")
|
||||
public R<Map<String, Object>> getRuntimeDetail(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
if (params.get("appId") == null) {
|
||||
return R.fail("appId不能为空");
|
||||
}
|
||||
if (params.get("version") == null) {
|
||||
return R.fail("version不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
Long appId = Long.parseLong(params.get("appId").toString());
|
||||
String version = params.get("version").toString();
|
||||
|
||||
Map<String, Object> result = onePanelService.getRuntimeDetail(serverId, appId, version);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用信息(包含版本列表)
|
||||
*/
|
||||
@PostMapping("/app/info")
|
||||
@Operation(summary = "获取应用信息")
|
||||
public R<Map<String, Object>> getAppInfo(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
if (params.get("appKey") == null) {
|
||||
return R.fail("appKey不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String appKey = params.get("appKey").toString();
|
||||
|
||||
Map<String, Object> result = onePanelService.getAppInfo(serverId, appKey);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建运行时
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建运行时")
|
||||
public R<Void> createRuntime(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
params.remove("serverId");
|
||||
|
||||
Map<String, Object> result = onePanelService.createRuntime(serverId, params);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取容器日志
|
||||
*/
|
||||
@PostMapping("/container/log")
|
||||
@Operation(summary = "获取容器日志")
|
||||
public R<Map<String, Object>> getContainerLog(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String containerName = params.get("containerName") != null ? params.get("containerName").toString() : "";
|
||||
String composePath = params.get("composePath") != null ? params.get("composePath").toString() : null;
|
||||
|
||||
if (containerName.isEmpty() && (composePath == null || composePath.isEmpty())) {
|
||||
return R.fail("containerName和composePath不能同时为空");
|
||||
}
|
||||
|
||||
Map<String, Object> result = onePanelService.getContainerLog(serverId, containerName, composePath);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新运行时
|
||||
*/
|
||||
@PostMapping("/update")
|
||||
@Operation(summary = "更新运行时")
|
||||
public R<Void> updateRuntime(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
if (params.get("id") == null) {
|
||||
return R.fail("id不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
params.remove("serverId");
|
||||
|
||||
Map<String, Object> result = onePanelService.updateRuntime(serverId, params);
|
||||
|
||||
if (result.containsKey("error")) {
|
||||
return R.fail(result.get("error").toString());
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
/**
|
||||
* 获取Node脚本
|
||||
*/
|
||||
@PostMapping("/node/scripts")
|
||||
@Operation(summary = "获取Node脚本")
|
||||
public R<List<Map<String, Object>>> getNodeScripts(@RequestBody Map<String, Object> params) {
|
||||
if (params.get("serverId") == null) {
|
||||
return R.fail("serverId不能为空");
|
||||
}
|
||||
if (params.get("codeDir") == null) {
|
||||
return R.fail("codeDir不能为空");
|
||||
}
|
||||
|
||||
Long serverId = Long.parseLong(params.get("serverId").toString());
|
||||
String codeDir = params.get("codeDir").toString();
|
||||
|
||||
List<Map<String, Object>> result = onePanelService.getNodeScripts(serverId, codeDir);
|
||||
return R.ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.dto.approval.ApprovalInstanceVO;
|
||||
import com.nanxiislet.admin.dto.approval.ApprovalStats;
|
||||
import com.nanxiislet.admin.dto.approval.ApprovalTemplateRequest;
|
||||
import com.nanxiislet.admin.dto.approval.ApprovalTemplateVO;
|
||||
import com.nanxiislet.admin.service.SysApprovalInstanceService;
|
||||
import com.nanxiislet.admin.service.SysApprovalTemplateService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审批流程管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/system/approval")
|
||||
@Tag(name = "审批流程管理", description = "审批模板、实例管理")
|
||||
public class SysApprovalController {
|
||||
|
||||
@Resource
|
||||
private SysApprovalTemplateService templateService;
|
||||
|
||||
@Resource
|
||||
private SysApprovalInstanceService instanceService;
|
||||
|
||||
// ==================== 统计 ====================
|
||||
|
||||
@GetMapping("/stats")
|
||||
@Operation(summary = "获取审批统计数据")
|
||||
public R<ApprovalStats> getStats() {
|
||||
return R.ok(templateService.getStats());
|
||||
}
|
||||
|
||||
// ==================== 模板管理 ====================
|
||||
|
||||
@GetMapping("/template/page")
|
||||
@Operation(summary = "分页查询模板列表")
|
||||
public R<IPage<ApprovalTemplateVO>> templatePage(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String scenario,
|
||||
@RequestParam(required = false) Boolean enabled) {
|
||||
return R.ok(templateService.listPage(page, pageSize, scenario, enabled));
|
||||
}
|
||||
|
||||
@GetMapping("/template/list")
|
||||
@Operation(summary = "获取所有模板列表")
|
||||
public R<List<ApprovalTemplateVO>> templateList(
|
||||
@RequestParam(required = false) String scenario) {
|
||||
return R.ok(templateService.listAll(scenario));
|
||||
}
|
||||
|
||||
@GetMapping("/template/{id}")
|
||||
@Operation(summary = "获取模板详情")
|
||||
public R<ApprovalTemplateVO> getTemplate(@PathVariable Long id) {
|
||||
return R.ok(templateService.getDetail(id));
|
||||
}
|
||||
|
||||
@PostMapping("/template")
|
||||
@Operation(summary = "创建模板")
|
||||
public R<Long> createTemplate(@RequestBody ApprovalTemplateRequest request) {
|
||||
return R.ok(templateService.create(request));
|
||||
}
|
||||
|
||||
@PutMapping("/template/{id}")
|
||||
@Operation(summary = "更新模板")
|
||||
public R<Void> updateTemplate(@PathVariable Long id, @RequestBody ApprovalTemplateRequest request) {
|
||||
request.setId(id);
|
||||
templateService.update(request);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/template/{id}")
|
||||
@Operation(summary = "删除模板")
|
||||
public R<Void> deleteTemplate(@PathVariable Long id) {
|
||||
templateService.delete(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/template/{id}/toggle")
|
||||
@Operation(summary = "切换模板启用状态")
|
||||
public R<Void> toggleTemplate(@PathVariable Long id) {
|
||||
templateService.toggle(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ==================== 实例管理 ====================
|
||||
|
||||
@GetMapping("/instance/page")
|
||||
@Operation(summary = "分页查询实例列表")
|
||||
public R<IPage<ApprovalInstanceVO>> instancePage(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String scenario,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
return R.ok(instanceService.listPage(page, pageSize, scenario, status, keyword));
|
||||
}
|
||||
|
||||
@GetMapping("/instance/{id}")
|
||||
@Operation(summary = "获取实例详情")
|
||||
public R<ApprovalInstanceVO> getInstance(@PathVariable Long id) {
|
||||
return R.ok(instanceService.getDetail(id));
|
||||
}
|
||||
|
||||
@PostMapping("/instance/{id}/submit")
|
||||
@Operation(summary = "提交审批")
|
||||
public R<Void> submitInstance(@PathVariable Long id) {
|
||||
instanceService.submit(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/instance/{id}/approve")
|
||||
@Operation(summary = "审批操作")
|
||||
public R<Void> approveInstance(@PathVariable Long id, @RequestBody ApproveRequest request) {
|
||||
instanceService.approve(id, request.getNodeId(), request.getApproverId(), request.getAction(), request.getComment());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/instance/{id}/withdraw")
|
||||
@Operation(summary = "撤回审批")
|
||||
public R<Void> withdrawInstance(@PathVariable Long id) {
|
||||
instanceService.withdraw(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/instance/{id}/cancel")
|
||||
@Operation(summary = "取消审批")
|
||||
public R<Void> cancelInstance(@PathVariable Long id) {
|
||||
instanceService.cancel(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ApproveRequest {
|
||||
private Long nodeId;
|
||||
private Long approverId;
|
||||
private String action; // approve/reject
|
||||
private String comment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.entity.SysDept;
|
||||
import com.nanxiislet.admin.service.SysDeptService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 部门管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/system/dept")
|
||||
@Tag(name = "部门管理", description = "部门树形结构管理")
|
||||
public class SysDeptController {
|
||||
|
||||
@Resource
|
||||
private SysDeptService deptService;
|
||||
|
||||
@GetMapping("/tree")
|
||||
@Operation(summary = "获取部门树")
|
||||
public R<List<SysDept>> tree() {
|
||||
return R.ok(deptService.listTree());
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取部门列表(扁平结构)")
|
||||
public R<List<SysDept>> list() {
|
||||
return R.ok(deptService.listAll());
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取部门详情")
|
||||
public R<SysDept> getById(@PathVariable Long id) {
|
||||
return R.ok(deptService.getById(id));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "创建部门")
|
||||
public R<Long> create(@RequestBody SysDept dept) {
|
||||
return R.ok(deptService.create(dept));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新部门")
|
||||
public R<Void> update(@PathVariable Long id, @RequestBody SysDept dept) {
|
||||
dept.setId(id);
|
||||
deptService.updateDept(dept);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除部门")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
deptService.deleteDept(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/users")
|
||||
@Operation(summary = "获取部门用户列表")
|
||||
public R<List<Long>> getDeptUsers(@PathVariable Long id) {
|
||||
return R.ok(deptService.getDeptUserIds(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.entity.SysDict;
|
||||
import com.nanxiislet.admin.entity.SysDictItem;
|
||||
import com.nanxiislet.admin.service.SysDictItemService;
|
||||
import com.nanxiislet.admin.service.SysDictService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字典管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-08
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/system/dict")
|
||||
@Tag(name = "字典管理", description = "字典类型和字典项CRUD")
|
||||
public class SysDictController {
|
||||
|
||||
@Resource
|
||||
private SysDictService dictService;
|
||||
|
||||
@Resource
|
||||
private SysDictItemService dictItemService;
|
||||
|
||||
// ==================== 字典类型 ====================
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "分页查询字典")
|
||||
public R<IPage<SysDict>> page(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String name,
|
||||
@RequestParam(required = false) String code) {
|
||||
return R.ok(dictService.page(page, pageSize, name, code));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取所有字典")
|
||||
public R<List<SysDict>> list() {
|
||||
return R.ok(dictService.list());
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取字典详情")
|
||||
public R<SysDict> getById(@PathVariable Long id) {
|
||||
return R.ok(dictService.getById(id));
|
||||
}
|
||||
|
||||
@GetMapping("/code/{code}")
|
||||
@Operation(summary = "根据编码获取字典")
|
||||
public R<SysDict> getByCode(@PathVariable String code) {
|
||||
return R.ok(dictService.getByCode(code));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "创建字典")
|
||||
public R<Long> create(@RequestBody SysDict dict) {
|
||||
return R.ok(dictService.create(dict));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新字典")
|
||||
public R<Void> update(@PathVariable Long id, @RequestBody SysDict dict) {
|
||||
dict.setId(id);
|
||||
dictService.updateDict(dict);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除字典")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
dictService.deleteDict(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ==================== 字典项 ====================
|
||||
|
||||
@GetMapping("/{dictId}/items")
|
||||
@Operation(summary = "获取字典项列表")
|
||||
public R<List<SysDictItem>> listItems(@PathVariable Long dictId) {
|
||||
return R.ok(dictItemService.listByDictId(dictId));
|
||||
}
|
||||
|
||||
@GetMapping("/code/{code}/items")
|
||||
@Operation(summary = "根据字典编码获取字典项")
|
||||
public R<List<SysDictItem>> listItemsByCode(@PathVariable String code) {
|
||||
return R.ok(dictItemService.listByDictCode(code));
|
||||
}
|
||||
|
||||
@GetMapping("/item/{id}")
|
||||
@Operation(summary = "获取字典项详情")
|
||||
public R<SysDictItem> getItemById(@PathVariable Long id) {
|
||||
return R.ok(dictItemService.getById(id));
|
||||
}
|
||||
|
||||
@PostMapping("/item")
|
||||
@Operation(summary = "创建字典项")
|
||||
public R<Long> createItem(@RequestBody SysDictItem item) {
|
||||
return R.ok(dictItemService.create(item));
|
||||
}
|
||||
|
||||
@PutMapping("/item/{id}")
|
||||
@Operation(summary = "更新字典项")
|
||||
public R<Void> updateItem(@PathVariable Long id, @RequestBody SysDictItem item) {
|
||||
item.setId(id);
|
||||
dictItemService.updateItem(item);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/item/{id}")
|
||||
@Operation(summary = "删除字典项")
|
||||
public R<Void> deleteItem(@PathVariable Long id) {
|
||||
dictItemService.deleteItem(id);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.entity.SysMenu;
|
||||
import com.nanxiislet.admin.service.SysMenuService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 菜单管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-08
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/system/menu")
|
||||
@Tag(name = "菜单管理", description = "菜单CRUD等")
|
||||
public class SysMenuController {
|
||||
|
||||
@Resource
|
||||
private SysMenuService menuService;
|
||||
|
||||
@Resource
|
||||
private com.nanxiislet.admin.service.PlatformProjectService projectService;
|
||||
|
||||
@GetMapping("/tree")
|
||||
@Operation(summary = "获取菜单树")
|
||||
public R<List<SysMenu>> tree() {
|
||||
return R.ok(menuService.listTree());
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取所有菜单,支持按项目ID筛选")
|
||||
public R<List<SysMenu>> list(@RequestParam(required = false) Long projectId) {
|
||||
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<SysMenu> wrapper =
|
||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>();
|
||||
|
||||
if (projectId != null) {
|
||||
// 特殊逻辑:如果是主框架项目(NanxiAdmin),包含 projectId 为空的记录
|
||||
// 为此我们需要先查项目信息
|
||||
com.nanxiislet.admin.entity.PlatformProject project = projectService.getById(projectId);
|
||||
if (project != null && ("NanxiAdmin".equalsIgnoreCase(project.getShortName()) || "NanxiAdmin".equals(project.getCode()))) {
|
||||
wrapper.and(w -> w.eq(SysMenu::getProjectId, projectId).or().isNull(SysMenu::getProjectId));
|
||||
} else {
|
||||
wrapper.eq(SysMenu::getProjectId, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
wrapper.orderByAsc(SysMenu::getParentId).orderByAsc(SysMenu::getSort);
|
||||
return R.ok(menuService.list(wrapper));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取菜单详情")
|
||||
public R<SysMenu> getById(@PathVariable Long id) {
|
||||
return R.ok(menuService.getById(id));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "创建菜单")
|
||||
public R<Long> create(@RequestBody SysMenu menu) {
|
||||
return R.ok(menuService.create(menu));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新菜单")
|
||||
public R<Void> update(@PathVariable Long id, @RequestBody SysMenu menu) {
|
||||
menu.setId(id);
|
||||
menuService.updateMenu(menu);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除菜单")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
menuService.deleteMenu(id);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.entity.SysRole;
|
||||
import com.nanxiislet.admin.service.SysRoleService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 角色管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-08
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/system/role")
|
||||
@Tag(name = "角色管理", description = "角色CRUD、权限分配等")
|
||||
public class SysRoleController {
|
||||
|
||||
@Resource
|
||||
private SysRoleService roleService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "分页查询角色")
|
||||
public R<IPage<SysRole>> page(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
return R.ok(roleService.listPage(page, pageSize, keyword));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取所有有效角色")
|
||||
public R<List<SysRole>> list() {
|
||||
return R.ok(roleService.listAll());
|
||||
}
|
||||
|
||||
@GetMapping("/list-all")
|
||||
@Operation(summary = "获取所有角色选项(简化版)")
|
||||
public R<List<java.util.Map<String, String>>> listAll() {
|
||||
List<SysRole> roles = roleService.listAll();
|
||||
List<java.util.Map<String, String>> options = roles.stream()
|
||||
.map(role -> {
|
||||
java.util.Map<String, String> map = new java.util.HashMap<>();
|
||||
map.put("code", role.getCode());
|
||||
map.put("name", role.getName());
|
||||
return map;
|
||||
})
|
||||
.toList();
|
||||
return R.ok(options);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取角色详情")
|
||||
public R<SysRole> getById(@PathVariable Long id) {
|
||||
return R.ok(roleService.getById(id));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "创建角色")
|
||||
public R<Long> create(@RequestBody SysRole role) {
|
||||
return R.ok(roleService.create(role));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新角色")
|
||||
public R<Void> update(@PathVariable Long id, @RequestBody SysRole role) {
|
||||
role.setId(id);
|
||||
roleService.updateRole(role);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除角色")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
roleService.deleteRole(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/menus")
|
||||
@Operation(summary = "获取角色的菜单ID列表")
|
||||
public R<List<Long>> getRoleMenus(@PathVariable Long id) {
|
||||
return R.ok(roleService.getRoleMenuIds(id));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/menus")
|
||||
@Operation(summary = "分配菜单权限")
|
||||
public R<Void> assignMenus(@PathVariable Long id, @RequestBody List<Long> menuIds) {
|
||||
roleService.assignMenus(id, menuIds);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 文件上传控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/upload")
|
||||
@Tag(name = "文件上传", description = "文件上传相关接口")
|
||||
public class UploadController {
|
||||
|
||||
@Value("${nanxiislet.upload.path:./uploads}")
|
||||
private String uploadPath;
|
||||
|
||||
@Value("${nanxiislet.upload.max-size:104857600}")
|
||||
private long maxSize;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "上传文件")
|
||||
public R<Map<String, String>> upload(@RequestParam("file") MultipartFile file) {
|
||||
if (file.isEmpty()) {
|
||||
throw new BusinessException("请选择文件");
|
||||
}
|
||||
|
||||
if (file.getSize() > maxSize) {
|
||||
throw new BusinessException(ResultCode.FILE_SIZE_EXCEEDED);
|
||||
}
|
||||
|
||||
try {
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = FileUtil.extName(originalFilename);
|
||||
String newFilename = IdUtil.fastSimpleUUID() + "." + extension;
|
||||
|
||||
// 按日期分目录
|
||||
String dateDir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
||||
String relativePath = dateDir + "/" + newFilename;
|
||||
String fullPath = uploadPath + "/" + relativePath;
|
||||
|
||||
File destFile = new File(fullPath);
|
||||
FileUtil.mkParentDirs(destFile);
|
||||
file.transferTo(destFile);
|
||||
|
||||
log.info("文件上传成功: {} -> {}", originalFilename, fullPath);
|
||||
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("url", "/uploads/" + relativePath);
|
||||
result.put("filename", originalFilename);
|
||||
result.put("size", String.valueOf(file.getSize()));
|
||||
|
||||
return R.ok(result);
|
||||
} catch (Exception e) {
|
||||
log.error("文件上传失败", e);
|
||||
throw new BusinessException(ResultCode.FILE_UPLOAD_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/image")
|
||||
@Operation(summary = "上传图片")
|
||||
public R<Map<String, String>> uploadImage(@RequestParam("file") MultipartFile file) {
|
||||
if (file.isEmpty()) {
|
||||
throw new BusinessException("请选择图片");
|
||||
}
|
||||
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null || !contentType.startsWith("image/")) {
|
||||
throw new BusinessException(ResultCode.FILE_TYPE_NOT_ALLOWED, "只能上传图片文件");
|
||||
}
|
||||
|
||||
return upload(file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.nanxiislet.admin.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.nanxiislet.admin.common.exception.BusinessException;
|
||||
import com.nanxiislet.admin.common.result.PageResult;
|
||||
import com.nanxiislet.admin.common.result.R;
|
||||
import com.nanxiislet.admin.common.result.ResultCode;
|
||||
import com.nanxiislet.admin.dto.system.UserQuery;
|
||||
import com.nanxiislet.admin.entity.SysUser;
|
||||
import com.nanxiislet.admin.service.UserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* 用户管理控制器
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/system/user")
|
||||
@Tag(name = "用户管理", description = "系统用户的增删改查")
|
||||
public class UserController {
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "用户列表", description = "分页查询用户列表")
|
||||
@SaCheckRole("admin")
|
||||
public R<PageResult<SysUser>> list(UserQuery query) {
|
||||
Page<SysUser> page = userService.listPage(query);
|
||||
return R.ok(PageResult.of(page.getRecords(), page.getTotal(), page.getCurrent(), page.getSize()));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "用户详情", description = "根据ID获取用户详情")
|
||||
@SaCheckRole("admin")
|
||||
public R<SysUser> getById(@PathVariable Long id) {
|
||||
SysUser user = userService.getById(id);
|
||||
if (user == null) {
|
||||
throw new BusinessException(ResultCode.USER_NOT_EXIST);
|
||||
}
|
||||
user.setPassword(null);
|
||||
return R.ok(user);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增用户", description = "创建新用户")
|
||||
@SaCheckRole("admin")
|
||||
public R<Void> create(@Valid @RequestBody SysUser user) {
|
||||
userService.createUser(user);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新用户", description = "更新用户信息")
|
||||
@SaCheckRole("admin")
|
||||
public R<Void> update(@PathVariable Long id, @Valid @RequestBody SysUser user) {
|
||||
user.setId(id);
|
||||
userService.updateUser(user);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除用户", description = "删除用户")
|
||||
@SaCheckRole("admin")
|
||||
public R<Void> delete(@PathVariable Long id) {
|
||||
userService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/reset-password")
|
||||
@Operation(summary = "重置密码", description = "管理员重置用户密码")
|
||||
@SaCheckRole("admin")
|
||||
public R<Void> resetPassword(@PathVariable Long id, @RequestBody ResetPasswordRequest request) {
|
||||
userService.resetPassword(id, request.getNewPassword());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/change-password")
|
||||
@Operation(summary = "修改密码", description = "用户修改自己的密码")
|
||||
public R<Void> changePassword(@RequestBody ChangePasswordRequest request) {
|
||||
cn.dev33.satoken.stp.StpUtil.checkLogin();
|
||||
Long userId = Long.parseLong(cn.dev33.satoken.stp.StpUtil.getLoginId().toString());
|
||||
userService.changePassword(userId, request.getOldPassword(), request.getNewPassword());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/status")
|
||||
@Operation(summary = "修改状态", description = "启用/禁用用户")
|
||||
@SaCheckRole("admin")
|
||||
public R<Void> updateStatus(@PathVariable Long id, @RequestParam Integer status) {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(id);
|
||||
user.setStatus(status);
|
||||
userService.updateById(user);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ResetPasswordRequest {
|
||||
private String newPassword;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ChangePasswordRequest {
|
||||
private String oldPassword;
|
||||
private String newPassword;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.nanxiislet.admin.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 证书申请请求DTO
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-13
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "证书申请请求")
|
||||
public class CertificateApplyRequest {
|
||||
|
||||
@NotNull(message = "服务器ID不能为空")
|
||||
@Schema(description = "服务器ID")
|
||||
private Long serverId;
|
||||
|
||||
@NotBlank(message = "主域名不能为空")
|
||||
@Schema(description = "主域名")
|
||||
private String primaryDomain;
|
||||
|
||||
@Schema(description = "其他域名(逗号分隔)")
|
||||
private String otherDomains;
|
||||
|
||||
@NotNull(message = "Acme账户ID不能为空")
|
||||
@Schema(description = "Acme账户ID")
|
||||
private Long acmeAccountId;
|
||||
|
||||
@NotNull(message = "DNS账户ID不能为空")
|
||||
@Schema(description = "DNS账户ID")
|
||||
private Long dnsAccountId;
|
||||
|
||||
@Schema(description = "密钥算法 P256/P384/RSA2048/RSA4096")
|
||||
private String keyType = "P256";
|
||||
|
||||
@Schema(description = "自动续签")
|
||||
private Boolean autoRenew = true;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.nanxiislet.admin.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 证书申请结果DTO
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-13
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "证书申请结果")
|
||||
public class CertificateApplyResult {
|
||||
|
||||
@Schema(description = "是否成功")
|
||||
private Boolean success;
|
||||
|
||||
@Schema(description = "结果消息")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "1Panel证书ID")
|
||||
private Long panelSslId;
|
||||
|
||||
@Schema(description = "本地证书记录ID")
|
||||
private Long certificateId;
|
||||
|
||||
@Schema(description = "申请步骤")
|
||||
private List<ApplyStep> steps = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "申请步骤")
|
||||
public static class ApplyStep {
|
||||
@Schema(description = "步骤名称")
|
||||
private String step;
|
||||
|
||||
@Schema(description = "状态 success/failed/skipped")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "消息")
|
||||
private String message;
|
||||
|
||||
public ApplyStep() {}
|
||||
|
||||
public ApplyStep(String step, String status, String message) {
|
||||
this.step = step;
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
public static CertificateApplyResult success(String message) {
|
||||
CertificateApplyResult result = new CertificateApplyResult();
|
||||
result.setSuccess(true);
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static CertificateApplyResult failed(String message) {
|
||||
CertificateApplyResult result = new CertificateApplyResult();
|
||||
result.setSuccess(false);
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void addSuccessStep(String step, String message) {
|
||||
this.steps.add(new ApplyStep(step, "success", message));
|
||||
}
|
||||
|
||||
public void addFailedStep(String step, String message) {
|
||||
this.steps.add(new ApplyStep(step, "failed", message));
|
||||
}
|
||||
|
||||
public void addSkippedStep(String step, String message) {
|
||||
this.steps.add(new ApplyStep(step, "skipped", message));
|
||||
}
|
||||
}
|
||||
30
src/main/java/com/nanxiislet/admin/dto/DeployRequest.java
Normal file
30
src/main/java/com/nanxiislet/admin/dto/DeployRequest.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package com.nanxiislet.admin.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 部署请求DTO
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-10
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "部署请求")
|
||||
public class DeployRequest {
|
||||
|
||||
@Schema(description = "项目ID", required = true)
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "是否启用HTTPS")
|
||||
private Boolean enableHttps = true;
|
||||
|
||||
@Schema(description = "Acme账户ID(启用HTTPS时需要)")
|
||||
private Long acmeAccountId;
|
||||
|
||||
@Schema(description = "DNS账户ID(启用HTTPS时需要)")
|
||||
private Long dnsAccountId;
|
||||
|
||||
@Schema(description = "如果网站不存在是否自动创建")
|
||||
private Boolean createIfNotExist = true;
|
||||
}
|
||||
114
src/main/java/com/nanxiislet/admin/dto/DeployResult.java
Normal file
114
src/main/java/com/nanxiislet/admin/dto/DeployResult.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package com.nanxiislet.admin.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.Builder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 部署结果DTO
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-10
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "部署结果")
|
||||
public class DeployResult {
|
||||
|
||||
@Schema(description = "是否成功")
|
||||
private Boolean success;
|
||||
|
||||
@Schema(description = "结果消息")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "1Panel网站ID")
|
||||
private Long websiteId;
|
||||
|
||||
@Schema(description = "1Panel证书ID")
|
||||
private Long sslCertificateId;
|
||||
|
||||
@Schema(description = "部署步骤详情")
|
||||
@Builder.Default
|
||||
private List<DeployStep> steps = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 部署步骤
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class DeployStep {
|
||||
|
||||
@Schema(description = "步骤名称")
|
||||
private String step;
|
||||
|
||||
@Schema(description = "步骤状态 success/failed/skipped")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "步骤消息")
|
||||
private String message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加成功步骤
|
||||
*/
|
||||
public void addSuccessStep(String stepName, String message) {
|
||||
this.steps.add(DeployStep.builder()
|
||||
.step(stepName)
|
||||
.status("success")
|
||||
.message(message)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加失败步骤
|
||||
*/
|
||||
public void addFailedStep(String stepName, String message) {
|
||||
this.steps.add(DeployStep.builder()
|
||||
.step(stepName)
|
||||
.status("failed")
|
||||
.message(message)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加跳过步骤
|
||||
*/
|
||||
public void addSkippedStep(String stepName, String message) {
|
||||
this.steps.add(DeployStep.builder()
|
||||
.step(stepName)
|
||||
.status("skipped")
|
||||
.message(message)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建成功结果
|
||||
*/
|
||||
public static DeployResult success(String message) {
|
||||
return DeployResult.builder()
|
||||
.success(true)
|
||||
.message(message)
|
||||
.steps(new ArrayList<>())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建失败结果
|
||||
*/
|
||||
public static DeployResult failed(String message) {
|
||||
return DeployResult.builder()
|
||||
.success(false)
|
||||
.message(message)
|
||||
.steps(new ArrayList<>())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.nanxiislet.admin.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 域名部署请求DTO
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-13
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "域名部署请求")
|
||||
public class DomainDeployRequest {
|
||||
|
||||
@NotNull(message = "域名ID不能为空")
|
||||
@Schema(description = "域名ID")
|
||||
private Long domainId;
|
||||
|
||||
@Schema(description = "是否启用HTTPS")
|
||||
private Boolean enableHttps;
|
||||
|
||||
@Schema(description = "Acme账户ID(申请SSL证书时需要)")
|
||||
private Long acmeAccountId;
|
||||
|
||||
@Schema(description = "DNS账户ID(申请SSL证书时需要)")
|
||||
private Long dnsAccountId;
|
||||
|
||||
@Schema(description = "如果网站不存在是否自动创建")
|
||||
private Boolean createIfNotExist = true;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.nanxiislet.admin.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 域名部署结果DTO
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-13
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "域名部署结果")
|
||||
public class DomainDeployResult {
|
||||
|
||||
@Schema(description = "是否成功")
|
||||
private Boolean success;
|
||||
|
||||
@Schema(description = "结果消息")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "1Panel网站ID")
|
||||
private Long websiteId;
|
||||
|
||||
@Schema(description = "1Panel证书ID")
|
||||
private Long sslCertificateId;
|
||||
|
||||
@Schema(description = "部署步骤列表")
|
||||
private List<DeployStep> steps = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "部署步骤")
|
||||
public static class DeployStep {
|
||||
@Schema(description = "步骤名称")
|
||||
private String step;
|
||||
|
||||
@Schema(description = "状态 success/failed/skipped")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "消息")
|
||||
private String message;
|
||||
|
||||
public DeployStep() {}
|
||||
|
||||
public DeployStep(String step, String status, String message) {
|
||||
this.step = step;
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
public static DomainDeployResult success(String message) {
|
||||
DomainDeployResult result = new DomainDeployResult();
|
||||
result.setSuccess(true);
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static DomainDeployResult failed(String message) {
|
||||
DomainDeployResult result = new DomainDeployResult();
|
||||
result.setSuccess(false);
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void addSuccessStep(String step, String message) {
|
||||
this.steps.add(new DeployStep(step, "success", message));
|
||||
}
|
||||
|
||||
public void addFailedStep(String step, String message) {
|
||||
this.steps.add(new DeployStep(step, "failed", message));
|
||||
}
|
||||
|
||||
public void addSkippedStep(String step, String message) {
|
||||
this.steps.add(new DeployStep(step, "skipped", message));
|
||||
}
|
||||
}
|
||||
26
src/main/java/com/nanxiislet/admin/dto/DomainQueryDTO.java
Normal file
26
src/main/java/com/nanxiislet/admin/dto/DomainQueryDTO.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.nanxiislet.admin.dto;
|
||||
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 域名查询参数
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-13
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DomainQueryDTO extends BasePageQuery {
|
||||
|
||||
@Schema(description = "服务器ID")
|
||||
private Long serverId;
|
||||
|
||||
@Schema(description = "域名状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "SSL状态")
|
||||
private String sslStatus;
|
||||
}
|
||||
30
src/main/java/com/nanxiislet/admin/dto/DomainStatsDTO.java
Normal file
30
src/main/java/com/nanxiislet/admin/dto/DomainStatsDTO.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package com.nanxiislet.admin.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 域名统计DTO
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-13
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "域名统计信息")
|
||||
public class DomainStatsDTO {
|
||||
|
||||
@Schema(description = "总计")
|
||||
private Long total;
|
||||
|
||||
@Schema(description = "正常数量")
|
||||
private Long active;
|
||||
|
||||
@Schema(description = "待配置数量")
|
||||
private Long pending;
|
||||
|
||||
@Schema(description = "SSL即将过期数量")
|
||||
private Long sslExpiring;
|
||||
|
||||
@Schema(description = "已部署数量")
|
||||
private Long deployed;
|
||||
}
|
||||
130
src/main/java/com/nanxiislet/admin/dto/ServerInfoDto.java
Normal file
130
src/main/java/com/nanxiislet/admin/dto/ServerInfoDto.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package com.nanxiislet.admin.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.Builder;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 服务器信息DTO(包含实时资源使用情况)
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-10
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "服务器信息DTO")
|
||||
public class ServerInfoDto {
|
||||
|
||||
@Schema(description = "服务器ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "服务器名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "公网IP")
|
||||
private String ip;
|
||||
|
||||
@Schema(description = "内网IP")
|
||||
private String internalIp;
|
||||
|
||||
@Schema(description = "SSH端口")
|
||||
private Integer port;
|
||||
|
||||
@Schema(description = "服务器类型 physical/virtual/cloud")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "状态 online/offline/warning/maintenance")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "操作系统")
|
||||
private String os;
|
||||
|
||||
@Schema(description = "标签")
|
||||
private List<String> tags;
|
||||
|
||||
|
||||
|
||||
@Schema(description = "1Panel面板地址")
|
||||
private String panelUrl;
|
||||
|
||||
@Schema(description = "1Panel面板端口")
|
||||
private Integer panelPort;
|
||||
|
||||
@Schema(description = "描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
// ==================== 资源使用情况(从1Panel获取) ====================
|
||||
|
||||
@Schema(description = "CPU信息")
|
||||
private CpuInfo cpu;
|
||||
|
||||
@Schema(description = "内存信息")
|
||||
private MemoryInfo memory;
|
||||
|
||||
@Schema(description = "磁盘信息")
|
||||
private DiskInfo disk;
|
||||
|
||||
/**
|
||||
* CPU信息
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class CpuInfo {
|
||||
@Schema(description = "CPU核心数")
|
||||
private Integer cores;
|
||||
|
||||
@Schema(description = "CPU使用率(%)")
|
||||
private Double usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内存信息
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class MemoryInfo {
|
||||
@Schema(description = "总内存(GB)")
|
||||
private Double total;
|
||||
|
||||
@Schema(description = "已用内存(GB)")
|
||||
private Double used;
|
||||
|
||||
@Schema(description = "内存使用率(%)")
|
||||
private Double usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 磁盘信息
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class DiskInfo {
|
||||
@Schema(description = "总磁盘(GB)")
|
||||
private Double total;
|
||||
|
||||
@Schema(description = "已用磁盘(GB)")
|
||||
private Double used;
|
||||
|
||||
@Schema(description = "磁盘使用率(%)")
|
||||
private Double usage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.nanxiislet.admin.dto.approval;
|
||||
|
||||
import com.nanxiislet.admin.entity.SysApprovalInstance;
|
||||
import com.nanxiislet.admin.entity.SysApprovalRecord;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审批实例VO(包含审批记录)
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ApprovalInstanceVO extends SysApprovalInstance {
|
||||
|
||||
/**
|
||||
* 审批记录列表
|
||||
*/
|
||||
private List<SysApprovalRecord> records;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.nanxiislet.admin.dto.approval;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 审批统计数据
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Data
|
||||
public class ApprovalStats {
|
||||
|
||||
/**
|
||||
* 模板总数
|
||||
*/
|
||||
private Long totalTemplates;
|
||||
|
||||
/**
|
||||
* 已启用模板数
|
||||
*/
|
||||
private Long enabledTemplates;
|
||||
|
||||
/**
|
||||
* 实例总数
|
||||
*/
|
||||
private Long totalInstances;
|
||||
|
||||
/**
|
||||
* 待处理实例数
|
||||
*/
|
||||
private Long pendingInstances;
|
||||
|
||||
/**
|
||||
* 审批中实例数
|
||||
*/
|
||||
private Long inProgressInstances;
|
||||
|
||||
/**
|
||||
* 已通过实例数
|
||||
*/
|
||||
private Long approvedInstances;
|
||||
|
||||
/**
|
||||
* 已拒绝实例数
|
||||
*/
|
||||
private Long rejectedInstances;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.nanxiislet.admin.dto.approval;
|
||||
|
||||
import com.nanxiislet.admin.entity.SysApprovalNode;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 创建/更新审批模板请求
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Data
|
||||
public class ApprovalTemplateRequest {
|
||||
|
||||
/**
|
||||
* 模板ID(更新时需要)
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 模板名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 适用场景
|
||||
*/
|
||||
private String scenario;
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* 审批节点列表
|
||||
*/
|
||||
private List<SysApprovalNode> nodes;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.nanxiislet.admin.dto.approval;
|
||||
|
||||
import com.nanxiislet.admin.entity.SysApprovalNode;
|
||||
import com.nanxiislet.admin.entity.SysApprovalTemplate;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审批模板VO(包含节点信息)
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ApprovalTemplateVO extends SysApprovalTemplate {
|
||||
|
||||
/**
|
||||
* 审批节点列表
|
||||
*/
|
||||
private List<SysApprovalNode> nodes;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.nanxiislet.admin.dto.auth;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 验证码响应DTO
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "验证码响应")
|
||||
public class CaptchaResponse {
|
||||
|
||||
@Schema(description = "验证码Key")
|
||||
private String captchaKey;
|
||||
|
||||
@Schema(description = "验证码图片(Base64)")
|
||||
private String captchaImage;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.nanxiislet.admin.dto.auth;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 登录请求DTO
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "登录请求")
|
||||
public class LoginRequest {
|
||||
|
||||
@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "验证码", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "验证码不能为空")
|
||||
private String captcha;
|
||||
|
||||
@Schema(description = "验证码Key", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "验证码Key不能为空")
|
||||
private String captchaKey;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.nanxiislet.admin.dto.auth;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 登录响应DTO
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "登录响应")
|
||||
public class LoginResponse {
|
||||
|
||||
@Schema(description = "访问令牌")
|
||||
private String token;
|
||||
|
||||
@Schema(description = "刷新令牌")
|
||||
private String refreshToken;
|
||||
|
||||
@Schema(description = "过期时间(秒)")
|
||||
private long expires;
|
||||
|
||||
@Schema(description = "用户信息")
|
||||
private UserInfoVO userInfo;
|
||||
}
|
||||
56
src/main/java/com/nanxiislet/admin/dto/auth/UserInfoVO.java
Normal file
56
src/main/java/com/nanxiislet/admin/dto/auth/UserInfoVO.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package com.nanxiislet.admin.dto.auth;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户信息VO
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "用户信息")
|
||||
public class UserInfoVO {
|
||||
|
||||
@Schema(description = "用户ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "昵称")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "角色")
|
||||
private String role;
|
||||
|
||||
@Schema(description = "权限列表")
|
||||
private List<String> permissions;
|
||||
|
||||
@Schema(description = "菜单列表")
|
||||
private List<com.nanxiislet.admin.entity.SysMenu> menus;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private String createTime;
|
||||
|
||||
@Schema(description = "最后登录时间")
|
||||
private String lastLoginTime;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.nanxiislet.admin.dto.query;
|
||||
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 支出查询条件
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "支出查询条件")
|
||||
public class ExpenseQuery extends BasePageQuery {
|
||||
|
||||
@Schema(description = "支出类型")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "项目ID")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "部门ID")
|
||||
private Long departmentId;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "开始日期")
|
||||
private String startDate;
|
||||
|
||||
@Schema(description = "结束日期")
|
||||
private String endDate;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.nanxiislet.admin.dto.query;
|
||||
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 收入查询条件
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "收入查询条件")
|
||||
public class IncomeQuery extends BasePageQuery {
|
||||
|
||||
@Schema(description = "收入类型")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "客户ID")
|
||||
private Long customerId;
|
||||
|
||||
@Schema(description = "项目ID")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "开始日期")
|
||||
private String startDate;
|
||||
|
||||
@Schema(description = "结束日期")
|
||||
private String endDate;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.nanxiislet.admin.dto.query;
|
||||
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 项目查询条件
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "项目查询条件")
|
||||
public class ProjectQuery extends BasePageQuery {
|
||||
|
||||
@Schema(description = "项目类型")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "服务器ID")
|
||||
private Long serverId;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
}
|
||||
26
src/main/java/com/nanxiislet/admin/dto/system/UserQuery.java
Normal file
26
src/main/java/com/nanxiislet/admin/dto/system/UserQuery.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.nanxiislet.admin.dto.system;
|
||||
|
||||
import com.nanxiislet.admin.common.base.BasePageQuery;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 用户查询参数
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class UserQuery extends BasePageQuery {
|
||||
|
||||
@Schema(description = "角色编码")
|
||||
private String role;
|
||||
|
||||
@Schema(description = "部门ID")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "状态 0-禁用 1-正常")
|
||||
private Integer status;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 财务账户实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("finance_account")
|
||||
@Schema(description = "财务账户")
|
||||
public class FinanceAccount extends BaseEntity {
|
||||
|
||||
@Schema(description = "账户名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "账户类型 corporate-对公账户 merchant-商户号")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "开户银行")
|
||||
private String bankName;
|
||||
|
||||
@Schema(description = "开户支行")
|
||||
private String bankBranch;
|
||||
|
||||
@Schema(description = "银行账号")
|
||||
private String accountNo;
|
||||
|
||||
@Schema(description = "商户ID")
|
||||
private String merchantId;
|
||||
|
||||
@Schema(description = "商户平台 wechat/alipay/unionpay/other")
|
||||
private String merchantPlatform;
|
||||
|
||||
@Schema(description = "AppID")
|
||||
private String appId;
|
||||
|
||||
@Schema(description = "当前余额")
|
||||
private BigDecimal balance;
|
||||
|
||||
@Schema(description = "状态 active-正常 inactive-禁用")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "是否默认账户")
|
||||
private Boolean isDefault;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
}
|
||||
88
src/main/java/com/nanxiislet/admin/entity/FinanceBudget.java
Normal file
88
src/main/java/com/nanxiislet/admin/entity/FinanceBudget.java
Normal file
@@ -0,0 +1,88 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 预算记录实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName(value = "finance_budget", autoResultMap = true)
|
||||
@Schema(description = "预算记录")
|
||||
public class FinanceBudget extends BaseEntity {
|
||||
|
||||
@Schema(description = "预算名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "预算周期 monthly/quarterly/yearly")
|
||||
private String period;
|
||||
|
||||
@Schema(description = "年份")
|
||||
private Integer year;
|
||||
|
||||
@Schema(description = "季度 1-4")
|
||||
private Integer quarter;
|
||||
|
||||
@Schema(description = "月份 1-12")
|
||||
private Integer month;
|
||||
|
||||
@Schema(description = "部门ID")
|
||||
private Long departmentId;
|
||||
|
||||
@Schema(description = "部门名称")
|
||||
private String departmentName;
|
||||
|
||||
@Schema(description = "项目ID")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "预算明细(JSON格式)")
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<BudgetItem> items;
|
||||
|
||||
@Schema(description = "总预算")
|
||||
private BigDecimal totalBudget;
|
||||
|
||||
@Schema(description = "已使用金额")
|
||||
private BigDecimal usedAmount;
|
||||
|
||||
@Schema(description = "剩余金额")
|
||||
private BigDecimal remainingAmount;
|
||||
|
||||
@Schema(description = "使用率 0-100")
|
||||
private BigDecimal usageRate;
|
||||
|
||||
@Schema(description = "状态 draft/active/completed/cancelled")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建人名称")
|
||||
private String createdByName;
|
||||
|
||||
/**
|
||||
* 预算明细项
|
||||
*/
|
||||
@Data
|
||||
public static class BudgetItem {
|
||||
private String expenseType;
|
||||
private BigDecimal budgetAmount;
|
||||
private BigDecimal usedAmount;
|
||||
private BigDecimal remainingAmount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 支出记录实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("finance_expense")
|
||||
@Schema(description = "支出记录")
|
||||
public class FinanceExpense extends BaseEntity {
|
||||
|
||||
@Schema(description = "支出编号")
|
||||
private String expenseNo;
|
||||
|
||||
@Schema(description = "支出类型 salary/office/rent/travel/marketing/equipment/service/tax/social_insurance/other")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "支出名称")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "收款方名称")
|
||||
private String payeeName;
|
||||
|
||||
@Schema(description = "收款方账号")
|
||||
private String payeeAccount;
|
||||
|
||||
@Schema(description = "收款方开户行")
|
||||
private String payeeBankName;
|
||||
|
||||
@Schema(description = "金额")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(description = "关联项目ID")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "关联项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "部门ID")
|
||||
private Long departmentId;
|
||||
|
||||
@Schema(description = "部门名称")
|
||||
private String departmentName;
|
||||
|
||||
@Schema(description = "付款账户ID")
|
||||
private Long accountId;
|
||||
|
||||
@Schema(description = "付款账户名称")
|
||||
private String accountName;
|
||||
|
||||
@Schema(description = "状态 draft/pending/approved/paid/rejected")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "审批流程ID")
|
||||
private Long approvalId;
|
||||
|
||||
@Schema(description = "审批状态")
|
||||
private String approvalStatus;
|
||||
|
||||
@Schema(description = "附件路径(JSON格式)")
|
||||
private String attachments;
|
||||
|
||||
@Schema(description = "付款日期")
|
||||
private LocalDate paymentDate;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建人名称")
|
||||
private String createdByName;
|
||||
}
|
||||
89
src/main/java/com/nanxiislet/admin/entity/FinanceIncome.java
Normal file
89
src/main/java/com/nanxiislet/admin/entity/FinanceIncome.java
Normal file
@@ -0,0 +1,89 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 收入记录实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("finance_income")
|
||||
@Schema(description = "收入记录")
|
||||
public class FinanceIncome extends BaseEntity {
|
||||
|
||||
@Schema(description = "收入编号")
|
||||
private String incomeNo;
|
||||
|
||||
@Schema(description = "收入类型 project/service_fee/consulting/commission/other")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "收入名称")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "客户ID")
|
||||
private Long customerId;
|
||||
|
||||
@Schema(description = "客户名称")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "客户联系方式")
|
||||
private String customerContact;
|
||||
|
||||
@Schema(description = "关联项目ID")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "关联项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "合同编号")
|
||||
private String contractNo;
|
||||
|
||||
@Schema(description = "总金额")
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
@Schema(description = "已收金额")
|
||||
private BigDecimal receivedAmount;
|
||||
|
||||
@Schema(description = "待收金额")
|
||||
private BigDecimal pendingAmount;
|
||||
|
||||
@Schema(description = "收款账户ID")
|
||||
private Long accountId;
|
||||
|
||||
@Schema(description = "收款账户名称")
|
||||
private String accountName;
|
||||
|
||||
@Schema(description = "状态 pending/partial/received/overdue")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "预计收款日期")
|
||||
private LocalDate expectedDate;
|
||||
|
||||
@Schema(description = "实际收款日期")
|
||||
private LocalDate actualDate;
|
||||
|
||||
@Schema(description = "是否需要发票")
|
||||
private Boolean invoiceRequired;
|
||||
|
||||
@Schema(description = "发票是否已开")
|
||||
private Boolean invoiceIssued;
|
||||
|
||||
@Schema(description = "发票号")
|
||||
private String invoiceNo;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建人名称")
|
||||
private String createdByName;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 发票记录实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("finance_invoice")
|
||||
@Schema(description = "发票记录")
|
||||
public class FinanceInvoice extends BaseEntity {
|
||||
|
||||
@Schema(description = "关联结算单ID")
|
||||
private Long settlementId;
|
||||
|
||||
@Schema(description = "发票类型 vat_special/vat_normal/personal")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "发票抬头")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "税号")
|
||||
private String taxCode;
|
||||
|
||||
@Schema(description = "金额")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(description = "电子发票文件URL")
|
||||
private String fileUrl;
|
||||
|
||||
@Schema(description = "状态 pending/issued/rejected")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "提交时间")
|
||||
private LocalDate submitTime;
|
||||
|
||||
@Schema(description = "开票时间")
|
||||
private LocalDate issueTime;
|
||||
|
||||
@Schema(description = "驳回原因")
|
||||
private String rejectReason;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 报销记录实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName(value = "finance_reimbursement", autoResultMap = true)
|
||||
@Schema(description = "报销记录")
|
||||
public class FinanceReimbursement extends BaseEntity {
|
||||
|
||||
@Schema(description = "报销单号")
|
||||
private String reimbursementNo;
|
||||
|
||||
@Schema(description = "报销类型 travel/meal/transport/communication/office/other")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "报销标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "申请人ID")
|
||||
private Long applicantId;
|
||||
|
||||
@Schema(description = "申请人名称")
|
||||
private String applicantName;
|
||||
|
||||
@Schema(description = "部门ID")
|
||||
private Long departmentId;
|
||||
|
||||
@Schema(description = "部门名称")
|
||||
private String departmentName;
|
||||
|
||||
@Schema(description = "总金额")
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
@Schema(description = "报销明细(JSON格式)")
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<ReimbursementItem> items;
|
||||
|
||||
@Schema(description = "状态 draft/pending/approved/paid/rejected")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "审批流程ID")
|
||||
private Long approvalId;
|
||||
|
||||
@Schema(description = "审批状态")
|
||||
private String approvalStatus;
|
||||
|
||||
@Schema(description = "当前审批人")
|
||||
private String currentApprover;
|
||||
|
||||
@Schema(description = "收款人银行户名")
|
||||
private String bankAccountName;
|
||||
|
||||
@Schema(description = "收款人银行账号")
|
||||
private String bankAccountNo;
|
||||
|
||||
@Schema(description = "收款人开户行")
|
||||
private String bankName;
|
||||
|
||||
@Schema(description = "付款账户ID")
|
||||
private Long paymentAccountId;
|
||||
|
||||
@Schema(description = "付款日期")
|
||||
private LocalDate paymentDate;
|
||||
|
||||
@Schema(description = "付款备注")
|
||||
private String paymentRemark;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 报销明细项
|
||||
*/
|
||||
@Data
|
||||
public static class ReimbursementItem {
|
||||
private Long id;
|
||||
private String type;
|
||||
private String description;
|
||||
private BigDecimal amount;
|
||||
private LocalDate occurDate;
|
||||
private List<String> attachments;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 结算记录实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("finance_settlement")
|
||||
@Schema(description = "结算记录")
|
||||
public class FinanceSettlement extends BaseEntity {
|
||||
|
||||
@Schema(description = "项目ID")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "人才ID")
|
||||
private Long talentId;
|
||||
|
||||
@Schema(description = "人才名称")
|
||||
private String talentName;
|
||||
|
||||
@Schema(description = "结算周期/月份")
|
||||
private String period;
|
||||
|
||||
@Schema(description = "项目总额")
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
@Schema(description = "平台服务费")
|
||||
private BigDecimal platformFee;
|
||||
|
||||
@Schema(description = "应纳税所得额")
|
||||
private BigDecimal taxableAmount;
|
||||
|
||||
@Schema(description = "税率")
|
||||
private BigDecimal taxRate;
|
||||
|
||||
@Schema(description = "扣税金额")
|
||||
private BigDecimal taxAmount;
|
||||
|
||||
@Schema(description = "实发金额")
|
||||
private BigDecimal actualAmount;
|
||||
|
||||
@Schema(description = "状态 pending/paying/completed/rejected")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "发票状态 none/pending/received")
|
||||
private String invoiceStatus;
|
||||
|
||||
@Schema(description = "银行户名")
|
||||
private String bankAccountName;
|
||||
|
||||
@Schema(description = "银行账号")
|
||||
private String bankAccountNo;
|
||||
|
||||
@Schema(description = "开户银行")
|
||||
private String bankName;
|
||||
|
||||
@Schema(description = "审核时间")
|
||||
private LocalDate auditTime;
|
||||
|
||||
@Schema(description = "打款时间")
|
||||
private LocalDate paymentTime;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 证书实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-13
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("platform_certificate")
|
||||
@Schema(description = "证书信息")
|
||||
public class PlatformCertificate extends BaseEntity {
|
||||
|
||||
@Schema(description = "关联服务器ID")
|
||||
private Long serverId;
|
||||
|
||||
@Schema(description = "服务器名称")
|
||||
private String serverName;
|
||||
|
||||
@Schema(description = "1Panel证书ID")
|
||||
private Long panelSslId;
|
||||
|
||||
@Schema(description = "主域名")
|
||||
private String primaryDomain;
|
||||
|
||||
@Schema(description = "其他域名(逗号分隔)")
|
||||
private String otherDomains;
|
||||
|
||||
@Schema(description = "证书主体名称")
|
||||
private String cn;
|
||||
|
||||
@Schema(description = "颁发组织")
|
||||
private String organization;
|
||||
|
||||
@Schema(description = "验证方式 dnsAccount/httpManual")
|
||||
private String provider;
|
||||
|
||||
@Schema(description = "Acme账户ID")
|
||||
private Long acmeAccountId;
|
||||
|
||||
@Schema(description = "Acme账户邮箱")
|
||||
private String acmeAccountEmail;
|
||||
|
||||
@Schema(description = "DNS账户ID")
|
||||
private Long dnsAccountId;
|
||||
|
||||
@Schema(description = "DNS账户名称")
|
||||
private String dnsAccountName;
|
||||
|
||||
@Schema(description = "DNS账户类型")
|
||||
private String dnsAccountType;
|
||||
|
||||
@Schema(description = "密钥算法 P256/P384/RSA2048/RSA4096")
|
||||
private String keyType;
|
||||
|
||||
@Schema(description = "状态 pending/valid/expired/error")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "自动续签")
|
||||
private Boolean autoRenew;
|
||||
|
||||
@Schema(description = "生效时间")
|
||||
private LocalDate startDate;
|
||||
|
||||
@Schema(description = "过期时间")
|
||||
private LocalDate expireDate;
|
||||
|
||||
@Schema(description = "证书内容")
|
||||
private String certContent;
|
||||
|
||||
@Schema(description = "私钥内容")
|
||||
private String keyContent;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "最后同步时间")
|
||||
private LocalDateTime lastSyncTime;
|
||||
}
|
||||
115
src/main/java/com/nanxiislet/admin/entity/PlatformDomain.java
Normal file
115
src/main/java/com/nanxiislet/admin/entity/PlatformDomain.java
Normal file
@@ -0,0 +1,115 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 域名实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("platform_domain")
|
||||
@Schema(description = "域名信息")
|
||||
public class PlatformDomain extends BaseEntity {
|
||||
|
||||
@Schema(description = "域名")
|
||||
private String domain;
|
||||
|
||||
@Schema(description = "关联项目ID")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "关联项目名称")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "关联服务器ID")
|
||||
private Long serverId;
|
||||
|
||||
@Schema(description = "关联服务器名称")
|
||||
private String serverName;
|
||||
|
||||
@Schema(description = "服务器IP")
|
||||
private String serverIp;
|
||||
|
||||
@Schema(description = "状态 active/pending/expired/error")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "DNS状态 resolved/unresolved/checking")
|
||||
private String dnsStatus;
|
||||
|
||||
@Schema(description = "DNS记录(JSON格式)")
|
||||
private String dnsRecords;
|
||||
|
||||
@Schema(description = "SSL状态 valid/expiring/expired/none")
|
||||
private String sslStatus;
|
||||
|
||||
@Schema(description = "SSL过期时间")
|
||||
private LocalDate sslExpireDate;
|
||||
|
||||
@Schema(description = "证书ID")
|
||||
private String certificateId;
|
||||
|
||||
@Schema(description = "证书名称")
|
||||
private String certificateName;
|
||||
|
||||
@Schema(description = "Nginx配置路径")
|
||||
private String nginxConfigPath;
|
||||
|
||||
@Schema(description = "代理地址")
|
||||
private String proxyPass;
|
||||
|
||||
@Schema(description = "端口")
|
||||
private Integer port;
|
||||
|
||||
@Schema(description = "是否启用HTTPS")
|
||||
private Boolean enableHttps;
|
||||
|
||||
@Schema(description = "是否强制HTTPS")
|
||||
private Boolean forceHttps;
|
||||
|
||||
@Schema(description = "描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "1Panel网站ID")
|
||||
private Long panelWebsiteId;
|
||||
|
||||
@Schema(description = "1Panel证书ID")
|
||||
private Long panelSslId;
|
||||
|
||||
@Schema(description = "网站目录路径")
|
||||
private String sitePath;
|
||||
|
||||
@Schema(description = "网站别名")
|
||||
private String alias;
|
||||
|
||||
@Schema(description = "部署状态 not_deployed/deploying/deployed/failed")
|
||||
private String deployStatus;
|
||||
|
||||
@Schema(description = "最后部署时间")
|
||||
private java.time.LocalDateTime lastDeployTime;
|
||||
|
||||
@Schema(description = "最后部署消息")
|
||||
private String lastDeployMessage;
|
||||
|
||||
@Schema(description = "关联运行环境ID")
|
||||
private Long runtimeId;
|
||||
|
||||
@Schema(description = "运行环境所属服务器ID")
|
||||
private Long runtimeServerId;
|
||||
|
||||
@Schema(description = "运行环境名称")
|
||||
private String runtimeName;
|
||||
|
||||
@Schema(description = "运行环境类型 java/node")
|
||||
private String runtimeType;
|
||||
|
||||
@Schema(description = "运行环境部署状态 not_deployed/deploying/deployed/failed")
|
||||
private String runtimeDeployStatus;
|
||||
}
|
||||
107
src/main/java/com/nanxiislet/admin/entity/PlatformProject.java
Normal file
107
src/main/java/com/nanxiislet/admin/entity/PlatformProject.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 项目实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("platform_project")
|
||||
@Schema(description = "项目信息")
|
||||
public class PlatformProject extends BaseEntity {
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "项目编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "项目简称")
|
||||
private String shortName;
|
||||
|
||||
@Schema(description = "项目类型")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "项目分组 default/business/internal/test")
|
||||
private String projectGroup;
|
||||
|
||||
@Schema(description = "Logo文字")
|
||||
private String logo;
|
||||
|
||||
@Schema(description = "主题色")
|
||||
private String color;
|
||||
|
||||
@Schema(description = "绑定域名")
|
||||
private String domain;
|
||||
|
||||
@Schema(description = "访问地址")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "关联域名ID")
|
||||
private Long domainId;
|
||||
|
||||
@Schema(description = "关联服务器ID")
|
||||
private Long serverId;
|
||||
|
||||
@Schema(description = "服务器名称")
|
||||
private String serverName;
|
||||
|
||||
@Schema(description = "部署路径")
|
||||
private String deployPath;
|
||||
|
||||
@Schema(description = "当前版本")
|
||||
private String version;
|
||||
|
||||
@Schema(description = "状态 active/inactive/deploying")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "图标")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "是否启用HTTPS")
|
||||
private Boolean enableHttps;
|
||||
|
||||
@Schema(description = "1Panel网站ID")
|
||||
private Long panelWebsiteId;
|
||||
|
||||
@Schema(description = "1Panel证书ID")
|
||||
private Long panelSslId;
|
||||
|
||||
@Schema(description = "最后部署时间")
|
||||
private LocalDateTime lastDeployTime;
|
||||
|
||||
@Schema(description = "最后部署状态 success/failed/deploying")
|
||||
private String lastDeployStatus;
|
||||
|
||||
@Schema(description = "最后部署消息")
|
||||
private String lastDeployMessage;
|
||||
|
||||
@Schema(description = "系统类型 admin/portal(字典项system)")
|
||||
private String systemType;
|
||||
|
||||
@Schema(description = "是否集成到框架(仅管理端有效,字典项integration)")
|
||||
private Boolean integrateToFramework;
|
||||
|
||||
@Schema(description = "菜单数量")
|
||||
@com.baomidou.mybatisplus.annotation.TableField(exist = false)
|
||||
private Long menuCount;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 服务器实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("platform_server")
|
||||
@Schema(description = "服务器信息")
|
||||
public class PlatformServer extends BaseEntity {
|
||||
|
||||
@Schema(description = "服务器名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "公网IP")
|
||||
private String ip;
|
||||
|
||||
@Schema(description = "内网IP")
|
||||
private String internalIp;
|
||||
|
||||
@Schema(description = "SSH端口")
|
||||
private Integer port;
|
||||
|
||||
@Schema(description = "服务器类型 physical/virtual/cloud")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "状态 online/offline/warning/maintenance")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "操作系统")
|
||||
private String os;
|
||||
|
||||
@Schema(description = "CPU核心数")
|
||||
private Integer cpuCores;
|
||||
|
||||
@Schema(description = "内存大小(GB)")
|
||||
private Integer memoryTotal;
|
||||
|
||||
@Schema(description = "磁盘大小(GB)")
|
||||
private Integer diskTotal;
|
||||
|
||||
@Schema(description = "标签(JSON格式)")
|
||||
private String tags;
|
||||
|
||||
|
||||
|
||||
@Schema(description = "1Panel面板地址")
|
||||
private String panelUrl;
|
||||
|
||||
@Schema(description = "1Panel面板端口")
|
||||
private Integer panelPort;
|
||||
|
||||
@Schema(description = "1Panel API密钥")
|
||||
private String panelApiKey;
|
||||
|
||||
@Schema(description = "描述")
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 审批实例实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_approval_instance")
|
||||
public class SysApprovalInstance {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 模板ID
|
||||
*/
|
||||
private Long templateId;
|
||||
|
||||
/**
|
||||
* 模板名称
|
||||
*/
|
||||
private String templateName;
|
||||
|
||||
/**
|
||||
* 适用场景
|
||||
*/
|
||||
private String scenario;
|
||||
|
||||
/**
|
||||
* 业务类型
|
||||
*/
|
||||
private String businessType;
|
||||
|
||||
/**
|
||||
* 业务ID
|
||||
*/
|
||||
private Long businessId;
|
||||
|
||||
/**
|
||||
* 业务标题
|
||||
*/
|
||||
private String businessTitle;
|
||||
|
||||
/**
|
||||
* 发起人ID
|
||||
*/
|
||||
private Long initiatorId;
|
||||
|
||||
/**
|
||||
* 发起人名称
|
||||
*/
|
||||
private String initiatorName;
|
||||
|
||||
/**
|
||||
* 发起人头像
|
||||
*/
|
||||
private String initiatorAvatar;
|
||||
|
||||
/**
|
||||
* 状态: pending-待提交 in_progress-审批中 approved-已通过 rejected-已拒绝 withdrawn-已撤回 cancelled-已取消
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 当前节点ID
|
||||
*/
|
||||
private Long currentNodeId;
|
||||
|
||||
/**
|
||||
* 当前节点名称
|
||||
*/
|
||||
private String currentNodeName;
|
||||
|
||||
/**
|
||||
* 提交时间
|
||||
*/
|
||||
private LocalDateTime submittedAt;
|
||||
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
private LocalDateTime completedAt;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审批流程节点实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Data
|
||||
@TableName(value = "sys_approval_node", autoResultMap = true)
|
||||
public class SysApprovalNode {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 模板ID
|
||||
*/
|
||||
private Long templateId;
|
||||
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 审批人类型: specified-指定人员 role-按角色 superior-上级领导 self_select-发起人自选
|
||||
*/
|
||||
private String approverType;
|
||||
|
||||
/**
|
||||
* 审批人ID列表
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<Long> approverIds;
|
||||
|
||||
/**
|
||||
* 审批角色编码
|
||||
*/
|
||||
private String approverRole;
|
||||
|
||||
/**
|
||||
* 部门ID(按部门时使用)
|
||||
*/
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
* 审批方式: and-会签 or-或签
|
||||
*/
|
||||
private String approvalMode;
|
||||
|
||||
/**
|
||||
* 超时时间(小时)
|
||||
*/
|
||||
private Integer timeoutHours;
|
||||
|
||||
/**
|
||||
* 超时操作: skip-跳过 reject-驳回
|
||||
*/
|
||||
private String timeoutAction;
|
||||
|
||||
/**
|
||||
* 节点顺序
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 审批记录实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_approval_record")
|
||||
public class SysApprovalRecord {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 实例ID
|
||||
*/
|
||||
private Long instanceId;
|
||||
|
||||
/**
|
||||
* 节点ID
|
||||
*/
|
||||
private Long nodeId;
|
||||
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private String nodeName;
|
||||
|
||||
/**
|
||||
* 审批人ID
|
||||
*/
|
||||
private Long approverId;
|
||||
|
||||
/**
|
||||
* 审批人名称
|
||||
*/
|
||||
private String approverName;
|
||||
|
||||
/**
|
||||
* 审批人头像
|
||||
*/
|
||||
private String approverAvatar;
|
||||
|
||||
/**
|
||||
* 操作: approve-通过 reject-驳回 transfer-转交 return-退回
|
||||
*/
|
||||
private String action;
|
||||
|
||||
/**
|
||||
* 审批意见
|
||||
*/
|
||||
private String comment;
|
||||
|
||||
/**
|
||||
* 操作时间
|
||||
*/
|
||||
private LocalDateTime operatedAt;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 审批流程模板实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_approval_template")
|
||||
public class SysApprovalTemplate {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 模板名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 适用场景
|
||||
*/
|
||||
private String scenario;
|
||||
|
||||
/**
|
||||
* 是否启用 0-禁用 1-启用
|
||||
*/
|
||||
private Integer enabled;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 创建人ID
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Long createdBy;
|
||||
|
||||
/**
|
||||
* 更新人ID
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Long updatedBy;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
}
|
||||
106
src/main/java/com/nanxiislet/admin/entity/SysDept.java
Normal file
106
src/main/java/com/nanxiislet/admin/entity/SysDept.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 部门实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_dept")
|
||||
public class SysDept {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 父部门ID,0表示顶级部门
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 部门编码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 部门负责人ID
|
||||
*/
|
||||
private Long leaderId;
|
||||
|
||||
/**
|
||||
* 部门负责人名称
|
||||
*/
|
||||
private String leaderName;
|
||||
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 状态 0-禁用 1-正常
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 创建人ID
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Long createdBy;
|
||||
|
||||
/**
|
||||
* 更新人ID
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private Long updatedBy;
|
||||
|
||||
/**
|
||||
* 删除标记
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
|
||||
/**
|
||||
* 子部门列表(非数据库字段)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<SysDept> children;
|
||||
}
|
||||
32
src/main/java/com/nanxiislet/admin/entity/SysDict.java
Normal file
32
src/main/java/com/nanxiislet/admin/entity/SysDict.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 字典类型实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("sys_dict")
|
||||
@Schema(description = "字典类型")
|
||||
public class SysDict extends BaseEntity {
|
||||
|
||||
@Schema(description = "字典名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "字典编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "状态 0-禁用 1-正常")
|
||||
private Integer status;
|
||||
}
|
||||
41
src/main/java/com/nanxiislet/admin/entity/SysDictItem.java
Normal file
41
src/main/java/com/nanxiislet/admin/entity/SysDictItem.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 字典项实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("sys_dict_item")
|
||||
@Schema(description = "字典项")
|
||||
public class SysDictItem extends BaseEntity {
|
||||
|
||||
@Schema(description = "字典ID")
|
||||
private Long dictId;
|
||||
|
||||
@Schema(description = "字典项标签")
|
||||
private String label;
|
||||
|
||||
@Schema(description = "字典项值")
|
||||
private String value;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "是否默认 0-否 1-是")
|
||||
private Integer isDefault;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "状态 0-禁用 1-正常")
|
||||
private Integer status;
|
||||
}
|
||||
59
src/main/java/com/nanxiislet/admin/entity/SysMenu.java
Normal file
59
src/main/java/com/nanxiislet/admin/entity/SysMenu.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 菜单实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("sys_menu")
|
||||
@Schema(description = "菜单信息")
|
||||
public class SysMenu extends BaseEntity {
|
||||
|
||||
@Schema(description = "关联项目ID")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "父菜单ID")
|
||||
private Long parentId;
|
||||
|
||||
@Schema(description = "菜单名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "菜单编码/Key")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "菜单类型 directory-目录 menu-菜单 button-按钮")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "路由路径")
|
||||
private String path;
|
||||
|
||||
@Schema(description = "组件路径")
|
||||
private String component;
|
||||
|
||||
@Schema(description = "图标")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "权限标识")
|
||||
private String permission;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "是否隐藏 0-显示 1-隐藏")
|
||||
private Integer hidden;
|
||||
|
||||
@Schema(description = "状态 0-禁用 1-正常")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
}
|
||||
35
src/main/java/com/nanxiislet/admin/entity/SysRole.java
Normal file
35
src/main/java/com/nanxiislet/admin/entity/SysRole.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 角色实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-08
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("sys_role")
|
||||
@Schema(description = "角色信息")
|
||||
public class SysRole extends BaseEntity {
|
||||
|
||||
@Schema(description = "角色编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "角色名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "角色描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "状态 0-禁用 1-正常")
|
||||
private Integer status;
|
||||
}
|
||||
35
src/main/java/com/nanxiislet/admin/entity/SysRoleMenu.java
Normal file
35
src/main/java/com/nanxiislet/admin/entity/SysRoleMenu.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 角色菜单关联实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-08
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_role_menu")
|
||||
@Schema(description = "角色菜单关联")
|
||||
public class SysRoleMenu implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "主键ID")
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "角色ID")
|
||||
private Long roleId;
|
||||
|
||||
@Schema(description = "菜单ID")
|
||||
private Long menuId;
|
||||
}
|
||||
59
src/main/java/com/nanxiislet/admin/entity/SysUser.java
Normal file
59
src/main/java/com/nanxiislet/admin/entity/SysUser.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package com.nanxiislet.admin.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.nanxiislet.admin.common.base.BaseEntity;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 用户实体
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2024-01-06
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("sys_user")
|
||||
@Schema(description = "用户信息")
|
||||
public class SysUser extends BaseEntity {
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "昵称")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "角色编码")
|
||||
private String role;
|
||||
|
||||
@Schema(description = "部门ID")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "部门名称")
|
||||
private String deptName;
|
||||
|
||||
@Schema(description = "状态 0-禁用 1-正常")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "最后登录时间")
|
||||
private java.time.LocalDateTime lastLoginTime;
|
||||
|
||||
@Schema(description = "最后登录IP")
|
||||
private String lastLoginIp;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.FinanceAccount;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 财务账户 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface FinanceAccountMapper extends BaseMapper<FinanceAccount> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.FinanceBudget;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 预算记录 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface FinanceBudgetMapper extends BaseMapper<FinanceBudget> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.FinanceExpense;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 支出记录 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface FinanceExpenseMapper extends BaseMapper<FinanceExpense> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.FinanceIncome;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 收入记录 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface FinanceIncomeMapper extends BaseMapper<FinanceIncome> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.FinanceInvoice;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 发票记录 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface FinanceInvoiceMapper extends BaseMapper<FinanceInvoice> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.FinanceReimbursement;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 报销记录 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface FinanceReimbursementMapper extends BaseMapper<FinanceReimbursement> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.FinanceSettlement;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 结算记录 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface FinanceSettlementMapper extends BaseMapper<FinanceSettlement> {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.PlatformCertificate;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 证书 Mapper
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-13
|
||||
*/
|
||||
@Mapper
|
||||
public interface PlatformCertificateMapper extends BaseMapper<PlatformCertificate> {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.PlatformDomain;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 域名 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface PlatformDomainMapper extends BaseMapper<PlatformDomain> {
|
||||
|
||||
@org.apache.ibatis.annotations.Select("SELECT * FROM platform_domain WHERE domain = #{domain} LIMIT 1")
|
||||
PlatformDomain findByDomainIncludeDeleted(@org.apache.ibatis.annotations.Param("domain") String domain);
|
||||
|
||||
@org.apache.ibatis.annotations.Update("UPDATE platform_domain SET deleted = 0, updated_at = NOW() WHERE id = #{id}")
|
||||
void restoreById(@org.apache.ibatis.annotations.Param("id") Long id);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.PlatformProject;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 项目 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface PlatformProjectMapper extends BaseMapper<PlatformProject> {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.PlatformServer;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 服务器 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface PlatformServerMapper extends BaseMapper<PlatformServer> {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.nanxiislet.admin.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.nanxiislet.admin.entity.SysApprovalInstance;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 审批实例Mapper
|
||||
*
|
||||
* @author NanxiIslet
|
||||
* @since 2026-01-09
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysApprovalInstanceMapper extends BaseMapper<SysApprovalInstance> {
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user