Smart Warehouse System Development with Spring Boot, Vue.js, and UniApp

This article outlines the design and implementation of a smart warehouse management system built using a modern technology stack. The backend is powered by Spring Boot, the web frontend leverages Vue.js, and a mobile application is developed with UniApp, providing a comprehensive solution for efficient warehouse operations.

Backend Framework: Spring Boot

Spring Boot simplifies the development of production-ready Spring applications. Its key advantages include convention-over-configuration, enabling rapid application setup and development. It integrates embedded servers like Tomcat, Jetty, or Undertow, eliminating the need for external server configurations. The robust auto-configuration feature intelligently configures the application based on existing dependencies, significantly reducing boilerplate code. Furthermore, Spring Boot offers seamless integration with the Spring ecosystem (e.g., Spring Data, Spring Security, Spring Cloud), facilitating the development of scalable and feature-rich applications.

Web Frontend Framework: Vue.js

Vue.js is a progressive JavaScript framework for building user interfaces. It emphasizes simplicity and developer efficiency, utilizing reactive data binding, a virtual DOM, and a component-based architecture. With Vue.js, developers can focus on application data logic, as the UI automatically updates in response to data changes. This reactive paradigm, combined with its lightweight nature and extensive tooling, makes Vue.js an excellent choice for developing dynamic and responsive web applications.

Data Persistence Layer: MyBatis-Plus

MyBatis-Plus serves as an enhancement toolkit for the MyBatis framework, streamlining database operations in Java applications. It provides a rich set of APIs and annotations that simplify Object-Relational Mapping (ORM) tasks, drastically reducing the amount of handwritten SQL code. Supporting various databases (MySQL, Oracle, PostgreSQL, etc.), MyBatis-Plus accelerates development through features like built-in CRUD operations, pagination, dynamic query builders, optimistic locking, and a code generator for entities and mappers. This significantly boosts efficiency in creating robust data access layers.

System Quality Assurance: Testing Approach

Rigorous system testing is an integral part of the development lifecycle, ensuring the application meets functional and non-functional requirements. The primary objective is to identify and rectify defects, guaranteeing system stability, reliability, and an optimal user experience. Through comprehensive testing, the system's adherence to design specifications is validated, and any discrepancies are addressed proactively.

Goals of System Testing

During the system's development, testing is a crucial, iterative process designed to validate its quality and robustness. It serves as the final quality gate before deployment, focusing on preventing user-facing issues and enhancing usability. By simulating diverse scenarios and user interactions, potential defects are uncovered and resolved. This process also provides insights into the system's overall quality, feature completeness, and logical flow. The ultimate aim is to ensure the system fully aligns with the specified requirements and user expectations.

Functional Testing Strategy

Functional testing involves evaluating individual system modules using black-box testing techniques. This includes interacting with the user interface, entering boundary values, and validating mandatory/optional field inputs. Test cases are meticulously designed and executed to verify that each function performs according to its itnended purpose.

Test Case: User Authentication

The authentication module is tested to ensure secure and correct login procedures. This involves verifying username, password, and (if applicable) captcha validation. The system should correctly authenticate valid credentials and provide appropriate error messages for incorrect inputs or unauthorized access attempts.

Input Data Expected Outcome Actual Outcome Analysis
Username: admin_user, Password: secure_pass, Captcha: Correct Successful system login Login successful Matches expectation
Username: admin_user, Password: wrong_pass, Captcha: Correct Password error message "Incorrect password, please try again." Matches expectation
Username: admin_user, Password: secure_pass, Captcha: Incorrect Captcha error message "Invalid verification code." Matches expectation
Username: [empty], Password: secure_pass, Captcha: Correct Prompt for missing username "Please enter username." Matches expectation
Username: admin_user, Password: [empty], Captcha: Correct Prompt for missing password "Password cannot be empty." Matches expectation
Test Case: User Account Management

The user account management module, encompassing operations such as adding, editing, deleting, and searching user profiles, undergoes thorough testing. This includes verifying data validation for required fields, handling of duplicate usernames, confirmation prompts for deletion, and accurate display of updated user information.

Input Data Expected Outcome Actual Outcome Analysis
Complete user details for a new user User successfully added and displayed in the list New user appears in the list Matches expectation
Modified user information for an existing user User details successfully updated and reflected User information is modified Matches expectation
Initiate deletion of a selected user System prompts for confirmation; user is removed upon confirmation System asks for confirmation; user is not found after confirmation Matches expectation
Attempt to add user with an empty username Validation error: "Username cannot be empty." "Username cannot be empty." is displayed Matches expectation
Attempt to add user with an already existing username Error: "Username already exists." "Username already exists." is displayed Matches expectation

Testing Outcomes

Utilizing a black-box testing methodology, the system's functionality was evaluated by simulating user interactions and defining detailed test cases. This approach ensures the correctness of system flows and validates that all functional modules operate as per the design specifications. The tests confirmed that the system effectively handles user authentication and management, providing a stable and user-friendly experience. The overall testing concluded that the implemented system successfully meets its functional and performance requirements.

Core Code Snippets

User Authentication Endpoint

This Spring Boot endpoint handles user login requests, authenticating credentials and issuing an authorization token upon successful validation.


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;

@RestController
public class AuthenticationController {

   private final UserAccountService userAccountService;
   private final SecurityTokenService securityTokenService;
   // Assuming a password encoder is available, e.g., BCryptPasswordEncoder

   public AuthenticationController(UserAccountService userAccountService, SecurityTokenService securityTokenService) {
       this.userAccountService = userAccountService;
       this.securityTokenService = securityTokenService;
   }

   // @PublicAccess (Custom annotation to bypass interceptor for this endpoint)
   @PostMapping(value = "/api/auth/login")
   public OperationResponse handleLogin(@RequestBody LoginRequest loginRequest, HttpServletRequest request) {
       // In a real application, implement captcha validation here
       if (loginRequest.getCaptcha() == null || !isValidCaptcha(loginRequest.getCaptcha())) {
           return OperationResponse.error("Invalid captcha provided.");
       }

       UserEntity storedUser = userAccountService.getOne(
           new QueryWrapper<UserEntity>().eq("username", loginRequest.getUsername())
       );

       // Replace with secure password verification, e.g., BCrypt.checkpw(request.getPassword(), storedUser.getHashedPassword())
       if (storedUser == null || !storedUser.getPasswordHash().equals(loginRequest.getPassword())) { 
           return OperationResponse.error("Incorrect username or password.");
       }
       
       String authToken = securityTokenService.generateUserToken(
           storedUser.getId(), 
           storedUser.getUsername(), 
           "users", // Entity type, e.g., "users", "administrators"
           storedUser.getUserRole()
       );
       return OperationResponse.success().put("token", authToken);
   }

   private boolean isValidCaptcha(String captcha) {
       // Placeholder for actual captcha validation logic
       return true; 
   }

   // Example DTO for login request
   static class LoginRequest {
       private String username;
       private String password;
       private String captcha;
       // Getters and Setters
       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 getCaptcha() { return captcha; }
       public void setCaptcha(String captcha) { this.captcha = captcha; }
   }

   // Example simplified response wrapper
   static class OperationResponse {
       private int code;
       private String message;
       private java.util.Map<String, Object> data = new java.util.HashMap<>();

       public static OperationResponse success() {
           OperationResponse response = new OperationResponse();
           response.setCode(200);
           response.setMessage("Operation successful.");
           return response;
       }

       public static OperationResponse error(String message) {
           OperationResponse response = new OperationResponse();
           response.setCode(500);
           response.setMessage(message);
           return response;
       }

       public OperationResponse put(String key, Object value) {
           this.data.put(key, value);
           return this;
       }

       // Getters and Setters
       public int getCode() { return code; }
       public void setCode(int code) { this.code = code; }
       public String getMessage() { return message; }
       public void setMessage(String message) { this.message = message; }
       public java.util.Map<String, Object> getData() { return data; }
       public void setData(java.util.Map<String, Object> data) { this.data = data; }
   }

   // Placeholder for UserEntity, replace with actual entity
   static class UserEntity {
       private Long id;
       private String username;
       private String passwordHash; // Renamed to reflect proper hashing
       private String userRole;
       // 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 getPasswordHash() { return passwordHash; }
       public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
       public String getUserRole() { return userRole; }
       public void setUserRole(String userRole) { this.userRole = userRole; }
   }

   // Placeholder for UserAccountService, replace with actual service
   static class UserAccountService {
       public UserEntity getOne(QueryWrapper<UserEntity> queryWrapper) {
           // Mock implementation: find user by username
           if (queryWrapper.getExpression().getNormal().get("username").equals("admin_user")) {
               UserEntity user = new UserEntity();
               user.setId(1L);
               user.setUsername("admin_user");
               user.setPasswordHash("secure_pass"); // In production, this would be a bcrypt hash
               user.setUserRole("Administrator");
               return user;
           }
           return null;
       }
   }
}

Security Token Management Service

This service manages the creation and updating of authentication tokens, ensuring each user session is assigned a unique, time-limited token.


import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.UUID; // Using UUID for random string generation

@Service
public class SecurityTokenService extends ServiceImpl<AuthTokenMapper, AuthTokenEntity> {

   public String generateUserToken(Long userIdentity, String userName, String entityType, String roleType) {
       AuthTokenEntity existingToken = this.getOne(
           new QueryWrapper<AuthTokenEntity>()
               .eq("user_id_ref", userIdentity)
               .eq("associated_role", roleType)
       );

       String newTokenValue = UUID.randomUUID().toString().replace("-", ""); // Generate a random string
       LocalDateTime tokenExpiry = LocalDateTime.now().plusHours(1); // Token expires in 1 hour

       if (existingToken != null) {
           existingToken.setTokenString(newTokenValue);
           existingToken.setExpirationTimestamp(tokenExpiry);
           this.updateById(existingToken);
       } else {
           AuthTokenEntity newToken = new AuthTokenEntity(
               userIdentity, 
               userName, 
               entityType, 
               roleType, 
               newTokenValue, 
               tokenExpiry
           );
           this.save(newToken);
       }
       return newTokenValue;
   }

   public AuthTokenEntity validateAndRetrieveToken(String tokenValue) {
       AuthTokenEntity tokenRecord = this.getOne(
           new QueryWrapper<AuthTokenEntity>().eq("token_string", tokenValue)
       );
       
       if (tokenRecord != null && tokenRecord.getExpirationTimestamp().isAfter(LocalDateTime.now())) {
           return tokenRecord;
       }
       return null;
   }

   // Placeholder for AuthTokenEntity, replace with actual entity
   static class AuthTokenEntity {
       private Long id;
       private Long userIdRef;
       private String username;
       private String entityType;
       private String associatedRole;
       private String tokenString;
       private LocalDateTime creationTimestamp;
       private LocalDateTime expirationTimestamp;

       public AuthTokenEntity() {
           this.creationTimestamp = LocalDateTime.now();
       }

       public AuthTokenEntity(Long userIdRef, String username, String entityType, String associatedRole, String tokenString, LocalDateTime expirationTimestamp) {
           this();
           this.userIdRef = userIdRef;
           this.username = username;
           this.entityType = entityType;
           this.associatedRole = associatedRole;
           this.tokenString = tokenString;
           this.expirationTimestamp = expirationTimestamp;
       }
       // Getters and Setters
       public Long getId() { return id; }
       public void setId(Long id) { this.id = id; }
       public Long getUserIdRef() { return userIdRef; }
       public void setUserIdRef(Long userIdRef) { this.userIdRef = userIdRef; }
       public String getUsername() { return username; }
       public void setUsername(String username) { this.username = username; }
       public String getEntityType() { return entityType; }
       public void setEntityType(String entityType) { this.entityType = entityType; }
       public String getAssociatedRole() { return associatedRole; }
       public void setAssociatedRole(String associatedRole) { this.associatedRole = associatedRole; }
       public String getTokenString() { return tokenString; }
       public void setTokenString(String tokenString) { this.tokenString = tokenString; }
       public LocalDateTime getCreationTimestamp() { return creationTimestamp; }
       public void setCreationTimestamp(LocalDateTime creationTimestamp) { this.creationTimestamp = creationTimestamp; }
       public LocalDateTime getExpirationTimestamp() { return expirationTimestamp; }
       public void setExpirationTimestamp(LocalDateTime expirationTimestamp) { this.expirationTimestamp = expirationTimestamp; }
   }

   // Placeholder for AuthTokenMapper, replace with actual mapper
   interface AuthTokenMapper extends com.baomidou.mybatisplus.core.mapper.BaseMapper<AuthTokenEntity> {}
}

API Authorization Interceptor

This interceptor ensures that all protected API endpoints require a valid authentication token, enforcing security policies across the application. It also handles CORS pre-flight requests.


import com.alibaba.fastjson.JSONObject; // Assuming Fastjson for JSON response
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.apache.commons.lang3.StringUtils; // For String utility

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Component
public class ApiSecurityInterceptor implements HandlerInterceptor {

   public static final String AUTH_HEADER_NAME = "X-Auth-Token"; // Standardized header name

   @Autowired
   private SecurityTokenService securityTokenService; // Renamed service

   // Custom annotation for public endpoints
   @Target(ElementType.METHOD)
   @Retention(RetentionPolicy.RUNTIME)
   public @interface PublicAccess {}
   
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

       // Configure CORS headers for all responses
       response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
       response.setHeader("Access-Control-Max-Age", "3600");
       response.setHeader("Access-Control-Allow-Credentials", "true");
       response.setHeader("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, X-Auth-Token, Authorization, Cache-Control, X-Requested-With");
       response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));

       // Handle OPTIONS pre-flight requests
       if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
           response.setStatus(HttpStatus.OK.value());
           return false;
       }
       
       PublicAccess publicAccessAnnotation;
       if (handler instanceof HandlerMethod) {
           publicAccessAnnotation = ((HandlerMethod) handler).getMethodAnnotation(PublicAccess.class);
       } else {
           return true; // For resources not handled by a controller method
       }

       // Methods annotated with @PublicAccess do not require authentication
       if (publicAccessAnnotation != null) {
           return true;
       }

       // Extract token from header
       String authTokenValue = request.getHeader(AUTH_HEADER_NAME);
       
       SecurityTokenService.AuthTokenEntity validTokenRecord = null;
       if (StringUtils.isNotBlank(authTokenValue)) {
           validTokenRecord = securityTokenService.validateAndRetrieveToken(authTokenValue);
       }
       
       if (validTokenRecord != null) {
           // Set user details in request attributes for controller access
           request.setAttribute("currentUserId", validTokenRecord.getUserIdRef());
           request.setAttribute("currentUserRole", validTokenRecord.getAssociatedRole());
           request.setAttribute("currentUserEntityType", validTokenRecord.getEntityType());
           request.setAttribute("currentUsername", validTokenRecord.getUsername());
           return true;
       }
       
       // If no valid token, send unauthorized response
       PrintWriter out = null;
       response.setCharacterEncoding("UTF-8");
       response.setContentType("application/json; charset=utf-8");
       try {
           out = response.getWriter();
           out.print(JSONObject.toJSONString(AuthenticationController.OperationResponse.error("Authentication required.")));
           response.setStatus(HttpStatus.UNAUTHORIZED.value());
       } finally {
           if (out != null) {
               out.close();
           }
       }
       return false;
   }
}

Database Schema for Authentication Tokens

The following SQL defines the table structure used to store user authentication tokens, including user references, roles, the token string itself, and expiration details.


-- Table structure for user_authentication_tokens
DROP TABLE IF EXISTS `user_authentication_tokens`;
CREATE TABLE `user_authentication_tokens` (
 `token_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary key for token record',
 `user_id_ref` bigint(20) NOT NULL COMMENT 'Reference to the user account ID',
 `username` varchar(100) NOT NULL COMMENT 'Username associated with the token',
 `entity_type` varchar(100) DEFAULT NULL COMMENT 'Type of entity (e.g., users, employees, admins)',
 `associated_role` varchar(100) DEFAULT NULL COMMENT 'Role of the user (e.g., Student, Administrator)',
 `token_string` varchar(200) NOT NULL COMMENT 'The unique authentication token string',
 `creation_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Timestamp when the token was created',
 `expiration_timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Timestamp when the token expires',
 PRIMARY KEY (`token_id`) USING BTREE,
 UNIQUE KEY `idx_user_role_type` (`user_id_ref`, `associated_role`) -- Ensure one token per user and role type
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Table to store user authentication tokens';

-- Example records for user_authentication_tokens
INSERT INTO `user_authentication_tokens` (`token_id`, `user_id_ref`, `username`, `entity_type`, `associated_role`, `token_string`, `creation_timestamp`, `expiration_timestamp`) VALUES
(1, 23, 'user_alpha', 'students', 'Student', 'alpha-token-string-1234567890abcdef', '2023-02-23 21:46:45', '2023-03-15 14:01:36'),
(2, 11, 'user_beta', 'students', 'Student', 'beta-token-string-abcdef1234567890', '2023-02-27 18:33:52', '2023-03-17 18:27:42'),
(3, 1, 'system_admin', 'system_users', 'Administrator', 'admin-token-string-0987654321fedcba', '2023-02-27 19:37:01', '2023-03-17 18:23:02');


Tags: Spring Boot Vue.js mybatis-plus UniApp java

Posted on Mon, 17 Aug 2026 16:18:41 +0000 by jefkin