2026/4/9 10:55:42
网站建设
项目流程
南京做网站dmooo,世纪城网站建设,太原网站建设哪家强,廊坊网站定制开发背景痛点#xff1a;毕业设计为何总被吐槽“像玩具”
每年 3 月#xff0c;学院 GitLab 上都会冒出 200 新仓库#xff0c;但答辩时老师只看三样东西#xff1a;README、测试报告、可运行的 jar。结果 70% 的同学卡在第一步——“选题太大、边界不清、功能堆砌”。典型症状…背景痛点毕业设计为何总被吐槽“像玩具”每年 3 月学院 GitLab 上都会冒出 200 新仓库但答辩时老师只看三样东西README、测试报告、可运行的 jar。结果 70% 的同学卡在第一步——“选题太大、边界不清、功能堆砌”。典型症状如下需求一句话“做一个教务系统”没有角色、没有用例后续不断拍脑袋加功能。代码一锅粥controller 里写 SQLservice 里调 Redis一个类 800 行分层仅存在于 PPT。测试靠主函数public static void main 里 new 一个对象System.out.println 一把梭连 Test 都没见过。回滚靠 CtrlZ没有分支、没有 tag答辩前夜“祖传”final_final_v2.zip 横空出世。这些问题的根因不是“不会写代码”而是缺少“工程化思维”。AI 辅助开发的价值恰恰是把“工程套路”以可复制的形式喂给你让你在 12 周内跑完完整 SDLC而不是 11 周都在调前端样式。技术选型对比三种开发模式的 24 小时交付实验为了量化差异我设计了一个最小实验实现“课程管理系统”的 5 个核心接口新增课程、选课、退课、查询课表、成绩录入。同一人、同一需求分别用三种方式实现记录“可运行时间”与“代码质量分”SonarQube 默认规则。模式可运行时间代码质量分优点缺点纯手动8 hC (2.1k 技术债)思路清晰完全可控重复代码多分层靠自觉Copilot 交互3.5 hB (0.9k 技术债)补全快命名较规范容易接受“看起来对”的幻觉代码测试仍需自己写自建 Agent 流程LLMPromptCLI2 hA (0.3k 技术债)一次性生成骨架、单元测试、OpenAPI 文档目录规范需要提前写 Prompt 模板对提示词质量敏感结论AI 不是替代思考而是把“体力活”压缩到 20% 时间让你把剩余精力投入到“边界划分、异常流程、安全审查”这些高阶任务。核心实现用 5 轮 Prompt 把“课程管理系统”拆成可交付工程以下流程全部脚本化仓库初始化后 30 分钟内即可得到可跑通的 Spring Boot 工程。需求澄清 Prompt“扮演产品经理输出一份用例文档覆盖学生、教师、管理员三类角色功能包括课程 CRUD、选课、退课、成绩录入使用 Markdown 表格描述主成功场景与异常流程。”架构草图 Prompt“基于上述用例生成 C4 的 Container 图前端 React后端 Spring Boot数据库 Postgres使用 PlantUML 语法。”模块划分 Prompt“按 DDD 分层输出 Maven 多模块结构course-domain、course-application、course-infrastructure给出每个模块的 pom 片段与包名约定。”API 设计 Prompt“为‘选课’用例设计 RESTful 接口返回统一包装结果 Result 提供 OpenAPI 3.0 YAML并生成 SpringDoc 注解。”数据库 Schema Prompt“根据实体 Course、Student、Enrollment输出 Postgres DDL要求外键级联删除、check 约束保证学分0、给 enrollment 建联合唯一索引防止重复选课。”每轮输出后人工做三件事用例是否闭环——没有“忘记密码”这类隐形需求。接口是否幂等——选课接口用 POST /enrollments幂等键studentIdcourseId放唯一索引。DDL 是否可逆——flyway 脚本命名加版本号本地可 rollback。Clean Code 示例让 AI 先生成再人工精炼以下代码由 Agent 生成我仅调整了两处把 JPA 查询抽象到 Repository并补充单元测试。亮点接口与实现分离、领域异常封装、可测试。// course-domain/src/main/java/course/domain/model/Course.java package course.domain.model; import jakarta.persistence.*; import lombok.*; import java.util.HashSet; import java.util.Set; Entity Table(name course) Getter NoArgsConstructor(access AccessLevel.PROTECTED) public class Course { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String name; private int credit; OneToMany(mappedBy course, cascade CascadeType.ALL, orphanRemoval true) private SetEnrollment enrollments new HashSet(); public Course(String name, int credit) { if (credit 0) throw new IllegalArgumentException(credit must be positive); this.name name; this.credit credit; } }// course-application/src/main/java/course/application/EnrollService.java package course.application; import course.domain.model.Course; import course.domain.model.Enrollment; import course.domain.model.Student; import course.domain.repo.CourseRepository; import course.domain.repo.EnrollmentRepository; import course.domain.repo.StudentRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; Service RequiredArgsConstructor public class EnrollService { private final CourseRepository courseRepository; private final StudentRepository studentRepository; private final EnrollmentRepository enrollmentRepository; Transactional public void enroll(Long studentId, Long courseId) { Student student studentRepository.findById(studentId) .orElseThrow(() - new NotFoundException(student)); Course course courseRepository.findById(courseId) .orElseThrow(() - new NotFoundException(course)); boolean exists enrollmentRepository.existsByStudentAndCourse(student, course); if (exists) throw new BusinessException(already enrolled); Enrollment enrollment new Enrollment(student, course); enrollmentRepository.save(enrollment); } }// course-application/src/test/java/course/application/EnrollServiceTest.java package course.application; import course.domain.model.Course; import course.domain.model.Student; import course.domain.repo.EnrollmentRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; import static org.assertj.core.api.Assertions.*; SpringBootTest Transactional class EnrollServiceTest { Autowired private EnrollService enrollService; Autowired private EnrollmentRepository enrollmentRepository; Autowired private TestDataBuilder builder; Test void shouldRejectDuplicateEnrollment() { Student s builder.saveStudent(); Course c builder.saveCourse(); enrollService.enroll(s.getId(), c.getId()); assertThatThrownBy(() - enrollService.enroll(s.getId(), c.getId())) .isInstanceOf(BusinessException.class) .hasMessageContaining(already enrolled); } }要点领域对象纯 POJO不依赖框架。应用服务只负责编排不写 SQL。测试用数据构造器 TestDataBuilder 复用保证测试独立。AI 生成内容的风险与人工审查清单安全漏洞LLM 喜欢在示例里把密码明文存 String必须提醒它“使用 char[] BCrypt”。非幂等批量更新语句忘了加版本号并发测试必挂。SQL 注入MyBatis XML 里用 ${} 拼接要替换成 #{}。许可证污染引入的第三方库需 OSS Review 工具扫描禁止 GPL 进商业仓库。性能幻觉AI 给出的“分页大 in 查询”在 100 万数据量下直接 OOM需用游标或分页子查询重写。人工审查三步走静态扫描SonarQube SpotBugs 强制质量门。差异 Review只审 AI 生成 commit标记AI-GEN标签方便追溯。场景测试把典型业务路径写成 BDD.feature用 Cucumber 每日回归。生产环境避坑指南让 Demo 能真正跑在服务器版本控制main 分支保护PR 至少 1 人 CI 通过。Tag 采用语义版本规范 v1.0.0打 tag 即触发 CD 到测试环境。文档同步采用“文档即接口”策略OpenAPI YAML 由代码注解反向生成提交时自动更新拒绝“代码和文档不同步”。架构图用 C4 模型 PlantUML 存 docs/ 目录CI 渲染成 PNG 后上传到 Wiki防止“图过时”。依赖锁定Maven/Gradle 使用 .lockfileNode 使用 package-lock.json禁止latest.release。每季度执行mvn versions:display-dependency-updates人工评估后批量升级。配置与环境分离采用 12-Factor敏感信息进 Vault/K8s Secret本地只留.env.example。数据库迁移用 Flywaybaseline-on-migratetrue防止生产库首次执行失败。监控与回滚启动探针Spring Boot Actuator /health/livenessK8s 就绪探针 5 秒一次。回滚策略保留前一镜像helm rollback 1 分钟内完成数据库迁移若回滚需准备 down 脚本并提前在预发环境验证。结语如果明天没有 AI你还能复现这套工程结构吗把 AI 当“加速器”而非“拐杖”才是毕业设计真正的收获。试着把本文所有 Prompt 删掉仅用白板和笔记本你依旧能徒手画出分层架构、写出幂等 SQL、补全单元测试——那一刻你交付的已不只是“课程管理系统”而是一份可迁移到任何技术栈的工程素养。