init backend
This commit is contained in:
92
pom.xml
92
pom.xml
@ -40,6 +40,26 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- https://github.com/xerial/sqlite-jdbc -->
|
||||
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.50.3.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/io.swagger.parser.v3/swagger-parser-v3 -->
|
||||
<dependency>
|
||||
<groupId>io.swagger.parser.v3</groupId>
|
||||
<artifactId>swagger-parser-v3</artifactId>
|
||||
<version>2.1.34</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/io.swagger.parser.v3/swagger-parser-core -->
|
||||
<dependency>
|
||||
<groupId>io.swagger.parser.v3</groupId>
|
||||
<artifactId>swagger-parser-core</artifactId>
|
||||
<version>2.1.34</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -48,6 +68,78 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!-- https://openapi-generator.tech/docs/plugins/ -->
|
||||
<groupId>org.openapitools</groupId>
|
||||
<artifactId>openapi-generator-maven-plugin</artifactId>
|
||||
<version>7.15.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>bookmark-api</id>
|
||||
<goals>
|
||||
<goal>generate</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<inputSpec>${project.basedir}/src/main/resources/config/bookmark/bookmark.yaml</inputSpec>
|
||||
<generatorName>spring</generatorName>
|
||||
<configOptions>
|
||||
<sourceFolder>src/gen/java/main</sourceFolder>
|
||||
<delegatePattern>false</delegatePattern>
|
||||
<useTags>true</useTags>
|
||||
<interfaceOnly>true</interfaceOnly>
|
||||
<skipDefaultInterface>true</skipDefaultInterface>
|
||||
<useJakartaEe>true</useJakartaEe>
|
||||
<useBeanValidation>true</useBeanValidation>
|
||||
<withJakartaValidation>true</withJakartaValidation>
|
||||
<openApiNullable>false</openApiNullable>
|
||||
</configOptions>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>user-np-api</id>
|
||||
<goals>
|
||||
<goal>generate</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<inputSpec>${project.basedir}/src/main/resources/config/user_np/user_np.yaml</inputSpec>
|
||||
<generatorName>spring</generatorName>
|
||||
<configOptions>
|
||||
<sourceFolder>src/gen/java/main</sourceFolder>
|
||||
<delegatePattern>false</delegatePattern>
|
||||
<useTags>true</useTags>
|
||||
<interfaceOnly>true</interfaceOnly>
|
||||
<skipDefaultInterface>true</skipDefaultInterface>
|
||||
<useJakartaEe>true</useJakartaEe>
|
||||
<useBeanValidation>true</useBeanValidation>
|
||||
<withJakartaValidation>true</withJakartaValidation>
|
||||
<openApiNullable>false</openApiNullable>
|
||||
</configOptions>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- <execution>
|
||||
<id>vfs-client</id>
|
||||
<goals>
|
||||
<goal>generate</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<inputSpec>${project.basedir}/src/main/resources/config/vfs/vfs.yaml</inputSpec>
|
||||
<generatorName>java</generatorName>
|
||||
<configOptions>
|
||||
<library>webclient</library>
|
||||
<dateLibrary>java</dateLibrary>
|
||||
<useBeanValidation>true</useBeanValidation>
|
||||
<withJakartaValidation>true</withJakartaValidation>
|
||||
<openApiNullable>false</openApiNullable>
|
||||
<groupId>com.zzyxyz.api</groupId>
|
||||
<artifactId>vfs-client</artifactId>
|
||||
<artifactVersion>1.0.0</artifactVersion>
|
||||
<modelPackage>com.zzyxyz.api.vfs.model</modelPackage>
|
||||
<apiPackage>com.zzyxyz.api.vfs.api</apiPackage>
|
||||
</configOptions>
|
||||
</configuration>
|
||||
</execution> -->
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
@ -2,12 +2,23 @@ package com.zzyxyz.api.bookmarks;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
@ComponentScan(basePackages = {"com.zzyxyz.api.bookmarks", "org.openapitools"})
|
||||
public class BookmarksApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BookmarksApplication.class, args);
|
||||
}
|
||||
|
||||
@GetMapping("/hello")
|
||||
public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
|
||||
return String.format("Hello %s!", name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
43
src/main/java/com/zzyxyz/api/bookmarks/CorsConfig.java
Normal file
43
src/main/java/com/zzyxyz/api/bookmarks/CorsConfig.java
Normal file
@ -0,0 +1,43 @@
|
||||
package com.zzyxyz.api.bookmarks;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
|
||||
@Bean
|
||||
public WebMvcConfigurer corsConfigurer() {
|
||||
return new WebMvcConfigurer() {
|
||||
@Override
|
||||
public void addCorsMappings(@NotNull CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOriginPatterns("*") // 使用allowedOriginPatterns而不是allowedOrigins
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(false); // 设置为false避免冲突
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
config.addAllowedOriginPattern("*"); // 使用addAllowedOriginPattern而不是addAllowedOrigin
|
||||
config.setAllowCredentials(false); // 设置为false避免冲突
|
||||
config.addAllowedMethod("*");
|
||||
config.addAllowedHeader("*");
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
package com.zzyxyz.api.bookmarks.bookmark;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class Bookmark {
|
||||
private Long id;
|
||||
private Long externalId;
|
||||
private String name;
|
||||
private String link;
|
||||
private String detail;
|
||||
private String description;
|
||||
private OffsetDateTime createdAt;
|
||||
private OffsetDateTime updatedAt;
|
||||
|
||||
// Constructors
|
||||
public Bookmark() {}
|
||||
|
||||
public Bookmark(Long externalId, String name, String link, String detail, String description) {
|
||||
this.externalId = externalId;
|
||||
this.name = name;
|
||||
this.link = link;
|
||||
this.detail = detail;
|
||||
this.description = description;
|
||||
this.createdAt = OffsetDateTime.now();
|
||||
this.updatedAt = OffsetDateTime.now();
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getExternalId() {
|
||||
return externalId;
|
||||
}
|
||||
|
||||
public void setExternalId(Long externalId) {
|
||||
this.externalId = externalId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getLink() {
|
||||
return link;
|
||||
}
|
||||
|
||||
public void setLink(String link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
public String getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
public void setDetail(String detail) {
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public OffsetDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(OffsetDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public OffsetDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(OffsetDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,124 @@
|
||||
package com.zzyxyz.api.bookmarks.bookmark;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.time.OffsetDateTime;
|
||||
import org.openapitools.api.DataApi;
|
||||
import org.openapitools.model.BookmarkRequest;
|
||||
import org.openapitools.model.BookmarkResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
|
||||
@Controller
|
||||
@CrossOrigin(origins = "*")
|
||||
public class BookmarkController implements DataApi {
|
||||
|
||||
@Autowired
|
||||
private BookmarkDAO bookmarkDAO;
|
||||
|
||||
// 初始化数据库表
|
||||
public BookmarkController() {
|
||||
try {
|
||||
bookmarkDAO = new BookmarkDAO();
|
||||
bookmarkDAO.createTableIfNotExists();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<BookmarkResponse> createBookmark(Long id, BookmarkRequest bookmarkRequest) {
|
||||
try {
|
||||
// 检查是否已存在相同externalId的书签
|
||||
Bookmark existingBookmark = bookmarkDAO.findByExternalId(id);
|
||||
if (existingBookmark != null) {
|
||||
return ResponseEntity.status(409).body(null);
|
||||
}
|
||||
|
||||
Bookmark bookmark = new Bookmark(
|
||||
id,
|
||||
bookmarkRequest.getName(),
|
||||
bookmarkRequest.getLink(),
|
||||
bookmarkRequest.getDetail(),
|
||||
bookmarkRequest.getDescription()
|
||||
);
|
||||
|
||||
Bookmark savedBookmark = bookmarkDAO.insert(bookmark);
|
||||
BookmarkResponse response = convertToResponse(savedBookmark);
|
||||
return ResponseEntity.status(201).body(response);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> deleteBookmark(Long id) {
|
||||
try {
|
||||
Bookmark bookmark = bookmarkDAO.findById(id);
|
||||
if (bookmark == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
bookmarkDAO.delete(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<BookmarkResponse> getBookmark(Long id) {
|
||||
try {
|
||||
Bookmark bookmark = bookmarkDAO.findById(id);
|
||||
if (bookmark == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
BookmarkResponse response = convertToResponse(bookmark);
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<BookmarkResponse> updateBookmark(Long id, BookmarkRequest bookmarkRequest) {
|
||||
try {
|
||||
Bookmark existingBookmark = bookmarkDAO.findById(id);
|
||||
if (existingBookmark == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
existingBookmark.setName(bookmarkRequest.getName());
|
||||
existingBookmark.setLink(bookmarkRequest.getLink());
|
||||
existingBookmark.setDetail(bookmarkRequest.getDetail());
|
||||
existingBookmark.setDescription(bookmarkRequest.getDescription());
|
||||
existingBookmark.setUpdatedAt(OffsetDateTime.now());
|
||||
|
||||
boolean updated = bookmarkDAO.update(existingBookmark);
|
||||
if (updated) {
|
||||
BookmarkResponse response = convertToResponse(existingBookmark);
|
||||
return ResponseEntity.ok(response);
|
||||
} else {
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
private BookmarkResponse convertToResponse(Bookmark bookmark) {
|
||||
return new BookmarkResponse()
|
||||
.id(bookmark.getId())
|
||||
.name(bookmark.getName())
|
||||
.link(bookmark.getLink())
|
||||
.detail(bookmark.getDetail())
|
||||
.description(bookmark.getDescription())
|
||||
.createdAt(bookmark.getCreatedAt())
|
||||
.updatedAt(bookmark.getUpdatedAt());
|
||||
}
|
||||
}
|
||||
170
src/main/java/com/zzyxyz/api/bookmarks/bookmark/BookmarkDAO.java
Normal file
170
src/main/java/com/zzyxyz/api/bookmarks/bookmark/BookmarkDAO.java
Normal file
@ -0,0 +1,170 @@
|
||||
package com.zzyxyz.api.bookmarks.bookmark;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.sql.*;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class BookmarkDAO {
|
||||
private static final String DB_URL = "jdbc:sqlite:bookmarks.db";
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void createTableIfNotExists() throws SQLException {
|
||||
String sql = """
|
||||
CREATE TABLE IF NOT EXISTS bookmarks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
external_id INTEGER UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
link TEXT,
|
||||
detail TEXT,
|
||||
description TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
)
|
||||
""";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
Statement statement = connection.createStatement()) {
|
||||
statement.executeUpdate(sql);
|
||||
}
|
||||
}
|
||||
|
||||
public Bookmark insert(Bookmark bookmark) throws SQLException {
|
||||
String sql = """
|
||||
INSERT INTO bookmarks (
|
||||
external_id, name, link, detail, description, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||||
|
||||
statement.setLong(1, bookmark.getExternalId());
|
||||
statement.setString(2, bookmark.getName());
|
||||
statement.setString(3, bookmark.getLink());
|
||||
statement.setString(4, bookmark.getDetail());
|
||||
statement.setString(5, bookmark.getDescription());
|
||||
statement.setString(6, bookmark.getCreatedAt().toString());
|
||||
statement.setString(7, bookmark.getUpdatedAt().toString());
|
||||
|
||||
int affectedRows = statement.executeUpdate();
|
||||
|
||||
if (affectedRows == 0) {
|
||||
throw new SQLException("Creating bookmark failed, no rows affected.");
|
||||
}
|
||||
|
||||
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
|
||||
if (generatedKeys.next()) {
|
||||
bookmark.setId(generatedKeys.getLong(1));
|
||||
} else {
|
||||
throw new SQLException("Creating bookmark failed, no ID obtained.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
public Bookmark findById(Long id) throws SQLException {
|
||||
String sql = "SELECT * FROM bookmarks WHERE id = ?";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
|
||||
statement.setLong(1, id);
|
||||
ResultSet rs = statement.executeQuery();
|
||||
|
||||
if (rs.next()) {
|
||||
return mapResultSetToBookmark(rs);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Bookmark findByExternalId(Long externalId) throws SQLException {
|
||||
String sql = "SELECT * FROM bookmarks WHERE external_id = ?";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
|
||||
statement.setLong(1, externalId);
|
||||
ResultSet rs = statement.executeQuery();
|
||||
|
||||
if (rs.next()) {
|
||||
return mapResultSetToBookmark(rs);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Bookmark> findAll() throws SQLException {
|
||||
String sql = "SELECT * FROM bookmarks";
|
||||
List<Bookmark> bookmarks = new ArrayList<>();
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
Statement statement = connection.createStatement();
|
||||
ResultSet rs = statement.executeQuery(sql)) {
|
||||
|
||||
while (rs.next()) {
|
||||
bookmarks.add(mapResultSetToBookmark(rs));
|
||||
}
|
||||
}
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
public boolean update(Bookmark bookmark) throws SQLException {
|
||||
String sql = """
|
||||
UPDATE bookmarks SET
|
||||
name = ?, link = ?, detail = ?, description = ?, updated_at = ?
|
||||
WHERE id = ?
|
||||
""";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
|
||||
statement.setString(1, bookmark.getName());
|
||||
statement.setString(2, bookmark.getLink());
|
||||
statement.setString(3, bookmark.getDetail());
|
||||
statement.setString(4, bookmark.getDescription());
|
||||
statement.setString(5, OffsetDateTime.now().toString());
|
||||
statement.setLong(6, bookmark.getId());
|
||||
|
||||
int affectedRows = statement.executeUpdate();
|
||||
return affectedRows > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean delete(Long id) throws SQLException {
|
||||
String sql = "DELETE FROM bookmarks WHERE id = ?";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
|
||||
statement.setLong(1, id);
|
||||
int affectedRows = statement.executeUpdate();
|
||||
return affectedRows > 0;
|
||||
}
|
||||
}
|
||||
|
||||
private Bookmark mapResultSetToBookmark(ResultSet rs) throws SQLException {
|
||||
Bookmark bookmark = new Bookmark();
|
||||
bookmark.setId(rs.getLong("id"));
|
||||
bookmark.setExternalId(rs.getLong("external_id"));
|
||||
bookmark.setName(rs.getString("name"));
|
||||
bookmark.setLink(rs.getString("link"));
|
||||
bookmark.setDetail(rs.getString("detail"));
|
||||
bookmark.setDescription(rs.getString("description"));
|
||||
bookmark.setCreatedAt(OffsetDateTime.parse(rs.getString("created_at")));
|
||||
bookmark.setUpdatedAt(OffsetDateTime.parse(rs.getString("updated_at")));
|
||||
return bookmark;
|
||||
}
|
||||
}
|
||||
96
src/main/java/com/zzyxyz/api/bookmarks/user_np/UserNP.java
Normal file
96
src/main/java/com/zzyxyz/api/bookmarks/user_np/UserNP.java
Normal file
@ -0,0 +1,96 @@
|
||||
package com.zzyxyz.api.bookmarks.user_np;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
public class UserNP {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String password;
|
||||
private String email;
|
||||
private String token;
|
||||
private OffsetDateTime createdAt;
|
||||
private OffsetDateTime updatedAt;
|
||||
|
||||
// Constructors
|
||||
public UserNP() {
|
||||
this.createdAt = OffsetDateTime.now();
|
||||
this.updatedAt = OffsetDateTime.now();
|
||||
}
|
||||
|
||||
public UserNP(String username, String password, String email) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.email = email;
|
||||
this.createdAt = OffsetDateTime.now();
|
||||
this.updatedAt = OffsetDateTime.now();
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public OffsetDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(OffsetDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public OffsetDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(OffsetDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
// Hash password method (placeholder)
|
||||
public void hashPassword(String rawPassword) {
|
||||
// In a real implementation, you would use a proper password hashing library like BCrypt
|
||||
this.password = rawPassword; // This is just a placeholder
|
||||
}
|
||||
|
||||
// Check password method (placeholder)
|
||||
public boolean checkPassword(String rawPassword) {
|
||||
// In a real implementation, you would use a proper password hashing library like BCrypt
|
||||
return this.password.equals(rawPassword); // This is just a placeholder
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,142 @@
|
||||
package com.zzyxyz.api.bookmarks.user_np;
|
||||
|
||||
import org.openapitools.api.AuthApi;
|
||||
import org.openapitools.api.DefaultApi;
|
||||
import org.openapitools.model.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Controller
|
||||
@CrossOrigin(origins = "*")
|
||||
public class UserNPController implements DefaultApi, AuthApi {
|
||||
|
||||
@Autowired
|
||||
private UserNPDAO userDAO;
|
||||
|
||||
// 初始化数据库表
|
||||
public UserNPController() {
|
||||
try {
|
||||
userDAO = new UserNPDAO();
|
||||
userDAO.createTableIfNotExists();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> deleteUser(String username) {
|
||||
try {
|
||||
UserNP user = userDAO.findByUsername(username);
|
||||
if (user == null) {
|
||||
// 如果用户不存在,按照API规范返回200状态
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
userDAO.delete(user.getId());
|
||||
return ResponseEntity.ok().build();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> updatePassword(String username, ChangePasswordRequest changePasswordRequest) {
|
||||
try {
|
||||
UserNP user = userDAO.findByUsername(username);
|
||||
if (user == null) {
|
||||
return ResponseEntity.status(401).build(); // 认证失败
|
||||
}
|
||||
|
||||
// 验证旧密码
|
||||
if (!user.checkPassword(changePasswordRequest.getOldPassword())) {
|
||||
return ResponseEntity.status(401).build(); // 认证失败
|
||||
}
|
||||
|
||||
// 更新密码
|
||||
user.hashPassword(changePasswordRequest.getNewPassword());
|
||||
user.setUpdatedAt(OffsetDateTime.now());
|
||||
userDAO.update(user);
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<LoginResponse> userLogin(String username, LoginRequest loginRequest) {
|
||||
try {
|
||||
UserNP user = userDAO.findByUsername(username);
|
||||
if (user == null) {
|
||||
return ResponseEntity.status(401).build(); // 认证失败
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (!user.checkPassword(loginRequest.getPassword())) {
|
||||
return ResponseEntity.status(401).build(); // 认证失败
|
||||
}
|
||||
|
||||
// 生成token
|
||||
String token = UUID.randomUUID().toString();
|
||||
user.setToken(token);
|
||||
user.setUpdatedAt(OffsetDateTime.now());
|
||||
userDAO.update(user);
|
||||
|
||||
LoginResponse response = new LoginResponse();
|
||||
response.setToken(token);
|
||||
response.setUserId(user.getId());
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> userRegister(String username, RegisterRequest registerRequest) {
|
||||
try {
|
||||
// 检查用户名是否已存在
|
||||
UserNP existingUser = userDAO.findByUsername(username);
|
||||
if (existingUser != null) {
|
||||
return ResponseEntity.status(409).build(); // 用户名已存在
|
||||
}
|
||||
|
||||
// 创建新用户
|
||||
UserNP user = new UserNP(username, "", registerRequest.getEmail());
|
||||
user.hashPassword(registerRequest.getPassword());
|
||||
|
||||
// 保存到数据库
|
||||
UserNP savedUser = userDAO.insert(user);
|
||||
|
||||
if (savedUser.getId() != null) {
|
||||
return ResponseEntity.status(201).build();
|
||||
} else {
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Object> getUserInfo(String username) {
|
||||
// 未实现
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> saveUserInfo(String username, Object body) {
|
||||
// 未实现
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
166
src/main/java/com/zzyxyz/api/bookmarks/user_np/UserNPDAO.java
Normal file
166
src/main/java/com/zzyxyz/api/bookmarks/user_np/UserNPDAO.java
Normal file
@ -0,0 +1,166 @@
|
||||
package com.zzyxyz.api.bookmarks.user_np;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.sql.*;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class UserNPDAO {
|
||||
private static final String DB_URL = "jdbc:sqlite:users.db";
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void createTableIfNotExists() throws SQLException {
|
||||
String sql = """
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL,
|
||||
email TEXT,
|
||||
token TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
)
|
||||
""";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
Statement statement = connection.createStatement()) {
|
||||
statement.executeUpdate(sql);
|
||||
}
|
||||
}
|
||||
|
||||
public UserNP insert(UserNP user) throws SQLException {
|
||||
String sql = """
|
||||
INSERT INTO users (
|
||||
username, password, email, token, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?)
|
||||
""";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||||
|
||||
statement.setString(1, user.getUsername());
|
||||
statement.setString(2, user.getPassword());
|
||||
statement.setString(3, user.getEmail());
|
||||
statement.setString(4, user.getToken());
|
||||
statement.setString(5, user.getCreatedAt().toString());
|
||||
statement.setString(6, user.getUpdatedAt().toString());
|
||||
|
||||
int affectedRows = statement.executeUpdate();
|
||||
|
||||
if (affectedRows == 0) {
|
||||
throw new SQLException("Creating user failed, no rows affected.");
|
||||
}
|
||||
|
||||
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
|
||||
if (generatedKeys.next()) {
|
||||
user.setId(generatedKeys.getLong(1));
|
||||
} else {
|
||||
throw new SQLException("Creating user failed, no ID obtained.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public UserNP findById(Long id) throws SQLException {
|
||||
String sql = "SELECT * FROM users WHERE id = ?";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
|
||||
statement.setLong(1, id);
|
||||
ResultSet rs = statement.executeQuery();
|
||||
|
||||
if (rs.next()) {
|
||||
return mapResultSetToUser(rs);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public UserNP findByUsername(String username) throws SQLException {
|
||||
String sql = "SELECT * FROM users WHERE username = ?";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
|
||||
statement.setString(1, username);
|
||||
ResultSet rs = statement.executeQuery();
|
||||
|
||||
if (rs.next()) {
|
||||
return mapResultSetToUser(rs);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<UserNP> findAll() throws SQLException {
|
||||
String sql = "SELECT * FROM users";
|
||||
List<UserNP> users = new ArrayList<>();
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
Statement statement = connection.createStatement();
|
||||
ResultSet rs = statement.executeQuery(sql)) {
|
||||
|
||||
while (rs.next()) {
|
||||
users.add(mapResultSetToUser(rs));
|
||||
}
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
public boolean update(UserNP user) throws SQLException {
|
||||
String sql = """
|
||||
UPDATE users SET
|
||||
username = ?, password = ?, email = ?, token = ?, updated_at = ?
|
||||
WHERE id = ?
|
||||
""";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
|
||||
statement.setString(1, user.getUsername());
|
||||
statement.setString(2, user.getPassword());
|
||||
statement.setString(3, user.getEmail());
|
||||
statement.setString(4, user.getToken());
|
||||
statement.setString(5, OffsetDateTime.now().toString());
|
||||
statement.setLong(6, user.getId());
|
||||
|
||||
int affectedRows = statement.executeUpdate();
|
||||
return affectedRows > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean delete(Long id) throws SQLException {
|
||||
String sql = "DELETE FROM users WHERE id = ?";
|
||||
|
||||
try (Connection connection = DriverManager.getConnection(DB_URL);
|
||||
PreparedStatement statement = connection.prepareStatement(sql)) {
|
||||
|
||||
statement.setLong(1, id);
|
||||
int affectedRows = statement.executeUpdate();
|
||||
return affectedRows > 0;
|
||||
}
|
||||
}
|
||||
|
||||
private UserNP mapResultSetToUser(ResultSet rs) throws SQLException {
|
||||
UserNP user = new UserNP();
|
||||
user.setId(rs.getLong("id"));
|
||||
user.setUsername(rs.getString("username"));
|
||||
user.setPassword(rs.getString("password"));
|
||||
user.setEmail(rs.getString("email"));
|
||||
user.setToken(rs.getString("token"));
|
||||
user.setCreatedAt(OffsetDateTime.parse(rs.getString("created_at")));
|
||||
user.setUpdatedAt(OffsetDateTime.parse(rs.getString("updated_at")));
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@ -1 +1,3 @@
|
||||
spring.application.name=bookmarks
|
||||
server.port=8081
|
||||
server.servlet.context-path=/api
|
||||
|
||||
238
src/main/resources/config/bookmark/bookmark.yaml
Normal file
238
src/main/resources/config/bookmark/bookmark.yaml
Normal file
@ -0,0 +1,238 @@
|
||||
openapi: '3.0.3'
|
||||
info:
|
||||
title: zzyxyz_bookmark_api
|
||||
description: bookmark API服务
|
||||
version: '1.0'
|
||||
servers:
|
||||
- url: http://localhost:8081/api
|
||||
description: 开发环境
|
||||
- url: https://api.zzyxyz.com/api
|
||||
description: 生产环境
|
||||
tags:
|
||||
- name: data
|
||||
description: 书签相关操作
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
|
||||
paths:
|
||||
/bookmarks/v1/data/{id}:
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
example: 1
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
|
||||
post:
|
||||
summary: 创建书签
|
||||
description: 在文件夹下创建一个书签
|
||||
operationId: createBookmark
|
||||
tags: [data]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/BookmarkRequest'
|
||||
responses:
|
||||
'201':
|
||||
description: 创建成功的书签
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/BookmarkResponse'
|
||||
'400':
|
||||
description: 请求参数错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
|
||||
get:
|
||||
summary: 获取书签详情
|
||||
description: 通过id获取书签内容
|
||||
operationId: getBookmark
|
||||
tags: [data]
|
||||
responses:
|
||||
'200':
|
||||
description: 书签详情
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/BookmarkResponse'
|
||||
'404':
|
||||
description: 书签不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
|
||||
put:
|
||||
summary: 更新书签
|
||||
description: 更新指定id的书签
|
||||
operationId: updateBookmark
|
||||
tags: [data]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/BookmarkRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 更新后的书签
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/BookmarkResponse'
|
||||
'400':
|
||||
description: 请求参数错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'404':
|
||||
description: 书签不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
|
||||
delete:
|
||||
summary: 删除书签
|
||||
description: 删除指定id的书签
|
||||
operationId: deleteBookmark
|
||||
tags: [data]
|
||||
responses:
|
||||
'204':
|
||||
description: 删除成功
|
||||
'404':
|
||||
description: 书签不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
ApiKeyAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-BookMark-Token
|
||||
responses:
|
||||
ServerInternalError:
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
Unauthorized:
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
Forbidden:
|
||||
description: 无权限
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
schemas:
|
||||
BookmarkRequest:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 255
|
||||
description: 书签名称
|
||||
example: 测试名称
|
||||
link:
|
||||
type: string
|
||||
description: 书签链接
|
||||
example: /swagger/index.html
|
||||
detail:
|
||||
type: string
|
||||
description: 书签详情链接
|
||||
description:
|
||||
type: string
|
||||
description: 书签描述
|
||||
required:
|
||||
- name
|
||||
|
||||
BookmarkResponse:
|
||||
type: object
|
||||
description: 书签相应结构体
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
description: 书签ID
|
||||
name:
|
||||
type: string
|
||||
description: 书签名称
|
||||
link:
|
||||
type: string
|
||||
description: 书签链接
|
||||
detail:
|
||||
type: string
|
||||
description: 书签详情链接
|
||||
description:
|
||||
type: string
|
||||
description: 书签描述
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 创建时间
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 更新时间
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- created_at
|
||||
- updated_at
|
||||
|
||||
Error:
|
||||
type: object
|
||||
description: 错误信息
|
||||
properties:
|
||||
errtype:
|
||||
type: string
|
||||
example: "ParameterError"
|
||||
description: 错误类型
|
||||
message:
|
||||
example: "传递的第一个参数错误"
|
||||
type: string
|
||||
description: 错误信息
|
||||
required:
|
||||
- errtype
|
||||
- message
|
||||
6
src/main/resources/config/bookmark/client.yaml
Normal file
6
src/main/resources/config/bookmark/client.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
# yaml-language-server: ...
|
||||
package: client
|
||||
generate:
|
||||
client: true
|
||||
models: true
|
||||
output: ./gen/bookmarks_client/client.go
|
||||
7
src/main/resources/config/bookmark/server.yaml
Normal file
7
src/main/resources/config/bookmark/server.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
# yaml-language-server: ...
|
||||
package: server
|
||||
generate:
|
||||
gin-server: true
|
||||
models: true
|
||||
strict-server: true
|
||||
output: ./gen/bookmarks_server/server.go
|
||||
7
src/main/resources/config/user_np/server.yaml
Normal file
7
src/main/resources/config/user_np/server.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
# yaml-language-server: ...
|
||||
package: server
|
||||
generate:
|
||||
gin-server: true
|
||||
models: true
|
||||
strict-server: true
|
||||
output: ./gen/user_np_server/server.go
|
||||
210
src/main/resources/config/user_np/user_np.yaml
Normal file
210
src/main/resources/config/user_np/user_np.yaml
Normal file
@ -0,0 +1,210 @@
|
||||
openapi: '3.0.3'
|
||||
info:
|
||||
title: zzyxyz_user_np_api
|
||||
description: 用户节点权限相关操作(user_name and password)
|
||||
version: '1.0'
|
||||
servers:
|
||||
- url: http://localhost:8081/api
|
||||
description: 开发环境
|
||||
- url: https://api.zzyxyz.com/api
|
||||
description: 生产环境
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
|
||||
paths:
|
||||
/auth/user/{username}/login:
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
example: user_name
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
post:
|
||||
summary: 用户登录
|
||||
description: 使用用户名和密码进行登录
|
||||
operationId: UserLogin
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LoginRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 登录成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/LoginResponse'
|
||||
'400':
|
||||
description: 请求参数错误
|
||||
'401':
|
||||
description: 认证失败
|
||||
|
||||
/auth/user/{username}:
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
example: user_name
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
|
||||
post:
|
||||
summary: 用户注册
|
||||
description: 创建新用户账户
|
||||
operationId: UserRegister
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegisterRequest'
|
||||
responses:
|
||||
'201':
|
||||
description: 注册成功
|
||||
'400':
|
||||
description: 请求参数错误
|
||||
'409':
|
||||
description: 用户名已存在
|
||||
|
||||
patch:
|
||||
summary: 修改密码
|
||||
description: 修改已登录用户的密码
|
||||
operationId: updatePassword
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ChangePasswordRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 密码修改成功
|
||||
'400':
|
||||
description: 请求参数错误
|
||||
'401':
|
||||
description: 认证失败
|
||||
|
||||
delete:
|
||||
summary: 删除用户
|
||||
description: 删除用户
|
||||
operationId: deleteUser
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: 用户注销成功
|
||||
'401':
|
||||
description: 认证失败
|
||||
|
||||
/auth/user/{username}/info:
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
example: user_name
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
get:
|
||||
summary: 获取用户信息
|
||||
description: 获取用户信息 json object
|
||||
operationId: getUserInfo
|
||||
tags: [auth]
|
||||
responses:
|
||||
'200':
|
||||
description: 用户信息
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
put:
|
||||
summary: 保存用户信息
|
||||
description: 保存用户信息 json object
|
||||
operationId: saveUserInfo
|
||||
tags: [auth]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
description: 保存成功
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
|
||||
components:
|
||||
responses:
|
||||
ServerInternalError:
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
schemas:
|
||||
LoginRequest:
|
||||
type: object
|
||||
required:
|
||||
- password
|
||||
properties:
|
||||
password:
|
||||
type: string
|
||||
|
||||
LoginResponse:
|
||||
type: object
|
||||
properties:
|
||||
token:
|
||||
type: string
|
||||
user_id:
|
||||
type: integer
|
||||
format: int64
|
||||
|
||||
RegisterRequest:
|
||||
type: object
|
||||
required:
|
||||
- password
|
||||
properties:
|
||||
password:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
|
||||
ChangePasswordRequest:
|
||||
type: object
|
||||
required:
|
||||
- old_password
|
||||
- new_password
|
||||
properties:
|
||||
old_password:
|
||||
type: string
|
||||
new_password:
|
||||
type: string
|
||||
|
||||
Error:
|
||||
type: object
|
||||
description: 错误信息
|
||||
properties:
|
||||
errtype:
|
||||
type: string
|
||||
example: "ParameterError"
|
||||
description: 错误类型
|
||||
message:
|
||||
example: "传递的第一个参数错误"
|
||||
type: string
|
||||
description: 错误信息
|
||||
required:
|
||||
- errtype
|
||||
- message
|
||||
|
||||
securitySchemes:
|
||||
ApiKeyAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: Authorization
|
||||
6
src/main/resources/config/vfs/client.yaml
Normal file
6
src/main/resources/config/vfs/client.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
# yaml-language-server: ...
|
||||
package: client
|
||||
generate:
|
||||
client: true
|
||||
models: true
|
||||
output: ./gen/vfs_client/client.go
|
||||
7
src/main/resources/config/vfs/server.yaml
Normal file
7
src/main/resources/config/vfs/server.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
# yaml-language-server: ...
|
||||
package: server
|
||||
generate:
|
||||
gin-server: true
|
||||
models: true
|
||||
strict-server: true
|
||||
output: ./gen/vfs_server/server.go
|
||||
375
src/main/resources/config/vfs/vfs.yaml
Normal file
375
src/main/resources/config/vfs/vfs.yaml
Normal file
@ -0,0 +1,375 @@
|
||||
openapi: '3.0.3'
|
||||
info:
|
||||
title: zzyxyz_vfs_api
|
||||
description: 虚拟文件系统API服务
|
||||
version: '1.0'
|
||||
servers:
|
||||
- url: http://localhost:8080/api
|
||||
description: 开发环境
|
||||
- url: https://api.zzyxyz.com/api
|
||||
description: 生产环境
|
||||
tags:
|
||||
- name: vfs
|
||||
description: 虚拟文件系统相关操作
|
||||
- name: user
|
||||
description: 用户相关操作
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
|
||||
paths:
|
||||
/vfs/v1/users/{username}:
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
example: user
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
|
||||
post:
|
||||
summary: 创建用户
|
||||
description: 创建一个用户
|
||||
operationId: createUser
|
||||
tags: [user]
|
||||
responses:
|
||||
'201':
|
||||
description: 创建成功
|
||||
headers:
|
||||
X-VFS-Token:
|
||||
schema:
|
||||
type: string
|
||||
description: 认证令牌
|
||||
'400':
|
||||
$ref: '#/components/responses/ParameterError'
|
||||
'401':
|
||||
$ref: '#/components/responses/UnauthorizedError'
|
||||
'403':
|
||||
$ref: '#/components/responses/ForbiddenError'
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
|
||||
delete:
|
||||
summary: 删除用户
|
||||
description: 删除一个用户
|
||||
operationId: deleteUser
|
||||
tags: [user]
|
||||
responses:
|
||||
'204':
|
||||
description: 删除成功
|
||||
'404':
|
||||
description: 用户不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'400':
|
||||
$ref: '#/components/responses/ParameterError'
|
||||
'401':
|
||||
$ref: '#/components/responses/UnauthorizedError'
|
||||
'403':
|
||||
$ref: '#/components/responses/ForbiddenError'
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
|
||||
/vfs/v1/files:
|
||||
parameters:
|
||||
- name: path
|
||||
in: query
|
||||
required: true
|
||||
description: |
|
||||
文件系统路径,例如:
|
||||
- "/documents/readme.txt" (文件路径)
|
||||
- "/services/sql.bk.api" (服务文件)
|
||||
- "/folder/" (目录路径,以/结尾)
|
||||
schema:
|
||||
type: string
|
||||
example: "/documents/readme.txt"
|
||||
|
||||
get:
|
||||
summary: 读取文件或列出目录
|
||||
description: 获取指定路径的文件内容或目录列表
|
||||
operationId: getVFSNode
|
||||
tags: [vfs]
|
||||
parameters:
|
||||
- name: op
|
||||
in: query
|
||||
required: false
|
||||
description: 操作类型, list表示列出目录内容, 不指定则读取文件内容
|
||||
schema:
|
||||
type: string
|
||||
enum: [list]
|
||||
responses:
|
||||
'200':
|
||||
description: 文件内容或目录列表
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
description: 目录条目列表
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/VFSDirectoryEntry'
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
'400':
|
||||
$ref: '#/components/responses/ParameterError'
|
||||
'401':
|
||||
$ref: '#/components/responses/UnauthorizedError'
|
||||
'403':
|
||||
$ref: '#/components/responses/ForbiddenError'
|
||||
'404':
|
||||
$ref: '#/components/responses/PathNotFoundError'
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
|
||||
post:
|
||||
summary: 创建文件或目录
|
||||
description: 创建文件或目录
|
||||
operationId: createVFSNode
|
||||
tags: [vfs]
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
description: 文件内容, 或者服务内容
|
||||
responses:
|
||||
'201':
|
||||
description: 创建成功
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/VFSNodeResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/ParameterError'
|
||||
'401':
|
||||
$ref: '#/components/responses/UnauthorizedError'
|
||||
'403':
|
||||
$ref: '#/components/responses/ForbiddenError'
|
||||
'404':
|
||||
$ref: '#/components/responses/PathNotFoundError'
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
|
||||
patch:
|
||||
summary: 修改文件或修改目录
|
||||
description: 修改/移动/重命名/复制 已存在的 文件/目录
|
||||
operationId: updateVFSNode
|
||||
tags: [vfs]
|
||||
parameters:
|
||||
- name: op
|
||||
in: query
|
||||
description: |
|
||||
更新操作模式:
|
||||
- move: 移动文件或目录到新位置
|
||||
- rename: 重命名文件或目录
|
||||
- change: 修改文件内容或目录属性
|
||||
- copy: 复制文件或目录到新位置
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [move, rename, change, copy]
|
||||
example: "rename"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
description: 目的文件夹路径 / 文件名路径
|
||||
example: "/home/"
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: 操作成功
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/VFSNodeResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/ParameterError'
|
||||
'401':
|
||||
$ref: '#/components/responses/UnauthorizedError'
|
||||
'403':
|
||||
$ref: '#/components/responses/ForbiddenError'
|
||||
'404':
|
||||
$ref: '#/components/responses/PathNotFoundError'
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
|
||||
delete:
|
||||
summary: 删除文件或目录
|
||||
description: 删除指定路径的文件或目录
|
||||
operationId: deleteVFSNode
|
||||
tags: [vfs]
|
||||
parameters:
|
||||
- name: op
|
||||
in: query
|
||||
description: |
|
||||
删除操作模式:
|
||||
- recursive: 递归删除目录及其所有内容
|
||||
- force: 强制删除,忽略只读等保护属性
|
||||
不指定时执行普通删除操作
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
enum: [recursive, force]
|
||||
example: "recursive"
|
||||
responses:
|
||||
'204':
|
||||
description: 删除成功
|
||||
'400':
|
||||
$ref: '#/components/responses/ParameterError'
|
||||
'401':
|
||||
$ref: '#/components/responses/UnauthorizedError'
|
||||
'403':
|
||||
$ref: '#/components/responses/ForbiddenError'
|
||||
'404':
|
||||
$ref: '#/components/responses/PathNotFoundError'
|
||||
'500':
|
||||
$ref: '#/components/responses/ServerInternalError'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
ApiKeyAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-VFS-Token
|
||||
|
||||
responses:
|
||||
ServerInternalError:
|
||||
description: 服务器内部错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
example:
|
||||
errtype: "InternalServerError"
|
||||
message: "服务器内部错误"
|
||||
|
||||
UnauthorizedError:
|
||||
description: 未授权
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
example:
|
||||
errtype: "Unauthorized"
|
||||
message: "访问被拒绝,缺少有效的认证令牌"
|
||||
|
||||
ForbiddenError:
|
||||
description: 禁止
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
example:
|
||||
errtype: "Forbidden"
|
||||
message: "访问被拒绝,您没有权限访问此资源"
|
||||
|
||||
PathNotFoundError:
|
||||
description: 路径不存在
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
example:
|
||||
errtype: "PathNotFound"
|
||||
message: "指定的路径不存在"
|
||||
|
||||
ParameterError:
|
||||
description: 请求参数错误
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
example:
|
||||
errtype: "ParameterError"
|
||||
message: "请求参数无效或缺失"
|
||||
|
||||
schemas:
|
||||
VFSNodeType:
|
||||
type: string
|
||||
description: 节点类型
|
||||
enum: [file, directory, service]
|
||||
|
||||
VFSNodeResponse:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: 节点名称
|
||||
type:
|
||||
$ref: '#/components/schemas/VFSNodeType'
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 创建时间
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 更新时间
|
||||
required:
|
||||
- name
|
||||
- type
|
||||
- created_at
|
||||
- updated_at
|
||||
|
||||
VFSDirectoryEntry:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: 条目名称
|
||||
type:
|
||||
$ref: '#/components/schemas/VFSNodeType'
|
||||
permissions:
|
||||
type: string
|
||||
description: 权限信息,如 "rwo" (读 写 拥有)
|
||||
required:
|
||||
- name
|
||||
- type
|
||||
|
||||
Error:
|
||||
type: object
|
||||
description: 错误信息
|
||||
properties:
|
||||
errtype:
|
||||
type: string
|
||||
description: |
|
||||
错误类型,可能的值包括:
|
||||
- "InternalServerError": 服务器内部错误
|
||||
- "Unauthorized": 客户端需要提供正确格式权限
|
||||
- "Forbidden": 无权限访问
|
||||
- "PathNotFound": 路径不存在
|
||||
- "ParameterError": 请求参数错误
|
||||
- "ConflictError": 资源冲突
|
||||
- "DatabaseError": 数据库操作错误
|
||||
- "NotFoundError": 资源未找到
|
||||
- "AccessDenied": 访问被拒绝
|
||||
- "ServiceProxyError": 代理服务错误
|
||||
example: "InternalServerError"
|
||||
enum:
|
||||
- "InternalServerError"
|
||||
- "Unauthorized"
|
||||
- "Forbidden"
|
||||
- "PathNotFound"
|
||||
- "ParameterError"
|
||||
- "ConflictError"
|
||||
- "DatabaseError"
|
||||
- "NotFoundError"
|
||||
- "AccessDenied"
|
||||
- "ServiceProxyError"
|
||||
message:
|
||||
type: string
|
||||
description: 详细的错误信息
|
||||
example: "传递的第一个参数错误"
|
||||
required:
|
||||
- errtype
|
||||
- message
|
||||
Reference in New Issue
Block a user