Java实现简易图书管理系统

前言

本人的技术笔记优先发布在个人站点longyxblg.cn

本文主要是通过 Java 语言进行手写一个简易的图书管理系统,不采用数据结构。主要完成的是一个简易框架的搭建,业务逻辑仅满足可用并不严谨。

梳理框架

这个简易图书管理系统设计为3个对象互相交互, 分别为:用户、书架、书。 整个系统的维护实际上是对书架进行操作。

用户分为普通用户和管理员用户,区别在于对书架操作的权限不同。

书包含书名,作者名,价格,是否借出这四个属性。

书架用于管理书籍。

用户类

新建一个 User 类, 用户分为普通用户和管理员用户, 这两个用户才是去执行业务的类,所以 User 类设计为一个抽象类。

java 复制代码
package Demo1;

public abstract class User {
//    不需要 User 类去执行业务
    protected String name;
    

    public User() {
        
    }

    public User(String name) {
        this.name = name;
    }
}
java 复制代码
package Demo1;

public class NormalUser extends User {

    public NormalUser(String name) {
        super(name);
    }

    public NormalUser() {
    }
}
java 复制代码
package Demo1;

public class AdminUser extends User{

    public AdminUser(String name) {
        super(name);
    }

    public AdminUser() {
    }
}

此时,需要一个登录方法 login() , 判断用户登录后是普通用户还是管理员。这个方法应不需要依赖对象就可以调用,所以应该被 static 修饰。

java 复制代码
    public static User login(){
//        登录方法不需要借助依赖对象进行调用
        System.out.println("请输入您的用户名:");
        Scanner scanner = new Scanner(System.in);
        String userName = scanner.nextLine();

        System.out.println("请选择您的身份:");
        System.out.println("1.普通用户  2.管理员");
        int choice = scanner.nextInt();
        if(choice == 1){
//            主要目的是设计一个框架,业务并没有处理的很严谨
            return new NormalUser(userName);
        }else {
            return new AdminUser(userName);
        }
    }

然后根据用户类型,会有不同的操作菜单。定义一个 menu() 方法。这个方法在 User 类中是一个抽象方法,具体的实现在 AdminUser 和 NormalUser 类中。

java 复制代码
    public abstract int menu();

在管理员用户中,设计可以进行以下操作:

  • 增加图书

  • 删除图书

  • 修改图书

  • 查找图书

  • 退出系统

java 复制代码
package Demo1;

import java.util.Scanner;

public class AdminUser extends User{

    public AdminUser(String name) {
        super(name);
    }

    public AdminUser() {
    }

    @Override
    public int menu() {
        System.out.println("请选择你要执行的操作:");
        System.out.println("1.新增图书:");
        System.out.println("2.删除图书:");
        System.out.println("3.修改图书:");
        System.out.println("4.查找图书:");
        System.out.println("0.退出系统");

        Scanner scanner = new Scanner(System.in);
//        通过返回的数值来确定执行什么操作
        int choice = scanner.nextInt();
        return choice;
    }
}

普通用户类设计为可以进行以下操作:

  • 展示图书(展示当前书架所有的图书)

  • 借阅图书

  • 归还图书

  • 查找图书

  • 退出系统

java 复制代码
package Demo1;

import java.util.Scanner;

public class NormalUser extends User {

    public NormalUser(String name) {
        super(name);
    }

    public NormalUser() {
    }

    @Override
    public int menu() {
        System.out.println("请选择你要执行的操作:");
        System.out.println("1.展示图书:");
        System.out.println("2.借阅图书:");
        System.out.println("3.归还图书:");
        System.out.println("4.查找图书:");
        System.out.println("0.退出系统");

        Scanner scanner = new Scanner(System.in);
//        通过返回的数值来确定执行什么操作
        int choice = scanner.nextInt();
        return choice;
    }
}

测试一下是否能正常运行

java 复制代码
        User user =  User.login();
        user.menu();

用户类的设计暂时到这里

书本类

书本类的设计较为简单,但是书本的成员变量需要将其封装起来。使用 private 修饰,想要修改查看就通过 get 、 set 方法。

java 复制代码
package Demo1;

public class Book {
    private String book_Name;

    private String author;

    private double price;

    // 判断是否借出
    // true 表示借出
    // false 表示未借出
    private boolean isBorrowed;

    public Book() {

    }

    public Book(String book_Name, String author, double price) {
//        每个 Book 类型对象实例化后默认都是未被借出的
//        boolean 类型不初始化的情况下默认值就是 false
        this.book_Name = book_Name;
        this.author = author;
        this.price = price;
    }

    public String getBook_Name() {
        return book_Name;
    }

    public String getAuthor() {
        return author;
    }

    public double getPrice() {
        return price;
    }

    public void setBook_Name(String book_Name) {
        this.book_Name = book_Name;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public void setBorrowed(boolean borrowed) {
        this.isBorrowed = borrowed;
    }
}

构造方法中不给 isBorrowed 赋值是因为每个 Book 类型对象实例化后默认都是未被借出的,即

isBorrowed 的值是 false 。而 boolean 类型不初始化的情况下默认值就是 false 。

书本类的设计暂时到此。

书架类

用于存放书本,采用数组进行存放

java 复制代码
package Demo1;

public class BookList {
    // 这个简易图书管理系统的维护实际上就是对 BookList 实例化的对象进行操作

    // 默认给一个可以存放 10 个 Book 类型对象的数组
    Book[] booklist = new Book[10];

    public BookList(){
//        在实例化这个书架时,默认里面放三本书
        booklist[0] = new Book("《三体》","刘慈欣",29.99);
        booklist[1] = new Book("《天父地母》","王晋康",19.99);
        booklist[2] = new Book("《逃出母宇宙》","王晋康",9.99);
    }
    
}

书架类的设计暂时到这里。

IOperation

用户做的所有操作都通过实现这个接口完成

java 复制代码
package Demo1;

public interface IOperation {
    void work(Book[] books);
}

FindOperation 类

通过这个类来实现用户操作中的查找图书

java 复制代码
package Demo2;

import Demo1.Book;
import Demo1.IOperation;

public class FindOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("查找图书");
    }
}

AddOperation 类

通过这个类实现管理员用户操作中的新增图书

java 复制代码
package Demo2;

import Demo1.Book;
import Demo1.IOperation;

public class AddOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("新增图书");
    }
}

BorrowOperation 类

通过这个类来实现普通用户操作中的借阅图书

java 复制代码
package Demo2;

import Demo1.Book;
import Demo1.IOperation;

public class BorrowOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("借阅图书");
    }
}

DelOperation 类

通过这个类来实现管理员用户操作中的删除图书

java 复制代码
package Demo2;

import Demo1.Book;
import Demo1.IOperation;

public class DelOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("删除图书");
    }
}

EditOperation 类

通过这个类来实现管理员用户操作中的修改图书

java 复制代码
package Demo2;

import Demo1.Book;
import Demo1.IOperation;

public class EditOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("修改图书");
    }
}

ReturnOperation 类

通过这个类来实现普通用户操作中的归还图书

java 复制代码
package Demo2;

import Demo1.Book;
import Demo1.IOperation;

public class ReturnOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("归还图书");
    }
}

ShowOperation 类

通过这个类来实现普通用户操作中的展示所有图书操作

java 复制代码
package Demo2;

import Demo1.Book;
import Demo1.IOperation;

public class ShowOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("归还所有图书");
    }
}

成员类的修改

在用户类中新增 IOperation 类型的数组,并实现对应的 get 方法

java 复制代码
package Demo1;

import java.util.Scanner;

public abstract class User {
//    不需要 User 类去执行业务
    protected String name;
//    多态的使用
    IOperation[] iOperations;

    public User() {

    }

    public User(String name) {
        this.name = name;
    }

    public static User login(){
//        登录方法不需要借助依赖对象进行调用
        System.out.println("请输入您的用户名:");
        Scanner scanner = new Scanner(System.in);
        String userName = scanner.nextLine();

        System.out.println("请选择您的身份:");
        System.out.println("1.普通用户  2.管理员");
        int choice = scanner.nextInt();
        if(choice == 1){
//            主要目的是设计一个框架,业务并没有处理的很严谨
            return new NormalUser(userName);
        }else {
            return new AdminUser(userName);
        }
    }

    // 根据不同用户类型显示不同的菜单
    public abstract int menu();

    public IOperation[] getiOperations() {
        return iOperations;
    }
}
java 复制代码
package Demo1;

import Demo2.*;

import java.util.Scanner;

public class NormalUser extends User {

    IOperation[] iOperations = {new EixtOperation(),new ShowOperation(),new BorrowOperation(), new ReturnOperation(),new FindOperation()};
    public NormalUser(String name) {
        super(name);
    }

    public NormalUser() {
    }

    @Override
    public int menu() {
        System.out.println("请选择你要执行的操作:");
        System.out.println("1.展示图书:");
        System.out.println("2.借阅图书:");
        System.out.println("3.归还图书:");
        System.out.println("4.查找图书:");
        System.out.println("0.退出系统");

        Scanner scanner = new Scanner(System.in);
//        通过返回的数值来确定执行什么操作
        int choice = scanner.nextInt();
        return choice;
    }

    @Override
    public IOperation[] getiOperations() {
        return this.iOperations;
    }
}
java 复制代码
package Demo1;

import Demo2.*;

import java.util.Scanner;

public class NormalUser extends User {

    IOperation[] iOperations = {new EixtOperation(),new ShowOperation(),new BorrowOperation(), new ReturnOperation(),new FindOperation()};
    public NormalUser(String name) {
        super(name);
    }

    public NormalUser() {
    }

    @Override
    public int menu() {
        System.out.println("请选择你要执行的操作:");
        System.out.println("1.展示图书:");
        System.out.println("2.借阅图书:");
        System.out.println("3.归还图书:");
        System.out.println("4.查找图书:");
        System.out.println("0.退出系统");

        Scanner scanner = new Scanner(System.in);
//        通过返回的数值来确定执行什么操作
        int choice = scanner.nextInt();
        return choice;
    }

    @Override
    public IOperation[] getiOperations() {
        return this.iOperations;
    }
}
java 复制代码
package Demo1;

import Demo2.*;

import java.util.Scanner;

public class AdminUser extends User{

    IOperation[] iOperations = {new EixtOperation(),new AddOperation(),new DelOperation(),new EditOperation() , new FindOperation()};
    public AdminUser(String name) {
        super(name);
    }

    public AdminUser() {
    }

    @Override
    public int menu() {
        System.out.println("请选择你要执行的操作:");
        System.out.println("1.新增图书:");
        System.out.println("2.删除图书:");
        System.out.println("3.修改图书:");
        System.out.println("4.查找图书:");
        System.out.println("0.退出系统");

        Scanner scanner = new Scanner(System.in);
//        通过返回的数值来确定执行什么操作
        int choice = scanner.nextInt();
        return choice;
    }

    @Override
    public IOperation[] getiOperations() {
        return this.iOperations;
    }
}

接下来就来测试是否能够成功调用对应的方法

java 复制代码
package Demo2;

import Demo1.BookList;
import Demo1.IOperation;
import Demo1.User;

public class Test {
    public static void main(String[] args) {
        User user =  User.login();
        BookList bookList = new BookList();

        while(true){
            int choice = user.menu();
            IOperation[] iOperations = user.getiOperations();
            iOperations[choice].work(bookList.getBooklist());
        }
    }

可以看到确实是能达到预期中的效果,接下来就是具体实现对应的方法。

FindOperation 方法的实现

java 复制代码
    public void work(Book[] books) {
        System.out.println("查找图书");
    }

逻辑是通过书名来进行查找,遍历整个 books 。先给 Book 类重写一个 toString 方法。

java 复制代码
    @Override
    public String toString() {
        return "Book{" +
                "book_Name='" + book_Name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                (isBorrowed ? ",已借出" : ",未借出") +
                '}';
    }

测试一下

java 复制代码
        Book book = new Book("《三体》","刘慈欣",29.99);
        System.out.println(book);

此时给 BookList 类中新增一个属性 usedSize 表示这个数组使用的空间长度,同时为这个属性写一个 get 方法。

java 复制代码
package Demo1;

public class BookList {
    // 这个简易图书管理系统的维护实际上就是对 BookList 实例化的对象进行操作

    // 默认给一个可以存放 10 个 Book 类型对象的数组
    Book[] booklist = new Book[10];

    int usedSize = 3;
    public BookList(){
//        在实例化这个书架时,默认里面放三本书
        booklist[0] = new Book("《三体》","刘慈欣",29.99);
        booklist[1] = new Book("《天父地母》","王晋康",19.99);
        booklist[2] = new Book("《逃出母宇宙》","王晋康",9.99);
    }

    public Book[] getBooklist() {
        return booklist;
    }

    public int getUsedSize() {
        return usedSize;
    }
}

逻辑是遍历数组,通过 name 这个成员来查找图书。

java 复制代码
package Demo2;

import Demo1.Book;
import Demo1.BookList;
import Demo1.IOperation;

import java.util.Scanner;

public class FindOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("请输入你要查找的书籍名称:");
        Scanner scanner = new Scanner(System.in);
        String name = scanner.nextLine();
        int currentSize = bookList.getUsedSize();
        for (int i = 0; i < currentSize; i++) {
                if(name.equals(bookList.getBooklist()[i].getBook_Name())){
//                    bookList.getBooklist() 拿到存放 book 的数组
//                    bookList.getBooklist()[i] 拿到下标为 i 的 book 对象
//                      再用 getBook_Name() 拿到书名
                    System.out.println("找到了");
                    System.out.println(bookList.getBooklist()[i]);
                    return;
            }
        }
//        出来说明没找到
        System.out.println("当前没有存放你想要查找的书籍");
    }
}

测试一下

ShowOperation 方法的实现

遍历数组,将每个对象都打印出来即可

java 复制代码
package Demo2;


import Demo1.BookList;
import Demo1.IOperation;



public class ShowOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("展示所有图书");
        int currentSize = bookList.getUsedSize();
        for (int i = 0; i < currentSize; i++) {
//                   bookList.getBooklist() 拿到存放 book 的数组
//                   bookList.getBooklist()[i] 拿到下标为 i 的 book 对象
            System.out.println(bookList.getBooklist()[i]);
        }
    }
}

BorrowOperation 方法的实现

给 Book 类中新增一个获取借阅状态的方法

java 复制代码
    public boolean isBorrowed() {
        return isBorrowed;
    }

借出书籍也是根据书籍名称来查找,找到了判断借出状态,如果是未借出状态就修改借出状态。否则告知已借出。没找到也告知。

java 复制代码
package Demo2;

import Demo1.BookList;
import Demo1.IOperation;

import java.util.Scanner;

public class BorrowOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("请输入你要借阅的书籍名称:");
        Scanner scanner = new Scanner(System.in);
        String name = scanner.nextLine();
        int currentSize = bookList.getUsedSize();
        for (int i = 0; i < currentSize; i++) {
            if (name.equals(bookList.getBooklist()[i].getBook_Name())) {
//                    bookList.getBooklist() 拿到存放 book 的数组
//                    bookList.getBooklist()[i] 拿到下标为 i 的 book 对象
//                      再用 isBorrowed() 拿到借阅状态
                if(bookList.getBooklist()[i].isBorrowed()){
                    System.out.println("该书已借出");
                }else {
                    System.out.println("借阅成功!");
                    bookList.getBooklist()[i].setBorrowed(true);
                }
                return;
            }
        }
//        出来说明没找到
        System.out.println("当前没有存放你需要的书籍");
    }
}

测试一下

ReturnOperation 方法的实现

根据书籍名称来查找,找到了判断借出状态,如果是已借出状态就修改借出状态。否则告知未借出。没找到也告知。

java 复制代码
package Demo2;


import Demo1.BookList;
import Demo1.IOperation;

import java.util.Scanner;

public class ReturnOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("请输入你要归还的书籍名称:");
        Scanner scanner = new Scanner(System.in);
        String name = scanner.nextLine();
        int currentSize = bookList.getUsedSize();
        for (int i = 0; i < currentSize; i++) {
            if (name.equals(bookList.getBooklist()[i].getBook_Name())) {
//                    bookList.getBooklist() 拿到存放 book 的数组
//                    bookList.getBooklist()[i] 拿到下标为 i 的 book 对象
//                      再用 isBorrowed() 拿到借阅状态
                if(bookList.getBooklist()[i].isBorrowed()){
                    System.out.println("归还成功!");
                    bookList.getBooklist()[i].setBorrowed(false);
                }else {
                    System.out.println("该书未借出");
                }
                return;
            }
        }
//        出来说明没找到
        System.out.println("当前系统没有存放该书籍");
    }
}

测试

EixtOperation 方法的实现

直接退出就可以了

java 复制代码
package Demo2;

import Demo1.BookList;
import Demo1.IOperation;

public class EixtOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("退出系统");
        System.exit(0);
    }
}

测试

AddOperation 方法的实现

采用尾插的方式添加书籍,直接在 usedSize 处插入即可,插入后要让 usedSize + 1。在 BookList 类中添加一个 set 方法。

java 复制代码
    public void setUsedSize(int usedSize) {
        this.usedSize = usedSize;
    }

在添加的时候要判断书架还能不能放得下

java 复制代码
package Demo2;

import Demo1.Book;
import Demo1.BookList;
import Demo1.IOperation;

import java.util.Scanner;

public class AddOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        if(bookList.getUsedSize() == 10){
            System.out.println("当前书架已满!");
            return;
        }
        System.out.println("请输入你要新增的书籍名称");
        Scanner scanner = new Scanner(System.in);
        String name = scanner.nextLine();

        System.out.println("请输入你要新增的书籍作者名");
        String author = scanner.nextLine();

        System.out.println("请输入你要新增的书籍价格");
        double price = scanner.nextDouble();

        Book newBook = new Book(name,author,price);
        int currentSize = bookList.getUsedSize();
        // 尾插进去
        bookList.getBooklist()[currentSize] = newBook;
        // 插入后让 usedSize + 1
        bookList.setUsedSize(++currentSize);
        System.out.println("插入成功!");
    }
}

给 AdminUser 类也加一个展示图书的方法方便查看

java 复制代码
    IOperation[] iOperations = {new EixtOperation(),new AddOperation(),new DelOperation(),new EditOperation() , new FindOperation(),new ShowOperation()};

    public int menu() {
        System.out.println("请选择你要执行的操作:");
        System.out.println("1.新增图书:");
        System.out.println("2.删除图书:");
        System.out.println("3.修改图书:");
        System.out.println("4.查找图书:");
        System.out.println("5.展示图书:");
        System.out.println("0.退出系统");

        Scanner scanner = new Scanner(System.in);
//        通过返回的数值来确定执行什么操作
        int choice = scanner.nextInt();
        return choice;
    }
java 复制代码
package Demo2;

import Demo1.Book;
import Demo1.BookList;
import Demo1.IOperation;

import java.util.Scanner;

public class AddOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        if(bookList.getUsedSize() == 10){
            System.out.println("当前书架已满!");
            return;
        }
        System.out.println("请输入你要新增的书籍名称");
        Scanner scanner = new Scanner(System.in);
        String name = scanner.nextLine();

        System.out.println("请输入你要新增的书籍作者名");
        String author = scanner.nextLine();

        System.out.println("请输入你要新增的书籍价格");
        double price = scanner.nextDouble();

        Book newBook = new Book(name,author,price);
        int currentSize = bookList.getUsedSize();
        // 尾插进去
        bookList.getBooklist()[currentSize] = newBook;
        // 插入后让 usedSize + 1
        bookList.setUsedSize(++currentSize);
        System.out.println("插入成功!");
    }
}

测试

EditOperation 方法的实现

也是通过 name 查找要修改的对象,查找到了再进行修改

java 复制代码
package Demo2;

import Demo1.BookList;
import Demo1.IOperation;

import java.util.Scanner;

public class EditOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        System.out.println("请输入你要修改的书籍名称:");
        Scanner scanner = new Scanner(System.in);
        String name = scanner.nextLine();
        int currentSize = bookList.getUsedSize();
        for (int i = 0; i < currentSize; i++) {
            if(name.equals(bookList.getBooklist()[i].getBook_Name())){
//                    bookList.getBooklist() 拿到存放 book 的数组
//                    bookList.getBooklist()[i] 拿到下标为 i 的 book 对象

                System.out.println("请输入修改后的书籍名称");
                String book_name = scanner.nextLine();

                System.out.println("请输入修改后的作者名");
                String author = scanner.nextLine();

                System.out.println("请输入修改后的价格");
                double price = scanner.nextDouble();

                bookList.getBooklist()[i].setBook_Name(book_name);
                bookList.getBooklist()[i].setAuthor(author);
                bookList.getBooklist()[i].setPrice(price);
                System.out.println("修改成功!");
                return;
            }
        }
//        出来说明没找到
        System.out.println("当前没有存放你想要修改的书籍");
    }
}

测试

DelOperation 方法的实现

也是通过 name 查找要删除的数据, 找到了将这个位置的后的数据整体向前移动一格, 然后让 usedSize - 1

java 复制代码
package Demo2;

import Demo1.Book;
import Demo1.BookList;
import Demo1.IOperation;

import java.util.Scanner;

public class DelOperation implements IOperation {
    @Override
    public void work(BookList bookList) {
        if(bookList.getUsedSize() == 0){
            System.out.println("当前书架已空!");
            return;
        }
        System.out.println("请输入你要删除的书籍名称:");
        Scanner scanner = new Scanner(System.in);
        String name = scanner.nextLine();
        int currentSize = bookList.getUsedSize();
        for (int i = 0; i < currentSize; i++) {
            if(name.equals(bookList.getBooklist()[i].getBook_Name())){
//                    bookList.getBooklist() 拿到存放 book 的数组
//                    bookList.getBooklist()[i] 拿到下标为 i 的 book 对象

                for (int j = i; j < currentSize - 1; j++) {
                    // 让删除的位置后的数据整体向前移动一位进行覆盖
                    bookList.getBooklist()[j] = bookList.getBooklist()[j + 1];
                }
//                删除后让 usedSize - 1
                bookList.setUsedSize(--currentSize);
                System.out.println("删除完成!");
                return;
            }
        }
//        出来说明没找到
        System.out.println("当前没有存放你想要删除的书籍");

    }
}

测试

这是测试用的代码

java 复制代码
        User user =  User.login();
        BookList bookList = new BookList();

        while(true){
            int choice = user.menu();
            IOperation[] iOperations = user.getiOperations();
            iOperations[choice].work(bookList);
        }

到底为止,这个简易的图书管理系统就完成了。本文旨在完成一个简单的逻辑,业务处理只是可用即可并不严谨(例如没有验证输入合法性等)。

相关推荐
重生之小比特1 小时前
【C++进阶】02 Linux基本指令
java·数据库·c++
Wang's Blog1 小时前
Java 项目实战: 外卖平台优化-Nginx常用命令与环境变量配置
java·网络·nginx
sp42a1 小时前
Java 加解密组件再设计
java
FYKJ_20101 小时前
springboot家政服务平台66766-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·mysql·spark·课程设计
caoerzhong1 小时前
开源 WMS 合规交付实务:JeeWMS 仓库管理系统在 GPL-3.0 下的源码台账与交付清单
java·开源
x-Achan1 小时前
Redis实现方案:更新策略、穿透、雪崩、击穿、工具封装
java·spring boot·redis·spring·bootstrap·mybatis
蜗牛互联网2 小时前
Java 17调用gpt-transcribe实现会议录音转写与术语提示
java·人工智能·后端
吠品2 小时前
DicomViewer24 的 CI 又红了:MPR 测试偶发失败排查记
java·服务器·前端
Wang's Blog2 小时前
Java 项目实战: 外卖平台优化-Nginx概述与源码编译安装
java·开发语言·nginx