用Lor框架搭建一个健身房首页发布网站。
最近太忙了,忙着做iOS项目,还有家里又有孕妇要照顾,本来早就答应糖果写这篇分享的,今天抽空来跟大家分享下做这个项目当中遇到坑,怎么解决的,很一些还有疑问的地方,请各位or同仁多多指教,长话短说就开始这次实践的分

我会以下几点来做比较完善分享

  1. 项目整体需求以及框架选择
  2. 开发过程中遇到的坑,以及怎么填坑
  3. 实践中还有觉得不足的有待完善的点

项目地址在:https://github.com/SandWind/trccms


  • 项目整体需求

这个项目做为给朋友做一个健身馆宣传首页,它有一个首页介绍和四个专题介绍,首页包含一个广告banner和四个健身项目专题链接,每个专题页面四个内容的展示,分别是活动照片,场馆图文介绍,教练图文介绍,每周课程简介。

这里项目就开始有2个部分一个部分是供非管理用户参观的展示页面,与管理员的后台页面。

首先,设计并构建好前端静态模板页面 第二,根据页面展示内容构建数据库模型。这里可以参考项目工程文件中model文件中model.sql文件。 第三,安装openresty后,我开始选择框架这个时候,之前我选择vanilla,这个框架没有处理好session,但是整体上看,它是一个完整的mvc的框架,然后参考了https://github.com/bungle/awesome-resty链接中的东西最后选择了lor,相比之前它小巧灵活,安装简捷。简直就是so easy,这是还是要感谢饭总等各位辛劳。 第四,框架搭建完了,这个时候首先要熟悉这个框架数据流程,这里不得不推荐看看 http://doc.lua.ren/ms2008_lor_framework_route.html 要熟悉整个框架的数据路由。按照数据路由,我开始写出如下路由:


router.lua 文件中

local userRouter = require("app.routes.user")
local HomePageRouter= require("app.routes.homepage")
local judoPageRouter= require("app.routes.judo")
local yogoPageRouter= require("app.routes.yoga")
local freepowerPageRouter =  require("app.routes.freepower")
local AerabicPageRouter= require("app.routes.aerabic")
local AdminPageRouter = require("app.routes.admin")
local AuthRouter =  require("app.routes.auth")
local uploadRouter = require("app.routes.upload")
local AdtopicRouter = require("app.routes.adtopic")
local CoverRouter= require("app.routes.cover")
local GymanasiumPicsRouter= require("app.routes.gymanasium_pics")
local GymnasiumDescriRouter = require("app.routes.gymnasium_descri")
local TrainnerPicRouter =  require("app.routes.trainner_pics")
local TrainnerDescriRouter = require("app.routes.trainner_descri")
local CourseRouter = require("app.routes.course")
return function(app)
    app:use("/",HomePageRouter()) --首页
    app:use("/judo",judoPageRouter()) 
    app:use("/yogo",yogoPageRouter())
    app:use("/freepower",freepowerPageRouter())
    app:use("/aerabic",AerabicPageRouter())
    app:use("/admin",AdminPageRouter())
    app:use("/auth",AuthRouter())
    app:use("/upload",uploadRouter())
    app:use("/adtopic",AdtopicRouter())
    app:use("/cover",CoverRouter())
    app:use("/gymnasiumPic",GymanasiumPicsRouter())
    app:use("/gymnasiumDescri",GymnasiumDescriRouter())
    app:use("/trainnerPic",TrainnerPicRouter())
    app:use("/trainnerDescri",TrainnerDescriRouter())
    app:use("/course",CourseRouter())

然后依次完善每个路由后面页面上业务处理。值得注意是可以先把每个页面功能完善了,最后再考虑插件的功能。比如session管理等。

第五,由于页面太多,我这里只能具体举例一个页面上业务逻辑处理,以及功能的完善的过程。我就来广告页面的功能实现为例。我首先完善后台页面

http://orchina.org/static/files/c5162ff9-cfb7-4816-878e-85a70f9c2354.png

local AdminPageRouter = require("app.routes.admin")
app:use("/admin",AdminPageRouter())

在/app/routes中admin.lua文件中加上路由如下


local category = {
					first="广告",
					second = "柔道",
					third  = "瑜伽",
					fouth  = "自由力量",
					fiveth = "有氧运动"
}

AdimPageRouter:get("/ad",function(req, res, next)
		
		res:render("admin_ad",{ 
			title = category.first,
			})

	end)

这里admin_ad对应view中admin_ad.html的静态模板文件。 然后在/app/model/中构造数据模型对象ad_model.lua 代码如下

local DB = require("app.libs.db")
local db = DB:new()
local uuid = require("app.libs.uuid.uuid")
local utils  = require("app.libs.utils")


local  Ad_Model = {}

function Ad_Model:new(title,coverImage,content)
	local unique_number = uuid();
    local creat_time = utils.now()
	return  db:query("insert into ad_topic(title, coverimage, content,ad_uuid,create_time) values(?,?,?,?,?)",
            {title,coverImage,content,unique_number,creat_time})

end

function Ad_Model:query_by_uuid(uuid)
	local result, err =  db:query("select * from ad_topic where ad_uuid=?", {uuid})
    if not result or err or type(result) ~= "table" or #result ~=1 then
        return nil, err
    else
        return result[1], err
    end
end

function Ad_Model:delete_by_uuid(uuid)
    local res,err
    res,err = db:query("delete from ad_topic where ad_uuid=?", {uuid})

    if res and not err then
        return true
    else
        return false
    end
end

function Ad_Model:update_ad_topic_by_uuid(title,coverImage,content,uuid)
	return db:query("update ad_topic set title=?,coverimage=?,content=? where ad_uuid=?", {title,coverImage,content,uuid})
end


function Ad_Model:get_total_count()
    local res,err
    res, err =  db:query("select count(ad_id) as c from ad_topic")

    if err or not res or #res~=1 or not res[1].c then
        return 0
    else
        return res[1].c
    end
end

function Ad_Model:get_all()
    local res,err
    -- body
    res,err = db:query("select * from ad_topic")

    if not res or err or type(res) ~= "table" or #res <= 0 then
        return {}
    else
        return res
    end
end


function Ad_Model:get_all_uuid()
    local res,err
    -- body
    res,err = db:query("select ad_uuid from ad_topic")

    if not res or err or type(res) ~= "table" or #res <= 0 then
        return {}
    else
        return res
    end
end

function Ad_Model:get_all_sample()
    local res,err
    -- body
    res,err = db:query("select title,coverimage,ad_uuid from ad_topic")

    if not res or err or type(res) ~= "table" or #res <= 0 then
        return {}
    else
        return res
    end
end


return Ad_Model

这个时候 就需要在页面上对广告数据进行增删查改的操作,同样就要四个操作数据的接口,所以需要路由上要加上这个四个接口路由管理:

  app:use("/adtopic",AdtopicRouter())

同理我会在/app/routes/adtopic.lua中加上详细业务处理:

local lor = require("lor.index")
local AdtopicRouter = lor:Router()
local Admodel= require("app.model.ad_model")



AdtopicRouter:post("/new",function(req, res, next)
	    local title = req.body.title
		local coverimg   =  req.body.coverimg
		local content    =  req.body.content
  	    local reslut,err = Admodel:new(title,coverimg,content)

		if not reslut or err then
        	res:json({
                success = false,
                msg = "保存失败"
            })
    	else
            res:json({
                success = true,
                msg = "保存成功",
                data = {
                    id = res.ad_uuid
                }
            })
    	end
   	end)


AdtopicRouter:get("/count",function(req, res, next)
	local adcount = Admodel:get_total_count()
	res:json({
            	success = true,
            	ad_count = adcount
        	})	
    end)



AdtopicRouter:get("/:ad_uuid/detail", function(req, res, next)
	local uuid = req.params.ad_uuid
	local adtopic = Admodel:query_by_uuid(uuid)

    res:render("adtopic/adtopic",{adtopic = adtopic })


	end)


AdtopicRouter:post("/:ad_uuid/delete", function(req, res, next)
    local uuid = req.params.ad_uuid
    local isDel = Admodel:delete_by_uuid(uuid)

    if isDel then
       res:json({
                success = true
            })  
    else
      res:json({
                success = false
            })  
    end


    end)

然后会到静态页面上撸js代码,把页面数据与后台数据打通。 我的js水平很烂,这里我也不好意思给大家秀了,具体说来就是用ajax中异步数据请求。


  • 开发过程中遇到的坑

这里我遇到2个比较坑得地方

  1. 上传插件那里出现当有字段数据与文件一起上传时,上传的图片老是有问题。文件老是丢失数据。 经过写测试,打印数据分析后找2个原因有: 我采用webuploader这个js插件开启了
 uploader = WebUploader.create({
            pick: {
                id: '#filePicker',
                label: '点击选择图片'
            },
            formData: {
                uid: 123
            },
            dnd: '#dndArea',
            paste: '#uploader',
            swf: '../../dist/Uploader.swf',
            chunked: false,
            chunkSize: 512 * 1024,
            server: '../../server/fileupload.php',
            // runtimeOrder: 'flash',

            // accept: {
            //     title: 'Images',
            //     extensions: 'gif,jpg,jpeg,bmp,png',
            //     mimeTypes: 'image/*'
            // },

            // 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。
            disableGlobalDnd: true,
            fileNumLimit: 300,
            fileSizeLimit: 200 * 1024 * 1024,    // 200 M
            fileSingleSizeLimit: 50 * 1024 * 1024    // 50 M
        });

我到时开启chunked分块功能,导致数据丢失。

还有改进uploader.lua插件(这个得到 游手 童鞋给我的提示)

local function _multipart_formdata(config)

	local form, err = upload:new(config.chunk_size)
	if not form then
		ngx.log(ngx.ERR, "failed to new upload: ", err)
		ngx.exit(500)
	end
	form:set_timeout(config.recieve_timeout)
	

	local unique_name = uuid()
	local success, msg = false, ""
	local file,origin_filename, filename, path, extname, url,err
	local prefix,suffix
	local isFile =false
	local params = {}
	local paramKey, paramValue
    
	while true do
		local typ, res, err = form:read()

		if not typ then
			success = false
			msg = "failed to read"
			ngx.log(ngx.ERR, "failed to read: ", err)
			return success, msg
		end

		if err then
		   ngx.log(ngx.ERR, "read form err: ", err)
		end

		if typ == "header" then
			prefix,suffix = res[1],res[2]

			if prefix == "Content-Disposition" then
				paramKey = match(suffix, "name=\"(.-)\"")
				origin_filename = match(suffix, "filename=\"(.-)\"")
				if origin_filename then
					isFile = true
				else
					isFile = false
				end
			elseif prefix == "Content-Type" then
				filetype = suffix
			end

			

			if isFile and origin_filename and filetype then
			   if not extname then
					extname = getextension(origin_filename)
					extname = extname:lower()
				end

				if extname ~= "png" and extname ~= "jpg" and extname ~= "jpeg"  and extname ~= "bmp"  and extname ~= "gif" then
					success = false
					msg = "not allowed upload file type"
					ngx.log(ngx.ERR, "not allowed upload file type:", origin_filename)
					return success, msg
				end  

				filename = unique_name .. "." .. extname
				path = config.dir.. "/" .. filename
			
				
				file, err = io.open(path, "w+")

				if err then
					success = false
					msg = "open file error"
					ngx.log(ngx.ERR, "open file error:", err)
					return success, msg
				end
			end

		elseif typ == "body" then

		  if isFile then 
		    if file then
			   file:write(res)
			   success = true
			else
			   success = false
			   msg = "upload file error"
			   ngx.log(ngx.ERR, "upload file error, path:", path)
			   return success, msg
			end
		  else
		  	success = true
		    paramValue = res
		  end
			
		elseif typ == "part_end" then
		
		  if isFile then
		     file:close()
		     url = '/static/images/'..filename
             file = nil
             filename = nil
             origin_filename = nil
             isFile =false
             filetype = nil
		  else
		  	params[paramKey] = paramValue
		  end 
     
		elseif typ == "eof" then
			break
		end
	end

	return success, msg, url,params
end

2.在数据模型建模时也遇到一个问题,当时候把页面数写一张表里,这样导致后面接口很难对内容做修改。所以对页面数据都创建要分而治之,以操作最小数据为单元来更新页面数据。

```