这个作业属于哪个课程 前往班级圈子
这个作业要求在哪里 查看作业要求
这个作业的目标 1. 个人平台搭建
2. 熟悉API调用
3. 自我认知与规划
学号 102401309

0. 准备工作


前往

1. huggingface API 调用


1.1 流程

步骤 任务 要点 注意
1 Token 生成 Settings - Access Tokens 额度限制可能需要多个账号
2 环境安装依赖 pip install flask huggingface_hub pillow python-dotenv 在项目虚拟环境下安装
3 构建有关文件 .env, app.py 写入 Token,运行启动 Flask
4 前后端交互与调用 http://127.0.0.1:5000 提示词迭代

1.2 代码

AI 生成,点击展开
.env

HF_TOKEN=hf_...

app.py
# ==================== 第1步:导入工具箱 ====================
import os
import base64
import io

from flask import Flask, request, jsonify
from huggingface_hub import InferenceClient
from PIL import Image
from dotenv import load_dotenv

# ==================== 第2步:加载密钥 ====================
load_dotenv()

HF_TOKEN = os.getenv("HF_TOKEN")
if not HF_TOKEN:
    raise ValueError("❌ 没有找到 HF_TOKEN!请检查 .env 文件是否存在并填写了令牌。")

MODEL_ID = "XLabs-AI/flux-RealismLora"
client = InferenceClient(token=HF_TOKEN)

# ==================== 第3步:创建Flask应用 ====================
app = Flask(__name__)


# ==================== 第4步:风格化前端页面 ====================
@app.route('/')
def index():
    return '''
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Flux 真实感 · AI 图像生成</title>

        <!-- 字体:Inter 和 JetBrains Mono,营造专业感 -->
        <link href=" https://fonts.googleapis.com/css2?family=Inter:opsz ,wght@14..32,400;14..32,500;14..32,600;14..32,700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />

        <style>
            /* ===== 全局重置 & 暗色主题 ===== */
            * {
                margin: 0;
                padding: 0;
                box-sizing: border-box;
            }

            body {
                font-family: 'Inter', -apple-system, sans-serif;
                background: #0b0d0f;
                min-height: 100vh;
                display: flex;
                align-items: center;
                justify-content: center;
                padding: 40px 24px;
                background-image: radial-gradient(ellipse at 20% 50%, rgba(30, 40, 60, 0.4) 0%, transparent 60%),
                                  radial-gradient(ellipse at 80% 50%, rgba(60, 30, 50, 0.3) 0%, transparent 60%);
            }

            /* ===== 主容器 - 16:9 比例约束 ===== */
            .container {
                max-width: 1280px;
                width: 100%;
                aspect-ratio: 16 / 9;
                background: rgba(18, 22, 28, 0.85);
                backdrop-filter: blur(16px);
                -webkit-backdrop-filter: blur(16px);
                border-radius: 40px;
                padding: 48px 56px;
                border: 1px solid rgba(255, 255, 255, 0.06);
                box-shadow: 0 30px 80px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.05);
                display: flex;
                flex-direction: column;
                overflow-y: auto;
            }

            /* ===== 滚动条美化 ===== */
            .container::-webkit-scrollbar {
                width: 4px;
            }
            .container::-webkit-scrollbar-track {
                background: transparent;
            }
            .container::-webkit-scrollbar-thumb {
                background: rgba(255, 255, 255, 0.15);
                border-radius: 10px;
            }

            /* ===== 顶部导航 / 品牌 ===== */
            .header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 36px;
                flex-shrink: 0;
            }

            .brand {
                display: flex;
                align-items: center;
                gap: 12px;
            }

            .brand-icon {
                width: 40px;
                height: 40px;
                background: linear-gradient(135deg, #6C5CE7, #a855f7);
                border-radius: 12px;
                display: flex;
                align-items: center;
                justify-content: center;
                font-size: 20px;
                font-weight: 700;
                color: #fff;
            }

            .brand h1 {
                font-size: 22px;
                font-weight: 600;
                color: #ffffff;
                letter-spacing: -0.3px;
            }

            .brand h1 span {
                color: #a78bfa;
                font-weight: 400;
            }

            .badge {
                font-size: 12px;
                font-weight: 500;
                color: rgba(255, 255, 255, 0.4);
                background: rgba(255, 255, 255, 0.06);
                padding: 6px 16px;
                border-radius: 100px;
                border: 1px solid rgba(255, 255, 255, 0.06);
                letter-spacing: 0.3px;
            }

            /* ===== CSS Grid 核心布局:左(输入) + 右(输出) ===== */
            .main-grid {
                display: grid;
                grid-template-columns: 1fr 1fr;
                gap: 40px;
                flex: 1;
                min-height: 0;
            }

            /* ===== 左卡片:输入区 ===== */
            .input-card {
                background: rgba(255, 255, 255, 0.03);
                border-radius: 24px;
                padding: 32px;
                border: 1px solid rgba(255, 255, 255, 0.06);
                display: flex;
                flex-direction: column;
                transition: border-color 0.3s ease;
            }

            .input-card:focus-within {
                border-color: rgba(167, 139, 250, 0.3);
            }

            .input-card label {
                font-size: 13px;
                font-weight: 600;
                color: rgba(255, 255, 255, 0.5);
                text-transform: uppercase;
                letter-spacing: 0.8px;
                margin-bottom: 12px;
            }

            .input-card textarea {
                flex: 1;
                background: rgba(255, 255, 255, 0.04);
                border: 1px solid rgba(255, 255, 255, 0.08);
                border-radius: 16px;
                padding: 20px;
                font-family: 'Inter', sans-serif;
                font-size: 16px;
                line-height: 1.7;
                color: #e8edf5;
                resize: none;
                outline: none;
                transition: all 0.3s ease;
                min-height: 140px;
            }

            .input-card textarea::placeholder {
                color: rgba(255, 255, 255, 0.2);
                font-weight: 400;
            }

            .input-card textarea:focus {
                border-color: rgba(167, 139, 250, 0.4);
                background: rgba(255, 255, 255, 0.06);
                box-shadow: 0 0 0 4px rgba(167, 139, 250, 0.05);
            }

            /* ===== 提示词示例快速填充 ===== */
            .prompt-examples {
                display: flex;
                gap: 8px;
                flex-wrap: wrap;
                margin: 16px 0 20px 0;
            }

            .prompt-examples button {
                background: rgba(255, 255, 255, 0.05);
                border: 1px solid rgba(255, 255, 255, 0.06);
                border-radius: 100px;
                padding: 6px 16px;
                font-size: 12px;
                font-family: 'Inter', sans-serif;
                color: rgba(255, 255, 255, 0.5);
                cursor: pointer;
                transition: all 0.2s ease;
                font-weight: 500;
            }

            .prompt-examples button:hover {
                background: rgba(167, 139, 250, 0.15);
                color: #c4b5fd;
                border-color: rgba(167, 139, 250, 0.2);
            }

            /* ===== 生成按钮 ===== */
            .generate-btn {
                width: 100%;
                padding: 16px 24px;
                background: linear-gradient(135deg, #6C5CE7, #8b5cf6);
                border: none;
                border-radius: 16px;
                font-family: 'Inter', sans-serif;
                font-size: 16px;
                font-weight: 600;
                color: #ffffff;
                cursor: pointer;
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                letter-spacing: 0.3px;
                position: relative;
                overflow: hidden;
                margin-top: auto;
            }

            .generate-btn:hover {
                transform: translateY(-2px);
                box-shadow: 0 12px 40px rgba(107, 92, 231, 0.35);
            }

            .generate-btn:active {
                transform: translateY(0px);
                box-shadow: 0 4px 20px rgba(107, 92, 231, 0.2);
            }

            .generate-btn:disabled {
                opacity: 0.5;
                cursor: not-allowed;
                transform: none !important;
                box-shadow: none !important;
            }

            /* ===== 右卡片:输出区 ===== */
            .output-card {
                background: rgba(255, 255, 255, 0.03);
                border-radius: 24px;
                border: 1px solid rgba(255, 255, 255, 0.06);
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                padding: 24px;
                position: relative;
                min-height: 300px;
                overflow: hidden;
            }

            .output-card .placeholder {
                color: rgba(255, 255, 255, 0.12);
                font-size: 14px;
                font-weight: 500;
                letter-spacing: 0.5px;
                text-align: center;
                line-height: 1.8;
            }

            .output-card .placeholder .icon {
                font-size: 48px;
                display: block;
                margin-bottom: 16px;
                opacity: 0.3;
            }

            #result-image {
                max-width: 100%;
                max-height: 100%;
                border-radius: 16px;
                display: none;
                object-fit: contain;
                box-shadow: 0 8px 40px rgba(0, 0, 0, 0.6);
                transition: opacity 0.6s ease;
            }

            #result-image.visible {
                display: block;
                animation: fadeIn 0.6s ease;
            }

            @keyframes fadeIn {
                0% { opacity: 0; transform: scale(0.96); }
                100% { opacity: 1; transform: scale(1); }
            }

            /* ===== 状态提示 ===== */
            #status {
                margin-top: 16px;
                font-size: 13px;
                font-weight: 500;
                color: rgba(255, 255, 255, 0.3);
                text-align: center;
                transition: all 0.3s ease;
                font-family: 'JetBrains Mono', monospace;
                min-height: 24px;
            }

            #status.loading {
                color: #a78bfa;
            }

            #status.success {
                color: #34d399;
            }

            #status.error {
                color: #f87171;
            }

            /* ===== 响应式:平板 ===== */
            @media (max-width: 1024px) {
                .container {
                    aspect-ratio: auto;
                    padding: 32px 28px;
                    border-radius: 28px;
                }
                .main-grid {
                    grid-template-columns: 1fr;
                    gap: 28px;
                }
                .input-card textarea {
                    min-height: 100px;
                }
            }

            /* ===== 响应式:手机 ===== */
            @media (max-width: 640px) {
                body {
                    padding: 16px;
                }
                .container {
                    padding: 24px 16px;
                    border-radius: 20px;
                }
                .header {
                    flex-direction: column;
                    align-items: flex-start;
                    gap: 12px;
                    margin-bottom: 24px;
                }
                .brand h1 {
                    font-size: 18px;
                }
                .input-card {
                    padding: 20px;
                }
                .input-card textarea {
                    font-size: 14px;
                    padding: 14px;
                    min-height: 80px;
                }
                .generate-btn {
                    padding: 14px 20px;
                    font-size: 14px;
                }
                .badge {
                    font-size: 10px;
                    padding: 4px 12px;
                }
            }
        </style>
    </head>
    <body>

        <div class="container">
            <!-- ===== 头部 ===== -->
            <header class="header">
                <div class="brand">
                    <div class="brand-icon">✦</div>
                    <h1>Flux <span>· 真实感</span></h1>
                </div>
                <span class="badge">Hugging Face · XLabs-AI</span>
            </header>

            <!-- ===== 主区域 Grid ===== -->
            <div class="main-grid">
                <!-- 左:输入卡片 -->
                <div class="input-card">
                    <label for="prompt">提示词</label>
                    <textarea id="prompt" placeholder="描述你想要的真实世界画面…&#10;例如:A photorealistic cat sitting on a wooden table, soft lighting, 8k">A photorealistic orange tabby cat sitting on a rustic wooden table, soft sunlight streaming through a window, cinematic lighting, shallow depth of field, highly detailed, 8k, national geographic style</textarea>

                    <!-- 快速示例 -->
                    <div class="prompt-examples">
                        <button data-prompt="A photorealistic Japanese street at night, neon lights reflecting on wet pavement, cinematic, 8k, ultra detailed">🌃 雨夜街头</button>
                        <button data-prompt="A photorealistic close-up portrait of a young woman with freckles, natural sunlight, shallow depth of field, sharp focus, 8k, shot on Canon EOS R5">👩 人像特写</button>
                        <button data-prompt="A photorealistic macro shot of a dew-covered rose petal, morning light, bokeh background, hyper-detailed, 8k, nature photography">🌹 微距花卉</button>
                    </div>

                    <button class="generate-btn" id="generateBtn" onclick="generateImage()">✦ 生成图像</button>
                </div>

                <!-- 右:输出卡片 -->
                <div class="output-card" id="outputCard">
                    <div class="placeholder" id="placeholder">
                        <span class="icon">🖼️</span>
                        输入提示词,点击生成<br />
                        <span style="font-size:12px;opacity:0.5;">Flux 真实感模型 · 约 10–30 秒</span>
                    </div>
                    <img id="result-image" alt="生成的图像" />
                    <div id="status">就绪</div>
                </div>
            </div>
        </div>

        <!-- ===== JavaScript ===== -->
        <script>
            // 快速填充示例
            document.querySelectorAll('.prompt-examples button').forEach(btn => {
                btn.addEventListener('click', function() {
                    document.getElementById('prompt').value = this.dataset.prompt;
                });
            });

            async function generateImage() {
                const prompt = document.getElementById('prompt');
                const btn = document.getElementById('generateBtn');
                const img = document.getElementById('result-image');
                const placeholder = document.getElementById('placeholder');
                const status = document.getElementById('status');

                const text = prompt.value.trim();
                if (!text) {
                    alert('请先输入一段提示词!');
                    return;
                }

                // UI 状态:加载中
                btn.disabled = true;
                btn.textContent = '⏳ 生成中...';
                status.textContent = '⏳ 正在请求 AI 模型…';
                status.className = 'loading';
                img.className = '';
                img.style.display = 'none';
                placeholder.style.display = 'block';

                try {
                    const response = await fetch('/generate', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ prompt: text })
                    });

                    const data = await response.json();

                    if (!response.ok || data.error) {
                        throw new Error(data.error || '服务器处理失败');
                    }

                    // 成功:显示图片
                    img.src = 'data:image/png;base64,' + data.image;
                    img.style.display = 'block';
                    img.className = 'visible';
                    placeholder.style.display = 'none';
                    status.textContent = '✅ 生成成功!';
                    status.className = 'success';

                } catch (error) {
                    status.textContent = '❌ ' + error.message;
                    status.className = 'error';
                    img.style.display = 'none';
                    placeholder.style.display = 'block';
                } finally {
                    btn.disabled = false;
                    btn.textContent = '✦ 生成图像';
                }
            }

            // 回车快捷键(Ctrl+Enter 生成)
            document.getElementById('prompt').addEventListener('keydown', function(e) {
                if (e.ctrlKey && e.key === 'Enter') {
                    e.preventDefault();
                    generateImage();
                }
            });
        </script>

    </body>
    </html>
    '''


# ==================== 第5步:后端接口 ====================
@app.route('/generate', methods=['POST'])
def generate():
    data = request.get_json()
    user_prompt = data.get('prompt', '').strip()

    if not user_prompt:
        return jsonify({'error': '提示词不能为空'}), 400

    try:
        print(f"📡 正在请求 AI 模型,提示词为: {user_prompt}")
        image: Image.Image = client.text_to_image(user_prompt, model=MODEL_ID)

        buffered = io.BytesIO()
        image.save(buffered, format="PNG")
        img_base64 = base64.b64encode(buffered.getvalue()).decode('utf-8')

        return jsonify({'image': img_base64})

    except Exception as e:
        print(f"🔥 API 调用异常: {e}")
        return jsonify({'error': f'AI 服务异常: {str(e)}'}), 500


# ==================== 第6步:启动服务 ====================
if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)

1.3 截图

  • 运行记录

debug

  • 前端页面

prompt1

prompt2

prompt3

1.4 提示词

增量修饰实现具体化迭代
  • 内容:主体+环境+细节(材质)
  • 形式:规格+风格+效果(光照)

1.5 总结

熟悉了 huggingface 模型 API 调用的流程,额度与账号注册限制增加了完成作业的不确定性。此外,前后端代码实现门槛较高,需要大量依赖 AI

2. 个人主页搭建


前往

3. 自我评估


3.1 已具备的专业知识和能力

类型 具体(程度)
基础理论 数据结构与算法、计算机组成原理、计算机网络、操作系统、计算机图形学
编程语言 C/C++ (进阶 - 竞赛)、C# (基础 - unity)、Python (基础)、Shaderlab / HLSL (入门)
工具环境 Linux / Git (服务器)、Unity、DCC (PS / Blender / 3dsMax)
AI 应用 提示词工程、Agent部署(ClaudeCode+Deepseek)

3.2 感兴趣的技术方向

  • 技术美术
  • 计算机动画与游戏设计
  • 全栈开发

3.3 目前欠缺的能力

能力 具体
系统化软件开发 参与完整软件工程生命周期
后端开发技术 Spring Boot、Django 等主流框架
前端工程化 HTML/CSS/JavaScript 外,React/Vue 等主流框架
数据库设计 SQL 操作,数据库设计与优化
软件测试与质量保障 测试理论与工具
设计模式与架构设计 设计方法论
团队协作与项目管理 多人协作开发
  • 软件开发有关代码界定模糊,总代码量不便统计,只讨论编程代码目前约一万行。截至目前所有课程缺少系统学习软件开发编程,且对开发过程代码增量暂无概念,借助 AI 的非个人编程代码量无参考价值,只能说明构建了链条,故不做预期。
  • 对于课程期望,希望能完备开发理论、熟悉开发全程、培养开发思维、锻炼架构能力,同步推进学习开发工具

4、课程学习规划


理论:过程框架(沟通→策划→建模→构建→部署)、需求工程、设计原则(抽象/模块化/信息隐藏/耦合与内聚)、测试策略、质量保障、团队协作

一、认知框架

点击查看

1.1 软件工程的本质

核心:如何将模糊的用户需求,经过系统化的分析、设计、实现和验证,转化为可运行、可维护、可扩展的软件产品。

1.2 生命周期模型

模型 核心思想 适用场景 课程项目建议
瀑布模型 阶段线性推进,每阶段有明确产出 需求稳定、规模可控 理解其“阶段划分”思想即可
敏捷开发 迭代增量,快速响应变化 需求不确定、需要快速交付 推荐采用:适合课程项目节奏
螺旋模型 迭代+风险驱动 大型高风险项目 理解其“风险优先”思想

理论“过程框架将软件工程活动归纳为沟通、策划、建模、构建、部署五个大类。敏捷开发是这一框架在“快速迭代”场景下的具体实践。

1.3 三要素框架

每个开发阶段都包含三个维度:

维度 含义 举例(以需求分析为例)
过程 要做哪些事、产出什么 需求获取→分析→建模→验证→SRS文档
方法 用什么方法论指导 用例驱动分析、用户故事
工具 用什么工具辅助 Visual Paradigm、Draw.io

1.4 课程知识地图

软件工程课程
├── 需求工程 ──────── 需求获取、分析、建模、验证
├── 软件设计 ──────── 架构设计、模块划分、接口设计、数据库设计
├── 编码实现 ──────── 编码规范、版本控制、代码审查
├── 软件测试 ──────── 单元测试、集成测试、系统测试、验收测试
├── 项目管理 ──────── 进度管理、风险管理、团队协作
└── 质量保障 ──────── 质量模型、评审、度量

1.5 学习路径设计原则

  • 整体优先:先理解全链条,再深入各阶段细节
  • 项目驱动:每学一个阶段,即在团队项目中实践对应内容
  • 理论-实践循环:课堂学理论 → 项目做实践 → 复盘提炼认知

二、五阶段详解

点击查看

阶段一:需求分析

维度 内容
是什么 将“用户想要什么”转化为“开发者能理解的、无歧义的规范描述”。核心产出:《软件需求规格说明书》(SRS)。
为什么 大量项目失败的根本原因不是技术不行,而是需求从一开始就理解错了。需求阶段修正问题的成本是编码阶段的1/10。
怎么做 ① 需求获取:访谈、问卷、原型演示
② 需求分析:区分功能性需求与非功能性需求
③ 需求建模:用例图、活动图、用户故事
④ 需求验证:与用户确认
⑤ 编写SRS
核心方法 用例驱动分析(适合交互型系统)、用户故事(适合敏捷场景)、领域建模(适合复杂业务)
主流工具 Visual Paradigm(UML建模)、Draw.io(轻量绘图)、Figma(原型设计)
课程关联 软件工程“需求工程”章节;配套SRS编写实验
实践任务 选取一个核心模块,完成:① 用户故事 ② 用例图(≥3个用例)③ 用例规约(前置条件/主流程/异常流程)

理论:需求工程分为获取、分析、规约、验证、管理五个活动。需求分析的核心是搞清楚用户真正需要什么,而非用户说了什么。

阶段二:系统设计

维度 内容
是什么 将需求转化为可实施的技术方案。需求回答“做什么”,设计回答“怎么做”。核心产出:架构图、数据库设计、API规范。
为什么 设计决定了系统的架构质量。架构缺陷在后期修复的成本极高,甚至需要推倒重来。
怎么做 ① 架构设计:选择架构模式
② 模块划分:高内聚、低耦合
③ 接口设计:REST API规范
④ 数据库设计:E-R图、表结构
⑤ UI/UX设计
架构模式 分层架构(MVC) :表现层/业务逻辑层/数据访问层,适合中小型项目, 课程项目首选
微服务架构:按业务拆分独立服务,适合大型系统,理解思想即可
事件驱动架构:消息队列解耦,适合异步场景,可用于通知等辅助模块
主流工具 MySQL Workbench(数据库设计)、Swagger/OpenAPI(接口设计)、C4 Model + Structurizr(架构描述)
课程关联 软件工程“软件设计”章节;数据库系统原理“数据库设计”实验
实践任务 ① 画三层架构图 ② MySQL Workbench设计≥3张核心表+E-R图 ③ Swagger定义≥5个REST API

理论回扣:设计概念包括抽象、模块化、信息隐藏、耦合与内聚。好的设计是让修改一个功能时,不需要改动其他功能。

阶段三:编码实现

维度 内容
是什么 将设计方案转化为可运行代码。不只是实现功能,还包括遵循编码规范、编写单元测试、进行代码审查、使用版本控制。
为什么 编码是设计的具体化。没有规范的编码,代码将难以维护、难以协作、难以扩展。
怎么做 ① 搭建开发环境 ② 按模块编码 ③ 遵循代码规范 ④ Git版本管理(小步提交、频繁集成)⑤ 编写单元测试
主流工具 VS Code / PyCharm / IntelliJ IDEA(IDE)、Postman/Apifox(API调试)、DBeaver/Navicat(数据库管理)、Git/GitHub(版本控制)
技术栈选择(决策框架)

前端技术栈

基础层                    生产层
├── HTML(结构)            ├── Vue 3 + TypeScript
├── CSS(表现)             ├── React + TypeScript
└── JavaScript(行为)      └── Element Plus / Ant Design

说明:HTML/CSS/JavaScript是前端的基础层,Vue/React是构建在基础层之上的效率工。课程项目推荐 Vue 3 + TypeScript + Element Plus——学习曲线平缓,组件丰富,适合快速构建界面。

后端语言选择

语言 框架 优势
**JavaScript / TypeScript Express / NestJS / Fastify 前后端同语言,npm生态庞大,适合I/O密集、实时应用、API服务
Java Spring Boot 行业主流,生态成熟,就业面广
C# ASP.NET Core 与Unity C#经验衔接
Python FastAPI / Django 推荐:上手快,与AI结合好
AI辅助编码
工具 用途 推荐场景
GitHub Copilot 代码补全、多文件重构 日常编码
Cursor AI驱动的代码编辑 复杂重构
Claude Code 任务级智能体,读取整个代码库 多步修改、测试生成

| 课程关联 | 面向对象程序设计、高级语言程序设计、数据结构与算法 |
| 实践任务 | ① 搭建项目基础框架 ② 实现≥1个端到端功能 ③ Git提交≥20次有意义的commit |

理论:代码规范是团队协作的基础。编码规范、代码审查、单元测试是保证代码质量的三道防线。

阶段四:测试验证

维度 内容
是什么 发现系统缺陷,验证系统是否满足需求,建立对软件质量的信心。测试不是“开发完之后随便点一点”,而是有策略、有层次、有工具支撑的系统化过程。
为什么 质量是构建出来的,不是测试出来的。但测试是验证质量是否达标的关键手段。
怎么做 ① 单元测试:单个函数/方法 ② 集成测试:模块间接口 ③ 系统测试:端到端功能 ④ 验收测试:是否满足用户需求
测试层次 单元测试(pytest/Jest/JUnit)→ 集成测试(Testcontainers/Mockito)→ 系统测试(Playwright/Cypress)→ 验收测试(手工+自动化)
AI辅助测试 GitHub Copilot Testing(直接生成单元测试)、code-testing-generator(多语言测试生成)
课程关联 软件工程“软件测试”章节
实践任务 ① 后端≥2个核心函数单元测试,覆盖率≥70% ② 前端≥1个端到端测试 ③ 记录≥2个缺陷及修复方案

理论:测试策略包括单元测试、集成测试、确认测试、系统测试。测试的目的是发现错误,而不是证明程序没有错误。

阶段五:部署与运维

维度 内容
是什么 将开发完成的软件发布到生产环境,确保运行稳定、高效。核心产出:可访问的线上系统。
为什么 软件的价值在于交付给用户使用。部署是软件从“代码”变为“产品”的关键环节。
怎么做 ① 环境配置:服务器、依赖、环境变量 ② CI/CD:自动构建、测试、部署 ③ 容器化:Docker打包 ④ 监控与日志
主流工具 GitHub Actions(CI/CD,采用率33%领先)、Docker + Kubernetes(容器化)、Jenkins(CI,28%)、GitLab CI/CD(19%)
课程关联 计算机网络(HTTP协议、端口)、计算机操作系统(Linux命令、进程管理)
实践任务 ① 编写Dockerfile ② 配置GitHub Actions流水线 ③ 部署前端静态页面(GitHub Pages)

理论:“部署”作为过程框架的最后一个活动。软件发布不是终点,而是运维的起点。

三、贯穿性主题

点击查看

3.1 团队协作与版本控制

实践 要点
Git工作流 小步提交、频繁集成、分支管理(feature branch)
代码审查 每次合并请求至少一人审查,关注逻辑、规范、安全
任务管理 明确分工,定期同步(每日站会/每周例会)
文档同步 文档与代码同步更新,不是“做完再补”

理论:“团队合作”列为软件工程的核心要素之一,团队协作的本质是降低沟通成本。

3.2 质量保障

层次 手段
代码质量 编码规范、静态分析、代码审查
测试质量 单元测试、集成测试、覆盖率度量
过程质量 里程碑评审、风险跟踪、持续改进

3.3 AI辅助开发

阶段 AI用法 推荐工具
需求分析 分析访谈记录、生成用例图初稿 Visual Paradigm AI、ChatGPT
系统设计 辅助生成数据库表结构、API定义 Cursor、Claude
编码实现 代码补全、多文件重构、代码解释 GitHub Copilot、Cursor、Claude Code
测试验证 自动生成单元测试、测试套件 GitHub Copilot Testing
代码审查 AI驱动的PR审查、安全扫描 CodeRabbit、SonarQube
部署运维 生成Dockerfile、CI/CD配置 GitHub Copilot、Amazon Q Developer

四、综合实践

点击查看

项目建议

项目名称:个人任务管理系统(To-Do List + 团队协作)

技术栈:Vue 3 + TypeScript(前端)/ Python FastAPI(后端)/ MySQL + Redis(数据层)/ Docker + GitHub Actions(部署)

周次安排

周次 阶段 具体任务 产出
第1-2周 需求分析 用户故事、用例图、SRS SRS文档 + 用例图
第3-4周 系统设计 三层架构图、E-R图、REST API 设计文档 + API规范
第5-9周 编码实现 前后端开发、Git管理、代码审查 可运行系统
第10-11周 测试验证 单元测试 + 端到端测试 测试报告
第12-14周 部署运维 Docker容器化、CI/CD、上线 线上可访问系统

验收标准

  • 系统完整运行,核心功能可用
  • Git提交≥30次有意义的commit
  • 单元测试覆盖率≥60%
  • 完整项目文档(SRS + 设计文档 + API文档 + 测试报告)
  • CI/CD流水线自动部署

附:博客园 Markdown 编辑器配置截图

md