Module:数据页校验
外观
此模块的文档可以在Module:数据页校验/doc创建
-- 数据页结构校验: 供各列表模块调用
-- 结构错误时显红字并归入 [[分类:数据页异常]], 避免静默渲染成空表
-- 背景: 2026-09 曾因数据页被写成裸数组(缺少 songs) 导致全站列表空白
local p = {}
local CAT = '[[分类:数据页异常]]'
local function fail(msg)
return '<span class="error">数据页错误(' .. msg .. ')</span>' .. CAT
end
function p.load(title, opts)
opts = opts or {}
local t = mw.title.new(title)
if not t or not t.exists then
return nil, fail('[[' .. title .. ']] 不存在')
end
local raw = t:getContent()
if not raw or raw == '' then
return nil, fail('[[' .. title .. ']] 内容为空')
end
local ok, data = pcall(mw.text.jsonDecode, raw)
if not ok then
return nil, fail('[[' .. title .. ']] JSON 解析失败(语法错误)')
end
if type(data) ~= 'table' then
return nil, fail('[[' .. title .. ']] 顶层必须是对象 {"songs":[…],"special":[…]}')
end
-- 可选段: tutorial(教程) / story({songs,training}) / special(石墩子模式)
for _, key in ipairs({ 'tutorial', 'special' }) do
if data[key] ~= nil and type(data[key]) ~= 'table' then
return nil, fail('[[' .. title .. ']] 的 ' .. key .. ' 必须是数组')
end
end
if data.story ~= nil then
if type(data.story) ~= 'table' then
return nil, fail('[[' .. title .. ']] 的 story 必须是对象 {songs:[…],training:[…]}')
end
for _, key in ipairs({ 'songs', 'training' }) do
if data.story[key] ~= nil and type(data.story[key]) ~= 'table' then
return nil, fail('[[' .. title .. ']] 的 story.' .. key .. ' 必须是数组')
end
end
end
if opts.songs ~= false then
if type(data.songs) ~= 'table' then
return nil, fail('[[' .. title .. ']] 缺少 songs 数组 —— 必须是 {"songs":[…],"special":[…]}')
end
if #data.songs == 0 then
return nil, fail('[[' .. title .. ']] 的 songs 数组为空(疑似被清空)')
end
end
if opts.special and type(data.special) ~= 'table' then
return nil, fail('[[' .. title .. ']] 缺少 special 数组')
end
return data
end
-- 自检入口: {{#invoke:数据页校验|check|模板:曲目列表.json}}
function p.check(frame) -- Lua 标识符不能是中文, 故用 check
local title = frame.args[1] or '模板:曲目列表.json'
local data, err = p.load(title)
if err then return err end
local story = data.story or {}
return string.format('数据页 <code>%s</code> 结构正常:songs=%d,tutorial=%d,story.songs=%d,story.training=%d,special=%d。',
title, #(data.songs or {}), #(data.tutorial or {}),
#(story.songs or {}), #(story.training or {}), #(data.special or {}))
end
return p