Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Developing an Online Matchmaking Service Using Spring Boot and Vue

Tech 1

The system offers a complete solution for managing matchmaking-related activities, including wedding company listings, booking and favoriting, marriage case studies with bookmarking, user profiles, matchmaking interactions such as messages and favorites, and administrative tools. It is implemented with Java, Spring Boot, and MySQL, combined with a Vue-based front end.

Technology Stack

  • Backend Language: Java
  • Framework: Spring Boot 2.x
  • JDK: 1.8
  • Database: MySQL 5.7
  • ORM: MyBatis-Plus
  • Build Tool: Maven 3.3+
  • Frontend: Vue.js
  • Server: Embedded Tomcat

Default admin credentials: admin / admin

Key Technologies

Java provides a robust, object-oriented environment with strong typing and automatic memory management. Encapsulation, inheritance, and polymorphism enable modular and reusable code.

Spring Boot minimizes configuration overhead by auto-configuring components and embedding the server. It integrates a wide ecosystem of libraries while avoiding dependency conflicts.

MySQL is a high-performance relational database offering data integrity, fast queries, and flexible access control. It is well suited for web applications requiring shared, low-redundancy data storage.

Core Implementation Snippets

File Upload and Download Handler

package com.platform.controller;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import com.annotation.IgnoreAuth;
import com.entity.SystemConfig;
import com.service.SystemConfigService;
import com.utils.ResponseResult;

@RestController
@RequestMapping("/storage")
public class FileStorageController {

    @Autowired
    private SystemConfigService configService;

    private static final String UPLOAD_DIR = "upload";

    @IgnoreAuth
    @PostMapping("/upload")
    public ResponseResult handleUpload(@RequestParam("file") MultipartFile file,
                                       @RequestParam(value = "type", required = false) String type) throws IOException {
        if (file.isEmpty()) {
            throw new RuntimeException("Selected file is empty");
        }
        String originalName = file.getOriginalFilename();
        String extension = originalName.substring(originalName.lastIndexOf('.') + 1);
        Path basePath = Paths.get("static");
        if (!Files.exists(basePath)) {
            basePath = Paths.get("");
        }
        Path uploadPath = basePath.resolve(UPLOAD_DIR);
        if (!Files.exists(uploadPath)) {
            Files.createDirectories(uploadPath);
        }
        String storedName = Instant.now().toEpochMilli() + "." + extension;
        Path target = uploadPath.resolve(storedName);
        file.transferTo(target.toFile());

        if ("1".equals(type)) {
            SystemConfig config = configService.getOne(
                    new com.baomidou.mybatisplus.mapper.EntityWrapper<SystemConfig>().eq("name", "faceFile"));
            if (config == null) {
                config = new SystemConfig();
                config.setName("faceFile");
                config.setValue(storedName);
            } else {
                config.setValue(storedName);
            }
            configService.saveOrUpdate(config);
        }
        return ResponseResult.success().put("file", storedName);
    }

    @IgnoreAuth
    @GetMapping("/download")
    public ResponseEntity<Resource> handleDownload(@RequestParam("fileName") String fileName) throws IOException {
        Path uploadPath = Paths.get("static", UPLOAD_DIR);
        Path filePath = uploadPath.resolve(fileName);
        if (!Files.exists(filePath)) {
            return ResponseEntity.notFound().build();
        }
        byte[] data = Files.readAllBytes(filePath);
        ByteArrayResource resource = new ByteArrayResource(data);
        return ResponseEntity.ok()
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
                .body(resource);
    }
}

Discussion Forum REST API

package com.platform.controller;

import java.util.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.annotation.IgnoreAuth;
import com.entity.ForumTopic;
import com.entity.view.ForumTopicView;
import com.service.ForumTopicService;
import com.utils.PageResult;
import com.utils.ResponseResult;
import com.utils.ToolUtil;

@RestController
@RequestMapping("/forum")
public class ForumTopicController {

    @Autowired
    private ForumTopicService topicService;

    @GetMapping("/page")
    public ResponseResult page(@RequestParam Map<String, Object> params,
                               ForumTopic topic,
                               HttpServletRequest request) {
        HttpSession session = request.getSession();
        if (!"admin".equals(session.getAttribute("role"))) {
            topic.setUserId((Long) session.getAttribute("userId"));
        }
        EntityWrapper<ForumTopic> wrapper = new EntityWrapper<>();
        PageResult page = topicService.queryPage(params, ToolUtil.buildSort(ToolUtil.buildCondition(wrapper, topic, params), params));
        return ResponseResult.success().put("data", page);
    }

    @IgnoreAuth
    @GetMapping("/public")
    public ResponseResult publicList(@RequestParam Map<String, Object> params,
                                     ForumTopic topic) {
        EntityWrapper<ForumTopic> wrapper = new EntityWrapper<>();
        PageResult page = topicService.queryPage(params, ToolUtil.buildSort(ToolUtil.buildCondition(wrapper, topic, params), params));
        return ResponseResult.success().put("data", page);
    }

    @GetMapping("/{id}")
    public ResponseResult detail(@PathVariable Long id) {
        ForumTopic topic = topicService.selectById(id);
        if (topic != null) {
            buildThreadHierarchy(topic);
        }
        return ResponseResult.success().put("data", topic);
    }

    private void buildThreadHierarchy(ForumTopic parent) {
        List<ForumTopic> children = topicService.selectList(
                new EntityWrapper<ForumTopic>().eq("parent_id", parent.getId()));
        if (children == null || children.isEmpty()) return;
        parent.setChildren(children);
        for (ForumTopic child : children) {
            buildThreadHierarchy(child);
        }
    }

    @PostMapping
    @Transactional
    public ResponseResult create(@RequestBody ForumTopic topic,
                                 HttpServletRequest request) {
        topic.setId(generateId());
        topic.setUserId((Long) request.getSession().getAttribute("userId"));
        topicService.insert(topic);
        return ResponseResult.success();
    }

    @PutMapping
    public ResponseResult update(@RequestBody ForumTopic topic) {
        topicService.updateById(topic);
        return ResponseResult.success();
    }

    @DeleteMapping
    public ResponseResult remove(@RequestBody Long[] ids) {
        topicService.deleteBatchIds(Arrays.asList(ids));
        return ResponseResult.success();
    }

    private Long generateId() {
        return System.currentTimeMillis() + (long) (Math.random() * 1000);
    }
}

Testing Approach

Testing was conducted both on local servers and through structured verification methods. White-box testing examined internal logic and path coverage, while black-box testing validated functional requirements from an end-user perspective. The primary goal was to uncover defects before deployment.

Tests followed principles such as tracing back to user needs, beginning with smaller components before integration, and focusing on high-risk modules (Pareto principle). Test cases were designed to cover major functional areas, including forum interactions, file handling, and role-based access.

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.