Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Building a Fuling District Specialty Agricultural Products Trading System with Spring Boot, Vue.js, and UniApp

Tech 2

Introduction

This article details the development of a trading system for specialty agricultural products in the Fuling District, utilizing a modern technology stack including Spring Boot for the backend, Vue.js for the web frontend, and UniApp for cross-platform mobile applications.

Technology Stack

Backend Framework: Spring Boot

Spring Boot is a development framework based on the Spring Framework, offering numerous advantages. It includes built-in servers like Tomcat, Jetty, and Undertow, eliminating the need for additional installation and configuration. A key feature is its powerful auto-configuration capability, which automatically configures the application based on project dependencies, greatly simplifying development. Spring Boot also provides a rich set of out-of-the-box features and plugins, such as Spring Data, Spring Security, and Spring Cloud, enabling developers to build applications more quickly and easily integrate other technologies. Additionally, it offers flexible configuration management, rapid development and deployment, strong community support, monitoring and diagnostic tools, and reliable testing support. These benefits make Spring Boot a popular framework for building high-quality applications with good configurability, scalability, and maintainability.

Below is a sample core code snippet:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class ApplicationEntryPoint {

    public static void main(String[] args) {
        SpringApplication.run(ApplicationEntryPoint.class, args);
    }

    @GetMapping("/greeting")
    public String getGreeting() {
        return "Welcome to the System!";
    }
}

This code defines a Spring Boot application entry class ApplicationEntryPoint, marked with @SpringBootApplication as a Spring Boot application and @RestController as a RESTful controller. The getGreeting method is mapped to the "/greeting" path using @GetMapping, returning a simple string response. After starting the application with SpringApplication.run, Spring Boot automatically configures and launches the embedded server, allowing access via "http://localhost:8080/greeting".

Frontend Framework: Vue.js

Vue.js is a popular JavaScript framework known for its advantages, including virtual DOM technology. The virtual DOM is an in-memory data structure that facilitates efficient DOM operations. Vue.js employs modern techniques like reactive data binding, virtual DOM, and componentization, providing a flexible, efficient, and maintainable development model. When data changes, Vue.js automatically updates the UI, allowing developers to focus on data processing without manual UI updates.

Here is an example demonstrating Vue.js core functionality:

<!DOCTYPE html>
<html>
<head>
  <title>Vue.js Demo</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    <h2>{{ message }}</h2>
    <button @click="updateMessage">Update Message</button>
  </div>

  <script>
    var app = new Vue({
      el: '#app',
      data: {
        message: 'Hello, Vue.js!'
      },
      methods: {
        updateMessage: function() {
          this.message = 'Vue.js is powerful!';
        }
      }
    });
  </script>
</body>
</html>

In this example, a Vue instance is created and bound to an element with id "app". The data property defines a variable message initialized to "Hello, Vue.js!". The page displays message using double curly braces ({{ message }}). The methods property defines an updateMessage method that changes message when the button is clicked. Due to Vue.js's reactive data binding, the displayed content updates automatically when message changes.

Persistence Layer Framework: MyBatis

MyBatis is an open-source persistence layer framework that simplifies database operations. Its core concept is separating SQL statements from Java code, using XML or annotations to describe database operations, thereby decoupling the data access layer and enhancing flexibility. Key advantages include:

  1. Simplified database operations: MyBatis provides powerful SQL mapping, allowing Java objects to be mapped to database tables without manual SQL writing.
  2. Flexible SQL control: It supports dynamic SQL, enabling generation of SQL statements based on different conditions and logic.
  3. Cache support: MyBatis offers first-level and second-level caching to reduce database access and improve performance.
  4. High extensibility: Its plugin mechanism allows easy customization to meet various business needs.

System Testing

To ensure the highest quality standards, comprehensive testing was conducted on the system from multiple perspectives to identify and address issues, ensuring integrity and reliability.

Purpose of System Testing

System testing is a critical phase in the development lifecycle, serving as the final checkpoint for quality and reliability. Its main goal is to prevent user issues and enhance the user experience by simulating various scenarios to detect and resolve defects. Testing validates that the system meets requirements and identifies discrepancies, focusing on user-centric scenarios to ensure expected outcomes match actual results.

Functional Testing

Functional testing involves testing system modules using methods like clicking, inputting boundary values, and validating required and optional fields through black-box testing. Test cases are written and executed to draw conclusions.

For example, login functionality was tested by verifying account passwords against database data. If inputs are incorrect, the system prompts errors. Role permissions are also validated, with errors for unauthorized roles. Below is a sample test case table for login:

Input Data Expected Result Actual Result Result Analysis
Username: admin, Passsword: 123456, Captcha: correct Login successful Login successful Consistent
Username: admin, Password: 111111, Captcha: correct Password error Password error, please re-enter Consistent
Username: admin, Password: 123456, Captcha: incorrect Captcha error Captcha error Consistent
Username: empty, Password: 123456, Captcha: correct Username required Please enter username Consistent
Username: admin, Password: empty, Captcha: correct Password error Password error, please re-enter Consistent

User management functions, including add, edit, delete, and search, were also tested with similar case tables.

Testing Conclusion

Black-box testing was primari used, simulating user interactions to validate system workflows. Testing is essential for system refinement and usability. The process confirmed that the system's functional modules meet design specifications, with logic correctly implemented for user-friendly operation. All scenarios aligned with user requirements, and final results indicate the system satisfeis functional and performance design criteria.

Code Reference

Below is a Java code snippet for login functionality, including token generation and authorization interception:

// Annotation to ignore authentication
@IgnoreAuth
@PostMapping(value = "/authenticate")
public Response loginUser(String username, String password, String captcha, HttpServletRequest request) {
   // Retrieve user information
   UserEntity user = userService.fetchUser(new EntityWrapper<UserEntity>().eq("username", username));
   // Validate user existence and password
   if(user == null || !user.getPassword().equals(password)) {
      return Response.error("Incorrect username or password");
   }
   // Generate token
   String token = tokenService.createToken(user.getId(), username, "users", user.getRole());
   return Response.ok().addData("token", token);
}

// Token generation method
@Override
public String createToken(Long userId, String username, String tableName, String role) {
   // Check for existing token
   TokenEntity tokenRecord = this.fetchToken(new EntityWrapper<TokenEntity>().eq("userId", userId).eq("role", role));
   // Generate random token string
   String token = Utility.generateRandomString(32);
   // Set token expiration to 1 hour from now
   Calendar calendar = Calendar.getInstance();   
   calendar.setTime(new Date());   
   calendar.add(Calendar.HOUR_OF_DAY, 1);
   if(tokenRecord != null) {
      // Update existing token
      tokenRecord.setToken(token);
      tokenRecord.setExpirationTime(calendar.getTime());
      this.updateById(tokenRecord);
   } else {
      // Insert new token record
      this.insert(new TokenEntity(userId, username, tableName, role, token, calendar.getTime()));
   }
   return token;
}

/**
 * Authorization interceptor for token validation
 */
@Component
public class AuthInterceptor implements HandlerInterceptor {

    // Key for token in request header
    public static final String TOKEN_HEADER_KEY = "Authorization";

    @Autowired
    private TokenService tokenService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // Enable CORS
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,Authorization, Origin,imgType, Content-Type, cache-control,postman-token,Cookie, Accept,authorization");
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));

        // Handle preflight OPTIONS requests
        if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
            response.setStatus(HttpStatus.OK.value());
            return false;
        }
        
        // Retrieve @IgnoreAuth annotation
        IgnoreAuth annotation;
        if (handler instanceof HandlerMethod) {
            annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
        } else {
            return true;
        }

        // Extract token from header
        String token = request.getHeader(TOKEN_HEADER_KEY);
        
        // Bypass methods without authentication requirement
        if(annotation != null) {
            return true;
        }
        
        // Fetch token entity based on token
        TokenEntity tokenEntity = null;
        if(StringUtils.isNotBlank(token)) {
            tokenEntity = tokenService.getTokenByValue(token);
        }
        
        if(tokenEntity != null) {
            // Store user info in session
            request.getSession().setAttribute("userId", tokenEntity.getUserId());
            request.getSession().setAttribute("role", tokenEntity.getRole());
            request.getSession().setAttribute("tableName", tokenEntity.getTableName());
            request.getSession().setAttribute("username", tokenEntity.getUsername());
            return true;
        }
        
        // Authentication failed, return 401 error
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(JSONObject.toJSONString(Response.error(401, "Please log in first")));
        } finally {
            if(writer != null){
                writer.close();
            }
        }
        return false;
    }
}

This code implements basic login functionality with token-based authorization, ensuring only users with valid tokens can access restricted resources.

Database Reference

An example SQL statement for designing a product table:

-- ----------------------------
-- Table structure for product
-- ----------------------------
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
  `name` varchar(100) NOT NULL COMMENT 'Product Name',
  `price` decimal(10, 2) NOT NULL COMMENT 'Product Price',
  `description` varchar(200) DEFAULT NULL COMMENT 'Product Description',
  `stock` int(11) NOT NULL COMMENT 'Product Stock',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation Time',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Product Table';

This product table includes fields for id, name, price, description, stock, create_time, and update_time.

Sample data insertion:

INSERT INTO `product` (`name`, `price`, `description`, `stock`)
VALUES ('Apple iPhone 13', 999.99, 'A high-performance smartphone', 100);

INSERT INTO `product` (`name`, `price`, `description`, `stock`)
VALUES ('Samsung Galaxy S21', 899.99, 'A flagship Android device', 150);

INSERT INTO `product` (`name`, `price`, `description`, `stock`)
VALUES ('Sony PlayStation 5', 499.99, 'Next-generation gaming console', 50);
Tags: Spring Boot

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.