今天我们用一个完整的实战案例,展示如何用自然语言指挥 OpenCode 在 5 分钟内写出一款 URL 短链接服务(URL Shortener)。
这个服务用 Go 语言编写,仅依赖标准库,代码约 90 行。它可以将任意长链接压缩为 6 位短码,访问短链接自动 302 跳转到原始地址,还提供了列表接口用于查看所有已生成的短链。
技术栈:Go 1.21+ 标准库(net/http + crypto/md5 + sync)。
确保本地已安装 Go(go version 确认版本 ≥ 1.21),然后打开终端进入工作目录:
mkdir urlshortener && cd urlshortener
打开 OpenCode,直接告诉它你要做什么——不需要手写一行代码。
在 OpenCode 对话框中输入我们的第一条指令。不需要说明具体目录结构,只说意图就够了。
> 我对 OpenCode 说:
> 用 Go 初始化一个 HTTP 服务项目,监听 8080 端口,使用标准库的 http.NewServeMux
OpenCode 立刻理解了我的意图,自动完成 go mod init 并生成了 main.go 主体框架:
package main
import (
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
addr := ":8080"
log.Printf("URL Shortener running on %s\n", "http://localhost"+addr)
log.Fatal(http.ListenAndServe(addr, mux))
}
关键点:你不需要事先想好完整的框架——OpenCode 会根据上下文推断后续步骤所需的基础设施,并且会选用符合你语言习惯的写法(比如这里用的是 http.NewServeMux 而非默认多路复用器)。
有了空框架,接下来添加核心功能。同样只需要一句话描述需求:
> 我对 OpenCode 说:
> 添加 POST /shorten 接口,接收 JSON {"url": "..."},用 MD5 生成 6 位短码,存入 map 并返回短链接
OpenCode 不仅实现了功能,还自动加上了互斥锁(sync.RWMutex)来保证 map 的并发安全——这可是新手容易忽略的细节:
var (
store = make(map[string]string)
mu sync.RWMutex
baseURL = "http://localhost:8080"
)
func handleShorten(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var body struct{ URL string `json:"url"` }
if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.URL == "" {
http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
return
}
code := generateCode(body.URL)
mu.Lock()
store[code] = body.URL
mu.Unlock()
resp := map[string]string{
"short_url": fmt.Sprintf("%s/s/%s", baseURL, code),
"code": code,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
值得注意:OpenCode 自动对空 URL 做了校验,方法是检查 body.URL == "",而非仅检查 err != nil——后者无法覆盖 JSON 中字段为空字符串的情况。
到这里我们只差两部分功能:访问短链接时跳转,以及查看所有已生成的短链。
> 我对 OpenCode 说:
> 添加两个接口:GET /s/{code} 做 302 重定向到原始 URL,GET /list 返回所有短链列表
OpenCode 一次性生成了两个完整的 Handler:
func handleRedirect(w http.ResponseWriter, r *http.Request) {
code := strings.TrimPrefix(r.URL.Path, "/s/")
mu.RLock()
url, ok := store[code]
mu.RUnlock()
if !ok {
http.NotFound(w, r)
return
}
http.Redirect(w, r, url, http.StatusFound)
}
func handleList(w http.ResponseWriter, r *http.Request) {
mu.RLock()
entries := make([]map[string]string, 0, len(store))
for code, url := range store {
entries = append(entries, map[string]string{
"code": code, "short_url": fmt.Sprintf("%s/s/%s", baseURL, code), "url": url,
})
}
mu.RUnlock()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"count": len(entries), "items": entries,
})
}
整个过程我只下达了 3 条自然语言指令,OpenCode 就完成了一个可以编译运行、具备生产级并发安全性的短链接服务。
最后一条指令是让 OpenCode 帮我们写测试脚本:
> 我对 OpenCode 说:
> 帮我写一个 bash 一行命令来测试 POST /shorten,然后验证 GET /s/{code} 的重定向
OpenCode 给出了可直接执行的 curl 命令序列:
# 生成短链接
curl -s -X POST http://localhost:8080/shorten \
-H "Content-Type: application/json" \
-d '{"url":"https://www.linweiqin.com"}'
# 查看列表
curl -s http://localhost:8080/list | python -m json.tool
# 测试重定向(-L 跟随跳转)
curl -s -L -o /dev/null -w "%{http_code}" http://localhost:8080/s/06a5fe
跑一遍,三个接口全部正常响应。
以下是经 OpenCode 最终生成的完整 main.go(不含注释,共 92 行):
package main
import (
"crypto/md5"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"sync"
"time"
)
var (
store = make(map[string]string)
mu sync.RWMutex
baseURL = "http://localhost:8080"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/shorten", handleShorten)
mux.HandleFunc("/s/", handleRedirect)
mux.HandleFunc("/list", handleList)
log.Printf("URL Shortener running on %s\n", baseURL)
log.Fatal(http.ListenAndServe(":8080", mux))
}
func handleShorten(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var body struct{ URL string `json:"url"` }
if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.URL == "" {
http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
return
}
code := generateCode(body.URL)
mu.Lock()
store[code] = body.URL
mu.Unlock()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"short_url": fmt.Sprintf("%s/s/%s", baseURL, code),
"code": code,
})
}
func handleRedirect(w http.ResponseWriter, r *http.Request) {
code := strings.TrimPrefix(r.URL.Path, "/s/")
mu.RLock()
url, ok := store[code]
mu.RUnlock()
if !ok {
http.NotFound(w, r)
return
}
http.Redirect(w, r, url, http.StatusFound)
}
func handleList(w http.ResponseWriter, r *http.Request) {
mu.RLock()
entries := make([]map[string]string, 0, len(store))
for code, url := range store {
entries = append(entries, map[string]string{
"code": code,
"short_url": fmt.Sprintf("%s/s/%s", baseURL, code),
"url": url,
})
}
mu.RUnlock()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"count": len(entries),
"items": entries,
})
}
func generateCode(url string) string {
raw := fmt.Sprintf("%s%d", url, time.Now().UnixNano())
hash := md5.Sum([]byte(raw))
return fmt.Sprintf("%x", hash)[:6]
}
用 OpenCode 开发这个短链接服务的实际耗时不到 5 分钟,期间我总共只输入了 4 条自然语言指令。对比传统开发方式——手写框架、查标准库文档、处理并发安全、写 JSON 序列化——至少要 20-30 分钟。
OpenCode 带来的效率提升不仅体现在速度上,更重要的三点:
自动补齐工程细节:sync.RWMutex 保护 map、请求方法校验、空值边界处理,这些新手容易遗漏的点 OpenCode 都帮你做了。
即时验证闭环:写好代码后可以直接让 OpenCode 生成测试命令,在一个对话窗口里完成"写→测→改"的全流程。
标准库优先:OpenCode 倾向于使用标准库而非拉取第三方依赖,生成的项目开箱即用,没有依赖地狱。
如果你还没试过用自然语言指挥 AI 写代码,不妨从这个小项目开始——打开 OpenCode,输入第一句话,看看它能帮你省下多少时间。