Building a Travel Website with SSM, Vue.js, and Uniapp

Technology Stack

Backend Framework: SSM

The SSM framework integrates Spring, Spring MVC, and MyBatis to construct robust Java web applications. Each component serves a distinct purpose:

  1. Spring Framework: A lightweight framework offering dependency injection, aspect-oriented programming (AOP), and transaction management. Its core container manages application objects, clarifying dependencies and reducing coupling.
  2. Spring MVC: A module within Spring for building web applications based on the Model-View-Controller (MVC) pattern. It separates business logic (Model), data presentation (View), and request handling (Controller).
  3. MyBatis: A persistence framwork that simplifies database interactions. It maps Java objects to database tables via configuration files and uses SQL for data operations, providing an elegant way to manage database access code within Spring.

SSM's advantages include high flexibility, simple configuration, and ease of use, enabling rapid development of stable and efficient applications.

Example core code:

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 ApplicationLauncher {

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

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

This code defines a Spring Boot application entry point. The @SpringBootApplication annotation marks it as a Spring Boot app, and @RestController indicates it's a REST controller. The sendGreeting method, mapped to "/greeting" via @GetMapping, returns a string response. Running the application starts an embedded server, allowing access to the endpoint.

Frontend Framework: Vue.js

Vue.js is a progressive JavaScript framework renowned for its virtual DOM technology, which enables efficient DOM manipulations. It employs reactive data binding and a component-based architecture, allowing automatic UI updates when data changes and letting developers focus on logic.

Example demonstrating core functionality:

<html>
<head>
  <title>Vue.js Example</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <div id="application">
    <h2>{{ greetingText }}</h2>
    <button @click="updateGreeting">Update Text</button>
  </div>

  <script>
    var vm = new Vue({
      el: '#application',
      data: {
        greetingText: 'Hello, Vue!'
      },
      methods: {
        updateGreeting: function() {
          this.greetingText = 'Vue is powerful!';
        }
      }
    });
  </script>
</body>
</html>

This creates a Vue instance bound to the #application element. The data property defines a reactive greetingText variable. The double curly braces {{ greetingText }} display its value. Clicking the button triggers the updateGreeting method, which changes the text, and Vue automatically updates the display.

Persistance Layer: MyBatis

MyBatis is an open-source persistence framework that decouples SQL from Java code using XML or annotations. Its benefits include:

  1. Simplified database operations through object-relational mapping.
  2. Dynamic SQL support for flexible query generation.
  3. Caching mechanisms (first and second level) to enhance performance.
  4. A plugin architecture for easy extensibility.

System Testing

Comprehensive testing was conducted to ensure system quality, reliability, and user satisfaction. The process aimed to identify and rectify defects from multiple perspectives.

Testing Objectives

System testing validates that the aplication meets specified requirements. It simulates user scenarios to uncover issues, assesses functionality completeness and logical flow, and ensures the final product aligns with user expectations, thereby enhancing quality and experience.

Functional Testing

Black-box testing was performed on functional modules using techniques like input validation, boundary value analysis, and checks for required fields. Test cases were designed and executed.

Login Function Test Example:

Input Data Expected Result Actual Result Analysis
Username: admin, Password: 123456, Correct CAPTCHA Login successful Login successful Pass
Username: admin, Password: wrong, Correct CAPTCHA Password error Password error message displayed Pass
Username: admin, Password: 123456, Wrong CAPTCHA CAPTCHA error CAPTCHA error message displayed Pass
Username: (empty), Password: 123456, Correct CAPTCHA Username required Username required message displayed Pass

User Management Tests covered adding, editing, deleting, and searching users.

Add User Test Example:

Input Data Expected Result Actual Result Analysis
Username: userA, Password: 123456, Role: User User added successfully User appears in list Pass
Username: userA, Password: 123456, Role: User Add failed (duplicate) Duplicate username error Pass
Username: (empty), Password: 123456, Role: User Add failed (empty name) Empty username error Pass

Testing Conclusion

Black-box testing confirmed correct system workflows. The process ensured the system meets design specifications with straightforward logic for ease of use. The final application satisfies functional and performance requirements.

Code Implementation Example

// Bypass auth verification annotation
@IgnoreAuth
@PostMapping(value = "/authenticate")
public ApiResponse userLogin(String userIdentifier, String userSecret, String captcha, HttpServletRequest req) {
    // Retrieve user by username
    UserAccount user = accountService.selectOne(new EntityWrapper<UserAccount>().eq("username", userIdentifier));
    // Validate user existence and password
    if(user == null || !user.getPassword().equals(userSecret)) {
        return ApiResponse.error("Invalid credentials");
    }
    // Generate access token
    String accessToken = tokenProvider.createToken(user.getId(), userIdentifier, "users", user.getRole());
    return ApiResponse.ok().put("accessToken", accessToken);
}

// Token generation logic
@Override
public String createToken(Long userId, String username, String table, String role) {
    // Check for existing token
    AuthToken existingToken = this.selectOne(new EntityWrapper<AuthToken>().eq("userid", userId).eq("role", role));
    String newToken = RandomStringUtils.randomAlphanumeric(32);
    Calendar expiration = Calendar.getInstance();
    expiration.add(Calendar.HOUR, 1); // Expire in 1 hour

    if(existingToken != null) {
        // Update existing token
        existingToken.setToken(newToken);
        existingToken.setExpiration(expiration.getTime());
        this.updateById(existingToken);
    } else {
        // Insert new token record
        this.insert(new AuthToken(userId, username, table, role, newToken, expiration.getTime()));
    }
    return newToken;
}

/**
 * Authentication Interceptor
 */
@Component
public class AuthInterceptor implements HandlerInterceptor {
    public static final String AUTH_HEADER = "Authorization";

    @Autowired
    private TokenService tokenService;

    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        // Configure CORS headers
        resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
        resp.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        resp.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Requested-With");
        resp.setHeader("Access-Control-Allow-Credentials", "true");

        // Handle preflight OPTIONS request
        if (req.getMethod().equals("OPTIONS")) {
            resp.setStatus(HttpStatus.OK.value());
            return false;
        }

        // Check for @IgnoreAuth annotation
        IgnoreAuth authAnnotation;
        if (handler instanceof HandlerMethod) {
            authAnnotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
        } else {
            return true;
        }

        // Skip auth for annotated methods
        if(authAnnotation != null) {
            return true;
        }

        // Extract token from header
        String token = req.getHeader(AUTH_HEADER);
        AuthToken tokenEntity = null;
        if(StringUtils.isNotEmpty(token)) {
            tokenEntity = tokenService.getTokenEntity(token);
        }

        if(tokenEntity != null) {
            // Store user context in session
            req.getSession().setAttribute("userId", tokenEntity.getUserid());
            req.getSession().setAttribute("userRole", tokenEntity.getRole());
            return true;
        }

        // Authentication failed
        resp.setStatus(HttpStatus.UNAUTHORIZED.value());
        resp.setContentType("application/json; charset=utf-8");
        try (PrintWriter out = resp.getWriter()) {
            out.print(JSONObject.toJSONString(ApiResponse.error(401, "Authentication required")));
        }
        return false;
    }
}

This code handles user authentication. The /authenticate endpoint validates credentials and issues a token. The AuthInterceptor checks for a valid token in the Authorization header for protected routes, granting access if valid or returning a 401 error if not. Methods annotated with @IgnoreAuth bypass this check.

Database Schema Example

Design for a product table:

-- Product table structure
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
  `product_name` varchar(100) NOT NULL COMMENT 'Product Name',
  `unit_price` decimal(10, 2) NOT NULL COMMENT 'Price',
  `product_desc` varchar(200) DEFAULT NULL COMMENT 'Description',
  `inventory` int(11) NOT NULL COMMENT 'Stock Quantity',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation Time',
  `modified_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Last Update Time',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Product Information';

-- Sample data insertion
INSERT INTO `product` (`product_name`, `unit_price`, `product_desc`, `inventory`)
VALUES ('iPhone 13', 999.99, 'Advanced smartphone', 100);

INSERT INTO `product` (`product_name`, `unit_price`, `product_desc`, `inventory`)
VALUES ('Samsung Galaxy S21', 899.99, 'Flagship Android phone', 150);

INSERT INTO `product` (`product_name`, `unit_price`, `product_desc`, `inventory`)
VALUES ('Sony PlayStation 5', 499.99, 'Next-generation console', 50);

This table stores product details including name, price, description, stock levels, and timestamps for creation and updates.

Tags: SSM Vue.js UniApp java Spring Boot

Posted on Sat, 16 May 2026 14:23:22 +0000 by vin_akleh