Family Financial Management System Built with SpringBoot, Vue.js and UniApp

Technical Stacks

Backend Framework SpringBoot

Spring Boot is a development framework based on Spring Framework, with multiple built-in servers like Tomcat, Jetty and Undertow that require no additional installation or configuration. Its core auto-configuration feature automatically sets up applications based on project dependencies, greatly simplifying development workflows. It also offers rich out-of-the-box components including Spring Data, Spring Security and Spring Cloud, enabling rapid application building and easy integration with other technologies. Additional benefits include flexible configuration management, fast development and deployment, strong community support, monitoring and diagnostic tools, and reliable testing support, making it a popular choice for building high-quality, maintainable applications.

Sample 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 FamilyFinanceApplication {

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

    @GetMapping("/api/health")
    public String checkHealth() {
        return "Service is running normally";
    }
}

This code defines the entry class for a Spring Boot application, marked with @SpringBootApplication and @RestController. The checkHealth method maps to the /api/health endpoint, returning a service status string when accessed. Starting the app via SpringApplication.run launches the embedded server, and visiting http://localhost:8080/api/health will return the status message.

Frontend Framework Vue.js

Vue.js is a popular JavaScript framework featuring reactive data binding, virtual DOM, and component-based architecture. Its reactive system automatically updates UI components when underlying data changes, letting developers focus on data logic rather than manual DOM manipulation. The virtual DOM optimizes rendering performance by minimizing actual DOM updates.

Sample Vue.js implementation:

<html>
<head>
  <title>Family Finance Dashboard</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    <h3>Current Monthly Expense: {{ monthlyExpense }}</h3>
    <button @click="refreshExpense">Refresh Data</button>
  </div>

  <script>
    const financeApp = new Vue({
      el: '#app',
      data: {
        monthlyExpense: '$0.00'
      },
      methods: {
        refreshExpense() {
          this.monthlyExpense = '$1,245.67';
        }
      }
    });
  </script>
</body>
</html>

This example creates a Vue instance bound to the #app element. It initializes monthlyExpense to $0.00, and updates the value when the button is clicked. The reactive binding automatically refreshes the displayed text without manual DOM changes.

Persistence Layer MyBatis

MyBatis is an open-source persistence framework that separates SQL statements from Java code via XML or annotations, decoupling data access logic from business code. Key advantages include:

  1. Simplified database operations with automatic Java object-to-database table mapping, reducing manual SQL writing
  2. Dynamic SQL support for generating tailored queries based on runtime conditions
  3. Built-in first-level and second-level caching to reduce database load
  4. Extensible plugin system for custom functionality

System Testing

To ensure high system quality, comprehensive testing was conducted across multiple dimensions to identify and resolve defects, ensuring system integrity and reliability. Functional testing was used to validate all modules, verify alignment with user requirements, and assess system performance.

System Testing Objectives

System testing is a critical final step in the development lifecycle, serving as the last checkpoint to ensure system quality and reliability. Its core goals include:

  • Preventing user-facing issues and improving user experience
  • Identifying defects by simulating real-world usage scenarios
  • Validating that the system meets specified requirements and aligns with user needs
  • Evaluating overall system quality and logical flow

Testing was conducted from a user perspective to avoid irrelevant scenarios, ensuring actual results match expected outcomes.

System Functional Testing

Black-box testing was used to validate all functional modules, including input boundary testing, required field validation, and scenario simulation. Test cases were written and executed for each module.

Login Function Test Cases

Input Data Expected Result Actual Result Analysis
Username: admin, Password: 123456, Valid Captcha Successful login Successful login Match
Username: admin, Password: wrongpass, Valid Captcha Password error prompt "Incorrect username or password" Match
Username: admin, Password:123456, Invalid Captcha Captcha error prompt "Invalid captcha" Match
Empty Username, Password:123456, Valid Captcha Required username prompt "Please enter username" Match
Username: admin, Empty Password, Valid Captcha Required password prompt "Please enter password" Match

User Management Function Tests

Add User Test Cases
Input Data Expected Result Actual Result Analysis
Username: user01, Password: 123456, Role: Regular User User added to list User01 appears in user list Match
Duplicate Username: user01, Password:123456, Role: Regular User Duplicate username error "Username already exists" Match
Empty Username Required username error "Please enter username" Match
Edit User Test Cases
Input Data Expected Result Actual Result Analysis
Edit user01 password to 654321 Password updated successfully User01 password changed to 654321 Match
Edit user01 role to Admin Role updated successfully User01 role set to Admin Match
Clear username during edit Required username error "Please enter username" Match
Delete User Test Cases
Input Data Expected Result Actual Result Analysis
Delete user01, confirm prompt User removed from list User01 deleted successfully Match
Delete user01, cancel prompt User remains in list User01 not deleted Match

System Testing Conclusion

Black-box testing was used to validate all system functions by simulating user interactions. All test cases passed, confirming that the system meets design requirements and functional specifications, with correct logical flow and reliable performance. The testing process ensured the system is ready for user use with high quality and stability.


Code Reference Examples

// Custom annotation to skip authentication
@IgnoreAuth
@PostMapping("/api/auth/login")
public Result login(String username, String password, String captcha, HttpServletRequest request) {
    // Fetch user by username
    UserEntity user = userService.selectOne(new EntityWrapper<UserEntity>().eq("username", username));
    // Validate user existence and password
    if (user == null || !user.getPassword().equals(password)) {
        return Result.error("Invalid username or password");
    }
    // Generate authentication token
    String token = tokenService.generateToken(user.getId(), username, "users", user.getRole());
    return Result.ok().put("token", token);
}

@Override
public String generateToken(Long userId, String username, String tableName, String userRole) {
    // Check for existing valid token
    TokenEntity existingToken = this.selectOne(new EntityWrapper<TokenEntity>()
        .eq("userid", userId)
        .eq("role", userRole));
    // Generate random 32-character token
    String newToken = CommonUtil.generateRandomString(32);
    // Set token expiration to 1 hour from now
    Calendar expiryTime = Calendar.getInstance();
    expiryTime.add(Calendar.HOUR_OF_DAY, 1);
    
    if (existingToken != null) {
        existingToken.setToken(newToken);
        existingToken.setExpiratedtime(expiryTime.getTime());
        this.updateById(existingToken);
    } else {
        this.insert(new TokenEntity(userId, username, tableName, userRole, newToken, expiryTime.getTime()));
    }
    return newToken;
}

/**
 * Token-based authentication interceptor
 */
@Component
public class AuthInterceptor implements HandlerInterceptor {
    public static final String TOKEN_HEADER = "Authorization";
    
    @Autowired
    private TokenService tokenService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // Configure CORS headers
        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, " + TOKEN_HEADER + ", Origin, Content-Type");
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        
        // Handle preflight OPTIONS requests
        if (HttpMethod.OPTIONS.name().equals(request.getMethod())) {
            response.setStatus(HttpStatus.OK.value());
            return false;
        }
        
        // Skip authentication for methods with @IgnoreAuth annotation
        IgnoreAuth skipAuth = null;
        if (handler instanceof HandlerMethod) {
            skipAuth = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
        }
        if (skipAuth != null) {
            return true;
        }
        
        // Extract token from request header
        String token = request.getHeader(TOKEN_HEADER);
        if (!StringUtils.hasText(token)) {
            return handleAuthFailure(response);
        }
        
        // Validate token
        TokenEntity validToken = tokenService.getValidToken(token);
        if (validToken != null) {
            // Attach user info to request session
            request.getSession().setAttribute("userId", validToken.getUserid());
            request.getSession().setAttribute("userRole", validToken.getRole());
            return true;
        }
        
        return handleAuthFailure(response);
    }
    
    private boolean handleAuthFailure(HttpServletResponse response) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try (PrintWriter writer = response.getWriter()) {
            writer.print(JSONObject.toJSONString(Result.error(401, "Please login first")));
        }
        return false;
    }
}

Database Reference

Sample financial record table schema:

DROP TABLE IF EXISTS `finance_record`;
CREATE TABLE `finance_record` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
  `record_type` tinyint(1) NOT NULL COMMENT 'Record Type: 0=Expense, 1=Income',
  `amount` decimal(12,2) NOT NULL COMMENT 'Transaction Amount',
  `category` varchar(50) NOT NULL COMMENT 'Transaction Category',
  `description` varchar(200) DEFAULT NULL COMMENT 'Transaction Notes',
  `transaction_date` datetime NOT NULL COMMENT 'Transaction Time',
  `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=utf8mb4 COMMENT='Financial Transaction Records';

-- Sample insert data
INSERT INTO `finance_record` (`record_type`, `amount`, `category`, `description`, `transaction_date`)
VALUES 
(0, 79.99, 'Groceries', 'Weekly grocery shopping', '2024-05-15 10:30:00'),
(1, 2500.00, 'Salary', 'Monthly salary deposit', '2024-05-01 09:00:00'),
(0, 129.99, 'Entertainment', 'Movie ticket purchase', '2024-05-20 19:00:00');

Tags: SpringBoot Vue.js UniApp Family Financial Management System Full Stack Development

Posted on Wed, 26 Aug 2026 16:47:39 +0000 by jenniferG