Design and Implementation of a Portfolio WeChat Mini Program with Spring Boot, Vue, and UniApp

Technical Architecture Overview

The system utilizes a decoupled architecture comprising a Spring Boot backend, a Vue.js-based administrative frontend, and a UniApp-based WeChat Mini Program client. This separation ensures maintainability and scalability across different platforms.

Backend Framework: Spring Boot

Spring Boot serves as the core backend engine, eliminating the need for complex XML configurations through its convention-over-configuration approach. By embedding servers like Tomcat, it simplifies deployment. The framework's auto-configuration mechanism detects project dependencies to set up the environment automatically. Integrating ecosystem tools such as Spring Security for authentication and Spring Data for database interaction accelerates the development of robust, enterprise-grade APIs.

Frontend Framework: Vue.js

Vue.js is employed for the management dashboard, leveraging its Virtual DOM to optimize rendering performance. Its reactive data binding system synchronizes the UI with the underlying data model automatically, reducing boilerplate code for DOM manipulation. The component-based architecture allows for reusable UI elements, making the codebase modular and easier to maintain as the application grows.

Persistence Layer: MyBatis-Plus

MyBatis-Plus enhances the standard MyBatis framework by providing robust CRUD operations without the need for extensive XML mapping files. It supports multiple databases and includes advanced features like pagination, optimistic locking, and code generation. This abstraction layer significantly reduces the development time required for data access objects (DAOs) and ensures consistent SQL handling across the application.

System Testing Strategy

Testing Objectives

Comprehensive testing is critical to ensuring system reliability and user satisfaction. The primary goal is to identify and rectify defects before deployment. By simulating real-world usage scenarios, the testing process validates that the system meets all functional requirements defined in the specification. This phase focuses on verifying logic correctness, data integrity, and the stability of the user interface.

Functional Testing

Black-box testing techniques are applied to verify the system's external behavior without examining the internal code structure. Test cases are designed to cover various inputs, including boundary values and invalid data entries.

Login Module Test Cases:

Test Scenario Input Data Expected Outcome Actual Result Status
Valid Credentials User: admin, Pass: correct123, Captcha: Valid Redirect to Dashboard Login Successful Pass
Invalid Password User: admin, Pass: wrongpass, Captcha: Valid Error: Invalid Password Error Message Displayed Pass
Invalid Captcha User: admin, Pass: correct123, Captcha: Invalid Error: Captcha Mismatch Error Message Displayed Pass
Empty Username User: , Pass: correct123, Captcha: Valid Error: Username Required Validation Warning Pass
Empty Password User: admin, Pass: , Captcha: Valid Error: Password Required Validation Warning Pass

User Management Module Test Cases:

Test Scenario Input Data Expected Outcome Actual Result Status
Create New User Valid user details User added to list Record appears in DB Pass
Update User Modify existing profile Changes saved List reflects updates Pass
Delete User Select user and confirm User removed Record no longer found Pass
Duplicate Username Existing username Error: Duplicate Entry Rejection alert Pass
Missing Required Fields Blank required fields Error: Fields Required Validation fails Pass

Code Implementation

Authentication Controller

@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {

    @Autowired
    private SecurityService securityService;

    @PostMapping("/login")
    public ApiResponse<LoginResponse> authenticate(@RequestBody LoginDto credentials) {
        AccountEntity account = securityService.findAccountByName(credentials.getUsername());
        
        if (account == null || !securityService.checkPassword(account, credentials.getPassword())) {
            return ApiResponse.error("Invalid username or password");
        }
        
        String sessionKey = securityService.generateSessionKey(account.getId(), account.getUsername(), account.getRole());
        return ApiResponse.success(new LoginResponse(sessionKey));
    }
}

Token Generation Service

@Service
public class SecurityService {

    @Autowired
    private TokenRepository tokenDao;

    public String generateSessionKey(Long userId, String username, String roleName) {
        // Check for existing session
        TokenEntity existingToken = tokenDao.findOneByUserIdAndRole(userId, roleName);
        
        // Generate a random secure key
        String newKey = UUID.randomUUID().toString().replace("-", "");
        
        // Set expiration to 1 hour from now
        Instant expiry = Instant.now().plus(1, ChronoUnit.HOURS);

        if (existingToken != null) {
            existingToken.setAccessKey(newKey);
            existingToken.setExpiryDate(expiry);
            tokenDao.save(existingToken);
        } else {
            TokenEntity freshToken = new TokenEntity(userId, username, "accounts", roleName, newKey, expiry);
            tokenDao.insert(freshToken);
        }
        
        return newKey;
    }
}

Request Interceptor

@Component
public class SecurityInterceptor implements HandlerInterceptor {

    public static final String AUTH_HEADER = "X-Auth-Token";

    @Autowired
    private SecurityService securityService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // Handle CORS preflight
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return false;
        }

        // Bypass authentication for public endpoints
        if (handler instanceof HandlerMethod) {
            PublicEndpoint annotation = ((HandlerMethod) handler).getMethodAnnotation(PublicEndpoint.class);
            if (annotation != null) {
                return true;
            }
        }

        String clientToken = request.getHeader(AUTH_HEADER);
        
        if (StringUtils.isEmpty(clientToken)) {
            sendErrorResponse(response, 401, "Authentication token missing");
            return false;
        }

        TokenEntity session = securityService.lookupToken(clientToken);
        
        if (session != null) {
            request.setAttribute("currentUserId", session.getUserId());
            request.setAttribute("currentUserRole", session.getRole());
            return true;
        }

        sendErrorResponse(response, 401, "Invalid or expired token");
        return false;
    }

    private void sendErrorResponse(HttpServletResponse response, int code, String msg) throws IOException {
        response.setContentType("application/;charset=UTF-8");
        response.setStatus(code);
        PrintWriter out = response.getWriter();
        out.print(JSON.toJSONString(Result.fail(code, msg)));
        out.flush();
    }
}

Database Schema

-- ------------------------------------------------
-- Table Structure for `access_tokens`
-- ------------------------------------------------
DROP TABLE IF EXISTS `access_tokens`;
CREATE TABLE `access_tokens` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
  `user_id` BIGINT(20) NOT NULL COMMENT 'Associated User ID',
  `username` VARCHAR(100) NOT NULL COMMENT 'Account Name',
  `table_name` VARCHAR(50) DEFAULT NULL COMMENT 'Target Table',
  `role_name` VARCHAR(50) DEFAULT NULL COMMENT 'User Role',
  `access_key` VARCHAR(255) NOT NULL COMMENT 'Session Token',
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation Time',
  `expires_at` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Expiration Time',
  PRIMARY KEY (`id`),
  KEY `idx_user_token` (`user_id`, `access_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='User Session Tokens';

-- ------------------------------------------------
-- Sample Data for `access_tokens`
-- ------------------------------------------------
INSERT INTO `access_tokens` (`id`, `user_id`, `username`, `table_name`, `role_name`, `access_key`, `created_at`, `expires_at`) VALUES
(1, 101, 'system_admin', 'sys_users', 'admin', 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6', '2023-10-01 10:00:00', '2023-10-01 11:00:00'),
(2, 205, 'student_zhang', 'students', 'student', 'z9y8x7w6v5u4t3s2r1q0p9o8n7m6l5k4', '2023-10-02 14:30:00', '2023-10-02 15:30:00');

Tags: Spring Boot Vue.js UniApp WeChat Mini Program System Architecture

Posted on Thu, 14 May 2026 19:05:57 +0000 by _SAi_