前端处理
local DataForwarder = {}
DataForwarder.__cname = "DataForwarder"
DataForwarder.NAME = DataForwarder.__cname
-- 数组包含检查工具函数
function table.contains(tbl, value)
for _, v in ipairs(tbl) do
if v == value then
return true
end
end
return false
end
-- 全局配置变量
local g_config = {
backend_endpoint = "http://127.0.0.1:8000/api/", -- 默认后端API基础地址
registered_msg_configs = {}, -- 已注册的消息配置列表
-- 配置将通过 init 方法从外部传入
}
--[[
初始化配置
从配置文件中读取后端端点名称和需要监听的消息配置
]]
function DataForwarder:init(config)
-- 从配置文件中读取后端端点名称
if config and config.backend_endpoint then
g_config.backend_endpoint = config.backend_endpoint
end
-- 从配置文件中读取需要监听的消息配置并注册
if config and config.msg_configs then
for _, msgConfig in ipairs(config.msg_configs) do
local msgID = msgConfig.id
local msgType = msgConfig.type or "lua" -- 默认是LUA类型
self:registerMsgListener(msgID, msgType)
end
end
end
--[[
注册消息监听器
msgID: 消息ID
msgType: 消息类型,"lua" 或 "txt" ,默认为 "lua"
]]
function DataForwarder:registerMsgListener(msgID, msgType)
msgType = msgType or "lua"
if g_config.registered_msg_configs[msgID] then
return
end
local function networkCB(msgID, ...)
-- 检查是否是sendluamsg格式的消息(参数数量和类型判断)
-- sendluamsg格式: sendluamsg(actor, 1000, 1, 2, 3, "我是来自服务器的消息 By Lua")
-- SENDCUSTMSG格式: SENDCUSTMSG 102 {"panel":"ActivityVip","action":"update","data":"data"}
local params = {...}
SL:Print("[DataForwarder] 收到消息ID " .. msgID .. " (" .. msgType .. ") 的原始参数: ")
SL:dump(params)
local msgData = {}
-- 首先检查是否是sendluamsg格式(通常包含至少4个参数,且最后一个参数是字符串)
if #params >= 4 then
local lastParam = params[#params]
if type(lastParam) == "string" and #lastParam > 0 then
-- 检查字符串是否是JSON格式
if string.sub(lastParam, 1, 1) == "{" or string.sub(lastParam, 1, 1) == "[" then
local success, result = pcall(SL.JsonDecode, SL, lastParam)
if success and result then
msgData = result
msgData.format = "json"
msgData.params = {} -- 初始化参数数组
for j = 1, #params - 1 do
table.insert(msgData.params, params[j])
end
-- SL:Print("[DataForwarder] 解析到LUA格式JSON消息")
else
-- 不是JSON格式,可能是普通字符串消息
msgData = {
message = lastParam,
format = "sendluamsg",
params = {}
}
-- 收集其他参数
for j = 1, #params - 1 do
table.insert(msgData.params, params[j])
end
-- SL:Print("[DataForwarder] 解析到sendluamsg格式字符串消息")
end
else
-- 最后一个参数不是JSON格式,是普通字符串
msgData = {
message = lastParam,
format = "sendluamsg",
params = {}
}
for j = 1, #params - 1 do
table.insert(msgData.params, params[j])
end
-- SL:Print("[DataForwarder] 解析到sendluamsg格式字符串消息")
end
else
-- 最后一个参数不是字符串,可能是其他格式
msgData = {
format = "unknown",
params = params
}
-- SL:Print("[DataForwarder] 消息格式未识别,包含所有原始参数")
end
else
-- 参数数量较少,可能是TXT脚本格式
if #params == 1 then
-- 只有一个参数,检查是否是JSON格式
local param = params[1]
if type(param) == "string" and #param > 0 and (string.sub(param, 1, 1) == "{" or string.sub(param, 1, 1) == "[") then
local success, result = pcall(SL.JsonDecode, SL, param)
if success and result then
msgData = result
msgData.format = "txt_json"
msgData.msgid = msgID -- 使用注册时的消息ID作为消息号
-- SL:Print("[DataForwarder] 解析到TXT格式JSON消息,消息号: " .. tostring(msgData.msgid))
else
-- 不是JSON格式,可能是其他类型
msgData = {
format = "txt_unknown",
params = params
}
-- SL:Print("[DataForwarder] TXT格式消息解析失败")
end
elseif type(param) == "table" then
-- 如果是table类型,直接使用
msgData = param
msgData.format = "txt_json"
msgData.msgid = msgID -- 使用注册时的消息ID作为消息号
-- SL:Print("[DataForwarder] 解析到TXT格式JSON消息(table类型),消息号: " .. tostring(msgData.msgid))
else
msgData = {
format = "txt_unknown",
params = params
}
-- SL:Print("[DataForwarder] TXT格式消息未识别")
end
else
-- 参数数量不符合预期格式
msgData = {
format = "unknown",
params = params
}
-- SL:Print("[DataForwarder] 消息格式未识别,包含所有原始参数")
end
end
-- 如果没有找到任何可解析的数据,创建一个包含所有参数的基础数据结构
if not msgData or not next(msgData) then
msgData = {
format = "unknown",
params = params
}
-- SL:Print("[DataForwarder] 消息格式未识别,包含所有原始参数")
end
-- SL:dump(msgData, "----------消息解析结果---", 3)
-- 调用模块的处理函数
self:handleMessage(msgID, msgData)
end
-- 根据消息类型选择注册方式
if msgType == "txt" then
SL:RegisterNetMsg(msgID, networkCB)
else
SL:RegisterLuaNetMsg(msgID, networkCB)
end
-- 将配置保存到注册信息中
g_config.registered_msg_configs[msgID] = {type = msgType}
end
--[[
处理服务端下发的消息
msgID: 消息ID
msgData: 解析后的消息数据
]]
function DataForwarder:handleMessage(msgID, msgData)
SL:Print("[DataForwarder] 收到消息ID " .. msgID .. " 的数据: ")
SL:dump(msgData)
-- 验证消息数据的有效性
if not msgData or type(msgData) ~= "table" then
SL:Print("[DataForwarder] 收到无效的消息数据: " .. type(msgData))
return
end
-- 处理HTTP请求方法
local method
if msgData.method then
method = string.upper(tostring(msgData.method))
-- 检查是否是标准HTTP方法
local supportedMethods = {"GET", "POST", "PUT", "DELETE", "PATCH"}
local isStandardMethod = false
for _, supportedMethod in ipairs(supportedMethods) do
if method == supportedMethod then
isStandardMethod = true
break
end
end
-- 如果不是标准HTTP方法,使用默认的POST方法
if not isStandardMethod then
SL:Print("[DataForwarder] 未识别的HTTP方法: " .. method .. ", 使用默认的POST方法")
method = "POST"
else
SL:Print("[DataForwarder] 使用标准HTTP方法: " .. method)
end
else
-- 默认使用POST方法
method = "POST"
SL:Print("[DataForwarder] 使用默认HTTP方法: " .. method)
end
-- 转发数据到后端API
self:forwardDataToBackend(msgData, msgID, method)
end
--[[
转发数据到后端API
data: 要转发的数据
msgID: 消息ID
method: HTTP请求方法,可选值: "GET", "POST", "PUT", "DELETE"等,默认"POST"
]]
function DataForwarder:forwardDataToBackend(data, msgID, method)
-- 验证数据有效性
if not data or type(data) ~= "table" then
SL:Print("[DataForwarder] 无效的数据类型: " .. type(data))
return
end
-- 设置默认请求方法
local method = method or data.method or "POST"
method = string.upper(method)
-- 从服务端消息中获取token(支持多种可能的token字段名)
local token = data.token or data.Token or (data.data and data.data.token) or (data.data and data.data.Token)
local headers = {
["Content-Type"] = "application/json",
}
-- 对于非POST和GET方法,添加HTTP方法覆盖头部
if method ~= "GET" and method ~= "POST" then
headers["X-HTTP-Method-Override"] = method
end
local url = g_config.backend_endpoint
local postDataString = nil
-- 动态获取资源名称(如 bank、user 等)
if data.resource then
SL:Print("[DataForwarder] 使用资源名称: " .. data.resource)
url = url .. data.resource .. "/"
end
local paramValue = nil
if method == "GET" or method == "PUT" or method == "DELETE" or method == "PATCH" then
-- 只通过 pk 字段自动识别路径参数,提高模块通用性
if data.pk then
paramValue = data.pk
SL:Print("[DataForwarder] 使用 pk 字段作为路径参数: " .. paramValue)
-- 直接使用参数值作为路径,符合 Django DRF ModelViewSet 格式
local pathPattern = paramValue .. "/"
url = url .. pathPattern
SL:Print("[DataForwarder] 最终使用的URL: " .. url)
end
end
-- 处理token传递
if token then
SL:Print("[DataForwarder] 使用Token进行身份验证: " .. token)
if method == "GET" then
-- GET请求无法设置请求头,将token作为查询参数
local tokenParam = "token=" .. token
if string.find(url, "?") then
url = url .. "&" .. tokenParam
else
url = url .. "?" .. tokenParam
end
-- SL:Print("[DataForwarder] Token已添加到GET请求查询参数中")
else
-- 其他请求方法,设置Authorization头
headers["Authorization"] = "Bearer " .. token
-- SL:Print("[DataForwarder] Token已添加到请求头中")
end
else
SL:Print("[DataForwarder] 消息中未包含Token,将不包含身份验证信息")
end
if method == "GET" then
-- GET请求将参数添加到URL查询参数中
-- 注意:当有 pk 字段时,已经是获取单个资源详情,不需要查询参数
if not data.pk then
local params = {}
-- URL编码函数
local function urlEncode(str)
if type(str) ~= "string" then
return tostring(str)
end
-- 对特殊字符进行URL编码
-- 匹配任何不是字母数字、空格、下划线、连字符、点号或波浪号的字符 。
-- 在URL编码中,这些被排除的字符( %w _-.~ )
-- 是URL中可以安全使用而不需要编码的字符(根据RFC 3986标准),其他所有字符都需要进行URL编码。
local result = string.gsub(str, "([^%w _%-%.~])", function(c)
return string.format("%%%02X", string.byte(c))
end)
-- 将空格替换为+号
result = string.gsub(result, " ", "+")
return result
end
-- 处理普通查询参数
for k, v in pairs(data) do
-- 跳过已在URL路径中的 resource 参数,以及 method 字段
if k ~= "resource" and k ~= "method" and k ~= "filter" and k ~= "ordering" and k ~= "page" and k ~= "pagesize" and k ~= "data" and k ~= "format" and k ~= "msgid" then
table.insert(params, tostring(k) .. "=" .. urlEncode(tostring(v)))
end
end
-- 处理过滤参数(支持复杂查询)
if data.filter and type(data.filter) == "table" then
-- SL:Print("[DataForwarder] 解析到过滤参数: ")
-- SL:dump(data.filter)
for field, value in pairs(data.filter) do
table.insert(params, "filter[" .. tostring(field) .. "]=" .. tostring(value))
end
end
-- 处理排序参数
if data.ordering then
-- SL:Print("[DataForwarder] 使用排序参数: " .. data.ordering)
table.insert(params, "ordering=" .. data.ordering)
end
-- 处理分页参数
if data.page then
-- SL:Print("[DataForwarder] 使用分页参数: 第 " .. data.page .. " 页")
table.insert(params, "page=" .. data.page)
end
if data.pagesize then
-- SL:Print("[DataForwarder] 使用页面大小参数: " .. data.pagesize)
table.insert(params, "pagesize=" .. data.pagesize)
end
-- 拼接查询参数到URL
if #params > 0 then
if string.find(url, "?") then
url = url .. "&" .. table.concat(params, "&")
else
url = url .. "?" .. table.concat(params, "&")
end
end
end
else
-- POST、PUT、DELETE等请求将数据放在请求体中
-- 对于非GET请求,我们可以保留pk和resource字段在请求体中,以便后端处理
-- 还可以选择是否保留 method 字段在请求体中
postDataString = SL:JsonEncode(data)
SL:Print("[DataForwarder] 转发数据到后端API: ", postDataString)
end
-- 定义回调函数
local function httpsCB(success, response)
-- 解析响应数据
local responseData
local returnData
if success then
-- SL:Print("[DataForwarder] 数据转发成功, 响应: ", response)
responseData = response and SL:JsonDecode(response) or nil
if responseData and responseData.success == true then
SL:Print("[DataForwarder] 后端API操作成功: " .. (responseData.msg or "操作成功"))
-- 如果有数据返回,打印数据信息
if responseData.data then
SL:Print("[DataForwarder] 返回数据: ")
SL:dump(responseData.data)
end
else
SL:Print("[DataForwarder] 后端API操作失败: " .. (responseData and responseData.msg or "未知错误"))
end
else
SL:Print("[DataForwarder] 数据转发失败, 错误: ", response)
-- 构造错误响应数据
responseData = {
success = false,
msg = response or "网络请求失败"
}
end
-- 确保返回数据存在且为table格式
if not responseData then
responseData = {
success = false,
msg = "响应解析失败"
}
end
-- 将响应数据转换为JSON字符串
returnData = SL:JsonEncode(responseData)
-- 根据消息类型返回数据给服务端(无论成功或失败)
local msgConfig = g_config.registered_msg_configs[msgID]
if msgConfig then
if msgConfig.type == "txt" then
SL:SendNetMsg(msgID, 1, 2, 3, returnData)
SL:Print("[DataForwarder] 使用TXT格式返回数据到服务端,消息ID: " .. msgID)
else
SL:SendLuaNetMsg(msgID, 1, 2, 3, returnData)
SL:Print("[DataForwarder] 使用LUA格式返回数据到服务端,消息ID: " .. msgID)
end
else
SL:Print("[DataForwarder] 未找到消息ID " .. msgID .. " 的配置,无法返回数据")
end
end
SL:Print("[DataForwarder] 发送" .. method .. "请求到后端API, URL: " .. url)
-- SL:ScheduleOnce(function ()
-- 根据请求方法选择相应的HTTP请求函数
if method == "GET" then
SL:HTTPRequestGet(url, httpsCB)
elseif method == "POST" then
SL:HTTPRequestPost(url, httpsCB, postDataString, headers)
elseif method == "PUT" then
SL:HTTPRequestPost(url, httpsCB, postDataString, headers)
elseif method == "DELETE" then
SL:HTTPRequestPost(url, httpsCB, postDataString, headers)
else
-- 默认使用POST方法
SL:HTTPRequestPost(url, httpsCB, postDataString, headers)
end
-- end, 0.0001)
end
return DataForwarder