Files
daydaytalk-fwutils/target/fwutils/develop/function/template.lua
2026-01-12 21:22:32 +08:00

365 lines
11 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
require("fwutils.webapi")
-- CREATE TABLE `fw_template` (
-- `id` int NOT NULL AUTO_INCREMENT,
-- `role_id` int DEFAULT NULL,
-- `key` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
-- `value` varchar(2048) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
-- `enable` tinyint NOT NULL COMMENT '是否启用',
-- PRIMARY KEY (`id`),
-- UNIQUE KEY `role_id` (`role_id`,`key`)
-- ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Fastweb-关键词模板';
local M = {
cfg = nil,
template_engine_bc_this_role = nil
}
-- 允许的扩展名
local allowed_extensions = {
"shtml",
"html",
"js"
}
function M.get_by_id(id,conn)
local select = conn:select()
select:table("fw_template LEFT JOIN fw_role ON fw_template.role_id = fw_role.id")
select:field({
"fw_template.id",
"fw_template.role_id",
"fw_template.`key`",
"fw_template.value",
"fw_template.enable",
"fw_role.title as role_title",
})
select:where_i32("fw_template.id","=",id)
select:limit(0,1)
local result = select:query()
if result:row_count() == 0 then
return nil
end
local d = result:table()[1]
return d
end
function M.add(data,conn)
local insert = conn:insert()
insert:table("fw_template")
if data.role_id ~= nil and data.role_id ~= cjson.null then
insert:set_i32("role_id",data.role_id)
end
if data.key ~= nil then
insert:set_str("`key`",data.key)
end
if data.value ~= nil then
insert:set_str("value",data.value)
end
if data.enable ~= nil then
insert:set_i32("enable",data.enable)
end
local d = insert:exec()
return d == 1
end
function M.update(data,conn)
local update = conn:update()
update:table("fw_template")
if data.role_id ~= nil then
update:set_i32("role_id",data.role_id)
end
if data.key ~= nil then
update:set_str("`key`",data.key)
end
if data.value ~= nil then
update:set_str("value",data.value)
end
if data.enable ~= nil then
update:set_i32("enable",data.enable)
end
update:where_i32("id","=",data.id)
local d = update:exec()
return d == 1
end
function M.delete(id,conn)
local del = conn:delete()
del:table("fw_template")
del:where_i32("id","=",id)
local d = del:exec()
return d == 1
end
function M.list(search,limit,conn)
return query_model_ex(conn,[=[
fw_template LEFT JOIN fw_role ON fw_template.role_id = fw_role.id
]=],{
"fw_template.id",
"fw_template.role_id",
"fw_template.`key`",
"fw_template.value",
"fw_template.enable",
"fw_role.title as role_title",
"fw_role.id as role_id",
},limit.start,limit.length,function(sel)
if search.role_id ~= nil and search.role_id ~= -1 then
sel:where_i32("fw_template.role_id","=",search.role_id)
end
end,function(sel_data)
sel_data:orderby("fw_template.enable DESC")
end)
end
-- 更新
M.make_bytecode = function(conn)
-- 查询权限表
local select = conn:select()
select:table("fw_template")
select:where_i32("enable","=",1)
local result = select:query()
local bc = {
public = {}
}
while result:next() do
local id = result:get("id")
local role_id = string.format("%d",result:get("role_id"))
local key = result:get("key")
local value = result:get("value")
if bc[role_id] == nil then
bc[role_id] = {}
end
if role_id == "0" then
bc["public"][key] = value
else
bc[role_id][key] = value
end
end
local code = "return " .. require("serpent").serialize(bc, {comment = false})
utils.save_file(fw.website_dir().."/"..(fwutils_config.path.luabytecode:gsub("%.", "/")).."/template_engine_bc.lua",code)
return true
end
-- 处理
-- @param static_content 静态内容
-- @param cfg 配置
-- @return 是否替换(TRUE则不需要继续处理FALSE则继续处理)
M.handle = function(__cfg,functions)
local function has_ext(ext)
for _, v in ipairs(allowed_extensions) do
if v == ext then
return true
end
end
return false
end
M.cfg = __cfg
M.template_engine_bc_this_role = {
template = {
private = {},
public = {}
},
}
local ext = utils.ext(M.cfg.filepath())
local static_content = nil
if ext ~= nil then
if not has_ext(ext) then
return false
end
-- 读取资源文件
static_content = utils.read_file(fw.website_dir()..M.cfg.filepath())
if static_content == nil or static_content == "" then
return false
end
else
-- 无需替换的扩展名
return false
end
local template_engine_bc = require(fwutils_config.path.luabytecode..".template_engine_bc")
M.template_engine_bc_this_role["template"]["private"] = template_engine_bc[string.format("%d",M.cfg.role_id())]
M.template_engine_bc_this_role["template"]["public"] = template_engine_bc["public"]
local replaced,content = M.replace(static_content)
if replaced then
static_content = content
end
-- 执行函数
static_content, n = static_content:gsub("%${<<<%s*(.-)%s*>>>}", function(code)
-- 尝试编译代码Lua 5.2+ 使用 loadLua 5.1 可用 loadstring
local env = {}
_G["engine"] = M
local env = require("fwutils.funs").regist(env)
if functions ~= nil then
env = functions.regist(env)
end
env["_G"] = _G
local chunk, errmsg = load(code,nil,nil,env)
if not chunk then
fw.throw_string(errmsg)
end
-- 使用 pcall 安全执行代码块
local status, result = pcall(chunk)
if not status then
fw.throw_string(result)
end
-- 如果代码没有返回值,则替换为空字符串,否则转换成字符串返回
return tostring(result)
end)
if n > 0 then
replaced = true
end
if replaced then
if ext == "shtml" or ext == "html" then
response.header("Content-Type","text/html")
elseif ext == "js" then
response.header("Content-Type","application/javascript")
end
response.send(static_content)
return true
end
return false
end
M.replace = function (content)
if content == nil or content == "" then
return false
end
-- 支持多级kvs替换如 ${people.age}
local function flatten_kvs(tbl, prefix, out)
if tbl == nil then
return {}
end
out = out or {}
prefix = prefix or ""
for k, v in pairs(tbl) do
local key = prefix ~= "" and (prefix .. "." .. k) or k
if type(v) == "table" then
flatten_kvs(v, key, out)
else
out[key] = v
end
end
return out
end
-- 先提取 content 中所有需要替换的占位符
-- 需要正确处理 ${<<<...>>>} 块:块内部的 ${...} 需要替换,但块本身不替换
local placeholders = {}
-- 第一步:提取所有 ${<<<...>>>} 块,临时替换它们,并收集所有占位符
local blocks = {}
local block_index = 0
local temp_content = content:gsub("%${<<<%s*(.-)%s*>>>}", function(block_content)
block_index = block_index + 1
local placeholder = "${__TEMP_BLOCK_" .. block_index .. "__}"
-- 收集块内部的 ${...} 占位符
for ph in string.gmatch(block_content, "%${([^}]+)}") do
placeholders[ph] = true
end
blocks[block_index] = {
placeholder = placeholder,
content = block_content
}
return placeholder
end)
-- 第二步:提取外部(不在 ${<<<...>>>} 块中)的 ${...} 占位符
for placeholder in string.gmatch(temp_content, "%${([^}]+)}") do
-- 忽略临时占位符
if not placeholder:match("^__TEMP_BLOCK_%d+__$") then
placeholders[placeholder] = true
end
end
-- print("PLACEHOLDERS:",cjson.encode(placeholders))
-- 如果没有任何占位符,直接返回
if next(placeholders) == nil then
return false
end
-- 合并所有数据源到一个查找表中(只 flatten 一次)
local value_map = {}
-- PUBLIC
local flat_kvs = flatten_kvs(M.template_engine_bc_this_role["template"]["public"])
for k, v in pairs(flat_kvs) do
value_map[k] = v
end
-- PRIVATE
flat_kvs = flatten_kvs(M.template_engine_bc_this_role["template"]["private"])
for k, v in pairs(flat_kvs) do
value_map[k] = v
end
-- REQUEST
flat_kvs = flatten_kvs(request.gets(), "request")
for k, v in pairs(flat_kvs) do
value_map[k] = v
end
-- TOKEN
flat_kvs = flatten_kvs(M.cfg.user_data(), "token")
for k, v in pairs(flat_kvs) do
value_map[k] = v
end
-- 转义函数:将 Lua 模式特殊字符转义为字面匹配
local function escape_pattern(str)
return str:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
end
-- 第三步:先处理 ${<<<...>>>} 块内部的占位符
local replaced = false
for i, block in ipairs(blocks) do
local block_content = block.content
for placeholder, _ in pairs(placeholders) do
if value_map[placeholder] ~= nil then
local escaped_placeholder = escape_pattern(placeholder)
local new_content, count = string.gsub(block_content, "%${" .. escaped_placeholder .. "}", tostring(value_map[placeholder]))
if count > 0 then
block_content = new_content
replaced = true
end
end
end
blocks[i].processed_content = block_content
end
-- 第四步:替换外部的 ${...} 占位符(在临时内容中,此时块已被替换为临时占位符)
local n = 0
for placeholder, _ in pairs(placeholders) do
if value_map[placeholder] ~= nil then
local escaped_placeholder = escape_pattern(placeholder)
temp_content, n = string.gsub(temp_content, "%${" .. escaped_placeholder .. "}", tostring(value_map[placeholder]))
if n > 0 then
replaced = true
end
end
end
-- 第五步:恢复 ${<<<...>>>} 块(使用处理后的内容)
for i, block in ipairs(blocks) do
local processed_block = "${<<<" .. blocks[i].processed_content .. ">>>}"
temp_content = temp_content:gsub(block.placeholder:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1"), processed_block)
end
content = temp_content
if replaced then
return true, content
end
return false
end
-- 检查内容中是否有TOKEN变量
-- @return boolean
M.hasToken = function()
if M.static_content == nil or M.static_content == "" then
return false
end
return M.static_content:match("%${token%.") ~= nil
end
return M