内容结构化改造(阶段 1):新增 topics/scenarios 目录、内容规范、frontmatter schema、校验与目录生成脚本、31 个技术栈首页骨架,周文件加 frontmatter
This commit is contained in:
1 parent
32b111606f
commit
0bae9916a9
58 files changed
+2995
-5
No files matched your search
@@ -0,0 +1,2 @@
|
||||
# zhuangang 结构化内容改造进度(每完成一个阶段追加一行;重启时跳过已完成阶段)
|
||||
phase1 done 2026-10-23 15:05:36:目录设计、CONTRIBUTING、schema、tools/build_catalog.py、run_examples.py、check_sensitive.py、31 个技术栈首页骨架、周文件 frontmatter
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
# 内容规范(CONTRIBUTING)
|
||||
|
||||
本仓库是**结构化的学习内容**:既能直接在 Git 平台上阅读,也能被以后的在线学习平台导入。所有新增、修改的内容都按本规范写,提交前运行校验脚本。
|
||||
|
||||
## 1. 目录结构
|
||||
|
||||
```text
|
||||
zhuangang/
|
||||
├── README.md # 仓库入口
|
||||
├── CONTRIBUTING.md # 本文件:内容规范
|
||||
├── catalog.json # 自动生成的内容目录(不要手改)
|
||||
├── schema/frontmatter.schema.json
|
||||
├── tools/
|
||||
│ ├── build_catalog.py # 校验 + 生成 catalog.json
|
||||
│ ├── run_examples.py # 运行示例代码
|
||||
│ └── check_sensitive.py # 提交前脱敏检查(第 8 周建立)
|
||||
├── plan/ # 16 周学习计划(日程),链接到专题,不重复讲概念
|
||||
│ ├── README.md # 总纲
|
||||
│ └── weekNN-*.md # type: week
|
||||
├── topics/ # 专项学习:按领域 / 技术栈组织
|
||||
│ ├── index.md
|
||||
│ └── <domain>/<stack>/
|
||||
│ ├── index.md # type: index,技术栈首页:学习路线、课程列表
|
||||
│ ├── NN-<slug>.md # type: lesson,课程,NN 是顺序
|
||||
│ ├── scenarios/<slug>.md # type: scenario,该技术栈的企业场景
|
||||
│ ├── interview.md # type: interview,分级面试题
|
||||
│ └── examples/ # 可运行的示例代码
|
||||
└── scenarios/ # 跨技术栈的企业服务场景(type: scenario,domain: cross)
|
||||
├── index.md
|
||||
└── <slug>.md
|
||||
```
|
||||
|
||||
- `domain` 取值:`backend`(Go、数据库、缓存、消息队列、微服务)、`devops`(Docker、Nginx、Linux、CI/CD、Git)、`python`、`ai`(RAG、Agent 等)、`frontend`、`cross`(跨栈场景)、`plan`(计划)。
|
||||
- `<stack>` 是技术栈标识,只用小写字母、数字和 `-`,例如 `redis`、`grpc-microservices`、`fe-testing-perf`。
|
||||
|
||||
## 2. frontmatter
|
||||
|
||||
每个 `.md` 文件(`examples/` 下的除外)第一行开始是 frontmatter。为了让校验脚本不依赖第三方库,**只允许两种写法**:`键: 标量` 和 `键: [a, b, c]`(列表写在一行)。
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: backend.redis.cache-patterns
|
||||
title: 缓存模式:旁路缓存与穿透、击穿、雪崩
|
||||
type: lesson
|
||||
domain: backend
|
||||
stack: [redis]
|
||||
tags: [缓存, 高并发, 一致性]
|
||||
level: intermediate
|
||||
prerequisites: [backend.redis.data-structures]
|
||||
est_hours: 2
|
||||
related: [backend.redis.interview, scenario.flash-sale]
|
||||
week: [3, 14]
|
||||
order: 2
|
||||
---
|
||||
```
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `id` | 是 | 全局唯一、**发布后不再修改**(平台用它做进度记录和链接)。格式:小写字母数字,用 `.` 分段 |
|
||||
| `title` | 是 | 标题 |
|
||||
| `type` | 是 | `index` / `lesson` / `scenario` / `interview` / `project` / `week` |
|
||||
| `domain` | 是 | 见上 |
|
||||
| `stack` | lesson、scenario、interview 必填 | 列表;跨栈场景写多个 |
|
||||
| `level` | lesson、scenario、interview 必填 | `basic`(基础)/ `intermediate`(进阶)/ `advanced`(高级) |
|
||||
| `tags` | 否 | 关键词,平台用于搜索 |
|
||||
| `prerequisites` | 否 | 先修内容的 id |
|
||||
| `related` | 否 | 相关内容的 id |
|
||||
| `est_hours` | 否 | 预计学习小时数 |
|
||||
| `week` | 否 | 在 16 周计划的哪几周用到(1~16) |
|
||||
| `order` | 否 | 同一技术栈内的排序 |
|
||||
|
||||
**id 约定**:
|
||||
|
||||
| 内容 | id 格式 | 例子 |
|
||||
| --- | --- | --- |
|
||||
| 技术栈首页 | `<domain>.<stack>.index` | `backend.redis.index` |
|
||||
| 课程 | `<domain>.<stack>.<slug>` | `backend.redis.cache-patterns` |
|
||||
| 技术栈场景 | `<domain>.<stack>.scenario.<slug>` | `backend.redis.scenario.leaderboard` |
|
||||
| 面试题 | `<domain>.<stack>.interview` | `backend.redis.interview` |
|
||||
| 跨栈场景 | `scenario.<slug>` | `scenario.flash-sale` |
|
||||
| 计划 | `plan.index`、`plan.weekNN` | `plan.week03` |
|
||||
|
||||
## 3. 各类型的写法
|
||||
|
||||
### 课程(lesson)
|
||||
|
||||
必须包含以下二级标题(标题里包含这些词即可,可以加后缀):
|
||||
|
||||
- `## 解释说明`:是什么、为什么,用大白话;和 JS / 前端经验类比更好。
|
||||
- `## 案例`:在真实业务里用在哪。
|
||||
- `## 代码示例`:最小、正确、能运行;代码块写语言标记。较长的完整示例放进 `examples/`,在正文里链接。
|
||||
- `## 易错点`:常见错误和原因。
|
||||
- 可选:`## 面试常问`(链接到本技术栈的 `interview.md`)、`## 延伸阅读`。
|
||||
|
||||
### 场景(scenario)
|
||||
|
||||
企业服务场景解决方案,必须包含:
|
||||
|
||||
- `## 业务背景与问题`
|
||||
- `## 约束`:规模、性能、成本、合规、团队等限制条件
|
||||
- `## 方案设计`:架构图(mermaid)、关键流程、数据模型
|
||||
- `## 关键代码`
|
||||
- `## 取舍`:为什么选这个方案、放弃了什么
|
||||
- `## 易错点`
|
||||
- `## 面试怎么讲`:一段 1~2 分钟的讲法 + 可能的追问
|
||||
|
||||
### 面试题(interview)
|
||||
|
||||
- 每道题一个三级标题,**必须**以级别开头:`### 【基础】…`、`### 【进阶】…`、`### 【场景题】…`。校验脚本按这个格式统计题数。
|
||||
- 题目下面写 `**参考答案**`,再写 `**追问**`(常见的追问和要点)。
|
||||
- 能结合项目的写上“在本计划的哪个项目里体现”。
|
||||
|
||||
### 计划(week)
|
||||
|
||||
- 日程安排:每天的任务、要建的文件、运行方式、完成标准。
|
||||
- 概念讲解放在专题里,计划文件用链接引用,不重复写。项目的分步实现代码保留在计划文件里(type 仍为 `week`)。
|
||||
|
||||
## 4. 写作规则
|
||||
|
||||
1. 中文写作,术语第一次出现时附英文。
|
||||
2. **代码必须正确**:能运行的示例放进 `examples/` 并能被 `tools/run_examples.py` 跑通;需要外部服务的在文件开头写 `不自动运行`,并说明怎么手动运行。
|
||||
3. 更新快的框架(LangChain、LangGraph、MCP、Vite、Next.js、Nuxt 等)标注“以官方文档为准”并附链接,写明验证时的版本。
|
||||
4. **脱敏**:不出现公司内部项目名、代码、接口路径、表名、内网地址、密钥、客户数据。场景都用通用的业务描述。
|
||||
5. 不编造数据:性能数字、统计数字必须注明来源,或写成“以实测为准”。
|
||||
6. 链接一律用相对路径,校验脚本会检查链接是否存在。
|
||||
|
||||
## 5. 提交前
|
||||
|
||||
```bash
|
||||
python3 tools/build_catalog.py # 校验并更新 catalog.json
|
||||
python3 tools/run_examples.py # 运行示例(可加关键词只跑部分,例如 redis)
|
||||
python3 tools/check_sensitive.py # 脱敏检查(如果已经按第 8 周装好钩子,会自动执行)
|
||||
```
|
||||
|
||||
## 6. 给学习平台的导入说明
|
||||
|
||||
- 以 `catalog.json` 为入口:`stacks` 是技术栈列表(含课程数、场景数、各级面试题数、示例路径),`items` 是全部内容(frontmatter + 文件路径 + 二级标题列表 + 面试题统计)。
|
||||
- 内容正文就是 Markdown 文件本身(去掉 frontmatter);mermaid 图需要前端渲染。
|
||||
- 学习进度按 `id` 记录;`prerequisites` 可以生成学习路线图,`week` 可以生成日程视图。
|
||||
- 面试题可以按 `### 【级别】` 标题切分成单题,`**参考答案**` 和 `**追问**` 是固定的小标题。
|
||||
@@ -1,5 +1,36 @@
|
||||
# zhuangang · 转岗学习仓库
|
||||
|
||||
16 周转岗计划和全部练习代码:Python AI 应用 / Agent 开发为主(RAG、LangGraph、MCP、评测),Go 全栈为辅。
|
||||
16 周转岗计划 + 按技术栈组织的专项学习内容 + 企业场景方案 + 分级面试题 + 可运行示例。方向:Python AI 应用 / Agent 开发为主(RAG、LangGraph、MCP、评测),Go 全栈为辅。
|
||||
|
||||
从 [plan/README.md](plan/README.md) 开始看:里面有总路线、岗位要求对照、每周计划文件和进度记录。
|
||||
## 从哪里开始
|
||||
|
||||
| 我想…… | 看这里 |
|
||||
| --- | --- |
|
||||
| 按 16 周计划一步步学 | [plan/README.md](plan/README.md)(总纲、进度记录),每周文件链接到对应专题 |
|
||||
| 单独补某个技术栈 | [topics/index.md](topics/index.md)(31 个技术栈:课程、场景、面试题、示例) |
|
||||
| 看企业里的综合方案 | [scenarios/index.md](scenarios/index.md)(客服知识库、工单、审批流、秒杀、多租户……) |
|
||||
| 准备面试 | 每个技术栈目录下的 `interview.md`(基础 / 进阶 / 场景题,带参考答案和追问) |
|
||||
| 写新内容 / 做学习平台 | [CONTRIBUTING.md](CONTRIBUTING.md)(目录、frontmatter、各类型写法)、`catalog.json`(自动生成的内容目录) |
|
||||
|
||||
## 目录
|
||||
|
||||
```text
|
||||
plan/ 16 周日程(type: week),链接到专题,不重复讲概念
|
||||
topics/ 专项学习:<领域>/<技术栈>/{index.md, NN-课程.md, scenarios/, interview.md, examples/}
|
||||
scenarios/ 跨技术栈的企业服务场景
|
||||
tools/ build_catalog.py(校验 + 生成目录)、run_examples.py(运行示例)、check_sensitive.py(脱敏检查)
|
||||
schema/ frontmatter 的 JSON Schema
|
||||
catalog.json 内容目录(由 tools/build_catalog.py 生成,不要手改)
|
||||
```
|
||||
|
||||
练习项目(`go-learning/`、`todo-api/`、`task-board/`、`py-learning/`、`rag-qa/`、`web/`、`agent-mini/`、`backend-extend/`、`career/` 等)按计划逐周建在仓库根目录。
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
python3 tools/build_catalog.py # 校验所有内容并更新 catalog.json
|
||||
python3 tools/run_examples.py redis # 运行某个技术栈的示例代码
|
||||
python3 tools/check_sensitive.py --self-test
|
||||
```
|
||||
|
||||
克隆:`git clone ssh://wsk@git.gozeros.com:30023/wsk/zhuangang.git`(SSH 不通时用 `https://git.gozeros.com/wsk/zhuangang.git`)。
|
||||
+1676
File diff suppressed because it is too large.
Load diff
+18
-3
@@ -1,3 +1,11 @@
|
||||
---
|
||||
id: plan.index
|
||||
title: 转岗计划总纲
|
||||
type: index
|
||||
domain: plan
|
||||
stack: []
|
||||
---
|
||||
|
||||
# 转岗计划总纲:Python AI 应用 / Agent 开发为主,Go 全栈为辅(16 周)
|
||||
|
||||
这是整个计划的入口文件。换了电脑或者隔一段时间回来,先看第 13 节“进度记录”,再打开当前所在周的计划文件继续做。
|
||||
@@ -314,15 +322,20 @@
|
||||
```text
|
||||
zhuangang/
|
||||
├── .gitignore # 至少包含 notes/private/、.env、*.db、data/、node_modules/、.venv/
|
||||
├── README.md
|
||||
├── plan/ # 本计划(见下面的文件清单)
|
||||
├── README.md # 仓库入口
|
||||
├── CONTRIBUTING.md # 内容规范(目录、frontmatter、各类型写法)
|
||||
├── catalog.json # 内容目录(tools/build_catalog.py 生成)
|
||||
├── schema/ # frontmatter 的 JSON Schema
|
||||
├── tools/ # build_catalog.py、run_examples.py、check_sensitive.py
|
||||
├── plan/ # 本计划:总纲 + 16 个周文件(见下面的文件清单)
|
||||
├── topics/ # 专项学习:<领域>/<技术栈>/ 课程、场景、面试题、示例
|
||||
├── scenarios/ # 跨技术栈的企业服务场景
|
||||
├── go-learning/ # 第 1 周:Go 语法练习
|
||||
├── todo-api/ # 第 1~2 周:Gin 接口 + PostgreSQL + JWT
|
||||
├── task-board/ # 第 3 周:项目一后端
|
||||
├── py-learning/ # 第 4 周:Python 练习 + FastAPI
|
||||
├── rag-qa/ # 第 5~6 周:项目二
|
||||
├── web/ # 第 7 周:pnpm monorepo(shared、board-vue、rag-react)
|
||||
├── tools/ # 第 8 周:check_sensitive.py(提交前脱敏检查)
|
||||
├── notes/ # 第 8~9 周:盘点、架构图、案例卡(脱敏);低代码体验笔记
|
||||
│ ├── cards/
|
||||
│ └── private/ # 不进仓库
|
||||
@@ -333,6 +346,8 @@ zhuangang/
|
||||
└── career/ # 第 15~16 周:简历、口述稿、面试笔记
|
||||
```
|
||||
|
||||
**周计划和专题的关系**:周文件是“日程”——每天做什么、建哪些文件、怎么验收;概念讲解、企业场景和面试题在 `topics/` 和 `scenarios/` 里,周文件开头的“本周相关专题”列出了链接。单独补某个技术栈时直接从 [topics/index.md](../topics/index.md) 进入。
|
||||
|
||||
### 计划文件清单(`plan/`)
|
||||
|
||||
| 文件 | 内容 |
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week01
|
||||
title: "第 1 周:Go 基础 + Gin 写 REST 接口"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [go, gin, git]
|
||||
week: [1]
|
||||
est_hours: 12
|
||||
order: 1
|
||||
---
|
||||
|
||||
# 第 1 周:Go 基础 + Gin 写 REST 接口
|
||||
|
||||
> 第一段“Go 全栈基础”(第 1~3 周)的第 1 周。总纲见 `README.md`,下周 `week02-db-auth.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week02
|
||||
title: "第 2 周:数据库(PostgreSQL 为主,兼顾 MySQL)+ 注册登录与 JWT"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [go, gin, postgresql, mysql]
|
||||
week: [2]
|
||||
est_hours: 12
|
||||
order: 2
|
||||
---
|
||||
|
||||
# 第 2 周:数据库(PostgreSQL 为主,兼顾 MySQL)+ 注册登录与 JWT
|
||||
|
||||
> 第一段“Go 全栈基础”的第 2 周。上周 `week01-go-gin.md`,下周 `week03-project1.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week03
|
||||
title: "第 3 周:项目一“团队任务看板”:权限 + Redis + 消息队列 + Docker 部署"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [go, gin, postgresql, redis, mq, docker, nginx, cicd]
|
||||
week: [3]
|
||||
est_hours: 12
|
||||
order: 3
|
||||
---
|
||||
|
||||
# 第 3 周:项目一“团队任务看板”:权限 + Redis + 消息队列 + Docker 部署
|
||||
|
||||
> 第一段“Go 全栈基础”的最后一周。上周 `week02-db-auth.md`,下周 `week04-python.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week04
|
||||
title: "第 4 周:Python 速成(给 JS 开发者)+ FastAPI 基础"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [python, asyncio, fastapi]
|
||||
week: [4]
|
||||
est_hours: 12
|
||||
order: 4
|
||||
---
|
||||
|
||||
# 第 4 周:Python 速成(给 JS 开发者)+ FastAPI 基础
|
||||
|
||||
> 第二段“Python 与 AI 应用”的第 1 周。从这周起 **Python 是求职主语言**。上周 `week03-project1.md`,下周 `week05-rag-1.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week05
|
||||
title: "第 5 周:RAG 知识库问答(上)——手写主流程"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [asyncio, fastapi, rag, vector-db, prompt-engineering]
|
||||
week: [5]
|
||||
est_hours: 12
|
||||
order: 5
|
||||
---
|
||||
|
||||
# 第 5 周:RAG 知识库问答(上)——手写主流程
|
||||
|
||||
> 第二段“Python 与 AI 应用”的第 2 周,也是**项目二 `rag-qa`** 的第 1 周。上周 `week04-python.md`,下周 `week06-rag-2.md`(评测、混合检索、重排、缓存、LangChain 对照、部署)。前端界面放到第 7 周统一做。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week06
|
||||
title: "第 6 周:RAG 知识库问答(下)——评测、混合检索、重排、记忆、缓存、LangChain、部署"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [postgresql, redis, docker, rag, vector-db, langchain]
|
||||
week: [6]
|
||||
est_hours: 12
|
||||
order: 6
|
||||
---
|
||||
|
||||
# 第 6 周:RAG 知识库问答(下)——评测、混合检索、重排、记忆、缓存、LangChain、部署
|
||||
|
||||
> 项目二 `rag-qa` 的第 2 周。上周 `week05-rag-1.md` 完成了手写主流程;下周 `week07-frontend.md` 做前端界面。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week07
|
||||
title: "第 7 周:前端工程化查漏补缺 + 两个项目的界面"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [nginx, cicd, vue, react, typescript, vite, webpack, ssr, fe-testing-perf]
|
||||
week: [7]
|
||||
est_hours: 12
|
||||
order: 7
|
||||
---
|
||||
|
||||
# 第 7 周:前端工程化查漏补缺 + 两个项目的界面
|
||||
|
||||
> 第二段的第 4 周。上周 `week06-rag-2.md`,下周 `week08-inventory-architecture.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week08
|
||||
title: "第 8 周:阶段 0 盘点 + 阶段 1 读通与画图"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [git]
|
||||
week: [8]
|
||||
est_hours: 12
|
||||
order: 8
|
||||
---
|
||||
|
||||
# 第 8 周:阶段 0 盘点 + 阶段 1 读通与画图
|
||||
|
||||
> 第三段“Agent 主线”的第 1 周。上周 `week07-frontend.md`,下周 `week09-case-cards.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week09
|
||||
title: "第 9 周:阶段 2——五个重点 A~E 深挖 + 案例卡"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: []
|
||||
week: [9]
|
||||
est_hours: 12
|
||||
order: 9
|
||||
---
|
||||
|
||||
# 第 9 周:阶段 2——五个重点 A~E 深挖 + 案例卡
|
||||
|
||||
> 第三段的第 2 周。上周 `week08-inventory-architecture.md`,下周 `week10-agent-m1-tools-mcp.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week10
|
||||
title: "第 10 周:项目三 M1——手写工具调用循环 + 手写多 Agent 循环 + MCP 服务"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [mcp, function-calling]
|
||||
week: [10]
|
||||
est_hours: 12
|
||||
order: 10
|
||||
---
|
||||
|
||||
# 第 10 周:项目三 M1——手写工具调用循环 + 手写多 Agent 循环 + MCP 服务
|
||||
|
||||
> 第三段的第 3 周,**项目三 `agent-mini`(阶段 3 主交付)** 的第 1 周。上周 `week09-case-cards.md`,下周 `week11-agent-m2-langgraph.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week11
|
||||
title: "第 11 周:项目三 M2——用 LangGraph 实现多 Agent 编排"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [langchain, langgraph, mcp, prompt-engineering]
|
||||
week: [11]
|
||||
est_hours: 12
|
||||
order: 11
|
||||
---
|
||||
|
||||
# 第 11 周:项目三 M2——用 LangGraph 实现多 Agent 编排
|
||||
|
||||
> 项目三的第 2 周。上周 `week10-agent-m1-tools-mcp.md`(手写版 + MCP),下周 `week12-agent-m3-persistence.md`(续跑、中断、后台作业、SSE、记忆)。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week12
|
||||
title: "第 12 周:项目三 M3——检查点续跑、人机协作、后台作业、SSE、长期记忆"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [asyncio, fastapi, langgraph, agent-ops]
|
||||
week: [12]
|
||||
est_hours: 12
|
||||
order: 12
|
||||
---
|
||||
|
||||
# 第 12 周:项目三 M3——检查点续跑、人机协作、后台作业、SSE、长期记忆
|
||||
|
||||
> 项目三的第 3 周,**整个计划最核心的一周**:第 9 周案例卡里“若重做的设计”在这里落地。上周 `week11-agent-m2-langgraph.md`,下周 `week13-agent-m4-eval-lowcode.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week13
|
||||
title: "第 13 周:项目三 M4——评测集、可观测性、文档、Docker + Dify / Coze 体验"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [docker, prompt-engineering, agent-ops, lowcode]
|
||||
week: [13]
|
||||
est_hours: 12
|
||||
order: 13
|
||||
---
|
||||
|
||||
# 第 13 周:项目三 M4——评测集、可观测性、文档、Docker + Dify / Coze 体验
|
||||
|
||||
> 项目三的最后一周(阶段 3 出门)。上周 `week12-agent-m3-persistence.md`,下周 `week14-backend-extend-deploy.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week14
|
||||
title: "第 14 周:后端补强——消息队列、gRPC、Redis / MySQL 专题、全部项目部署、CI/CD"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [go, mysql, redis, mq, grpc-microservices, docker, nginx, linux, cicd]
|
||||
week: [14]
|
||||
est_hours: 12
|
||||
order: 14
|
||||
---
|
||||
|
||||
# 第 14 周:后端补强——消息队列、gRPC、Redis / MySQL 专题、全部项目部署、CI/CD
|
||||
|
||||
> 第四段的第 1 周。上周 `week13-agent-m4-eval-lowcode.md`,下周 `week15-package-resume.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week15
|
||||
title: "第 15 周:阶段 4(上)——作品集、简历、STAR 经历、口述稿"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: [git]
|
||||
week: [15]
|
||||
est_hours: 12
|
||||
order: 15
|
||||
---
|
||||
|
||||
# 第 15 周:阶段 4(上)——作品集、简历、STAR 经历、口述稿
|
||||
|
||||
> 第四段的第 2 周。上周 `week14-backend-extend-deploy.md`,下周 `week16-interview.md`。
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
---
|
||||
id: plan.week16
|
||||
title: "第 16 周:阶段 4(下)——面试准备、终点验收、投递"
|
||||
type: week
|
||||
domain: plan
|
||||
stack: []
|
||||
week: [16]
|
||||
est_hours: 12
|
||||
order: 16
|
||||
---
|
||||
|
||||
# 第 16 周:阶段 4(下)——面试准备、终点验收、投递
|
||||
|
||||
> 整个计划的最后一周。上周 `week15-package-resume.md`。总纲见 `README.md` 第 14 节“终点验收清单”。
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
id: scenarios.index
|
||||
title: 跨技术栈企业服务场景
|
||||
type: index
|
||||
domain: cross
|
||||
stack: []
|
||||
---
|
||||
|
||||
# 跨技术栈企业服务场景
|
||||
|
||||
每个场景按“业务背景与问题 → 约束 → 方案设计 → 关键代码 → 取舍 → 易错点 → 面试怎么讲”组织,综合多个技术栈。
|
||||
|
||||
> 建设中,计划包含:
|
||||
|
||||
- 客服知识库问答(`customer-service-kb`)
|
||||
- 工单系统(智能分类与流转)(`ticket-system`)
|
||||
- 审批流 + 人工介入的 Agent(`approval-human-in-loop`)
|
||||
- 报表生成 Agent(自然语言查数)(`report-agent`)
|
||||
- 秒杀 / 高并发抢购(`flash-sale`)
|
||||
- 多租户 SaaS(`multi-tenant-saas`)
|
||||
- 内容审核(`content-moderation`)
|
||||
- 数据同步(数据库到搜索 / 向量库)(`data-sync`)
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "zhuangang/frontmatter.schema.json",
|
||||
"title": "zhuangang 内容文件的 frontmatter",
|
||||
"description": "plan/、topics/、scenarios/ 下每个 .md 文件开头的 YAML frontmatter。说明见 CONTRIBUTING.md。",
|
||||
"type": "object",
|
||||
"required": ["id", "title", "type", "domain"],
|
||||
"properties": {
|
||||
"id": {"type": "string", "pattern": "^[a-z0-9]+(\\.[a-z0-9-]+)+$", "description": "全局唯一、稳定不变的内容 ID"},
|
||||
"title": {"type": "string"},
|
||||
"type": {"enum": ["index", "lesson", "scenario", "interview", "project", "week"]},
|
||||
"domain": {"enum": ["backend", "devops", "python", "ai", "frontend", "cross", "plan"]},
|
||||
"stack": {"type": "array", "items": {"type": "string"}, "description": "技术栈标识,与 topics/<domain>/<stack>/ 目录名一致"},
|
||||
"tags": {"type": "array", "items": {"type": "string"}},
|
||||
"level": {"enum": ["basic", "intermediate", "advanced"]},
|
||||
"prerequisites": {"type": "array", "items": {"type": "string"}, "description": "先修内容的 id"},
|
||||
"related": {"type": "array", "items": {"type": "string"}, "description": "相关内容的 id"},
|
||||
"est_hours": {"type": "number", "minimum": 0},
|
||||
"week": {"type": "array", "items": {"type": "integer", "minimum": 1, "maximum": 16}, "description": "在 16 周计划中的哪几周用到"},
|
||||
"order": {"type": "integer", "description": "同一技术栈内的排序"}
|
||||
},
|
||||
"allOf": [
|
||||
{"if": {"properties": {"type": {"enum": ["lesson", "scenario", "interview"]}}}, "then": {"required": ["level", "stack"]}}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
#!/usr/bin/env python3
|
||||
"""内容校验 + 目录生成(只用标准库,Python 3.10+)。
|
||||
|
||||
用法:
|
||||
python3 tools/build_catalog.py # 校验并写出 catalog.json
|
||||
python3 tools/build_catalog.py --check # 只校验,不写文件(CI / 提交前用)
|
||||
python3 tools/build_catalog.py --strict # 额外要求:每个技术栈至少 1 课、1 个场景、1 份面试题
|
||||
|
||||
校验内容:frontmatter 字段和取值、id 唯一、prerequisites / related 指向存在的 id、
|
||||
相对链接指向存在的文件、代码块围栏闭合、各类型必需的小节标题。规范见 CONTRIBUTING.md。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
CONTENT_DIRS = ["plan", "topics", "scenarios"]
|
||||
SCHEMA_VERSION = 1
|
||||
|
||||
TYPES = {"index", "lesson", "scenario", "interview", "project", "week"}
|
||||
DOMAINS = {"backend", "devops", "python", "ai", "frontend", "cross", "plan"}
|
||||
LEVELS = {"basic", "intermediate", "advanced"}
|
||||
QUESTION_LEVELS = ["基础", "进阶", "场景题"]
|
||||
ID_RE = re.compile(r"^[a-z0-9]+(?:\.[a-z0-9-]+)+$")
|
||||
REQUIRED = {"id", "title", "type", "domain"}
|
||||
LIST_FIELDS = {"stack", "tags", "prerequisites", "related", "week"}
|
||||
REQUIRED_SECTIONS = {
|
||||
"lesson": ["解释说明", "案例", "代码示例", "易错点"],
|
||||
"scenario": ["业务背景", "约束", "方案设计", "关键代码", "取舍", "易错点", "面试怎么讲"],
|
||||
}
|
||||
FENCE_RE = re.compile(r"^\s*(```|~~~)")
|
||||
LINK_RE = re.compile(r"(?<!!)\[[^\]]*\]\(([^)\s]+)(?:\s+\"[^\"]*\")?\)")
|
||||
QUESTION_RE = re.compile(r"^###\s+【(基础|进阶|场景题)】")
|
||||
|
||||
|
||||
def parse_scalar(v: str):
|
||||
v = v.strip()
|
||||
if v == "":
|
||||
return ""
|
||||
if (v[0] == v[-1]) and v[0] in "\"'" and len(v) >= 2:
|
||||
return v[1:-1]
|
||||
if re.fullmatch(r"-?\d+", v):
|
||||
return int(v)
|
||||
if re.fullmatch(r"-?\d+\.\d+", v):
|
||||
return float(v)
|
||||
if v in ("true", "false"):
|
||||
return v == "true"
|
||||
return v
|
||||
|
||||
|
||||
def parse_frontmatter(text: str) -> tuple[dict | None, str, list[str]]:
|
||||
"""只支持规范里约定的子集:`键: 标量` 和 `键: [a, b]`(单行流式列表)。"""
|
||||
errs: list[str] = []
|
||||
if not text.startswith("---\n"):
|
||||
return None, text, ["缺少 frontmatter(文件第一行必须是 ---)"]
|
||||
end = text.find("\n---\n", 4)
|
||||
if end < 0:
|
||||
return None, text, ["frontmatter 没有结束的 ---"]
|
||||
meta: dict = {}
|
||||
for n, line in enumerate(text[4:end].splitlines(), 2):
|
||||
if not line.strip() or line.lstrip().startswith("#"):
|
||||
continue
|
||||
m = re.match(r"^([a-z_]+):\s*(.*)$", line)
|
||||
if not m:
|
||||
errs.append(f"frontmatter 第 {n} 行无法解析:{line!r}")
|
||||
continue
|
||||
k, v = m.group(1), m.group(2).strip()
|
||||
if v.startswith("["):
|
||||
if not v.endswith("]"):
|
||||
errs.append(f"frontmatter 字段 {k} 的列表必须写在一行内")
|
||||
continue
|
||||
inner = v[1:-1].strip()
|
||||
meta[k] = [parse_scalar(x) for x in inner.split(",")] if inner else []
|
||||
else:
|
||||
meta[k] = parse_scalar(v)
|
||||
return meta, text[end + 5:], errs
|
||||
|
||||
|
||||
def scan_body(body: str) -> tuple[bool, list[str], list[str], dict[str, int]]:
|
||||
"""返回:围栏是否闭合、二级标题、链接目标、按级别统计的面试题数。"""
|
||||
in_fence, fence_mark = False, ""
|
||||
headings, links = [], []
|
||||
counts = {k: 0 for k in QUESTION_LEVELS}
|
||||
for line in body.splitlines():
|
||||
m = FENCE_RE.match(line)
|
||||
if m:
|
||||
if not in_fence:
|
||||
in_fence, fence_mark = True, m.group(1)
|
||||
elif m.group(1) == fence_mark and line.strip() == fence_mark:
|
||||
in_fence = False
|
||||
continue
|
||||
if in_fence:
|
||||
continue
|
||||
if line.startswith("## "):
|
||||
headings.append(line[3:].strip())
|
||||
q = QUESTION_RE.match(line)
|
||||
if q:
|
||||
counts[q.group(1)] += 1
|
||||
links += LINK_RE.findall(line)
|
||||
return not in_fence, headings, links, counts
|
||||
|
||||
|
||||
def main() -> int:
|
||||
check_only = "--check" in sys.argv
|
||||
strict = "--strict" in sys.argv
|
||||
errors: list[str] = []
|
||||
items: list[dict] = []
|
||||
files = sorted(p for d in CONTENT_DIRS for p in (ROOT / d).rglob("*.md")
|
||||
if "examples" not in p.relative_to(ROOT).parts and "node_modules" not in p.parts)
|
||||
for path in files:
|
||||
rel = path.relative_to(ROOT).as_posix()
|
||||
text = path.read_text(encoding="utf-8")
|
||||
meta, body, errs = parse_frontmatter(text)
|
||||
errors += [f"{rel}: {e}" for e in errs]
|
||||
if meta is None:
|
||||
continue
|
||||
for f in REQUIRED - meta.keys():
|
||||
errors.append(f"{rel}: 缺少必填字段 {f}")
|
||||
for f in LIST_FIELDS & meta.keys():
|
||||
if not isinstance(meta[f], list):
|
||||
errors.append(f"{rel}: 字段 {f} 必须是列表,例如 {f}: [a, b]")
|
||||
t = meta.get("type")
|
||||
if t not in TYPES:
|
||||
errors.append(f"{rel}: type={t!r} 不在 {sorted(TYPES)} 中")
|
||||
if meta.get("domain") not in DOMAINS:
|
||||
errors.append(f"{rel}: domain={meta.get('domain')!r} 不在 {sorted(DOMAINS)} 中")
|
||||
if not ID_RE.match(str(meta.get("id", ""))):
|
||||
errors.append(f"{rel}: id={meta.get('id')!r} 格式不对(小写字母数字,用 . 分段)")
|
||||
if t in ("lesson", "scenario", "interview") and meta.get("level") not in LEVELS:
|
||||
errors.append(f"{rel}: {t} 必须有 level({sorted(LEVELS)})")
|
||||
if "est_hours" in meta and not isinstance(meta["est_hours"], (int, float)):
|
||||
errors.append(f"{rel}: est_hours 必须是数字")
|
||||
for w in meta.get("week", []) if isinstance(meta.get("week"), list) else []:
|
||||
if not isinstance(w, int) or not 1 <= w <= 16:
|
||||
errors.append(f"{rel}: week 取值 {w!r} 必须是 1~16 的整数")
|
||||
closed, headings, links, counts = scan_body(body)
|
||||
if not closed:
|
||||
errors.append(f"{rel}: 代码块围栏没有闭合")
|
||||
for sec in REQUIRED_SECTIONS.get(t, []):
|
||||
if not any(sec in h for h in headings):
|
||||
errors.append(f"{rel}: {t} 缺少小节“## {sec}”")
|
||||
if t == "interview" and sum(counts.values()) == 0:
|
||||
errors.append(f"{rel}: 面试题文件没有题目(标题格式:### 【基础】问题)")
|
||||
for target in links:
|
||||
if re.match(r"^[a-z]+:", target) or target.startswith("#"):
|
||||
continue
|
||||
dest = (path.parent / target.split("#", 1)[0]).resolve()
|
||||
if not dest.exists():
|
||||
errors.append(f"{rel}: 链接指向的文件不存在:{target}")
|
||||
item = {
|
||||
"id": meta.get("id"), "title": meta.get("title"), "type": t, "domain": meta.get("domain"),
|
||||
"stack": meta.get("stack", []), "tags": meta.get("tags", []), "level": meta.get("level"),
|
||||
"prerequisites": meta.get("prerequisites", []), "related": meta.get("related", []),
|
||||
"est_hours": meta.get("est_hours"), "week": meta.get("week", []), "order": meta.get("order"),
|
||||
"path": rel, "sections": headings,
|
||||
}
|
||||
if t == "interview":
|
||||
item["questions"] = counts
|
||||
items.append(item)
|
||||
|
||||
ids = {}
|
||||
for it in items:
|
||||
if it["id"] in ids:
|
||||
errors.append(f"{it['path']}: id {it['id']} 与 {ids[it['id']]} 重复")
|
||||
ids[it["id"]] = it["path"]
|
||||
for it in items:
|
||||
for f in ("prerequisites", "related"):
|
||||
for ref in it[f] if isinstance(it[f], list) else []:
|
||||
if ref not in ids:
|
||||
errors.append(f"{it['path']}: {f} 引用了不存在的 id:{ref}")
|
||||
|
||||
stacks = []
|
||||
for it in items:
|
||||
if it["type"] == "index" and it["path"].startswith("topics/") and it["path"].count("/") == 3:
|
||||
sid = it["stack"][0] if it["stack"] else None
|
||||
mine = [x for x in items if x["path"].startswith(it["path"].rsplit("/", 1)[0] + "/")]
|
||||
q = {k: 0 for k in QUESTION_LEVELS}
|
||||
for x in mine:
|
||||
for k, v in x.get("questions", {}).items():
|
||||
q[k] += v
|
||||
stack_dir = ROOT / it["path"].rsplit("/", 1)[0]
|
||||
examples = sorted(p.relative_to(ROOT).as_posix() for p in (stack_dir / "examples").glob("*")) \
|
||||
if (stack_dir / "examples").exists() else []
|
||||
entry = {"stack": sid, "domain": it["domain"], "title": it["title"], "index": it["path"],
|
||||
"lessons": sum(x["type"] == "lesson" for x in mine),
|
||||
"scenarios": sum(x["type"] == "scenario" for x in mine),
|
||||
"interview_questions": q, "examples": examples}
|
||||
stacks.append(entry)
|
||||
if strict and (entry["lessons"] == 0 or entry["scenarios"] == 0 or sum(q.values()) == 0):
|
||||
errors.append(f"{it['path']}: 严格模式要求技术栈 {sid} 至少有 1 课、1 个场景和面试题")
|
||||
stacks.sort(key=lambda s: (s["domain"], s["stack"] or ""))
|
||||
|
||||
if errors:
|
||||
print(f"校验失败:{len(errors)} 个问题")
|
||||
for e in errors:
|
||||
print(" -", e)
|
||||
return 1
|
||||
q_total = {k: sum(s["interview_questions"][k] for s in stacks) for k in QUESTION_LEVELS}
|
||||
print(f"校验通过:{len(items)} 个文件,{len(stacks)} 个技术栈,"
|
||||
f"{sum(i['type'] == 'lesson' for i in items)} 课,{sum(i['type'] == 'scenario' for i in items)} 个场景,"
|
||||
f"面试题 {sum(q_total.values())} 道({'、'.join(f'{k} {v}' for k, v in q_total.items())})")
|
||||
if not check_only:
|
||||
catalog = {"schema_version": SCHEMA_VERSION, "generated_by": "tools/build_catalog.py",
|
||||
"stacks": stacks, "items": sorted(items, key=lambda x: x["id"])}
|
||||
(ROOT / "catalog.json").write_text(json.dumps(catalog, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
print("已写出 catalog.json")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,67 @@
|
||||
# tools/check_sensitive.py —— 提交前扫描暂存区内容
|
||||
# 不自动运行:作为 pre-commit 钩子在仓库根目录执行;加 --self-test 可以单独自检
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
PATTERNS = {
|
||||
"内网 IP": re.compile(r"\b10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b|\b192\.168\.\d{1,3}\.\d{1,3}\b|\b172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}\b"),
|
||||
"疑似密钥": re.compile(r"(?i)\b(?:sk|ak|api[_-]?key|secret|token)[\w-]*\s*[:=]\s*['\"]?[A-Za-z0-9_\-]{16,}"),
|
||||
"私钥": re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"),
|
||||
"root 绝对路径": re.compile(r"(?<![\w/])/root/\S+"), # sensitive-ok
|
||||
}
|
||||
WORDS_FILE = Path("notes/private/sensitive-words.txt")
|
||||
ALLOW_MARK = "sensitive-ok" # 行内写这个标记表示人工确认过,可以放行(比如文档里讲解正则的例子)
|
||||
|
||||
|
||||
def load_words() -> list[str]:
|
||||
if not WORDS_FILE.exists():
|
||||
return []
|
||||
return [w.strip() for w in WORDS_FILE.read_text(encoding="utf-8").splitlines() if w.strip() and not w.startswith("#")]
|
||||
|
||||
|
||||
def scan_text(name: str, text: str, words: list[str]) -> list[str]:
|
||||
problems = []
|
||||
for no, line in enumerate(text.splitlines(), start=1):
|
||||
if ALLOW_MARK in line:
|
||||
continue
|
||||
for label, pat in PATTERNS.items():
|
||||
if pat.search(line):
|
||||
problems.append(f"{name}:{no} {label}")
|
||||
for w in words:
|
||||
if w.lower() in line.lower():
|
||||
problems.append(f"{name}:{no} 敏感词(见私有清单)") # 不打印敏感词本身
|
||||
return problems
|
||||
|
||||
|
||||
def staged_files() -> list[str]:
|
||||
out = subprocess.run(["git", "diff", "--cached", "--name-only", "--diff-filter=ACM"],
|
||||
capture_output=True, text=True, check=True).stdout
|
||||
return [f for f in out.splitlines() if f]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
words = load_words()
|
||||
problems = []
|
||||
for f in staged_files():
|
||||
content = subprocess.run(["git", "show", f":{f}"], capture_output=True).stdout # 读暂存区版本
|
||||
try:
|
||||
problems += scan_text(f, content.decode("utf-8"), words)
|
||||
except UnicodeDecodeError:
|
||||
continue # 跳过二进制文件
|
||||
for p in problems:
|
||||
print("✗", p)
|
||||
if problems:
|
||||
print("发现疑似敏感内容,已阻止提交。确认无误的行可加注释 sensitive-ok。")
|
||||
return 1 if problems else 0
|
||||
|
||||
|
||||
if __name__ == "__main__" and "--self-test" in sys.argv:
|
||||
sample = "url: http://192.168.1.20:8080\napi_key = 'abcdefghijklmnopqrstuvwxyz'\n正常的一行\n" # sensitive-ok
|
||||
found = scan_text("demo.md", sample, ["某客户"])
|
||||
assert len(found) == 2, found
|
||||
assert scan_text("demo.md", "某客户的需求", ["某客户"]) == ["demo.md:1 敏感词(见私有清单)"]
|
||||
print("自检通过:", found)
|
||||
elif __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
"""运行 topics/**/examples/ 下的示例代码(抽查“代码能跑”)。
|
||||
|
||||
- Go:含 go.mod 的目录执行 `go vet ./... && go test ./...`
|
||||
- Python:*.py 直接运行(文件开头可写 `# requires: 包1, 包2`,缺包时跳过);test_*.py 用 pytest 运行
|
||||
- TypeScript:*.ts 用 Node.js 直接运行(需要 Node 23.6+ 的类型剥离,推荐 Node 24 LTS)
|
||||
- 文件开头写 `# 不自动运行` / `// 不自动运行` 的跳过(需要外部服务的示例)
|
||||
|
||||
用法:python3 tools/run_examples.py [路径过滤关键词]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def run(cmd: list[str] | str, cwd: Path, timeout: int = 180) -> tuple[bool, str]:
|
||||
try:
|
||||
p = subprocess.run(cmd, cwd=cwd, shell=isinstance(cmd, str), capture_output=True, text=True, timeout=timeout,
|
||||
env={**os.environ, "PYTHONPATH": str(cwd)})
|
||||
return p.returncode == 0, (p.stdout + p.stderr)[-1500:]
|
||||
except subprocess.TimeoutExpired:
|
||||
return False, "超时"
|
||||
|
||||
|
||||
def node_ok() -> bool:
|
||||
if not shutil.which("node"):
|
||||
return False
|
||||
v = subprocess.run(["node", "-v"], capture_output=True, text=True).stdout.strip().lstrip("v").split(".")
|
||||
return (int(v[0]), int(v[1])) >= (23, 6)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
flt = sys.argv[1] if len(sys.argv) > 1 else ""
|
||||
results: list[tuple[str, str, str]] = []
|
||||
has_node = node_ok()
|
||||
for ex in sorted(ROOT.glob("topics/**/examples/*")):
|
||||
rel = ex.relative_to(ROOT).as_posix()
|
||||
if flt and flt not in rel:
|
||||
continue
|
||||
if ex.is_dir() and (ex / "go.mod").exists():
|
||||
if not shutil.which("go"):
|
||||
results.append(("SKIP", rel, "没有安装 Go"))
|
||||
continue
|
||||
ok, out = run("go vet ./... && go test ./...", ex)
|
||||
results.append(("PASS" if ok else "FAIL", rel, "" if ok else out))
|
||||
continue
|
||||
targets = [ex] if ex.is_file() else sorted(ex.rglob("*"))
|
||||
for f in targets:
|
||||
frel = f.relative_to(ROOT).as_posix()
|
||||
if f.suffix not in (".py", ".ts") or "node_modules" in f.parts:
|
||||
continue
|
||||
head = f.read_text(encoding="utf-8")[:400]
|
||||
if "不自动运行" in head:
|
||||
results.append(("SKIP", frel, "标记为不自动运行"))
|
||||
continue
|
||||
if f.suffix == ".py":
|
||||
m = re.search(r"^# requires:\s*(.+)$", head, re.M)
|
||||
missing = [p.strip() for p in m.group(1).split(",")] if m else []
|
||||
missing = [p for p in missing if importlib.util.find_spec(p) is None]
|
||||
if missing:
|
||||
results.append(("SKIP", frel, f"缺少依赖 {missing}"))
|
||||
continue
|
||||
cmd = [sys.executable, "-m", "pytest", "-q", f.name] if f.name.startswith("test_") \
|
||||
else [sys.executable, f.name]
|
||||
ok, out = run(cmd, f.parent)
|
||||
else:
|
||||
if not has_node:
|
||||
results.append(("SKIP", frel, "需要 Node 23.6+"))
|
||||
continue
|
||||
ok, out = run(["node", f.name], f.parent)
|
||||
results.append(("PASS" if ok else "FAIL", frel, "" if ok else out))
|
||||
for status, rel, out in results:
|
||||
print(f"{status:4} {rel}" + (f"\n{out}" if out and status == "FAIL" else f" ({out})" if out else ""))
|
||||
n = {s: sum(r[0] == s for r in results) for s in ("PASS", "FAIL", "SKIP")}
|
||||
print(f"\n通过 {n['PASS']},失败 {n['FAIL']},跳过 {n['SKIP']}")
|
||||
return 1 if n["FAIL"] else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: ai.agent-ops.index
|
||||
title: Agent 记忆、评测与可观测性
|
||||
type: index
|
||||
domain: ai
|
||||
stack: [agent-ops]
|
||||
tags: []
|
||||
week: [12, 13]
|
||||
---
|
||||
|
||||
# Agent 记忆、评测与可观测性
|
||||
|
||||
短期 / 长期记忆、评测集、LLM 评分、追踪、成本。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: ai.function-calling.index
|
||||
title: Function Calling 工具调用
|
||||
type: index
|
||||
domain: ai
|
||||
stack: [function-calling]
|
||||
tags: []
|
||||
week: [10]
|
||||
---
|
||||
|
||||
# Function Calling 工具调用
|
||||
|
||||
工具定义、调用循环、参数校验、安全。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: ai.langchain.index
|
||||
title: LangChain
|
||||
type: index
|
||||
domain: ai
|
||||
stack: [langchain]
|
||||
tags: []
|
||||
week: [6, 11]
|
||||
---
|
||||
|
||||
# LangChain
|
||||
|
||||
模型与消息、LCEL、工具、结构化输出。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: ai.langgraph.index
|
||||
title: LangGraph
|
||||
type: index
|
||||
domain: ai
|
||||
stack: [langgraph]
|
||||
tags: []
|
||||
week: [11, 12]
|
||||
---
|
||||
|
||||
# LangGraph
|
||||
|
||||
状态图、条件路由、检查点续跑、interrupt、长期记忆。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: ai.lowcode.index
|
||||
title: Dify / Coze 低代码平台
|
||||
type: index
|
||||
domain: ai
|
||||
stack: [lowcode]
|
||||
tags: []
|
||||
week: [13]
|
||||
---
|
||||
|
||||
# Dify / Coze 低代码平台
|
||||
|
||||
知识库、工作流、插件,和写代码的取舍。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: ai.mcp.index
|
||||
title: MCP 模型上下文协议
|
||||
type: index
|
||||
domain: ai
|
||||
stack: [mcp]
|
||||
tags: []
|
||||
week: [10, 11]
|
||||
---
|
||||
|
||||
# MCP 模型上下文协议
|
||||
|
||||
协议概念、tools / resources / prompts、服务端与客户端。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: ai.prompt-engineering.index
|
||||
title: 提示词与上下文工程
|
||||
type: index
|
||||
domain: ai
|
||||
stack: [prompt-engineering]
|
||||
tags: []
|
||||
week: [5, 11, 13]
|
||||
---
|
||||
|
||||
# 提示词与上下文工程
|
||||
|
||||
提示词结构、结构化输出、上下文工程、提示词注入防护。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: ai.rag.index
|
||||
title: RAG 检索增强生成
|
||||
type: index
|
||||
domain: ai
|
||||
stack: [rag]
|
||||
tags: []
|
||||
week: [5, 6]
|
||||
---
|
||||
|
||||
# RAG 检索增强生成
|
||||
|
||||
加载切分、嵌入、检索、混合检索与重排、引用与拒答、评测。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: ai.vector-db.index
|
||||
title: 向量数据库
|
||||
type: index
|
||||
domain: ai
|
||||
stack: [vector-db]
|
||||
tags: []
|
||||
week: [5, 6]
|
||||
---
|
||||
|
||||
# 向量数据库
|
||||
|
||||
近似最近邻索引、距离度量、Chroma / Milvus / pgvector 选型。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: backend.gin.index
|
||||
title: Gin Web 框架
|
||||
type: index
|
||||
domain: backend
|
||||
stack: [gin]
|
||||
tags: []
|
||||
week: [1, 2, 3]
|
||||
---
|
||||
|
||||
# Gin Web 框架
|
||||
|
||||
路由、参数绑定、中间件、鉴权、统一错误处理。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: backend.go.index
|
||||
title: Go 语言
|
||||
type: index
|
||||
domain: backend
|
||||
stack: [go]
|
||||
tags: []
|
||||
week: [1, 2, 3, 14]
|
||||
---
|
||||
|
||||
# Go 语言
|
||||
|
||||
语法、接口、错误处理、并发、测试和工程化。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: backend.grpc-microservices.index
|
||||
title: gRPC 与微服务
|
||||
type: index
|
||||
domain: backend
|
||||
stack: [grpc-microservices]
|
||||
tags: []
|
||||
week: [14]
|
||||
---
|
||||
|
||||
# gRPC 与微服务
|
||||
|
||||
proto、四种调用方式、超时重试、服务治理、分布式事务。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: backend.mq.index
|
||||
title: 消息队列(RabbitMQ / Kafka)
|
||||
type: index
|
||||
domain: backend
|
||||
stack: [mq]
|
||||
tags: []
|
||||
week: [3, 14]
|
||||
---
|
||||
|
||||
# 消息队列(RabbitMQ / Kafka)
|
||||
|
||||
异步解耦、可靠投递、幂等消费、顺序、延迟消息。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: backend.mysql.index
|
||||
title: MySQL
|
||||
type: index
|
||||
domain: backend
|
||||
stack: [mysql]
|
||||
tags: []
|
||||
week: [2, 14]
|
||||
---
|
||||
|
||||
# MySQL
|
||||
|
||||
InnoDB 索引、EXPLAIN、事务隔离与锁、慢查询、分库分表与主从。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: backend.postgresql.index
|
||||
title: PostgreSQL
|
||||
type: index
|
||||
domain: backend
|
||||
stack: [postgresql]
|
||||
tags: []
|
||||
week: [2, 3, 6]
|
||||
---
|
||||
|
||||
# PostgreSQL
|
||||
|
||||
表设计、SQL、索引与 EXPLAIN、事务与 MVCC、JSONB、pgvector。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: backend.redis.index
|
||||
title: Redis
|
||||
type: index
|
||||
domain: backend
|
||||
stack: [redis]
|
||||
tags: []
|
||||
week: [3, 6, 14]
|
||||
---
|
||||
|
||||
# Redis
|
||||
|
||||
数据结构、缓存模式、分布式锁、限流、Streams。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: devops.cicd.index
|
||||
title: CI/CD
|
||||
type: index
|
||||
domain: devops
|
||||
stack: [cicd]
|
||||
tags: []
|
||||
week: [3, 7, 14]
|
||||
---
|
||||
|
||||
# CI/CD
|
||||
|
||||
流水线设计、测试与构建、镜像发布、部署与回滚。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: devops.docker.index
|
||||
title: Docker 与 Compose
|
||||
type: index
|
||||
domain: devops
|
||||
stack: [docker]
|
||||
tags: []
|
||||
week: [3, 6, 13, 14]
|
||||
---
|
||||
|
||||
# Docker 与 Compose
|
||||
|
||||
镜像、多阶段构建、网络、数据卷、Compose 编排。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: devops.git.index
|
||||
title: Git
|
||||
type: index
|
||||
domain: devops
|
||||
stack: [git]
|
||||
tags: []
|
||||
week: [1, 8, 15]
|
||||
---
|
||||
|
||||
# Git
|
||||
|
||||
分支模型、提交规范、合并与变基、误操作恢复、协作流程。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: devops.linux.index
|
||||
title: Linux 运维基础
|
||||
type: index
|
||||
domain: devops
|
||||
stack: [linux]
|
||||
tags: []
|
||||
week: [14]
|
||||
---
|
||||
|
||||
# Linux 运维基础
|
||||
|
||||
常用命令、进程与端口、日志、磁盘、权限、线上排查。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: devops.nginx.index
|
||||
title: Nginx
|
||||
type: index
|
||||
domain: devops
|
||||
stack: [nginx]
|
||||
tags: []
|
||||
week: [3, 7, 14]
|
||||
---
|
||||
|
||||
# Nginx
|
||||
|
||||
反向代理、静态资源、HTTPS、SSE / WebSocket、限流、灰度。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: frontend.fe-testing-perf.index
|
||||
title: 前端测试与性能
|
||||
type: index
|
||||
domain: frontend
|
||||
stack: [fe-testing-perf]
|
||||
tags: []
|
||||
week: [7]
|
||||
---
|
||||
|
||||
# 前端测试与性能
|
||||
|
||||
Vitest、组件测试、Web Vitals、性能优化。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: frontend.react.index
|
||||
title: React
|
||||
type: index
|
||||
domain: frontend
|
||||
stack: [react]
|
||||
tags: []
|
||||
week: [7]
|
||||
---
|
||||
|
||||
# React
|
||||
|
||||
Hooks、渲染与性能、状态管理、路由、流式界面。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: frontend.ssr.index
|
||||
title: SSR(Nuxt / Next.js)
|
||||
type: index
|
||||
domain: frontend
|
||||
stack: [ssr]
|
||||
tags: []
|
||||
week: [7]
|
||||
---
|
||||
|
||||
# SSR(Nuxt / Next.js)
|
||||
|
||||
CSR / SSR / SSG、水合、Nuxt 与 Next.js 基础。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: frontend.typescript.index
|
||||
title: TypeScript
|
||||
type: index
|
||||
domain: frontend
|
||||
stack: [typescript]
|
||||
tags: []
|
||||
week: [7]
|
||||
---
|
||||
|
||||
# TypeScript
|
||||
|
||||
类型系统、泛型、类型收窄、工程配置。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: frontend.vite.index
|
||||
title: Vite
|
||||
type: index
|
||||
domain: frontend
|
||||
stack: [vite]
|
||||
tags: []
|
||||
week: [7]
|
||||
---
|
||||
|
||||
# Vite
|
||||
|
||||
开发服务器原理、构建、配置、插件、环境变量。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: frontend.vue.index
|
||||
title: Vue 3 + Pinia + Router
|
||||
type: index
|
||||
domain: frontend
|
||||
stack: [vue]
|
||||
tags: []
|
||||
week: [7]
|
||||
---
|
||||
|
||||
# Vue 3 + Pinia + Router
|
||||
|
||||
响应式、组合式 API、状态管理、路由与权限。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: frontend.webpack.index
|
||||
title: Webpack
|
||||
type: index
|
||||
domain: frontend
|
||||
stack: [webpack]
|
||||
tags: []
|
||||
week: [7]
|
||||
---
|
||||
|
||||
# Webpack
|
||||
|
||||
核心概念、Loader 与 Plugin、拆包、构建优化。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: topics.index
|
||||
title: 专项学习总目录
|
||||
type: index
|
||||
domain: plan
|
||||
stack: []
|
||||
---
|
||||
|
||||
# 专项学习总目录
|
||||
|
||||
按技术栈组织的独立学习内容。每个技术栈包含:课程(lesson)、企业场景(scenario)、分级面试题(interview)和可运行的示例代码(examples)。16 周计划([plan/README.md](../plan/README.md))按周链接到这里。
|
||||
|
||||
跨技术栈的企业服务场景见 [scenarios/index.md](../scenarios/index.md)。内容规范见 [CONTRIBUTING.md](../CONTRIBUTING.md)。
|
||||
|
||||
## 后端(`backend`)
|
||||
|
||||
| 技术栈 | 内容 | 用在第几周 |
|
||||
| --- | --- | --- |
|
||||
| [Go 语言](backend/go/index.md) | 语法、接口、错误处理、并发、测试和工程化 | 1、2、3、14 |
|
||||
| [Gin Web 框架](backend/gin/index.md) | 路由、参数绑定、中间件、鉴权、统一错误处理 | 1、2、3 |
|
||||
| [PostgreSQL](backend/postgresql/index.md) | 表设计、SQL、索引与 EXPLAIN、事务与 MVCC、JSONB、pgvector | 2、3、6 |
|
||||
| [MySQL](backend/mysql/index.md) | InnoDB 索引、EXPLAIN、事务隔离与锁、慢查询、分库分表与主从 | 2、14 |
|
||||
| [Redis](backend/redis/index.md) | 数据结构、缓存模式、分布式锁、限流、Streams | 3、6、14 |
|
||||
| [消息队列(RabbitMQ / Kafka)](backend/mq/index.md) | 异步解耦、可靠投递、幂等消费、顺序、延迟消息 | 3、14 |
|
||||
| [gRPC 与微服务](backend/grpc-microservices/index.md) | proto、四种调用方式、超时重试、服务治理、分布式事务 | 14 |
|
||||
|
||||
## 运维与工程(`devops`)
|
||||
|
||||
| 技术栈 | 内容 | 用在第几周 |
|
||||
| --- | --- | --- |
|
||||
| [Docker 与 Compose](devops/docker/index.md) | 镜像、多阶段构建、网络、数据卷、Compose 编排 | 3、6、13、14 |
|
||||
| [Nginx](devops/nginx/index.md) | 反向代理、静态资源、HTTPS、SSE / WebSocket、限流、灰度 | 3、7、14 |
|
||||
| [Linux 运维基础](devops/linux/index.md) | 常用命令、进程与端口、日志、磁盘、权限、线上排查 | 14 |
|
||||
| [CI/CD](devops/cicd/index.md) | 流水线设计、测试与构建、镜像发布、部署与回滚 | 3、7、14 |
|
||||
| [Git](devops/git/index.md) | 分支模型、提交规范、合并与变基、误操作恢复、协作流程 | 1、8、15 |
|
||||
|
||||
## Python(`python`)
|
||||
|
||||
| 技术栈 | 内容 | 用在第几周 |
|
||||
| --- | --- | --- |
|
||||
| [Python 语言](python/python/index.md) | 给 JS 开发者的 Python:语法、类型、异常、包管理、测试 | 4 |
|
||||
| [asyncio 异步编程](python/asyncio/index.md) | 协程、任务、并发控制、超时与取消 | 4、5、12 |
|
||||
| [FastAPI](python/fastapi/index.md) | 路由与校验、依赖注入、中间件与鉴权、流式输出、测试 | 4、5、12 |
|
||||
|
||||
## AI 应用(`ai`)
|
||||
|
||||
| 技术栈 | 内容 | 用在第几周 |
|
||||
| --- | --- | --- |
|
||||
| [RAG 检索增强生成](ai/rag/index.md) | 加载切分、嵌入、检索、混合检索与重排、引用与拒答、评测 | 5、6 |
|
||||
| [向量数据库](ai/vector-db/index.md) | 近似最近邻索引、距离度量、Chroma / Milvus / pgvector 选型 | 5、6 |
|
||||
| [LangChain](ai/langchain/index.md) | 模型与消息、LCEL、工具、结构化输出 | 6、11 |
|
||||
| [LangGraph](ai/langgraph/index.md) | 状态图、条件路由、检查点续跑、interrupt、长期记忆 | 11、12 |
|
||||
| [MCP 模型上下文协议](ai/mcp/index.md) | 协议概念、tools / resources / prompts、服务端与客户端 | 10、11 |
|
||||
| [Function Calling 工具调用](ai/function-calling/index.md) | 工具定义、调用循环、参数校验、安全 | 10 |
|
||||
| [提示词与上下文工程](ai/prompt-engineering/index.md) | 提示词结构、结构化输出、上下文工程、提示词注入防护 | 5、11、13 |
|
||||
| [Agent 记忆、评测与可观测性](ai/agent-ops/index.md) | 短期 / 长期记忆、评测集、LLM 评分、追踪、成本 | 12、13 |
|
||||
| [Dify / Coze 低代码平台](ai/lowcode/index.md) | 知识库、工作流、插件,和写代码的取舍 | 13 |
|
||||
|
||||
## 前端(`frontend`)
|
||||
|
||||
| 技术栈 | 内容 | 用在第几周 |
|
||||
| --- | --- | --- |
|
||||
| [Vue 3 + Pinia + Router](frontend/vue/index.md) | 响应式、组合式 API、状态管理、路由与权限 | 7 |
|
||||
| [React](frontend/react/index.md) | Hooks、渲染与性能、状态管理、路由、流式界面 | 7 |
|
||||
| [TypeScript](frontend/typescript/index.md) | 类型系统、泛型、类型收窄、工程配置 | 7 |
|
||||
| [Vite](frontend/vite/index.md) | 开发服务器原理、构建、配置、插件、环境变量 | 7 |
|
||||
| [Webpack](frontend/webpack/index.md) | 核心概念、Loader 与 Plugin、拆包、构建优化 | 7 |
|
||||
| [SSR(Nuxt / Next.js)](frontend/ssr/index.md) | CSR / SSR / SSG、水合、Nuxt 与 Next.js 基础 | 7 |
|
||||
| [前端测试与性能](frontend/fe-testing-perf/index.md) | Vitest、组件测试、Web Vitals、性能优化 | 7 |
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: python.asyncio.index
|
||||
title: asyncio 异步编程
|
||||
type: index
|
||||
domain: python
|
||||
stack: [asyncio]
|
||||
tags: []
|
||||
week: [4, 5, 12]
|
||||
---
|
||||
|
||||
# asyncio 异步编程
|
||||
|
||||
协程、任务、并发控制、超时与取消。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: python.fastapi.index
|
||||
title: FastAPI
|
||||
type: index
|
||||
domain: python
|
||||
stack: [fastapi]
|
||||
tags: []
|
||||
week: [4, 5, 12]
|
||||
---
|
||||
|
||||
# FastAPI
|
||||
|
||||
路由与校验、依赖注入、中间件与鉴权、流式输出、测试。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: python.python.index
|
||||
title: Python 语言
|
||||
type: index
|
||||
domain: python
|
||||
stack: [python]
|
||||
tags: []
|
||||
week: [4]
|
||||
---
|
||||
|
||||
# Python 语言
|
||||
|
||||
给 JS 开发者的 Python:语法、类型、异常、包管理、测试。
|
||||
|
||||
> 本专题建设中:课程、企业场景、面试题和示例代码会陆续补充。
|
||||
Reference in new issue
Block a user