前言
根据多年做网站和最近客户需求,要求我们有个CMS网站内容过来后台可以管理多个网站,由于客户有多个公司,开发多个网站,可是按照传统CMS只能是一个后台管理一个网站,而且还需要独立部署;因此我们开发和维护也麻烦,用户后期管理网站也麻烦。还有我们之前用户php开发的后台管理,政府性网站经常遭到同行攻击,主要是和php不可编译有关。所以我们综合以上种种,思考再三,最后决定结合Go和PHP各自优点开发一套CMS内容管理后台,支持多企业、多个站点、在线编辑网站,无需每次建站都部署,一次部署即可一直新增网站。目前CMS已经用于实际企业网站开发。
下面我将分享一下项目开发思路和主要核心部分代码,希望您或您的公司可以受到启发,应用到项目生产中,减少开发成本,让您及公司在开发定制网站时候可以做个和模板网站相近价格,让您公司可以在网站项目中获得更多收益,也让企业单位可以用最少得钱获得性价比更高的服务。
重点来了,GoFly团队不仅是分享一个思路或宣传自己产品,而是为大家提供整套项目免费的私有部署代码。您可以安装说明文档下载代码安装在自己的服务器上使用,不担心我们限制您。本文将提供示例demo在线体验,符不符合您需求提了看看您就知道!还提供代码下载路径,和开发文档,实在是保姆级知道,让你真正的可以用上我们分享的技术,而不是过来看我们打广告!
设计思想及技术点
后台管理基于GoFly单服务版开发,需要了解请移步GoFly开始开发这篇文章看,CMS管理系统也是GoFly系列产品,也是方便有些企业可能有其他业务需求可以二次开发。网站内容展示部分由PHP来实现,根据我们需同时管理多个网站,所以php选择的框架需要能绑定多个网站的域名,我们找了我们熟悉的thinkphp,比较多个版本后我们选择了thinkphp5.1,满足了我们需求。不过thinkphp5.1没有做好多个域名绑定和网站标签这些功能,这些都需要我们自己动手开发。下面就是我们自己写的路由和网站标签。需要更多GoFly框架技术信息请您到开发者社区。
1.体验和代码下载、安装文档
(1)、总管理后台系统(添加管理多个企业账号):在线体验地址
(2)、CMS网站内容管理系统(添加网站,制作网站编辑代码,管理网站内容,文章发布,网站留言):在线体验地址
(3)、下载代码:免费代码下载地址
(4)、网站开发文档:GoFlyCMS网站开发在线文档
2.thinkphp5.1多路由配置
(1)、能实现多网站管理核心技术是采用thinkphp5.1版本 域名路由支持完整域名、子域名和IP部署的路由和绑定功能,路由绑定域名代码如下:
//前台路由部分
$sitelist= Db::name('website_site')
->field('id,domain')
->where("status",0)
->where("step",1)
->select();
foreach ($sitelist as$site){
if($site["domain"]){
Route::domain($site["domain"], 'home')->append(['site_id'=>$site["id"]]);
}
}
(2)、网站页面路由配置
//前台路由部分
$cate = Db::name('website_article_cate')
->alias('a')
->leftJoin('website_module m','a.module_id = m.id')
->field('a.id,a.catname,a.catdir,m.name as modulename,m.model_name as moduleurl')
->order('a.weigh ASC,a.id ASC')
->select();
$home_rote=[];
foreach ($cate as $k=>$v){
//只有设置了栏目目录的栏目才配置路由
if($v['catdir']){
if($v['moduleurl']=='page'){
//单页模型
//1.1公司官网
$home_rote[''.$v['catdir'].'-:catId'] = 'home/'.$v['catdir'].'/index';
//Mobile
$home_rote['mobile/'.$v['catdir'].'-:catId'] = 'mobile/'.$v['catdir'].'/index';
}else{
//列表+详情模型
//1.1公司官网
$home_rote[''.$v['catdir'].'-:catId/:id'] = 'home/'.$v['catdir'].'/info';
$home_rote[''.$v['catdir'].'-:catId'] = 'home/'.$v['catdir'].'/index';
//Mobile
$home_rote['mobile/'.$v['catdir'].'-:catId/:id'] = 'mobile/'.$v['catdir'].'/info';
$home_rote['mobile/'.$v['catdir'].'-:catId'] = 'mobile/'.$v['catdir'].'/index';
}
}
}
3.thinkphp5.1自己定义的网站模板标签
<?php
/**
* +----------------------------------------------------------------------
* | 自定义标签
* +----------------------------------------------------------------------
*
* AUTHOR: gofly
* EMAIL: 504500934@qq.com
* QQ: 504500934
* WECHAT: taijidao101
* |DATETIME: 2023/06/15
*
* ██████╗ ██████╗ ███████╗██╗ ██╗ ██╗
* ██╔════╝ ██╔═══██╗██╔════╝██║ ╚██╗ ██╔╝
* ██║ ███╗██║ ██║█████╗ ██║ ╚████╔╝
* ██║ ██║██║ ██║██╔══╝ ██║ ╚██╔╝
* ╚██████╔╝╚██████╔╝██║ ███████╗██║
* ╚═════╝ ╚═════╝ ╚═╝ ╚══════╝╚═╝
*
* +----------------------------------------------------------------------
*/
namespace app\common\taglib;
use think\template\TagLib;
use think\facade\Request;
class Tp extends TagLib {
protected $tags = array(
// 标签定义: attr 属性列表 close 是否闭合(0 或者1 默认1) alias 标签别名 level 嵌套层次
'close' => ['attr' => 'time,format', 'close' => 0], //闭合标签,默认为不闭合
'open' => ['attr' => 'name,type', 'close' => 1],
'nav' => ['attr' => 'id,limit', 'close' => 1], //通用导航信息
'cate' => ['attr' => 'id,type','close' => 0], //通用栏目信息
'catelist' => ['attr' => 'pid,name,limit','close' => 1], //通用栏目列表
'hascate' => ['attr' => 'id,name,field','close' => 1], //判断是否存在分类
'position' => ['attr' => 'name','close' => 1], //通用位置信息
'link' => ['attr' => 'name','close' => 1], //获取友情链接
'ad' => ['attr' => 'name,type','close' => 1], //获取广告信息
'debris' => ['attr' => 'name,type','close' => 0], //获取碎片信息
'count' => ['attr' => 'id','close' => 0], // 分类下文章数
'list' => ['attr' => 'id,name,pagesize,where,limit,order','close' => 1], //通用列表
'search' => ['attr' => 'search,site_id,table,name,pagesize,where,order','close' => 1], //通用搜索
'prev' => ['attr' => 'len','close' => 0], //上一篇
'prevplus' => ['attr' => 'name','close' => 1], //上一篇闭环
'next' => ['attr' => 'len','close' => 0], //下一篇
'nextplus' => ['attr' => 'name','close' => 1], //下一篇闭环
);
//这是一个闭合标签的简单演示
public function tagClose($tag)
{
$format = empty($tag['format']) ? 'Y-m-d H:i:s' : $tag['format'];
$time = empty($tag['time']) ? time() : $tag['time'];
$parse = '<?php ';
$parse .= 'echo date("' . $format . '",' . $time . ');';
$parse .= ' ?>';
return $parse;
}
//获取分类下文章个数
public function tagCount($tag)
{
$id = $tag['id']?$tag['id']:'';
$str = '<?php ';
$str .= 'echo Db::name("website_article_content")->where(\'status\',0)->where("cid",\''.$id.'\')->count();';
$str .= '?>';
return $str;
}
//这是一个非闭合标签的简单演示
public function tagOpen($tag, $content)
{
$type = empty($tag['type']) ? 0 : 1; // 这个type目的是为了区分类型,一般来源是数据库
$name = $tag['name']; // name是必填项,这里不做判断了
$parse = '<?php ';
$parse .= '$test_arr=[[1,3,5,7,9],[2,4,6,8,10]];'; // 这里是模拟数据
$parse .= '$__LIST__ = $test_arr[' . $type . '];';
$parse .= ' ?>';
$parse .= '{volist name="__LIST__" id="' . $name . '"}';
$parse .= $content;
$parse .= '{/volist}';
return $parse;
}
//通用导航信息
Public function tagNav($tag,$content){
$tag['limit'] = isset($tag['limit']) ? $tag['limit'] : '0';
$tag['id'] = isset($tag['id']) ? $tag['id'] : '';
$name = isset($tag['name']) ? $tag['name'] : 'nav';
$site_id =isset($tag['site_id']) ? $tag['site_id'] : '0';
if(!empty($tag['id'])){
$catestr = '$__CATE__ = Db::name(\'website_article_cate\')->where(\'is_menu\',1)->order(\'weigh ASC,id DESC\')->select();';
$catestr.= '$__LIST__ = getChildsOn($__CATE__,'.$tag['id'].');';
}else{
$catestr = '$__CATE__ = Db::name(\'website_article_cate\')->where(\'is_menu\',1)->where(\'site_id\','.$site_id.')->order(\'weigh ASC,id DESC\')->select();';
$catestr.= '$__LIST__ = unlimitedForLayer($__CATE__);';
}
//提取前N条数据,因为sql的LIMIT避免不了子栏目的问题
if(!empty($tag['limit'])){
$catestr.= '$__LIST__ = array_slice($__LIST__, 0,'.$tag['limit'].');';
}
$parse = '<?php ';
$parse .= $catestr;
$parse .= ' ?>';
$parse .= '{volist name="__LIST__" id="' . $name . '"}';
$parse .= $content;
$parse .= '{/volist}';
return $parse;
}
//通用栏目信息
Public function tagCate($tag){
$id = isset($tag['id'])?$tag['id']:"input('catId')";
$type = $tag['type']?$tag['type']:'catname';
$str = '<?php ';
$str .= '$__CATE__=Db::name("website_article_cate")->where("id",'.$id.')->find();';
$str .= 'if(is_array($__CATE__)){ ';
$str .= '$__CATE__[\'url\']=getUrl($__CATE__);';
$str .= 'echo $__CATE__[\''.$type.'\'];';
$str .= '}';
$str .= '?>';
return $str;
}
//通用位置信息
Public function tagPosition($tag,$content){
$name = $tag['name']?$tag['name']:'position';
$parse = '<?php ';
$parse .= '$__CATE__ = Db::name(\'website_article_cate\')->select();';
$parse .= '$__LIST__ = getParents($__CATE__,input(\'catId\'));';
$parse .= ' ?>';
$parse .= '{volist name="__LIST__" id="' . $name . '"}';
$parse .= '<?php $' . $name . '[\'url\']=getUrl( $' . $name . '); ?>';
$parse .= $content;
$parse .= '{/volist}';
return $parse;
}
//获取友情链接
Public function tagLink($tag,$content){
$name = $tag['name']?$tag['name']:'link';
$site_id =isset($tag['site_id']) ? $tag['site_id'] : '0';
$parse = '<?php ';
$parse .= '$__LIST__ = Db::name(\'website_link\')->where(\'status\',1)->where(\'site_id\','.$site_id.')->order(\'weigh ASC,id desc\')->select();';
$parse .= ' ?>';
$parse .= '{volist name="__LIST__" id="' . $name . '"}';
$parse .= $content;
$parse .= '{/volist}';
return $parse;
}
//获取广告信息
Public function tagAd($tag,$content){
$name = isset($tag['name']) ? $tag['name'] : 'ad';
$flag = isset($tag['flag']) ? $tag['flag'] : '';
$cid = isset($tag['cid']) ? $tag['cid'] : '';
$parse = '<?php ';
$parse .= '
$__WHERE__ = array();
if (!empty(\'' . $cid . '\')) {
$__WHERE__[] = [\'cid\', \'=\', ' . $cid . '];
}
if (!empty(\'' . $flag . '\')) {
$__WHERE__[] = [\'flag\', \'like\', \'%' . $flag . '%\'];
}';
$parse .= '$__LIST__ = Db::name(\'website_article_content\')
->field(\'id,title,swipimg,image,des as description,link as url\')
->where(\'status\',0)
->where(\'site_id\',input(\'site_id\'))
->where($__WHERE__)
->order(\'weigh ASC,id desc\')
->select();
$__LIST__=changImg($__LIST__);
';
$parse .= ' ?>';
$parse .= '{volist name="__LIST__" id="' . $name . '"}';
$parse .= $content;
$parse .= '{/volist}';
return $parse;
}
//通用碎片信息
Public function tagDebris($tag){
$id = $tag['id']?$tag['id']:'';
$type = $tag['type']?$tag['type']:'';
$str = '<?php ';
$str .= 'echo Db::name("website_article_content")->where("id",\''.$id.'\')->value("'.$type.'");';
$str .= '?>';
return $str;
}
//通用列表
Public function tagList($tag,$content){
$id = isset($tag['id']) ? $tag['id'] : "input('catId')"; //可以为空
$name = isset($tag['name']) ? $tag['name'] : "list"; //不可为空
$order = isset($tag['order']) ? $tag['order'] : 'weigh ASC,id DESC'; //排序
$limit = isset($tag['limit']) ? $tag['limit'] : '0'; //多少条数据,传递时不再进行分页
$where = isset($tag['where']) ? $tag['where'].' AND status = 0 ' : 'status = 0'; //查询条件
$pagesize = isset($tag['pagesize']) ? $tag['pagesize'] : config('page_size');
//paginate(1,false,['query' => request()->param()]); //用于传递所有参数,目前只需要page参数
$parse = '<?php ';
$parse .='
//查找栏目对应的表信息
$__TABLE_=Db::name(\'website_article_cate\')
->alias(\'a\')
->leftJoin(\'website_module m\',\'a.module_id = m.id\')
->field(\'a.id,a.module_id,a.pagesize,a.catname,a.site_id,m.model_name as modulename\')
->where(\'a.id\',\'=\','.$id.')
->find();
//获取表名称
$__TABLENAME_ = $__TABLE_[\'modulename\'];
//获取模型ID
$__MODULEID__ = $__TABLE_[\'module_id\'];
$__SITEID__ = $__TABLE_[\'site_id\'];
//查询子分类,列表要包含子分类内容
$__ALLCATE__ = Db::name(\'website_article_cate\')->field(\'id,pid\')->select();
$__IDS__ = getChildsIdStr(getChildsId($__ALLCATE__,'.$id.'),'.$id.');
//表名称为空时(id不存在)直接返回
if(!empty($__TABLENAME_)){
//当传递limit时,不再进行分页
if('.$limit.'!=0){
$__LIST__ = Db::name($__TABLENAME_)
->order(\''.$order.'\')
->limit(\''.$limit.'\')
->where(" '.$where.'")
->where(\'cid\',\'in\',$__IDS__)
->select();
$page = \'\';
}else{
$__TABLE_[\'pagesize\'] = empty($__TABLE_[\'pagesize\'])?'.$pagesize.':$__TABLE_[\'pagesize\'];
$__LIST__ = Db::name($__TABLENAME_)
->order(\''.$order.'\')
->where(" '.$where.'")
->where(\'cid\',\'in\',$__IDS__)
->paginate($__TABLE_[\'pagesize\']);
$page = $__LIST__->render();
}
//处理数据(把列表中需要处理的字段转换成数组和对应的值)
$__LIST__ = changeFields($__LIST__,$__SITEID__);
}else{
$__LIST__ = [];
}
';
$parse .= ' ?>';
$parse .= '{volist name="__LIST__" id="'.$name.'"}';
$parse .= $content;
$parse .= '{/volist}';
return $parse;
}
//通用搜索 search,table,name,pagesize,where,order
Public function tagSearch($tag,$content){
$search = isset($tag['search']) ? $tag['search'] : ""; //关键字
$site_id = isset($tag['site_id']) ? $tag['site_id'] : ""; //关键字
$table = isset($tag['table']) ? $tag['table'] : "website_article_content"; //表名称
$name = isset($tag['name']) ? $tag['name'] : "list"; //不可为空
$order = isset($tag['order']) ? $tag['order'] : 'weigh ASC,id DESC'; //排序
$where = isset($tag['where']) ? $tag['where'].' AND status = 0 ' : 'status = 0'; //查询条件
$pagesize = isset($tag['pagesize']) ? $tag['pagesize'] : config('page_size');
$parse = '<?php ';
$parse .='
$__LIST__ = Db::name("'.$table.'")
->order("'.$order.'")
->where(\'site_id\',input(\'site_id\'))
->where("'.$where.'")
->where("title", "like", "%'.$search.'%")
->paginate("'.$pagesize.'",false,[\'query\' => request()->param()]);
$page = $__LIST__->render();
//处理数据(把列表中需要处理的字段转换成数组和对应的值)
$__LIST__ = changeFieldsSearch($__LIST__,'.$site_id.');
';
$parse .= ' ?>';
$parse .= '{volist name="__LIST__" id="'.$name.'"}';
$parse .= $content;
$parse .= '{/volist}';
return $parse;
}
//详情上一篇
Public function tagPrev($tag){
$len = $tag['len']?$tag['len']:'';
$str = '<?php ';
$str .= '
//查找表名称
$__TABLENAME__ = Db::name(\'website_article_cate\')
->alias(\'c\')
->leftJoin(\'website_module m\',\'c.module_id = m.id\')
->field(\'m.model_name as table_name\')
->where(\'c.id\',input(\'catId\'))
->find();
//根据ID查找上一篇的信息
$__PREV__ = Db::name($__TABLENAME__[\'table_name\'])
->where(\'cid\',input(\'catId\'))
->where(\'id\',\'<\',input(\'id\'))
->field(\'id,cid,title\')
->order(\'weigh ASC,id DESC\')
->find();
if($__PREV__){
//处理上一篇中的URL
$__PREV__[\'url\'] = getShowUrl($__PREV__);
$__PREV__ = "<a class=\"prev\" title=\" ".$__PREV__[\'title\']." \" href=\" ".$__PREV__[\'url\']." \">".$__PREV__[\'title\']."</a>";
}else{
$__PREV__ = "<a class=\"prev_no\" href=\"javascript:;\">暂无数据</a>";
}
echo $__PREV__;
';
$str .= '?>';
return $str;
}
//详情下一篇
Public function tagNext($tag){
$len = $tag['len']?$tag['len']:'';
$str = '<?php ';
$str .= '
//查找表名称
$__TABLENAME__ = Db::name(\'website_article_cate\')
->alias(\'c\')
->leftJoin(\'website_module m\',\'c.module_id = m.id\')
->field(\'m.model_name as table_name\')
->where(\'c.id\',input(\'catId\'))
->find();
//根据ID查找下一篇的信息
$__PREV__ = Db::name($__TABLENAME__[\'table_name\'])
->where(\'cid\',input(\'catId\'))
->where(\'id\',\'>\',input(\'id\'))
->field(\'id,cid,title\')
->order(\'weigh ASC,id DESC\')
->find();
if($__PREV__){
//处理下一篇中的URL
$__PREV__[\'url\'] = getShowUrl($__PREV__);
$__PREV__ = "<a class=\"next\" title=\" ".$__PREV__[\'title\']." \" href=\" ".$__PREV__[\'url\']." \">".$__PREV__[\'title\']."</a>";
}else{
$__PREV__ = "<a class=\"next_no\" href=\"javascript:;\">暂无数据</a>";
}
echo $__PREV__;
';
$str .= '?>';
return $str;
}
/*****翻篇plus**/
//详情上一篇
public function tagPrevplus($tag,$content){
$name = $tag['name']?$tag['name']:'item';
$parse = '<?php ';
$parse .= '
//查找表名称
$__TABLENAME__ = Db::name(\'website_article_cate\')
->alias(\'c\')
->leftJoin(\'website_module m\',\'c.module_id = m.id\')
->field(\'m.model_name as table_name\')
->where(\'c.id\',input(\'catId\'))
->find();
//根据ID查找上一篇的信息
$__LIST__ =Db::name($__TABLENAME__[\'table_name\'])
->where(\'cid\',input(\'catId\'))
->where(\'id\',\'<\',input(\'id\'))
->limit(1)
->field(\'id,cid,title,image,createtime\')
->order(\'weigh ASC,id DESC\')->select();';
$parse .= ' ?>';
$parse .= '{volist name="__LIST__" id="' . $name . '"}';
$parse .= '<?php $' . $name . '[\'url\']=getShowUrl( $' . $name . '); ?>';
$parse .= $content;
$parse .= '{/volist}';
return $parse;
}
//详情下一篇-闭环
public function tagNextplus($tag,$content){
$name = $tag['name']?$tag['name']:'item';
$parse = '<?php ';
$parse .= '
//查找表名称
//查找表名称
$__TABLENAME__ = Db::name(\'website_article_cate\')
->alias(\'c\')
->leftJoin(\'website_module m\',\'c.module_id = m.id\')
->field(\'m.model_name as table_name\')
->where(\'c.id\',input(\'catId\'))
->find();
//根据ID查找下一篇的信息
$__LIST__ = Db::name($__TABLENAME__[\'table_name\'])
->where(\'cid\',input(\'catId\'))
->where(\'id\',\'>\',input(\'id\'))
->field(\'id,cid,title,image,createtime\')
->limit(1)
->order(\'weigh ASC,id DESC\')->select();';
$parse .= ' ?>';
$parse .= '{volist name="__LIST__" id="' . $name . '"}';
$parse .= '<?php $' . $name . '[\'url\']=getShowUrl( $' . $name . '); ?>';
$parse .= $content;
$parse .= '{/volist}';
return $parse;
}
//获取分类列表
Public function tagCatelist($tag,$content){
$pid = isset($tag['pid']) ? $tag['pid'] : '';
$name = isset($tag['name']) ? $tag['name'] : 'list';
$limit = isset($tag['limit']) ? $tag['limit'] : '10'; //多少条数据
$parse = '<?php ';
$parse .= '
$__LIST__ = Db::name(\'website_article_cate\')
->where("pid",'.$pid.')
->limit(\''.$limit.'\')
->field(\'id,catname,url,pagesize,is_next,is_blank,catdir,module_id,icoimage\')
->order(\'weigh ASC,id DESC\')->select();';
$parse .= ' ?>';
$parse .= '{volist name="__LIST__" id="' . $name . '"}';
$parse .= '<?php $' . $name . '[\'url\']=getUrl( $' . $name . '); ?>';
$parse .= $content;
$parse .= '{/volist}';
return $parse;
}
//是否存在分类
Public function tagHascate($tag,$content){
$id = isset($tag['id']) ? $tag['id'] : '';
$field = isset($tag['field']) ? $tag['field'] : '';
if($field){
$field="id,catname,url,pagesize,is_next,is_blank,catdir,module_id,".$field;
}else{
$field="id,catname,url,pagesize,is_next,is_blank,catdir,module_id";
}
$name = isset($tag['name']) ? $tag['name'] : 'list';
$parse = '<?php ';
$parse .= '
$__LIST__ = Db::name(\'website_article_cate\')
->where("id",'.$id.')
->where("status",0)
->field("'.$field.'")
->order(\'weigh ASC,id DESC\')->select();';
$parse .= ' ?>';
$parse .= '{volist name="__LIST__" id="' . $name . '"}';
$parse .= '<?php $' . $name . '[\'url\']=getUrl( $' . $name . '); ?>';
$parse .= $content;
$parse .= '{/volist}';
return $parse;
}
}
图片效果预览
1.统计页面
2.管理多个网站页面
3.编辑网站,在线制作网站编辑器
4.管理对应网站内容,管理、发布文章
总结
到此,就分享完成了,有其他需要问的或者需要帮助的朋友,请到GoFly全栈开发者社区给我们留言,谢谢您拜读!希望可以一同开发,共同实现最好最快GoCMS开发的应用。