首先先丢出来几个需要依赖的开源项目,都是我经过千挑万选甄选出来的精品项目。哈哈

      1 mysql 驱动:https://github.com/hehr/lua-resty-mysql

      2 http 服务:https://github.com/hehr/lua-resty-http

      3 xml解析:https://github.com/hehr/xml

       项目中引用的上述大神的项目,在此深表感谢。

        分析(哔哔一下)

        这里先说一下微信公众号的特点,因为微信公众号的所有消息都是经过腾讯后台将不同的事件消息请求到后台设定的一个路径。所以,为了保障项目具有良好的可扩展性,后台整体选择消息分发模式来实现。腾讯推送的路径,设置一个dispatch,按照不同的事件类型,分发给不同的模块去处理。  查看腾讯的公众号接口文档,可以看到腾讯的消息接口类型还是用的XML格式,这点很反人类,我在项目中用到的是https://github.com/hehr/xml 这个xml解析工具,不能说很好用,但是最少终于找到一个相对来说比较稳定的xml解析和生成工具了,如果各位有更好的开源项目推荐,欢迎留言。我在后文会丢出来实际编写的一个消息工具类,依据上述项目,创建腾讯公众号的消息格式。数据库我这边使用的是mysql去存储用户关系,这个没什么好嘟嘟的,毕竟mysql早就深入人心了,不过还是要给贡献像lua-resty-mysql 这样优秀开源项目的大神致敬。项目中所有的联系操作都是依赖 lua-resty-http 这个开源项目,支持https很好用,个人觉得这样常规的需求都完全应该整合进openResty常规组件中去。

      实现

      nginx的配置文件

     

        location = /xxx/message{
              content_by_lua_file lua/response/dispatch.lua ; 

      }   //我在腾讯的公众号后台填写的消息推送路径也就是该路径了。

 

     dispatch:

    这里做成一个消息分发的模式,不同的消息不同的模块去处理,方便后期业务拓展。我这里只是默认给出来一个简单的留言消息处理,随便回复一段文本。

    local xml = require("xml")
    local leave_message = require("message_handle.leave_message") --留言消息

   if 'GET' == ngx.req.get_method() then --微信服务器验证
       local args = ngx.req.get_uri_args()
       ngx.say(args.echostr)
        return
   end

   ngx.req.read_body()
   local args = ngx.req.get_body_data()

   if not args then
      ngx.log(ngx.ERR, "bad request ,check message_dispach.lua")
      return
   end

   local tb_xml = xml.load(args)
   local open_id =  xml.find(tb_xml,'FromUserName')[1] -- open_id
   local count = xml.find(tb_xml,'ToUserName')[1]-- 公众号平台账号
   local message_type = xml.find(tb_xml,'MsgType')[1] --推送消息类型

   if message_type == 'text' then  --文本消息,用户留言
        
          leave_message.handle_leaving_message(open_id,count)    

   elseif message_type == 'event' then  --事件推送
        
        --todo 处理事件推送

   else  --未处理消息

      --todo 处理事件推送

   end

 

handle :

    这里负责处理不同事件的具体逻辑,如,一个简单的自动回复文本功能:

   local message_util =  require("utils.message")

   local _M= {}

   function _M.handle_leaving_message( open_id , count )
    
    local text = { user = open_id , count = count , info = '我们已经收到了您的留言了哦!' }
    local message_xml = message_util.text_message( text )
    ngx.say(message_xml)

   return _M

看到这里,大家一定很好奇,我的message_util.lua里面到底是个什么鬼。下面我把这个代码给pull出来,其实这个里面就是按照腾讯要求的文档格式,去创建不同的消息类型。

message.lua

--[[
   
 腾讯公众号微信消息

 1 普通文本消息
2 图文消息
3 文本链接消息    
]]--
 
 local xml = require ("xml")
 local _M = {}

--[[

 图文消息

image = {user = 用户openId, count = 公众号账号, title = 消息标题, description = 消息描述 , pic_url = 图片链接, url = 消息跳转链接 }

  ]]--
  20 function _M.image_message( image )
  
   --用来生成dump解析的table格式
   local function  dump_tb( tb )
   local tb_dump = {}
       for k , v in pairs( tb ) do
         local tb_tmp = {}
         tb_tmp.xml = k
         tb_tmp[1] = v
         table.insert(tb_dump,tb_tmp)
       end
     return tb_dump
  end


   local tb_dump = { ToUserName = image.user ,FromUserName = image.count ,CreateTime = ngx.time() , MsgType = 'news' , ArticleCount = 1 } --没有放入item的table
   local tb_item = { Title = image.title , Description = image.description , PicUrl = image.pic_url , Url = image.url } --item中内容

    tb_dump = dump_tb(tb_dump)
    tb_item = dump_tb(tb_item)

    tb_dump.xml = 'xml'
    tb_item.xml = 'item'
  
   local tb_articles = { xml = 'Articles'}

   table.insert(tb_articles , tb_item)
    table.insert(tb_dump,tb_articles)

   return xml.dump(tb_dump) , 'success'
  
  end--[[

   音频文件消息
   @param  tb = {user = open_id , count = count ,  title = '星晴' , description = '描述' , music_url = '音乐路径' , HQ_music_url = '高品质音乐路径' , thumb_media_id = '微信id上传的' }

  ]]--
 function _M.media_message( tb )

   local function  dump_tb( t )

    local tb_dump = {}
  
      for k , v in pairs( t ) do
       local tb_tmp = {}
        tb_tmp.xml = k
        tb_tmp[1] = v
        table.insert(tb_dump,tb_tmp)
 end

     return tb_dump
  end 
--[[

  文本消息
 text = {user = 用户openId , count = 公众账号 , info = 消息文本}
  ]]--
 function _M.text_message( text )

    local tb_message = {ToUserName = text.user, FromUserName =  text.count,CreateTime = ngx.time() , MsgType = 'text', Content = text.info}
    local tb_dumpXML = {xml  = 'xml'}--生成一个用来dump xml的表

     for k ,v in pairs(tb_message) do
        local tb_temp = {}
       tb_temp[1] = v
        tb_temp.xml = k
        table.insert(tb_dumpXML,tb_temp)
      end
  
    return xml.dump( tb_dumpXML ) , 'success'
  
  end

  --[[
  发送链接文本消息,如: text  + title(url)  ,如 我要去北京天安门(链接北京天安门首页) 
  tb= {text = 文本消息 , title = 超文本标题 , url = 链接地址,user = open_id ,count = count}
  ]]--
  function _M.url_text_message( tb )

     local tb_message = { ToUserName = tb.user, FromUserName = tb.count , CreateTime = ngx.time() , MsgType = 'text' , Content = tb.text .. '<a href= \"'  .. tb.url ..' \">' .. tb.title .. '</a>'  }
     local tb_dumpXML = {xml  = 'xml'}--生成一个用来dump xml的表

     for k ,v in pairs(tb_message) do
       local tb_temp = {}
       tb_temp[1] = v
       tb_temp.xml = k
       table.insert(tb_dumpXML,tb_temp)
    end
  
     return xml.dump( tb_dumpXML ) , 'success'
  
  end  return _M

  很不好意思又在网上晒了代码,代码很粗浅,大家凑合看。