GoWind CMS 内置 Lua 脚本引擎,支持通过脚本动态扩展业务逻辑,无需编译部署。本教程讲解 CMS 中 Lua 脚本的应用场景、API 接口和实战案例。
| 对比项 | Admin | CMS |
|---|
| 事件类型 | 用户/角色/系统 | 内容/评论/翻译/媒体 |
| API 扩展 | 系统管理 | 内容管理 + 前台 API |
| 定时任务 | 系统维护 | 内容同步 + 数据分析 |
eventbus.subscribe("post.published", function(event)
local post_id = event.PostId
local title = event.Title
local author = event.AuthorName
local post = api.call("GET", "/admin/v1/posts/" .. post_id)
local summary = http.post("https://api.ai-service.com/summarize", {
headers = { ["Authorization"] = "Bearer your-key" },
body = json.encode({ text = post.content })
})
api.call("PUT", "/admin/v1/posts/" .. post_id, {
data = { summary = summary }
})
log.info("已自动生成摘要: postId=" .. post_id)
end)
eventbus.subscribe("comment.created", function(event)
local content = event.Content
local comment_id = event.CommentId
local sensitive_words = {"广告", "垃圾", "恶意"}
for _, word in ipairs(sensitive_words) do
if string.find(content, word) then
api.call("PUT", "/admin/v1/comments/" .. comment_id .. "/reject", {
data = { reason = "包含敏感内容: " .. word }
})
log.warn("评论被自动拒绝: commentId=" .. comment_id)
return
end
end
api.call("PUT", "/admin/v1/comments/" .. comment_id .. "/approve")
end)
api.register("GET", "/app/v1/posts/popular", function(ctx)
local limit = tonumber(ctx.query.limit) or 10
local days = tonumber(ctx.query.days) or 7
local start_time = os.time() - (days * 24 * 3600)
local posts = db.query([[
SELECT p.id, p.title, p.slug, COUNT(v.id) as view_count
FROM posts p
JOIN post_views v ON v.post_id = p.id
WHERE p.status = 1 AND v.created_at >= ?
GROUP BY p.id
ORDER BY view_count DESC
LIMIT ?
]], start_time, limit)
return json.encode({ items = posts })
end)
api.register("GET", "/admin/v1/dashboard/stats", function(ctx)
local stats = {
post_count = db.scalar("SELECT COUNT(*) FROM posts WHERE status = 1"),
comment_count = db.scalar("SELECT COUNT(*) FROM comments WHERE status = 1"),
user_count = db.scalar("SELECT COUNT(*) FROM users WHERE status = 1"),
today_views = db.scalar([[
SELECT COUNT(*) FROM post_views
WHERE created_at >= CURRENT_DATE
]]),
}
return json.encode(stats)
end)
cron.register("0 2 * * *", function()
log.info("开始生成内容热力图...")
local daily_stats = db.query([[
SELECT DATE(created_at) as date, COUNT(*) as count
FROM posts
WHERE status = 1 AND created_at >= NOW() - INTERVAL '30 days'
GROUP BY DATE(created_at)
ORDER BY date
]])
redis.set("stats:content_heatmap", json.encode(daily_stats))
log.info("热力图数据已更新")
end)
cron.register("0 3 1 * *", function()
local threshold = os.time() - (90 * 24 * 3600)
local expired = db.query([[
SELECT id FROM posts
WHERE status = 2 AND updated_at < ?
]], threshold)
for _, post in ipairs(expired) do
db.execute("INSERT INTO posts_archive SELECT * FROM posts WHERE id = ?", post.id)
db.execute("DELETE FROM posts WHERE id = ?", post.id)
log.info("已归档文章: " .. post.id)
end
end)
eventbus.subscribe("post.created", function(event)
local post_id = event.PostId
local post = api.call("GET", "/admin/v1/posts/" .. post_id)
if not post or not post.content then return end
if post.content_format == "markdown" then
local html = http.post("https://api.markdown.com/render", {
body = json.encode({ markdown = post.content })
})
api.call("PUT", "/admin/v1/posts/" .. post_id, {
data = {
content_html = html,
content_format = "html"
}
})
end
end)
eventbus.subscribe("post.published", function(event)
local post = api.call("GET", "/admin/v1/posts/" .. event.PostId)
if not post.sections then return end
for _, section in ipairs(post.sections) do
if section.type == "SECTION_TYPE_IMAGE" then
local url = section.data.image.url
local file_info = oss.stat(url)
if file_info.size > 2 * 1024 * 1024 then
local compressed_url = image.compress(url, {
quality = 85,
maxWidth = 1920,
format = "webp"
})
section.data.image.url = compressed_url
log.info("已压缩图片: " .. url .. " → " .. compressed_url)
end
end
end
api.call("PUT", "/admin/v1/posts/" .. event.PostId, {
data = { sections = post.sections }
})
end)
| 对象 | 说明 |
|---|
api | CMS API 调用 |
db | 数据库查询 |
redis | Redis 操作 |
http | HTTP 客户端 |
oss | 对象存储操作 |
image | 图片处理 |
log | 日志记录 |
json | JSON 编解码 |
eventbus | 事件订阅 |
cron | 定时任务注册 |
os | 系统函数 |
string | 字符串操作 |
api.call(method, path, options)
api.register(method, path, handler)
db.query(sql, params)
db.scalar(sql, params)
db.execute(sql, params)
redis.get(key)
redis.set(key, value, ttl)
redis.del(key)
redis.incr(key)
http.get(url, options)
http.post(url, options)
log.info(msg)
log.warn(msg)
log.error(msg)
| 检查项 | 说明 |
|---|
| Lua 引擎初始化 | 启动时加载脚本 |
| 事件订阅 | 内容/评论事件处理 |
| 自定义 API | 注册路由 |
| 定时任务 | cron 注册 |
| 安全沙箱 | 限制危险操作 |
| 错误恢复 | panic 不影响主流程 |