chore: 从Idea local history中恢复文件,完成至用户预约列表查询接口
This commit is contained in:
commit
275ca89962
78
.gitignore
vendored
Normal file
78
.gitignore
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# -----------------------------
|
||||||
|
# Java & Spring Boot 项目通用
|
||||||
|
# -----------------------------
|
||||||
|
|
||||||
|
# 编译输出
|
||||||
|
/out/
|
||||||
|
/target/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# 日志
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# 临时文件
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# 操作系统垃圾文件
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Maven
|
||||||
|
# -----------------------------
|
||||||
|
/.mvn/
|
||||||
|
/**/target/
|
||||||
|
!src/**/target/keepme.txt # 可选:防止误删有用文件夹
|
||||||
|
|
||||||
|
# Maven Wrapper
|
||||||
|
mvnw
|
||||||
|
mvnw.cmd
|
||||||
|
.mvn/
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Gradle(如果用的是 Gradle)
|
||||||
|
# -----------------------------
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Gradle Wrapper
|
||||||
|
gradlew
|
||||||
|
gradlew.bat
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# IntelliJ IDEA
|
||||||
|
# -----------------------------
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# VSCode
|
||||||
|
# -----------------------------
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# 数据库 & 配置
|
||||||
|
# -----------------------------
|
||||||
|
|
||||||
|
# 忽略本地配置文件(避免提交数据库密码等)
|
||||||
|
application-local.yaml
|
||||||
|
application-dev.yaml
|
||||||
|
application.properties
|
||||||
|
application.yaml
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# 图片 / 文件上传目录(如有)
|
||||||
|
# -----------------------------
|
||||||
|
/device_image/
|
||||||
|
/uploads/
|
||||||
|
/images/
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Git 忽略规则文件自身
|
||||||
|
# -----------------------------
|
||||||
|
!.gitignore
|
33
HELP.md
Normal file
33
HELP.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Getting Started
|
||||||
|
|
||||||
|
### Reference Documentation
|
||||||
|
|
||||||
|
For further reference, please consider the following sections:
|
||||||
|
|
||||||
|
* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
|
||||||
|
* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/3.4.6/maven-plugin)
|
||||||
|
* [Create an OCI image](https://docs.spring.io/spring-boot/3.4.6/maven-plugin/build-image.html)
|
||||||
|
* [MyBatis Framework](https://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/)
|
||||||
|
* [Spring Security](https://docs.spring.io/spring-boot/3.4.6/reference/web/spring-security.html)
|
||||||
|
* [Spring Web](https://docs.spring.io/spring-boot/3.4.6/reference/web/servlet.html)
|
||||||
|
|
||||||
|
### Guides
|
||||||
|
|
||||||
|
The following guides illustrate how to use some features concretely:
|
||||||
|
|
||||||
|
* [MyBatis Quick Start](https://github.com/mybatis/spring-boot-starter/wiki/Quick-Start)
|
||||||
|
* [Securing a Web Application](https://spring.io/guides/gs/securing-web/)
|
||||||
|
* [Spring Boot and OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2/)
|
||||||
|
* [Authenticating a User with LDAP](https://spring.io/guides/gs/authenticating-ldap/)
|
||||||
|
* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
|
||||||
|
* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
|
||||||
|
* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)
|
||||||
|
|
||||||
|
### Maven Parent overrides
|
||||||
|
|
||||||
|
Due to Maven's design, elements are inherited from the parent POM to the project POM.
|
||||||
|
While most of the inheritance is fine, it also inherits unwanted elements like `<license>` and `<developers>` from the
|
||||||
|
parent.
|
||||||
|
To prevent this, the project POM contains empty overrides for these elements.
|
||||||
|
If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides.
|
||||||
|
|
100
db/equip_reserve.sql
Normal file
100
db/equip_reserve.sql
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
-- 创建数据库
|
||||||
|
CREATE
|
||||||
|
DATABASE IF NOT EXISTS equip_reserve DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||||
|
USE
|
||||||
|
equip_reserve;
|
||||||
|
|
||||||
|
-- 用户表
|
||||||
|
CREATE TABLE users
|
||||||
|
(
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
username VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
password VARCHAR(255) NOT NULL,
|
||||||
|
name VARCHAR(50),
|
||||||
|
phone VARCHAR(20),
|
||||||
|
team_id BIGINT,
|
||||||
|
enabled BOOLEAN DEFAULT TRUE,
|
||||||
|
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 角色表
|
||||||
|
CREATE TABLE roles
|
||||||
|
(
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
code VARCHAR(30) NOT NULL UNIQUE,
|
||||||
|
name VARCHAR(50) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 用户-角色关联表
|
||||||
|
CREATE TABLE user_roles
|
||||||
|
(
|
||||||
|
user_id BIGINT NOT NULL,
|
||||||
|
role_id BIGINT NOT NULL,
|
||||||
|
PRIMARY KEY (user_id, role_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 团队表
|
||||||
|
CREATE TABLE teams
|
||||||
|
(
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
leader_id BIGINT,
|
||||||
|
created_time DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 团队成员表
|
||||||
|
CREATE TABLE team_members
|
||||||
|
(
|
||||||
|
team_id BIGINT NOT NULL,
|
||||||
|
user_id BIGINT NOT NULL,
|
||||||
|
PRIMARY KEY (team_id, user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 设备表
|
||||||
|
CREATE TABLE devices
|
||||||
|
(
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
usage_requirement TEXT,
|
||||||
|
location VARCHAR(255),
|
||||||
|
image_path VARCHAR(255),
|
||||||
|
status VARCHAR(20) NOT NULL default 'AVAILABLE',
|
||||||
|
team_id BIGINT NOT NULL,
|
||||||
|
device_admin_id BIGINT,
|
||||||
|
created_time DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 预约记录表
|
||||||
|
CREATE TABLE reservations
|
||||||
|
(
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
device_id BIGINT NOT NULL,
|
||||||
|
user_id BIGINT NOT NULL,
|
||||||
|
start_time DATE NOT NULL,
|
||||||
|
end_time DATE NOT NULL,
|
||||||
|
applicant_name VARCHAR(50) NOT NULL,
|
||||||
|
applicant_team VARCHAR(50),
|
||||||
|
applicant_contact VARCHAR(50),
|
||||||
|
status VARCHAR(30) NOT NULL,
|
||||||
|
device_admin_id BIGINT,
|
||||||
|
created_time DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 审批记录表
|
||||||
|
CREATE TABLE approvals
|
||||||
|
(
|
||||||
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
reservation_id BIGINT NOT NULL,
|
||||||
|
step TINYINT NOT NULL,
|
||||||
|
approver_id BIGINT NOT NULL,
|
||||||
|
decision TINYINT NOT NULL,
|
||||||
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 初始角色数据
|
||||||
|
INSERT INTO roles (code, name)
|
||||||
|
VALUES ('ADMIN', '系统管理员'),
|
||||||
|
('LEADER', '团队负责人'),
|
||||||
|
('DEVICE_ADMIN', '设备管理员'),
|
||||||
|
('USER', '普通用户');
|
113
pom.xml
Normal file
113
pom.xml
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?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>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>3.4.6</version>
|
||||||
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
|
</parent>
|
||||||
|
<groupId>github.benjamin</groupId>
|
||||||
|
<artifactId>equip-reserve-backend</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>equip-reserve-backend</name>
|
||||||
|
<description>equip-reserve-backend</description>
|
||||||
|
<url/>
|
||||||
|
<licenses>
|
||||||
|
<license/>
|
||||||
|
</licenses>
|
||||||
|
<developers>
|
||||||
|
<developer/>
|
||||||
|
</developers>
|
||||||
|
<scm>
|
||||||
|
<connection/>
|
||||||
|
<developerConnection/>
|
||||||
|
<tag/>
|
||||||
|
<url/>
|
||||||
|
</scm>
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
|
<version>3.5.5</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<version>8.3.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
<version>0.12.6</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-spring-boot3-starter-test</artifactId>
|
||||||
|
<version>3.5.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-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>-->
|
||||||
|
<!-- <annotationProcessorPaths>-->
|
||||||
|
<!-- <path>-->
|
||||||
|
<!-- <groupId>org.projectlombok</groupId>-->
|
||||||
|
<!-- <artifactId>lombok</artifactId>-->
|
||||||
|
<!-- </path>-->
|
||||||
|
<!-- </annotationProcessorPaths>-->
|
||||||
|
<!-- </configuration>-->
|
||||||
|
<!-- </plugin>-->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,15 @@
|
|||||||
|
package github.benjamin.equipreservebackend;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@MapperScan("github.benjamin.equipreservebackend.mapper")
|
||||||
|
public class EquipReserveBackendApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(EquipReserveBackendApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package github.benjamin.equipreservebackend.config;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.DbType;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class MybatisPlusConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||||
|
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||||
|
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package github.benjamin.equipreservebackend.config;
|
||||||
|
|
||||||
|
import github.benjamin.equipreservebackend.security.JwtAuthFilter;
|
||||||
|
import github.benjamin.equipreservebackend.service.UserService;
|
||||||
|
import github.benjamin.equipreservebackend.utils.JwtUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableMethodSecurity
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
|
||||||
|
return http.csrf(AbstractHttpConfigurer::disable)
|
||||||
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
|
.authorizeHttpRequests(auth -> auth
|
||||||
|
.requestMatchers("/login").permitAll()
|
||||||
|
.requestMatchers("/device_image/**").permitAll()
|
||||||
|
.anyRequest().authenticated())
|
||||||
|
.addFilterBefore(jwtAuthFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JwtAuthFilter jwtAuthFilter() {
|
||||||
|
return new JwtAuthFilter(jwtUtil, userService);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package github.benjamin.equipreservebackend.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class WebConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
|
registry.addResourceHandler("/device_image/**")
|
||||||
|
.addResourceLocations("file:" + System.getProperty("user.dir") + "/device_image/");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package github.benjamin.equipreservebackend.constant;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum DeviceReservationState {
|
||||||
|
|
||||||
|
FREE("可预约"),
|
||||||
|
RESERVED("有预约");
|
||||||
|
|
||||||
|
private final String label;
|
||||||
|
|
||||||
|
DeviceReservationState(String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package github.benjamin.equipreservebackend.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备内部状态
|
||||||
|
*/
|
||||||
|
public enum DeviceStatus {
|
||||||
|
|
||||||
|
AVAILABLE,
|
||||||
|
MAINTENANCE,
|
||||||
|
DISABLED
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package github.benjamin.equipreservebackend.constant;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum ReservationStatus {
|
||||||
|
PENDING_LEADER("团队负责人审批中"),
|
||||||
|
PENDING_DEVICE_ADMIN("设备负责人审批中"),
|
||||||
|
APPROVED("通过"),
|
||||||
|
APPROVED_ASSIST("需要协助实验"),
|
||||||
|
REJECTED("审批不通过");
|
||||||
|
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
ReservationStatus(String label) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package github.benjamin.equipreservebackend.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Device;
|
||||||
|
import github.benjamin.equipreservebackend.response.ResponseResult;
|
||||||
|
import github.benjamin.equipreservebackend.service.DeviceService;
|
||||||
|
import github.benjamin.equipreservebackend.vo.DeviceUserVO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/device")
|
||||||
|
public class DeviceController {
|
||||||
|
|
||||||
|
private final DeviceService deviceService;
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('USER')")
|
||||||
|
@GetMapping
|
||||||
|
public ResponseResult<Page<DeviceUserVO>> getDevices(@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "10") Integer size) {
|
||||||
|
Page<Device> pageRequest = new Page<>(page, size);
|
||||||
|
Page<DeviceUserVO> res = deviceService.getDeviceVO(pageRequest);
|
||||||
|
return ResponseResult.success(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('DEVICE_ADMIN')")
|
||||||
|
@PostMapping
|
||||||
|
public ResponseResult<?> addDevice(@RequestBody Device device){
|
||||||
|
deviceService.addDevice(device);
|
||||||
|
return ResponseResult.success(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('DEVICE_ADMIN')")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ResponseResult<?> deleteDevice(@PathVariable("id") Long id) {
|
||||||
|
deviceService.deleteDevice(id);
|
||||||
|
return ResponseResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('DEVICE_ADMIN')")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public ResponseResult<?> updateDevice(@PathVariable("id") Long id,
|
||||||
|
@RequestBody Device device) {
|
||||||
|
device.setId(id);
|
||||||
|
Device updatedDevice = deviceService.updateDevice(device);
|
||||||
|
return ResponseResult.success(updatedDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('DEVICE_ADMIN')")
|
||||||
|
@PostMapping("/{id}/image")
|
||||||
|
public ResponseResult<?> uploadImage(@PathVariable("id") Long id,
|
||||||
|
@RequestParam("image") MultipartFile image) throws IOException {
|
||||||
|
String imagePath = deviceService.saveImage(id, image);
|
||||||
|
return ResponseResult.success(imagePath);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package github.benjamin.equipreservebackend.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Reservation;
|
||||||
|
import github.benjamin.equipreservebackend.response.ResponseResult;
|
||||||
|
import github.benjamin.equipreservebackend.service.ReservationService;
|
||||||
|
import github.benjamin.equipreservebackend.vo.UserReservationVO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/reservation")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ReservationController {
|
||||||
|
|
||||||
|
private final ReservationService reservationService;
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('USER')")
|
||||||
|
@PostMapping
|
||||||
|
public ResponseResult<?> addReservation(@RequestBody Reservation reservation) {
|
||||||
|
reservationService.addReservation(reservation);
|
||||||
|
return ResponseResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasRole('USER')")
|
||||||
|
@GetMapping("/{userId}")
|
||||||
|
public ResponseResult<Page<UserReservationVO>> getUserReservation(@PathVariable("userId") Long userId,
|
||||||
|
@RequestParam(defaultValue = "10") Integer page,
|
||||||
|
@RequestParam(defaultValue = "10") Integer size) {
|
||||||
|
Page<Reservation> pageRequest = new Page<>(page, size);
|
||||||
|
Page<UserReservationVO> res = reservationService.getUserReservationVO(userId, pageRequest);
|
||||||
|
return ResponseResult.success(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package github.benjamin.equipreservebackend.controller;
|
||||||
|
|
||||||
|
import github.benjamin.equipreservebackend.vo.LoginResponse;
|
||||||
|
import github.benjamin.equipreservebackend.response.ResponseResult;
|
||||||
|
import github.benjamin.equipreservebackend.entity.User;
|
||||||
|
import github.benjamin.equipreservebackend.security.SecurityUser;
|
||||||
|
import github.benjamin.equipreservebackend.service.UserService;
|
||||||
|
import github.benjamin.equipreservebackend.utils.JwtUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserController {
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@PostMapping("/login")
|
||||||
|
public ResponseResult<LoginResponse> login(String username, String password) {
|
||||||
|
User user = userService.login(username, password);
|
||||||
|
SecurityUser securityUser = userService.loadSecurityUserById(user.getId());
|
||||||
|
String token = jwtUtil.generateToken(securityUser);
|
||||||
|
return ResponseResult.success(new LoginResponse(user.getId(), user.getName(), token));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package github.benjamin.equipreservebackend.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("approvals")
|
||||||
|
public class Approval {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long reservationId;
|
||||||
|
private Integer step;
|
||||||
|
private Long approverId;
|
||||||
|
private Integer decision;
|
||||||
|
private LocalDateTime timeStamp;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package github.benjamin.equipreservebackend.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("devices")
|
||||||
|
public class Device {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private String usageRequirement;
|
||||||
|
private String location;
|
||||||
|
private String imagePath;
|
||||||
|
private String status;
|
||||||
|
private Long teamId;
|
||||||
|
private Long deviceAdminId;
|
||||||
|
private LocalDateTime createdTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package github.benjamin.equipreservebackend.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("reservations")
|
||||||
|
public class Reservation {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Long deviceId;
|
||||||
|
private Long userId;
|
||||||
|
private LocalDate startTime;
|
||||||
|
private LocalDate endTime;
|
||||||
|
private String applicantName;
|
||||||
|
private String applicantTeam;
|
||||||
|
private String applicantContact;
|
||||||
|
private String status;
|
||||||
|
private Long deviceAdminId;
|
||||||
|
private LocalDateTime createdTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package github.benjamin.equipreservebackend.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("roles")
|
||||||
|
public class Role {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String code;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package github.benjamin.equipreservebackend.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("teams")
|
||||||
|
public class Team {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private Long leaderId;
|
||||||
|
private LocalDateTime createdTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package github.benjamin.equipreservebackend.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("users")
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private String name;
|
||||||
|
private String phone;
|
||||||
|
private Long teamId;
|
||||||
|
private Boolean enabled;
|
||||||
|
private LocalDateTime createdTime;
|
||||||
|
private LocalDateTime updatedTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package github.benjamin.equipreservebackend.exception;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class ApiException extends RuntimeException{
|
||||||
|
|
||||||
|
private final int code;
|
||||||
|
|
||||||
|
public ApiException(String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiException(int code, String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package github.benjamin.equipreservebackend.exception;
|
||||||
|
|
||||||
|
import github.benjamin.equipreservebackend.response.ResponseCode;
|
||||||
|
import github.benjamin.equipreservebackend.response.ResponseResult;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(ApiException.class)
|
||||||
|
public ResponseResult<?> handleApiException(ApiException e) {
|
||||||
|
return ResponseResult.fail(e.getCode(), e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(AuthorizationDeniedException.class)
|
||||||
|
public ResponseResult<?> handleAuthorizationDeniedException(AuthorizationDeniedException e) {
|
||||||
|
return ResponseResult.fail(ResponseCode.UNAUTHORIZED, "该用户无权访问此功能");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ResponseResult<?> handleException(Exception e) {
|
||||||
|
// TODO 日志
|
||||||
|
e.printStackTrace();
|
||||||
|
return ResponseResult.fail(ResponseCode.UNKNOWN_ERROR,"服务器内部异常,请联系开发人员");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package github.benjamin.equipreservebackend.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Device;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface DeviceMapper extends BaseMapper<Device> {
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package github.benjamin.equipreservebackend.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Reservation;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ReservationMapper extends BaseMapper<Reservation> {
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package github.benjamin.equipreservebackend.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Role;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface RoleMapper extends BaseMapper<Role> {
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
select r.id, r.code, r.name
|
||||||
|
from roles r
|
||||||
|
join user_roles ur on r.id = ur.role_id
|
||||||
|
where ur.user_id = #{userId}
|
||||||
|
""")
|
||||||
|
List<Role> selectRoleByUserId(@Param("userId") Long userId);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package github.benjamin.equipreservebackend.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import github.benjamin.equipreservebackend.entity.User;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface UserMapper extends BaseMapper<User> {
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package github.benjamin.equipreservebackend.response;
|
||||||
|
|
||||||
|
public class ResponseCode {
|
||||||
|
|
||||||
|
// 通用
|
||||||
|
public static final int SUCCESS = 0;
|
||||||
|
public static final int UNKNOWN_ERROR = -1;
|
||||||
|
|
||||||
|
// 登录相关
|
||||||
|
public static final int PASSWORD_ERROR = 4001;
|
||||||
|
public static final int USER_NOT_EXIST = 4002;
|
||||||
|
|
||||||
|
public static final int UNAUTHORIZED = 4010;
|
||||||
|
|
||||||
|
public static final int FORBIDDEN = 4030;
|
||||||
|
|
||||||
|
public static final int NOT_FOUND = 4040;
|
||||||
|
|
||||||
|
// 业务相关错误码
|
||||||
|
public static final int DUPLICATE_USERNAME = 10001;
|
||||||
|
public static final int TEAM_NOT_EXIST = 10002;
|
||||||
|
public static final int DEVICE_ALREADY_BOOKED = 10003;
|
||||||
|
|
||||||
|
private ResponseCode() {} // 不允许实例化
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package github.benjamin.equipreservebackend.response;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ResponseResult<T> {
|
||||||
|
|
||||||
|
private int code;
|
||||||
|
private String message;
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
public static <T> ResponseResult<T> success() {
|
||||||
|
return ResponseResult.success("success", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ResponseResult<T> success(T data) {
|
||||||
|
return ResponseResult.success("success", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ResponseResult<T> success(String message, T data) {
|
||||||
|
return new ResponseResult<>(ResponseCode.SUCCESS, message, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ResponseResult<T> fail(Integer code, String message) {
|
||||||
|
return new ResponseResult<>(code, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ResponseResult<T> of(Integer code, String message, T data) {
|
||||||
|
return new ResponseResult<>(code, message, data);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package github.benjamin.equipreservebackend.security;
|
||||||
|
|
||||||
|
import github.benjamin.equipreservebackend.service.UserService;
|
||||||
|
import github.benjamin.equipreservebackend.utils.JwtUtil;
|
||||||
|
import io.jsonwebtoken.JwtException;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JwtAuthFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
String token = getTokenFromHeader(request);
|
||||||
|
if (token != null) {
|
||||||
|
try {
|
||||||
|
Long userId = jwtUtil.getUserId(token);
|
||||||
|
SecurityUser userDetails = userService.loadSecurityUserById(userId);
|
||||||
|
UsernamePasswordAuthenticationToken authToken =
|
||||||
|
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||||
|
|
||||||
|
// 续期
|
||||||
|
if (jwtUtil.shouldRefresh(token)) {
|
||||||
|
String newToken = jwtUtil.generateToken(userDetails);
|
||||||
|
response.setHeader("Authorization", "Bearer " + newToken);
|
||||||
|
}
|
||||||
|
} catch (JwtException e) {
|
||||||
|
// token 失效或非法,忽略即可
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTokenFromHeader(HttpServletRequest request) {
|
||||||
|
String authHeader = request.getHeader("Authorization");
|
||||||
|
if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {
|
||||||
|
return authHeader.substring(7);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package github.benjamin.equipreservebackend.security;
|
||||||
|
|
||||||
|
import github.benjamin.equipreservebackend.entity.Role;
|
||||||
|
import github.benjamin.equipreservebackend.entity.User;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class SecurityUser implements UserDetails {
|
||||||
|
|
||||||
|
private final User user;
|
||||||
|
|
||||||
|
private final List<Role> roles;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return roles.stream()
|
||||||
|
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getCode()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPassword() {
|
||||||
|
return user.getPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return user.getUsername();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package github.benjamin.equipreservebackend.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Device;
|
||||||
|
import github.benjamin.equipreservebackend.vo.DeviceUserVO;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface DeviceService {
|
||||||
|
Page<DeviceUserVO> getDeviceVO(Page<Device> pageRequest);
|
||||||
|
|
||||||
|
void addDevice(Device device);
|
||||||
|
|
||||||
|
void deleteDevice(Long id);
|
||||||
|
|
||||||
|
Device updateDevice(Device device);
|
||||||
|
|
||||||
|
String saveImage(Long id, MultipartFile image) throws IOException;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package github.benjamin.equipreservebackend.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Reservation;
|
||||||
|
import github.benjamin.equipreservebackend.vo.UserReservationVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ReservationService {
|
||||||
|
|
||||||
|
List<Reservation> getApprovedReservationsByDeviceIds(List<Long> devicesIds);
|
||||||
|
|
||||||
|
void addReservation(Reservation reservation);
|
||||||
|
|
||||||
|
Page<UserReservationVO> getUserReservationVO(Long userId, Page<Reservation> pageRequest);
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package github.benjamin.equipreservebackend.service;
|
||||||
|
|
||||||
|
import github.benjamin.equipreservebackend.entity.User;
|
||||||
|
import github.benjamin.equipreservebackend.security.SecurityUser;
|
||||||
|
|
||||||
|
public interface UserService {
|
||||||
|
|
||||||
|
User login(String username, String password);
|
||||||
|
|
||||||
|
SecurityUser loadSecurityUserById(Long userId);
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
package github.benjamin.equipreservebackend.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import github.benjamin.equipreservebackend.constant.DeviceReservationState;
|
||||||
|
import github.benjamin.equipreservebackend.constant.DeviceStatus;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Device;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Reservation;
|
||||||
|
import github.benjamin.equipreservebackend.exception.ApiException;
|
||||||
|
import github.benjamin.equipreservebackend.mapper.DeviceMapper;
|
||||||
|
import github.benjamin.equipreservebackend.response.ResponseCode;
|
||||||
|
import github.benjamin.equipreservebackend.service.DeviceService;
|
||||||
|
import github.benjamin.equipreservebackend.service.ReservationService;
|
||||||
|
import github.benjamin.equipreservebackend.utils.FileUtil;
|
||||||
|
import github.benjamin.equipreservebackend.utils.PageUtil;
|
||||||
|
import github.benjamin.equipreservebackend.vo.DeviceUserVO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DeviceServiceImpl implements DeviceService {
|
||||||
|
|
||||||
|
private final DeviceMapper deviceMapper;
|
||||||
|
|
||||||
|
private final ReservationService reservationService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<DeviceUserVO> getDeviceVO(Page<Device> pageRequest) {
|
||||||
|
Page<Device> devices = deviceMapper.selectPage(pageRequest, new LambdaQueryWrapper<Device>()
|
||||||
|
.eq(Device::getStatus, DeviceStatus.AVAILABLE)
|
||||||
|
.orderByAsc(Device::getName));
|
||||||
|
List<Long> deviceIds = devices.getRecords().stream()
|
||||||
|
.map(Device::getId)
|
||||||
|
.toList();
|
||||||
|
List<Reservation> reservations = reservationService.getApprovedReservationsByDeviceIds(deviceIds);
|
||||||
|
List<DeviceUserVO> deviceUserVOS = devices.getRecords().stream()
|
||||||
|
.map(device -> buildDeviceVO(device, reservations))
|
||||||
|
.toList();
|
||||||
|
Page<DeviceUserVO> res = PageUtil.copyPage(devices);
|
||||||
|
res.setRecords(deviceUserVOS);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addDevice(Device device) {
|
||||||
|
deviceMapper.insert(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteDevice(Long id) {
|
||||||
|
deviceMapper.delete(new LambdaQueryWrapper<Device>()
|
||||||
|
.eq(Device::getId, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Device updateDevice(Device device) {
|
||||||
|
LambdaUpdateWrapper<Device> wrapper =new LambdaUpdateWrapper<>();
|
||||||
|
wrapper.eq(Device::getId, device.getId());
|
||||||
|
|
||||||
|
wrapper.set(Objects.nonNull(device.getName()), Device::getName, device.getName());
|
||||||
|
wrapper.set(Objects.nonNull(device.getUsageRequirement()), Device::getUsageRequirement, device.getUsageRequirement());
|
||||||
|
wrapper.set(Objects.nonNull(device.getLocation()), Device::getLocation, device.getLocation());
|
||||||
|
wrapper.set(Objects.nonNull(device.getStatus()), Device::getStatus, device.getStatus());
|
||||||
|
wrapper.set(Objects.nonNull(device.getTeamId()), Device::getTeamId, device.getTeamId());
|
||||||
|
|
||||||
|
if (deviceMapper.update(wrapper) <= 0) {
|
||||||
|
throw new ApiException(ResponseCode.UNKNOWN_ERROR, "更新设备失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
return deviceMapper.selectOne(new LambdaQueryWrapper<Device>()
|
||||||
|
.eq(Device::getId, device.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String saveImage(Long id, MultipartFile image) throws IOException {
|
||||||
|
Device device = deviceMapper.selectOne(new LambdaQueryWrapper<Device>().eq(Device::getId, id));
|
||||||
|
String basePath = System.getProperty("user.dir");
|
||||||
|
if (StringUtils.hasText(device.getImagePath())) {
|
||||||
|
Path fullPath = Paths.get(basePath, device.getImagePath()).normalize();
|
||||||
|
File file = fullPath.toFile();
|
||||||
|
if (file.exists() && file.isFile()) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
File dir = new File(basePath, "device_image");
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
String newFilename = FileUtil.getUniqueFileName(device.getName(), image);
|
||||||
|
|
||||||
|
// 保存图片
|
||||||
|
Path savePath = Paths.get(basePath, "device_image", newFilename);
|
||||||
|
Files.copy(image.getInputStream(), savePath, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
|
||||||
|
String imagePath = "device_image/" + newFilename;
|
||||||
|
device.setImagePath(imagePath);
|
||||||
|
deviceMapper.update(new LambdaUpdateWrapper<Device>()
|
||||||
|
.eq(Device::getId, id)
|
||||||
|
.set(Device::getImagePath, device.getImagePath()));
|
||||||
|
|
||||||
|
return imagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceUserVO buildDeviceVO(Device device, List<Reservation> reservations) {
|
||||||
|
DeviceUserVO vo = new DeviceUserVO(device);
|
||||||
|
// TODO 显示设备状态逻辑,根据客户需求修改
|
||||||
|
if (reservations.stream().anyMatch(r -> Objects.equals(r.getDeviceId(), device.getId()))) {
|
||||||
|
vo.setState(DeviceReservationState.RESERVED.getLabel());
|
||||||
|
} else {
|
||||||
|
vo.setState(DeviceReservationState.FREE.getLabel());
|
||||||
|
}
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
package github.benjamin.equipreservebackend.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import github.benjamin.equipreservebackend.constant.ReservationStatus;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Device;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Reservation;
|
||||||
|
import github.benjamin.equipreservebackend.entity.User;
|
||||||
|
import github.benjamin.equipreservebackend.mapper.DeviceMapper;
|
||||||
|
import github.benjamin.equipreservebackend.mapper.ReservationMapper;
|
||||||
|
import github.benjamin.equipreservebackend.mapper.UserMapper;
|
||||||
|
import github.benjamin.equipreservebackend.service.ReservationService;
|
||||||
|
import github.benjamin.equipreservebackend.utils.PageUtil;
|
||||||
|
import github.benjamin.equipreservebackend.vo.UserReservationVO;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ReservationServiceImpl implements ReservationService {
|
||||||
|
|
||||||
|
private final ReservationMapper reservationMapper;
|
||||||
|
|
||||||
|
private final DeviceMapper deviceMapper;
|
||||||
|
|
||||||
|
private final UserMapper userMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未来days天内有预约的设备显示为“有预约”
|
||||||
|
*/
|
||||||
|
@Value("${equip-reserve.device-days}")
|
||||||
|
private Integer days;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Reservation> getApprovedReservationsByDeviceIds(List<Long> devicesIds) {
|
||||||
|
LocalDateTime endTime = LocalDateTime.now().plusDays(days);
|
||||||
|
return reservationMapper.selectList(new LambdaQueryWrapper<Reservation>()
|
||||||
|
.in(Reservation::getDeviceId, devicesIds)
|
||||||
|
.eq(Reservation::getStatus, ReservationStatus.APPROVED)
|
||||||
|
.lt(Reservation::getEndTime, endTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addReservation(Reservation reservation) {
|
||||||
|
reservation.setStatus(String.valueOf(ReservationStatus.PENDING_LEADER));
|
||||||
|
reservationMapper.insert(reservation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<UserReservationVO> getUserReservationVO(Long userId, Page<Reservation> pageRequest) {
|
||||||
|
Page<Reservation> reservations = reservationMapper.selectPage(pageRequest, new LambdaQueryWrapper<Reservation>()
|
||||||
|
.eq(Reservation::getUserId, userId)
|
||||||
|
.orderByDesc(Reservation::getCreatedTime));
|
||||||
|
// 获取设备名称
|
||||||
|
List<Long> deviceIds = reservations.getRecords().stream()
|
||||||
|
.map(Reservation::getDeviceId)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
Map<Long, String> deviceNameMap = deviceMapper.selectList(new LambdaQueryWrapper<Device>()
|
||||||
|
.in(Device::getId, deviceIds))
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.toMap(Device::getId, Device::getName));
|
||||||
|
|
||||||
|
// 获取设备管理员信息
|
||||||
|
List<Long> deviceAdminIDs = reservations.getRecords().stream()
|
||||||
|
.map(Reservation::getDeviceAdminId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
Page<UserReservationVO> res = PageUtil.copyPage(reservations);
|
||||||
|
List<UserReservationVO> vos;
|
||||||
|
if (deviceAdminIDs.isEmpty()) {
|
||||||
|
vos = reservations.getRecords().stream()
|
||||||
|
.map(reservation -> new UserReservationVO(reservation, deviceNameMap))
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
Map<Long, User> deviceAdminMap = userMapper.selectList(new LambdaQueryWrapper<User>()
|
||||||
|
.in(User::getId, deviceAdminIDs))
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.toMap(User::getId, Function.identity()));
|
||||||
|
vos = reservations.getRecords().stream()
|
||||||
|
.map(reservation -> new UserReservationVO(reservation, deviceNameMap, deviceAdminMap))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
res.setRecords(vos);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package github.benjamin.equipreservebackend.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Role;
|
||||||
|
import github.benjamin.equipreservebackend.entity.User;
|
||||||
|
import github.benjamin.equipreservebackend.exception.ApiException;
|
||||||
|
import github.benjamin.equipreservebackend.mapper.RoleMapper;
|
||||||
|
import github.benjamin.equipreservebackend.mapper.UserMapper;
|
||||||
|
import github.benjamin.equipreservebackend.response.ResponseCode;
|
||||||
|
import github.benjamin.equipreservebackend.security.SecurityUser;
|
||||||
|
import github.benjamin.equipreservebackend.service.UserService;
|
||||||
|
import github.benjamin.equipreservebackend.utils.JwtUtil;
|
||||||
|
import github.benjamin.equipreservebackend.utils.PasswordUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserServiceImpl implements UserService {
|
||||||
|
|
||||||
|
private final UserMapper userMapper;
|
||||||
|
|
||||||
|
private final RoleMapper roleMapper;
|
||||||
|
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User login(String username, String password) {
|
||||||
|
User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
|
||||||
|
if (Objects.isNull(user)) {
|
||||||
|
throw new ApiException(ResponseCode.USER_NOT_EXIST, "用户不存在");
|
||||||
|
}
|
||||||
|
if (!PasswordUtil.matches(password, user.getPassword())) {
|
||||||
|
throw new ApiException(ResponseCode.PASSWORD_ERROR, "密码错误");
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityUser loadSecurityUserById(Long userId) {
|
||||||
|
User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getId, userId));
|
||||||
|
List<Role> roles = roleMapper.selectRoleByUserId(userId);
|
||||||
|
return new SecurityUser(user, roles);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package github.benjamin.equipreservebackend.utils;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class FileUtil {
|
||||||
|
|
||||||
|
public static String getFileExtension(MultipartFile file) {
|
||||||
|
String originalFilename = file.getOriginalFilename();
|
||||||
|
return originalFilename.contains(".")
|
||||||
|
? originalFilename.substring(originalFilename.lastIndexOf('.'))
|
||||||
|
: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getUniqueFileName(String name, MultipartFile file) {
|
||||||
|
String extension = FileUtil.getFileExtension(file);
|
||||||
|
|
||||||
|
// 构建新文件名:设备名称 + UUID + 扩展名
|
||||||
|
String safeName = name.replaceAll("[^a-zA-Z0-9\\u4e00-\\u9fa5_-]", "_"); // 防止非法字符
|
||||||
|
return safeName + "_" + UUID.randomUUID() + extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileUtil(){}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package github.benjamin.equipreservebackend.utils;
|
||||||
|
|
||||||
|
import github.benjamin.equipreservebackend.security.SecurityUser;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.security.Keys;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JwtUtil {
|
||||||
|
|
||||||
|
@Value("${jwt.secret}")
|
||||||
|
private String SECRET_KEY;
|
||||||
|
|
||||||
|
public String generateToken(SecurityUser securityUser) {
|
||||||
|
return Jwts.builder()
|
||||||
|
.subject(securityUser.getUser().getId().toString())
|
||||||
|
.claim("role", securityUser.getRoles())
|
||||||
|
.issuedAt(new Date())
|
||||||
|
.expiration(new Date(System.currentTimeMillis() + 3600_000))
|
||||||
|
.signWith(getKey(), Jwts.SIG.HS256)
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getUserId(String token) {
|
||||||
|
return Long.valueOf(parseToken(token).getSubject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Claims parseToken(String token) {
|
||||||
|
return Jwts.parser()
|
||||||
|
.verifyWith(getKey())
|
||||||
|
.build()
|
||||||
|
.parseSignedClaims(token)
|
||||||
|
.getPayload();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果token在十分钟内会过期则需要刷新
|
||||||
|
* @param token
|
||||||
|
* @return 是否需要刷新token
|
||||||
|
*/
|
||||||
|
public Boolean shouldRefresh(String token) {
|
||||||
|
Date expiration = getExpiration(token);
|
||||||
|
Long currentTime = System.currentTimeMillis();
|
||||||
|
Long expireTime = expiration.getTime();
|
||||||
|
return expireTime - currentTime < TimeUnit.MINUTES.toMillis(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getExpiration(String token) {
|
||||||
|
return Jwts.parser()
|
||||||
|
.verifyWith(getKey())
|
||||||
|
.build()
|
||||||
|
.parseSignedClaims(token)
|
||||||
|
.getPayload()
|
||||||
|
.getExpiration();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecretKey getKey() {
|
||||||
|
return Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package github.benjamin.equipreservebackend.utils;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
|
||||||
|
public class PageUtil {
|
||||||
|
|
||||||
|
public static <T> Page<T> copyPage(Page<?> page) {
|
||||||
|
return new Page<T>(page.getCurrent(), page.getSize(), page.getTotal(), page.searchCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private PageUtil() {}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package github.benjamin.equipreservebackend.utils;
|
||||||
|
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
|
||||||
|
public class PasswordUtil {
|
||||||
|
|
||||||
|
private static final BCryptPasswordEncoder ENCODER = new BCryptPasswordEncoder();
|
||||||
|
|
||||||
|
public static String encode(String rawPassword) {
|
||||||
|
return ENCODER.encode(rawPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean matches(String rawPassword, String encodedPassword) {
|
||||||
|
return ENCODER.matches(rawPassword, encodedPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PasswordUtil(){}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package github.benjamin.equipreservebackend.vo;
|
||||||
|
|
||||||
|
import github.benjamin.equipreservebackend.entity.Device;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class DeviceUserVO {
|
||||||
|
|
||||||
|
private Long deviceId;
|
||||||
|
private String name;
|
||||||
|
private String usageRequirement;
|
||||||
|
private String location;
|
||||||
|
private String imagePath;
|
||||||
|
/**
|
||||||
|
* 直接给出前端要显示的内容:
|
||||||
|
* 可预约、有预约
|
||||||
|
*/
|
||||||
|
private String state;
|
||||||
|
|
||||||
|
public DeviceUserVO(Device device) {
|
||||||
|
this.deviceId = device.getId();
|
||||||
|
this.name = device.getName();
|
||||||
|
this.usageRequirement = device.getUsageRequirement();
|
||||||
|
this.location = device.getLocation();
|
||||||
|
this.imagePath = device.getImagePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package github.benjamin.equipreservebackend.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LoginResponse {
|
||||||
|
|
||||||
|
private Long userId;
|
||||||
|
private String name;
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package github.benjamin.equipreservebackend.vo;
|
||||||
|
|
||||||
|
import github.benjamin.equipreservebackend.constant.ReservationStatus;
|
||||||
|
import github.benjamin.equipreservebackend.entity.Reservation;
|
||||||
|
import github.benjamin.equipreservebackend.entity.User;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UserReservationVO {
|
||||||
|
private String deviceName;
|
||||||
|
private LocalDate startTime;
|
||||||
|
private LocalDate endTime;
|
||||||
|
private String statusLabel;
|
||||||
|
private String deviceAdminName;
|
||||||
|
private String deviceAdminContact;
|
||||||
|
private LocalDateTime createdTime;
|
||||||
|
|
||||||
|
public UserReservationVO(Reservation r, Map<Long, String> deviceNameMap) {
|
||||||
|
this.deviceName = deviceNameMap.get(r.getDeviceId());
|
||||||
|
this.startTime = r.getStartTime();
|
||||||
|
this.endTime = r.getEndTime();
|
||||||
|
this.statusLabel = ReservationStatus.valueOf(r.getStatus()).getLabel();
|
||||||
|
this.createdTime = r.getCreatedTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public UserReservationVO(Reservation r, Map<Long, String> deviceNameMap, Map<Long, User> deviceAdminMap) {
|
||||||
|
this(r, deviceNameMap);
|
||||||
|
ReservationStatus status = ReservationStatus.valueOf(r.getStatus());
|
||||||
|
if (status == ReservationStatus.APPROVED_ASSIST) {
|
||||||
|
User deviceAdmin = deviceAdminMap.get(r.getDeviceAdminId());
|
||||||
|
this.deviceAdminName = deviceAdmin.getName();
|
||||||
|
this.deviceAdminContact = deviceAdmin.getPhone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
src/main/resources/application-template.yaml
Normal file
8
src/main/resources/application-template.yaml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: equip-reserve-backend
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
username: your-username
|
||||||
|
password: your-password
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/equip_reserve?serverTimeZone=UTC
|
@ -0,0 +1,13 @@
|
|||||||
|
package github.benjamin.equipreservebackend;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class EquipReserveBackendApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package github.benjamin.equipreservebackend;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import github.benjamin.equipreservebackend.constant.DeviceReservationState;
|
||||||
|
import github.benjamin.equipreservebackend.controller.DeviceController;
|
||||||
|
import github.benjamin.equipreservebackend.response.ResponseResult;
|
||||||
|
import github.benjamin.equipreservebackend.vo.DeviceUserVO;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
@Slf4j
|
||||||
|
public class MybatisInitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSelect(){
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
DeviceController deviceController;
|
||||||
|
@Test
|
||||||
|
public void testGetDevice() {
|
||||||
|
log.info(DeviceReservationState.RESERVED.getLabel());
|
||||||
|
log.info(DeviceReservationState.RESERVED.name());
|
||||||
|
log.info(DeviceReservationState.valueOf("RESERVED").getLabel());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user