✨ 我是 Muzi 的「文章捕手」,擅长在文字的星海中打捞精华。每当新的篇章诞生,我就会像整理贝壳一样,将思想的闪光点串成珍珠项链~

本文介绍了Thesis Project Generator毕设项目生成平台后端核心功能的开发进展。主要完成了版本管理模块的API设计与实现,包括版本列表、详情查询、创建及下载接口,新增了11个Java文件覆盖领域层、基础设施层及API层。数据库设计以project_versions表为基础,前端versionNumber字段由前端根据索引计算。文章还详细阐述了WebSocket推送从原生Handler升级到支持STOMP协议,提升了连接稳定性和消息推送效率。开发过程中解决了Maven构建时JAR文件锁定、前端字段不匹配及Windows代理问题。项目整体进度约63%,后续计划实现Admin用户和模板管理API及AI多智能体生成功能。

# Thesis Project Generator - 开发日记 #1

# 前言

今天继续完善 Thesis Project Generator(毕设项目生成平台)的后端核心功能。主要完成了版本管理模块的 API 实现,以及 WebSocket 推送从原生协议升级到 STOMP 协议。


# 项目概述

# 项目目标

帮助计算机专业学生通过分析论文或输入需求,自动生成可运行的项目脚手架代码。

# 技术架构

  • 后端:Java 17 + Spring Boot 3.2.3 + DDD 六边形架构
  • 前端:Next.js 14 + TypeScript + Tailwind CSS
  • 数据库:MySQL + Redis
  • AI:Claude API / 通义千问

# DDD 分层架构

backend/
├── trigger-api/       # 触发层 - HTTP Controllers
├── domain/            # 领域层 - 核心业务逻辑
├── application/       # 应用层 - 业务编排
└── infrastructure/     # 基础设施层 - Repository实现、DAO

# 上午:版本管理模块 API 实现

# 1. 需求背景

前端版本历史页面调用 /projects/{id}/versions 接口,但后端原 ProjectController 缺少该接口,导致前端降级使用 Mock 数据。

# 2. 实现内容

# 2.1 新增数据库表(已存在)

CREATE TABLE IF NOT EXISTS project_versions (
    id VARCHAR(50) PRIMARY KEY,
    project_id VARCHAR(50) NOT NULL,
    version_name VARCHAR(50) NOT NULL,
    description TEXT,
    status VARCHAR(20) DEFAULT 'ACTIVE',
    file_path VARCHAR(500),
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

# 2.2 Domain 层新增

VersionEntity.java - 版本实体

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VersionEntity {
    private String id;
    private String projectId;
    private String versionName;
    private String description;
    private String status;
    private String filePath;
    private LocalDateTime createdAt;
}

IVersionRepository.java - 版本仓储接口

public interface IVersionRepository {
    List<VersionEntity> findByProjectId(String projectId);
    Optional<VersionEntity> findById(String id);
    void save(VersionEntity version);
    void deleteById(String id);
}

# 2.3 Infrastructure 层新增

VersionPO.java - 持久化对象

@Data
@TableName("project_versions")
public class VersionPO {
    @TableId(type = IdType.INPUT)
    private String id;
    private String projectId;
    private String versionName;
    private String description;
    private String status;
    private String filePath;
    private LocalDateTime createdAt;
}

VersionMapper.java - PO-Entity 转换器

VersionDAO.java - MyBatis-Plus DAO 接口

VersionRepositoryImpl.java - 仓储实现

@Repository
public class VersionRepositoryImpl implements IVersionRepository {
    private final VersionDAO versionDAO;
    // 实现 findByProjectId, findById, save, deleteById
}

# 2.4 ProjectDomainService 新增方法

// 获取项目版本列表
public List<VersionEntity> getProjectVersions(String projectId) {
    getProject(projectId); // 验证项目存在
    return versionRepository.findByProjectId(projectId);
}

// 获取版本详情
public VersionEntity getVersion(String versionId) {
    return versionRepository.findById(versionId)
        .orElseThrow(() -> new IllegalArgumentException("版本不存在"));
}

// 创建版本
public VersionEntity createVersion(String projectId, String versionName, String description) {
    getProject(projectId); // 验证项目存在
    VersionEntity version = VersionEntity.builder()
        .id(generateVersionId())
        .projectId(projectId)
        .versionName(versionName)
        .description(description)
        .status("ACTIVE")
        .createdAt(LocalDateTime.now())
        .build();
    versionRepository.save(version);
    return version;
}

# 2.5 ProjectController 新增 API

接口 方法 路径
获取版本列表 GET /projects/{projectId}/versions
获取版本详情 GET /projects/{projectId}/versions/{versionId}
创建版本 POST /projects/{projectId}/versions

# 3. API 测试结果

# 获取版本列表
curl http://localhost:8080/projects/P1775701806138/versions \
  -H "Authorization: Bearer $TOKEN"
# 返回: {"code":200,"data":{"items":[...],"total":1}}

# 创建版本
curl -X POST http://localhost:8080/projects/P1775701806138/versions \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"versionName":"v1.0.0","description":"初始版本"}'
# 返回: {"code":200,"data":{"id":"V1775713567456",...}}

# 下午:版本下载 API 与 STOMP 升级

# 1. 版本下载 API 实现

# 1.1 新增 DTO

VersionDownloadVO.java

@Data
@Builder
public class VersionDownloadVO {
    private String downloadUrl;
    private String message;
    private Long fileSize;
}

VersionArtifactsVO.java

@Data
@Builder
public class VersionArtifactsVO {
    private List<ArtifactItem> artifacts;
    private int total;

    @Data
    public static class ArtifactItem {
        private String name;
        private String path;
        private String type;
        private Long size;
    }
}

# 1.2 新增 API 端点

接口 方法 路径
获取下载链接 GET /projects/{projectId}/versions/{versionId}/download
获取版本产物 GET /projects/{projectId}/versions/{versionId}/artifacts

# 1.3 前后端字段对齐

⚠️ 重要:以数据库字段类型为准

前端期望 后端实现 说明
versionNumber (number) 不存在 前端根据列表索引计算

前端 ProjectVersionDTO 期望 versionNumber 字段,但数据库 project_versions 表只有 version_name (VARCHAR)。处理方式:

  • 后端仅返回数据库已有字段
  • 前端根据版本列表的 index + 1 计算 versionNumber

# 2. WebSocket STOMP 协议升级(已完成)

# 2.1 升级背景

原有 WebSocket 使用原生 Handler 模式,前端未使用 WebSocket 仍在轮询、服务端使用 new Thread()、缺少重连/心跳机制。

# 2.2 STOMP 配置

WebSocketConfig.java

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");           // 广播前缀
        registry.setApplicationDestinationPrefixes("/app"); // 应用前缀
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws/generation")
                .setAllowedOrigins("*")
                .withSockJS();  // SockJS 降级支持
    }
}

# 2.3 进度推送简化

GenerationProgressListener.java - 使用 SimpMessagingTemplate

@Component
@RequiredArgsConstructor
public class GenerationProgressListener {
    private final MessageSendingOperations<String> messagingTemplate;

    @EventListener
    public void handleGenerationProgress(GenerationProgressEvent event) {
        String destination = "/topic/job/" + event.getJobDTO().getId() + "/progress";
        messagingTemplate.convertAndSend(destination, event.getJobDTO());
    }
}

# 2.4 前端连接方式

// STOMP over SockJS
const socket = new SockJS('/ws/generation');
const stompClient = Stomp.over(socket);

stompClient.connect({}, () => {
    stompClient.subscribe('/topic/job/{jobId}/progress', (message) => {
        const progress = JSON.parse(message.body);
        // 更新UI
    });
});

# 今日成果

# 功能完成

  • ✅ 版本列表 API (GET /projects/{id}/versions)
  • ✅ 版本详情 API (GET /projects/{id}/versions/{versionId})
  • ✅ 创建版本 API (POST /projects/{id}/versions)
  • ✅ 版本下载 API (GET /projects/{id}/versions/{vid}/download)
  • ✅ 版本产物 API (GET /projects/{id}/versions/{vid}/artifacts)

# 新增文件统计

层级 文件 说明
Domain VersionEntity.java 版本实体
Domain IVersionRepository.java 版本仓储接口
Infrastructure VersionPO.java 持久化对象
Infrastructure VersionMapper.java PO转换器
Infrastructure VersionDAO.java DAO接口
Infrastructure VersionRepositoryImpl.java 仓储实现
API VersionDTO.java 响应DTO
API VersionListData.java 列表数据
API VersionDownloadVO.java 下载响应
API VersionArtifactsVO.java 产物响应
API CreateVersionRequest.java 创建请求

总计:新增约 11 个 Java 文件


# 遇到的问题

# 1. Maven 构建时 JAR 文件被锁定

问题mvn clean 时提示 Failed to delete trigger-api-1.0.0-SNAPSHOT.jar

原因:后端服务正在运行,JAR 文件被占用

解决方案

# 先停止运行中的 Java 进程
taskkill /F /PID <pid>

# 2. 前端 versionNumber 字段问题

问题:前端期望 versionNumber (number),但数据库没有该字段

解决方案:以数据库为准,后端不返回该字段;前端根据列表索引计算

# 3. Windows Git Bash 代理问题

问题curl 请求通过代理导致连接失败

解决方案:使用 --noproxy '*' 参数绕过代理

curl --noproxy '*' http://localhost:8080/auth/login

# 明日计划


# 项目进度

模块 进度 说明
M1 基础架构 100% DDD架构、数据库、Redis
M2 核心功能 96% 用户、项目、模板、论文解析
M3 AI 功能 52% 论文解析、WebSocket、版本管理
M4 模板后台 31% 前端UI完成,Admin API缺失
M5 测试优化 17% 单元测试框架

总体进度:约 63%


# 前后端 API 对齐状态

模块 前端 后端 状态
用户认证 authApi AuthController
用户资料 userApi UserController
项目管理 projectApi ProjectController
版本管理 versionApi ProjectController
生成任务 generationApi GenerationController
模板 templateApi TemplateController
Admin adminApi ❌ Mock

项目地址:Thesis Project Generator