"""
课程内容 - 为每个课程提供丰富的Markdown内容
"""
from typing import Optional
def get_lesson_content(lesson_id: str, title: str, lesson_type: str, description: str) -> str:
"""根据课程ID返回对应的Markdown内容"""
# 优先查找特定课程的内容
custom = _get_custom_content(lesson_id, title)
if custom:
return custom
# 通用模板
return _get_generic_content(title, description, lesson_type)
def _get_custom_content(lesson_id: str, title: str) -> Optional[str]:
"""为特定课程ID返回个性化内容"""
content_map = {
# ======== 前端 - HTML/CSS ========
"fe-html-01": """## 学习目标
- 理解 HTML 文档的基本结构
- 掌握 DOCTYPE、html、head、body 等核心标签
- 了解 meta 标签的常见用法
## HTML 文档的基本结构
每个 HTML 文档都遵循一个基本的骨架结构:
```html
文档标题
Hello, World!
这是我的第一个网页。
```
### 各部分作用
| 标签 | 作用 |
|------|------|
| `` | 声明文档类型为 HTML5 |
| `` | 文档的根元素,所有内容包含在内 |
| `` | 文档的元数据区(不可见) |
| `` | 文档的内容区(用户可见) |
### `` 中的常用标签
- **``** — 浏览器标签页显示的标题,对 SEO 也很重要
- **``** — 设置字符编码,避免乱码
- **``** — 移动端适配声明
- **``** — 引入外部 CSS 文件
- **`
```
**3. 行内样式(不推荐)**
```html
这段文字是红色
```
## 三种基本选择器
### 元素选择器
选中所有该类型的标签:
```css
p {{ color: #333; }}
h1 {{ font-size: 24px; }}
```
### 类选择器(`.`)
选中所有带有该 class 的元素,可重复使用:
```css
.highlight {{ background-color: yellow; }}
.card {{ border: 1px solid #ccc; }}
```
```html
这段有高亮背景
这是一个卡片
```
### ID 选择器(`#`)
**唯一**,一个页面中每个 ID 只能使用一次:
```css
#header {{ height: 60px; }}
#main-content {{ padding: 20px; }}
```
```html
```
## 优先级(权重)
当多个选择器冲突时,按权重决定:
| 选择器 | 权重 | 示例 |
|--------|------|------|
| 元素选择器 | 最低 | `p`、`h1` |
| 类选择器 | 中等 | `.card`、`.highlight` |
| ID选择器 | 最高 | `#header` |
| 行内样式 | 更高 | `style="..."` |
| `!important` | 最高(慎用) | `color: red !important` |
> 🧪 **实验**:给同一个元素同时设置类和 ID 样式,观察哪个生效。
## 练习
创建一个 HTML 页面并添加 CSS:
1. 用元素选择器设置全局字体
2. 用类选择器创建两个不同颜色的卡片
3. 用 ID 选择器设置页面标题的样式
""",
"fe-css-02": """## 学习目标
- 理解盒模型的四个组成部分
- 掌握 width/height、padding、border、margin 的用法
- 学会使用 `box-sizing` 控制盒模型行为
## 盒模型
每个 HTML 元素都可以看作一个"盒子",从内到外包含:
```
┌─────────────────────────────────┐
│ Margin (外边距) │
│ ┌───────────────────────────┐ │
│ │ Border (边框) │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ Padding (内边距) │ │ │
│ │ │ ┌───────────────┐ │ │ │
│ │ │ │ Content │ │ │ │
│ │ │ │ (内容区域) │ │ │ │
│ │ │ └───────────────┘ │ │ │
│ │ └─────────────────────┘ │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
```
### 代码示例
```css
.box {{
width: 200px;
padding: 20px; /* 内容与边框之间的距离 */
border: 2px solid #333; /* 边框 */
margin: 10px; /* 盒子与其他元素的距离 */
}}
```
### 盒模型的计算
默认情况下(`box-sizing: content-box`):
**实际宽度 = width + padding × 2 + border × 2**
这意味着 `width: 200px` 加上 `padding: 20px` 和 `border: 2px` 后,实际占用的宽度是 **244px**!
### `box-sizing: border-box`
推荐做法——让 width 包含 padding 和 border:
```css
* {{
box-sizing: border-box;
}}
```
这样 `width: 200px` 就是最终渲染宽度,padding 和 border 向内压缩。
## display 属性
| 值 | 行为 |
|----|------|
| `block` | 块级,独占一行 |
| `inline` | 行内,不换行 |
| `inline-block` | 行内但可设宽高 |
| `none` | 隐藏元素,不占空间 |
## 练习
用 HTML + CSS 实现下图效果(描述:三个卡片并排,每个卡片有标题、文字、边框和间距):
1. 三个 `div` 卡片横向排列
2. 每个卡片有 1px 边框、16px 内边距
3. 卡片之间用 margin 隔开
4. 使用 `box-sizing: border-box`
""",
"fe-css-03": """## 学习目标
- 理解 Flexbox 的核心概念:主轴与交叉轴
- 掌握容器属性和项目属性
- 能使用 Flexbox 实现常见布局
## Flexbox 核心概念
Flexbox 是一种一维布局模型,适合在**一行或一列**中排列元素。
```css
.container {{
display: flex; /* 开启 Flexbox */
}}
```
### 主轴与交叉轴
- **主轴(main axis)** — `flex-direction` 决定的方向
- **交叉轴(cross axis)** — 与主轴垂直的方向
```
flex-direction: row; → 主轴水平,从左到右
flex-direction: column; → 主轴垂直,从上到下
```
## 容器属性
```css
.container {{
display: flex;
flex-direction: row; /* row | column | row-reverse | column-reverse */
justify-content: center; /* 主轴对齐方式 */
align-items: center; /* 交叉轴对齐方式 */
flex-wrap: wrap; /* 是否换行 */
gap: 16px; /* 项目间距(推荐) */
}}
```
### justify-content
```
flex-start ┃ [项目1][项目2][项目3]
center ┃ [项目1][项目2][项目3]
space-between ┃ [项目1] [项目2] [项目3]
space-around ┃ [项目1] [项目2] [项目3]
```
### align-items
```
stretch ┃ 项目高度拉伸填满容器(默认)
center ┃ 项目在交叉轴居中
flex-start ┃ 项目在交叉轴起始位置
```
## 项目属性
```css
.item {{
flex: 1; /* 分配剩余空间的比例 */
align-self: center; /* 单独对齐 */
order: 2; /* 排列顺序(越小越前) */
}}
```
## 常见布局示例
### 水平居中
```css
.parent {{
display: flex;
justify-content: center;
align-items: center;
}}
```
### 两端对齐导航
```css
.nav {{
display: flex;
justify-content: space-between;
align-items: center;
}}
```
### 响应式卡片网格
```css
.grid {{
display: flex;
flex-wrap: wrap;
gap: 20px;
}}
.card {{
flex: 1 1 300px; /* 最小300px,自动换行 */
}}
```
## 练习
用 Flexbox 实现一个导航栏:
- 左侧是 Logo
- 中间是导航链接(首页、关于、服务、联系)
- 右侧是登录/注册按钮
- 垂直居中
""",
# ======== 前端 - JavaScript ========
"fe-js-01": """## 学习目标
- 理解 `let`、`const`、`var` 的区别
- 掌握 JavaScript 的基本数据类型
- 了解引用类型与基本类型的差异
## 变量声明
### `let` vs `const` vs `var`
| 关键字 | 可修改 | 作用域 | 推荐 |
|--------|--------|--------|------|
| `const` | ❌ 不可重新赋值 | 块级作用域 | **默认选择** |
| `let` | ✅ 可重新赋值 | 块级作用域 | 需要改变时使用 |
| `var` | ✅ 可重新赋值 | 函数作用域 | ❌ 避免使用 |
```javascript
const PI = 3.14159;
let count = 0;
count = 1; // ✅ 可以
// 常见错误
const user = {{ name: 'Alice' }};
user.name = 'Bob'; // ✅ const 对象的内容可以修改
user = {{ name: 'Bob' }}; // ❌ 不能重新赋值
```
## 基本数据类型
| 类型 | 示例 | 说明 |
|------|------|------|
| `number` | `42`, `3.14` | 整数和浮点数 |
| `string` | `'hello'`, `"world"` | 文本 |
| `boolean` | `true`, `false` | 布尔值 |
| `null` | `null` | 空值 |
| `undefined` | `undefined` | 未定义 |
| `symbol` | `Symbol()` | 唯一标识符 |
```javascript
const name = 'Alice';
const age = 25;
const isStudent = true;
const score = null;
let grade; // undefined
// typeof 运算符
console.log(typeof name); // "string"
console.log(typeof age); // "number"
```
### 动态类型
JavaScript 是动态类型语言,变量可以随时改变类型:
```javascript
let value = 'hello';
value = 42; // 变成了 number
value = true; // 变成了 boolean
```
### 类型转换
```javascript
// 隐式转换
const result = '5' - 2; // 3 (字符串转数字)
const msg = '结果是: ' + 42; // "结果是: 42"
// 显式转换
Number('42') // 42
String(42) // "42"
Boolean(0) // false
Boolean('') // false
Boolean('hello') // true
```
> ⚠️ **常见陷阱**:`'5' + 2` 结果是 `'52'`(字符串拼接),而 `'5' - 2` 结果是 `3`(数值减法)。
## 引用类型
对象和数组是引用类型,赋值传递的是引用:
```javascript
const a = {{ name: 'Alice' }};
const b = a; // b 引用同一个对象
b.name = 'Bob';
console.log(a.name); // "Bob" — a 也被改了!
```
## 练习
1. 用 `const` 定义一个对象,包含你的姓名、年龄和爱好
2. 用 `let` 定义一个计数器,从 0 递增到 3
3. 分别使用 `typeof` 检查 `null`、`[]`、`{{}}` 的类型
""",
"fe-js-02": """## 学习目标
- 掌握函数定义的多种方式
- 理解作用域和闭包的概念
- 了解箭头函数的特性
## 函数定义
### 函数声明 vs 函数表达式
```javascript
// 函数声明(会被提升)
function add(a, b) {{
return a + b;
}}
// 函数表达式(不会被提升)
const multiply = function(a, b) {{
return a * b;
}};
```
### 箭头函数
```javascript
// 基本语法
const add = (a, b) => a + b;
const square = x => x * x; // 一个参数可省略括号
const greet = () => 'Hello!';
// 多行需要 {{}} 和 return
const sum = (a, b) => {{
const result = a + b;
return result;
}};
```
### 箭头函数 vs 普通函数
| 区别 | 普通函数 | 箭头函数 |
|------|----------|----------|
| `this` | 动态绑定 | 继承外层作用域 |
| `arguments` | 有 | 没有 |
| 作为构造函数 | ✅ | ❌ |
## 作用域
```javascript
const global = '全局变量';
function outer() {{
const outerVar = '外部函数变量';
function inner() {{
const innerVar = '内部函数变量';
console.log(global); // ✅ 可访问
console.log(outerVar); // ✅ 可访问
}}
console.log(innerVar); // ❌ 不可访问
}}
```
## 闭包
函数 + 其被创建时所在的作用域环境的组合:
```javascript
function createCounter() {{
let count = 0;
return function() {{
count++;
return count;
}};
}}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
```
> 💡 **用途**:数据私有化、函数工厂、模块模式
## 练习
1. 写一个箭头函数 `isEven(n)` 判断数字是否为偶数
2. 用闭包实现一个 `makeMultiplier(x)`,返回一个乘以 `x` 的函数
3. 比较普通函数和箭头函数中 `this` 的行为差异
""",
# ======== 前端 - Vue ========
"fe-vue-01": """## 学习目标
- 理解 Vue 3 的核心概念
- 掌握 `ref` 和 `reactive` 响应式 API
- 学会使用模板语法
## 什么是 Vue?
Vue 是一个用于构建用户界面的**渐进式框架**。核心特性:
- **声明式渲染** — 通过模板语法将数据绑定到 DOM
- **响应式系统** — 数据变化自动更新视图
- **组件化** — UI 拆分为独立的可复用组件
## 创建 Vue 应用
```javascript
import {{ createApp, ref }} from 'vue'
createApp({{
setup() {{
const count = ref(0)
const increment = () => count.value++
return {{ count, increment }}
}}
}}).mount('#app')
```
```html
计数: {{ "{{ count }}" }}
```
## 响应式 API
### `ref` — 基本响应式
```javascript
import {{ ref }} from 'vue'
const count = ref(0)
console.log(count.value) // 0
count.value = 1
```
> 在模板中使用时自动解包,不需要 `.value`
### `reactive` — 对象响应式
```javascript
import {{ reactive }} from 'vue'
const user = reactive({{
name: 'Alice',
age: 25
}})
user.age = 26 // 直接修改,无需 .value
```
### `computed` — 计算属性
```javascript
import {{ ref, computed }} from 'vue'
const price = ref(100)
const quantity = ref(2)
const total = computed(() => price.value * quantity.value)
```
## 模板语法
```html
{{ "{{ message }}" }}
可见
{{ "{{ item }}" }}
```
## 练习
创建一个简单的 Vue 应用:
1. 用 `ref` 定义用户名和年龄
2. 用 `computed` 计算是否成年(>=18)
3. 在模板中展示这些数据
""",
# ======== 后端 - Python ========
"be-py-01": """## 学习目标
- 回顾 Python 基础语法
- 理解列表推导式、装饰器等进阶用法
- 掌握 Python 常见最佳实践
## Python 基础回顾
```python
# 变量与类型
name: str = "Python"
version: float = 3.11
# 列表推导式
squares = [x**2 for x in range(10) if x % 2 == 0]
# 字典操作
user = {{"name": "Alice", "age": 25}}
print(user.get("name", "Unknown"))
```
## 装饰器
装饰器是一种在不修改原函数代码的情况下扩展其功能的方式:
```python
from functools import wraps
def log_calls(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[LOG] 调用 {{func.__name__}}")
result = func(*args, **kwargs)
print(f"[LOG] {{func.__name__}} 返回 {{result}}")
return result
return wrapper
@log_calls
def add(a, b):
return a + b
add(3, 5)
# [LOG] 调用 add
# [LOG] add 返回 8
```
## 上下文管理器
```python
# 使用 with 语句
with open("file.txt", "r") as f:
content = f.read()
# 自定义上下文管理器
from contextlib import contextmanager
@contextmanager
def timer():
import time
start = time.time()
try:
yield
finally:
elapsed = time.time() - start
print(f"耗时: {{elapsed:.2f}}秒")
with timer():
sum(range(1000000))
```
## 类型提示
```python
from typing import List, Optional, Dict
def process_users(users: List[Dict[str, str]]) -> List[str]:
\"\"\"提取所有用户名\"\"\"
return [u.get("name", "") for u in users]
def find_user(id: int) -> Optional[Dict[str, str]]:
\"\"\"根据 ID 查找用户\"\"\"
return None # 未找到
```
## 练习
1. 编写一个装饰器 `@retry(max_attempts=3)`,让函数在抛出异常时自动重试
2. 写一个上下文管理器,用于测量代码块的执行时间
3. 使用类型提示定义一个函数签名,处理用户列表数据
""",
"be-py-02": """## 学习目标
- 理解文件读写的基本模式
- 掌握异常处理的正确方式
- 学会使用标准库处理常见任务
## 文件操作
```python
# 推荐方式:使用 with 语句
with open("data.txt", "r", encoding="utf-8") as f:
content = f.read()
# 逐行读取
with open("data.txt", "r") as f:
for line in f:
print(line.strip())
# 写入文件
with open("output.txt", "w") as f:
f.write("Hello, World!\\n")
```
## 异常处理
```python
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"不能除以零: {{e}}")
except ValueError as e:
print(f"值错误: {{e}}")
except Exception as e:
print(f"未知错误: {{e}}")
else:
print("没有发生异常")
finally:
print("总是执行")
```
### 自定义异常
```python
class ValidationError(Exception):
\"\"\"数据验证失败\"\"\"
pass
def validate_age(age: int):
if age < 0 or age > 150:
raise ValidationError(f"无效年龄: {{age}}")
```
## 标准库常用模块
```python
import json
import os
import sys
from datetime import datetime, timedelta
# JSON 处理
data = {{"name": "Alice", "age": 25}}
json_str = json.dumps(data, ensure_ascii=False)
parsed = json.loads(json_str)
# 路径操作
path = os.path.join("data", "subdir", "file.txt")
print(os.path.exists(path))
# 日期时间
now = datetime.now()
tomorrow = now + timedelta(days=1)
print(now.strftime("%Y-%m-%d %H:%M:%S"))
```
## 练习
1. 读取一个 JSON 配置文件,解析其中的设置
2. 编写一个函数,安全地将字符串转换为整数,转换失败时返回 None
3. 使用 `os` 模块遍历一个目录下的所有 Python 文件
4. 使用 `datetime` 计算距离下个生日还有多少天
""",
# ======== 后端 - API ========
"be-api-01": """## 学习目标
- 理解 REST API 的核心原则
- 掌握 HTTP 方法、状态码的正确使用
- 学会设计资源导向的 URL
## REST 核心原则
REST(Representational State Transfer)是一种 API 设计风格:
1. **资源导向** — URL 表示资源(名词),而不是操作(动词)
2. **HTTP 方法表示操作** — GET/ POST/ PUT/ DELETE
3. **无状态** — 每个请求包含所有必要信息
4. **统一接口** — 一致的 URL 模式和响应格式
## HTTP 方法
| 方法 | 作用 | 幂等 | 请求体 |
|------|------|------|--------|
| GET | 获取资源 | ✅ | 通常无 |
| POST | 创建资源 | ❌ | 有 |
| PUT | 完整更新资源 | ✅ | 有 |
| PATCH | 部分更新资源 | ❌ | 有 |
| DELETE | 删除资源 | ✅ | 通常无 |
## 状态码
| 状态码 | 含义 | 使用场景 |
|--------|------|----------|
| 200 OK | 请求成功 | GET、PUT 成功 |
| 201 Created | 创建成功 | POST 创建资源 |
| 204 No Content | 成功无返回体 | DELETE 成功 |
| 400 Bad Request | 客户端请求错误 | 参数校验失败 |
| 404 Not Found | 资源不存在 | 查询不存在的 ID |
| 409 Conflict | 资源冲突 | 创建重复资源 |
| 500 Internal Server Error | 服务器错误 | 未预期的异常 |
## URL 设计
```python
# ✅ 好的设计(资源导向)
GET /api/users # 获取用户列表
GET /api/users/{{id}} # 获取单个用户
POST /api/users # 创建用户
PUT /api/users/{{id}} # 更新用户
DELETE /api/users/{{id}} # 删除用户
# ✅ 查询参数用于过滤/排序/分页
GET /api/tasks?status=done&page=2&sort=created_at
# ✅ 子资源
GET /api/users/{{id}}/posts # 用户的文章列表
# ❌ 不好的设计(动词在 URL 中)
GET /api/getUser # ❌
POST /api/createUser # ❌
POST /api/deleteUser # ❌
```
## FastAPI 快速开始
```python
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.get("/items")
async def list_items():
return [{{"id": 1, "name": "Item 1", "price": 9.99}}]
@app.post("/items")
async def create_item(item: Item):
return {{"id": 2, **item.model_dump()}}
```
## 练习
1. 为一个"博客系统"设计 RESTful API 端点(文章、评论、标签)
2. 说明每个端点的 HTTP 方法、URL、请求参数和响应状态码
""",
"be-api-02": """## 学习目标
- 掌握 FastAPI 路由和参数处理
- 理解请求体验证和响应模型
- 学会使用依赖注入
## FastAPI 路由进阶
### 路径参数与查询参数
```python
from fastapi import FastAPI, Query, Path
app = FastAPI()
@app.get("/users/{{user_id}}")
async def get_user(
user_id: int = Path(..., title="用户ID"),
include_details: bool = Query(False, title="是否包含详情")
):
return {{"user_id": user_id, "details": include_details}}
```
### 请求体验证
```python
from pydantic import BaseModel, Field
class CreateUserRequest(BaseModel):
username: str = Field(..., min_length=3, max_length=50)
email: str = Field(..., pattern=r"^[\\w.-]+@[\\w.-]+\\.\\w{{2,}}$")
age: int = Field(ge=0, le=150)
@app.post("/users")
async def create_user(user: CreateUserRequest):
return {{"message": "用户创建成功", "user": user}}
```
### 响应模型
```python
from typing import List
from pydantic import BaseModel
class UserResponse(BaseModel):
id: int
username: str
email: str
@app.get("/users", response_model=List[UserResponse])
async def list_users():
return [
UserResponse(id=1, username="alice", email="alice@example.com")
]
```
## 依赖注入
```python
from fastapi import Depends, HTTPException
def get_current_user(token: str = Query(...)):
if token != "secret":
raise HTTPException(status_code=401, detail="未授权")
return {{"id": 1, "username": "alice"}}
@app.get("/profile")
async def get_profile(user: dict = Depends(get_current_user)):
return user
```
## 错误处理
```python
from fastapi import HTTPException
from fastapi.responses import JSONResponse
@app.get("/items/{{item_id}}")
async def get_item(item_id: int):
if item_id <= 0:
raise HTTPException(
status_code=400,
detail="无效的项目ID"
)
# ... 查找项目
return {{"id": item_id, "name": "Sample Item"}}
```
## 练习
1. 创建一个 Todo 的 CRUD API(使用内存列表存储)
2. 添加参数校验(标题非空、状态只能是 pending/done)
3. 使用响应模型控制返回字段
""",
# ======== 全栈 - Web基础 ========
"fs-web-01": """## 学习目标
- 理解 HTTP 协议的基本原理
- 掌握请求-响应模型
- 了解浏览器如何加载网页
## HTTP 协议
HTTP(超文本传输协议)是 Web 的基础通信协议。
### 请求-响应模型
```
浏览器 服务器
│ │
├── GET /index.html ──────────►│
│ ├── 查找文件
│◄── 200 OK + HTML 内容 ──────┤
│ │
├── GET /style.css ───────────►│
│◄── 200 OK + CSS 内容 ───────┤
│ │
├── GET /app.js ──────────────►│
│◄── 200 OK + JS 内容 ────────┤
```
### HTTP 请求结构
```
GET /api/users HTTP/1.1
Host: example.com
Authorization: Bearer token123
Content-Type: application/json
{{"name": "Alice"}}
```
### HTTP 响应结构
```
HTTP/1.1 200 OK
Content-Type: application/json
{{"id": 1, "name": "Alice"}}
```
## 常见请求头
| 请求头 | 作用 |
|--------|------|
| `Authorization` | 认证信息 |
| `Content-Type` | 请求体格式 |
| `Accept` | 期望的响应格式 |
| `User-Agent` | 客户端标识 |
## 常见响应头
| 响应头 | 作用 |
|--------|------|
| `Content-Type` | 响应体格式 |
| `Set-Cookie` | 设置 Cookie |
| `Cache-Control` | 缓存策略 |
| `Access-Control-Allow-Origin` | CORS 设置 |
## 浏览器加载流程
1. 解析 HTML → 构建 DOM 树
2. 加载 CSS → 构建 CSSOM 树
3. 合并为渲染树(Render Tree)
4. 布局(Layout)→ 计算位置和大小
5. 绘制(Paint)→ 渲染到屏幕
> 💡 **关键**:CSS 会阻塞渲染,JavaScript 会阻塞解析。所以 `