简介:友点企业网站管理系统(YouDianCMS)v8.0 是一套基于 PHP 与 MySQL 的开源建站程序,定位给中小企业和建站开发者,用来快速搭建专业的企业官网。系统将电脑站、手机站、微信站、APP 与小程序五个终端合并为一个管理后台,录入一次数据即可自动同步到所有端,减少重复维护,也只需一个虚拟主机即可运行。资源包约 22.71MB,主体为 PHP 源码文件,适配 PHP5.3 到 7.3 环境;后台内置自定义模型、SEO 优化、静态页生成、评论留言、会员、广告、招聘、自助表单、数据采集、英文自动翻译、一键备份等功能,并开放微信接口、微网站、微活动等模块,方便二次开发和扩展。已有 1120 人学习浏览,适合用来研究五站合一架构、后台权限设计、前台模板分离机制,也可以直接作为企业建站的起步模板。
1. YouDianCMS v8.0 是什么:一套五合一源码如何同时撑起 PC、手机、微信、App 和小程序
第一次拆开 YouDianCMS v8.0 这个「五合一」源码包,是在给一家建材企业做官网的时候:电脑、手机、微信、App、小程序五个端都要有,预算却只够做一套系统。它解决的正是这个问题——纯 PHP 源码,企业网站最常用的技术栈,后台只管一份内容,五个终端共享同一份数据,改一次全端同步。适合接企业官网外包的开发者、给客户搭站点的自由职业者,也适合想省掉多后台维护成本的公司运维。后面几章会从目录结构讲到伪静态配置,再到小程序接口联调,把它怎么跑、怎么改一次讲透。
2. 五合一架构拆解:从入口文件到数据表,一次请求走完的链路
很多第一次拆这套源码的人会被目录数量吓到,理清楚之后发现它就是一个很标准的 MVC 分层:入口、控制器、模型、视图。这一层想明白,后面所有的部署和改造都顺了。我把安装包的目录结构、五端适配方式、核心数据表三块拆开讲,看完你就明白一份内容是怎样同时出现在五个端的。
2.1 目录结构:后台、前台、接口层各管哪块
解压后的目录(以 v8.0 默认结构为准)是这个样子:
YouDianCMS/ ├── install/ # 安装向导,部署完成后务必删除或改名 ├── admin/ # 后台管理端入口,登录后才能操作 ├── api/ # API 接口层,App 和微信小程序都走这里 ├── template/ # 模板目录,PC 端与手机端各一套 │ ├── pc/ │ └── mobile/ ├── data/ # 缓存文件、上传图片、备份数据 ├── include/ # 核心类库、公共函数、数据库操作封装 └── index.php # 前台唯一入口,所有页面都由它路由先说一个设计上很讨巧的点:整站只有一个前台入口 index.php,所有页面 URL 都长这样——index.php?m=content&a=lists&catid=5。m 是模块名,a 是动作名,catid 是栏目 ID。入口文件并不是直接渲染页面,而是先解析这三个参数,加载对应模块的控制器,控制器处理业务逻辑、模型负责查数据库,最后模板引擎把数据渲染成 HTML 返回。index.php 里的路由部分,简化后大致是这段逻辑:
$module = isset($_GET['m']) ? $_GET['m'] : 'content'; $action = isset($_GET['a']) ? $_GET['a'] : 'lists'; $path = './include/modules/' . $module . '.php'; if (file_exists($path)) { require_once $path; $class = ucfirst($module); $controller = new $class(); $controller->$action(); }看懂这段,你就知道新增一个页面模块要动哪里:在 include/modules 目录下建一个同名 PHP 文件,类名首字母大写,里面写对应的方法。不需要碰入口文件,也不需要改服务器配置——这是这套源码扩展性好的根本原因。
后台 admin 目录和前台物理隔离,所有后台操作先过登录态校验,校验通过才进入内容管理、栏目管理、模型管理这些页面。api 目录则是一组独立控制器,只输出 JSON 不做页面渲染,App 和小程序的数据源头就在这里。include 目录是核心库,数据库封装、缓存、上传、验证码都在里面,二次开发动这里要格外小心,改错一个公共函数影响的是全站。
2.2 五端适配原理:模板渲染与 JSON 接口双通道
五合一并不是五个独立站点,而是一个后台、两条通道、五种呈现。
| 终端 | 数据获取方式 | 页面呈现 |
|---|---|---|
| PC 网站 | 模板引擎渲染 HTML | template/pc 下模板 |
| 手机网站 | 模板引擎渲染 HTML | template/mobile 下模板 |
| 微信(公众号/分享页) | H5 页面 + JS-SDK | 手机模板 + 微信定制脚本 |
| App | JSON 接口 | App 端自行渲染 |
| 微信小程序 | JSON 接口 | 小程序 WXML 渲染 |
PC 和手机是最传统的服务端渲染:访问 index.php,框架先识别 UA 中的移动端标记,然后选择对应模板目录,把列表、详情渲染成完整 HTML。UA 判断的核心代码很短:
$ua = $_SERVER['HTTP_USER_AGENT']; $is_mobile = preg_match('/Mobile|Android|iPhone|iPad/i', $ua); $template_dir = $is_mobile ? 'template/mobile' : 'template/pc';这段正则覆盖主流移动端设备就够了,个别小众设备命中不了也无所谓,手机站和 PC 站内容一样,只是模板不同。微信端在业务上复用的就是手机 H5 模板,只是在分享、授权这类页面额外挂了微信 JS-SDK 的签名逻辑,用来处理分享鉴权和公众号登录。
App 和小程序端不拿 HTML,它们请求 api 目录下的接口,拿到的是 JSON 数组。比如小程序首页要显示文章列表,就请求api.php?module=content&action=list&catid=5,接口层从数据库查完数据格式化后返回。这个设计最大的优点是数据源始终是同一张内容表——后台改标题、传图片、下架文章,五个端下次请求时全部生效,不存在各自为政的数据孤岛。
2.3 数据库设计:内容表、栏目表与模型表的关联
这套源码的数据库核心是五六张表,最关键的是下面这几张:
| 表名 | 职责 | 关键字段 |
|---|---|---|
| ydcms_content | 内容主表 | id, catid, title, content, thumb, status, inputtime |
| ydcms_category | 栏目表 | catid, name, parentid, modelid, listorder |
| ydcms_model | 内容模型表 | modelid, name, tablename, fields |
| ydcms_admin | 管理员表 | id, username, password, roleid |
| ydcms_config | 站点配置表 | name, value |
内容主表和栏目表是典型的「内容属于栏目」关系:content.catid 指向 category.catid,这是所有列表查询的关联依据。status 字段控制上下架状态,前台默认只查 status=1,后台可以存草稿。
容易被忽略的是 ydcms_model 这张模型表。CMS 之所以是 CMS,一个重要能力就是内容结构可配置。管理员可以在后台动态新增「产品模型」,创建时系统自动生成一张扩展表,主表存公共字段(标题、内容、缩略图),扩展表存业务字段(价格、型号、参数)。新增字段的正确姿势是走后台模型管理,而不是直接改主表结构——直接改主表,升级或覆盖安装后字段就丢了,这是我见过最多的翻车现场之一。
3. 本地部署全流程:从解压 ZIP 到后台发布第一篇文章
讲完结构就该动手跑了。这套源码的安装属于典型的「下载即用」型,没有复杂的命令行构建步骤,但环境里有几个隐性要求不满足,安装过程会反复卡住。这一章按我实际部署的顺序写,照着走基本一次过。
3.1 环境准备:PHP 版本、扩展与伪静态配置
先看环境要求。这套源码的核心代码写得比较早,PHP 版本兼容区间很宽,5.6 到 7.2 都没问题;但如果本机是 PHP 8.x,就得先确认底层框架是否兼容——我自己就在 PHP 8 下翻过车,进入后台列表页直接白屏,换回 7.4 才正常。
另外有三个扩展必须确认开启:curl(App 接口和微信签名要用)、gd(后台生成缩略图)、mbstring(中文截断和多字节处理)。本地开发建议直接用集成环境,PHP 版本切到 7.4,MySQL 用 5.7。如果不确定扩展是否加载,可以用一个临时探针文件确认:
<?php phpinfo();浏览器打开这个文件,搜索 curl、gd、mbstring,三个都出现在列表中就没有问题。确认完记得删掉探针文件,免得泄露服务器信息。
如果你用 Nginx,还需要配一段伪静态规则,否则栏目页、详情页的 URL 会全部 404。核心配置如下:
location / { index index.php index.html; if (!-e $request_filename) { rewrite ^/(.*)$ /index.php?$1 last; } } location ~ \.php$ { include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass 127.0.0.1:9000; }这段规则把所有不存在的文件路径重写到 index.php,由框架内部根据参数路由。注意 if (!-e $request_filename) 这一行是关键,它保证真实存在的静态资源(图片、CSS、JS)不会被重写,否则上传的图片会全部打不开——这个坑在第五章详细讲。Apache 环境简单得多,源码根目录自带的 .htaccess 已经写好了,开启 mod_rewrite 即可。
3.2 安装向导:环境检查、数据库配置与创始人账号
环境就绪后,把源码解压到 Web 根目录,浏览器访问http://localhost/YouDianCMS/install/,进入安装向导。安装过程分三步,每一步都有对应的表单要填。
第一步是环境检查,页面会把 PHP 版本、扩展加载情况、目录可写权限列成一张表,有哪项不满足会直接标红。最常见的红色项是 data 目录和 upload 目录不可写,解决方法是给这两个目录加写权限,本地集成环境直接右键设置权限即可,Linux 服务器上执行chmod -R 777 data。
第二步填数据库信息,需要四个值:数据库主机(默认 localhost)、数据库名、用户名、密码。这里有个实操注意点:如果数据库名包含特殊字符,安装脚本拼接 SQL 时可能报错,建议用纯小写字母加下划线,比如 youdian_cms。勾选「创建数据库」后,安装脚本会自动建库、导表、写配置文件。
第三步设置创始人管理员账号和密码,以及站点名称、站点域名。站点域名这一项要填实际部署的地址,比如http://localhost或最终要用的域名,它会被写进 ydcms_config 配置表,很多前台 URL 和微信签名都依赖这个值,装完再改容易出现图片路径和分享链接对不上的问题,所以这一项值得多花十秒钟填准确。
3.3 后台初始化:建栏目、选模型、配权限
安装完成后记得删除 install 目录或改名,否则部分环境会出现安全告警,某些主机商会直接拦截。接下来登录后台做三件事。
第一件是建立栏目结构。以企业官网为例,至少要有「公司简介」「产品中心」「新闻资讯」「联系我们」几个栏目。在「栏目管理 → 添加栏目」里注意父栏目和列表模板两个选项:父栏目决定导航层级,列表模板决定这个栏目用哪套模板渲染。建栏目时顺手把排序值 listorder 规划好,数字小的排前面,这是导航排序的唯一依据。
第二件是确认内容模型。默认只有一个「文章」模型,如果站点要展示产品,需要先到「模型管理」新增产品模型并添加字段(价格、型号、参数图),再回头把「产品中心」栏目的模型绑定成产品模型。模型和栏目是绑定的,一个栏目只能绑定一个模型,这是这套源码数据组织的核心约束。
第三件是管理员权限。后台的「角色管理」支持自定义角色,可以创建一个只允许维护「产品中心」的内容编辑角色,限制它访问系统设置。给客户做交付时这是加分项——客户员工只能改内容,碰不到模板和配置,省得一个误操作把整站搞挂。
4. 核心模块代码讲解:内容发布、模板标签与小程序接口
这一章深入到代码层面。我选了最有代表性的三段逻辑拆开讲:后台内容发布的完整链路、前台模板标签的渲染机制、以及小程序端 JSON 接口的实现。读完这三段,你对这套源码的二次开发就有基本把握了。
4.1 内容发布链路:从表单提交到入库
后台发布文章提交到的控制器方法,核心逻辑大致如下:
public function save() { // 1. 接收并过滤表单数据 $catid = intval($_POST['catid']); $title = trim($_POST['title']); $content = addslashes($_POST['content']); $status = isset($_POST['status']) ? intval($_POST['status']) : 0; // 2. 基础校验 if ($catid <= 0 || empty($title)) { $this->error('栏目和标题不能为空'); } // 3. 组装数据写入主表 $data = array( 'catid' => $catid, 'title' => $title, 'content' => $content, 'status' => $status, 'username' => $_SESSION['admin_name'], 'inputtime' => time(), ); $id = $this->db->table('content')->add($data); // 4. 模型扩展字段单独写扩展表 if (!empty($_POST['model_field'])) { $ext = array('contentid' => $id); foreach ($_POST['model_field'] as $k => $v) { $ext[$k] = addslashes($v); } $this->db->table('content_ext')->add($ext); } $this->success('发布成功', 'index.php?m=content&a=lists'); }这段代码有三个值得注意的细节。第一是 intval 和 addslashes 的组合,虽然这套写法偏老派,但作为 CMS 的通用防护手段,它挡住了大多数注入和类型错误;二次开发时新加字段记得保持同样的过滤习惯。第二是 inputtime 用服务器时间戳,前台列表和 App 端的时间显示都依赖它,不要用日期字符串替代,否则按时间倒序排列时会出现排序错乱。第三是模型扩展字段的处理——它没有和主内容一起写进 content 表,而是单独写进 content_ext 扩展表,这就是第二章说的「主表存公共字段、扩展表存业务字段」的落地实现。
4.2 模板标签引擎:前台列表和详情页怎么渲染
前台模板不是纯 PHP 文件,而是混合了模板标签的 HTML 文件。以新闻列表为例,template/pc 下的列表模板核心结构长这样:
{list table="content" where="catid=5 and status=1" order="inputtime desc" num="10"} <li> <a href="{$item['url']}">{$item['title']}</a> <span>{date('Y-m-d', $item['inputtime'])}</span> </li> {/list}{list} 是这套源码使用频率最高的标签,渲染时会被替换成一段 PHP 查询循环。参数含义如下:
| 参数 | 含义 | 示例 |
|---|---|---|
| table | 要查询的数据表 | content 或 content_ext |
| where | 查询条件,与 SQL WHERE 一致 | catid=5 and status=1 |
| order | 排序规则 | inputtime desc |
| num | 取几条数据 | 10 |
| item | 循环变量名 | item |
模板标签的工作原理是:控制器加载模板文件时先执行一个正则替换,把 {list ...} 替换成真实的 PHP 循环代码,再交给 PHP 解释器执行。这意味着标签的本质就是帮你写循环,但写死的 SQL 条件不可能覆盖所有需求,所以创建模板时如果标签不够用,可以直接在模板里写原生 PHP 语句块,框架允许标签和原生 PHP 混用,只是原生写法性能更好。
这类模板驱动的引擎最大的好处是改版成本低。企业官网要换风格,不需要动控制器和数据库,只替换 template 目录下的模板文件即可——这也是这套源码适合接外包的原因:交付后客户要改版,只收模板费用,不动程序。
4.3 API 接口层:小程序文章列表怎么拿数据
App 和小程序端不渲染模板,它们请求 api 目录下的控制器。文章列表接口的实现:
public function get_article_list() { $catid = isset($_GET['catid']) ? intval($_GET['catid']) : 0; $page = max(1, intval($_GET['page'])); $pagesize = 10; $where = "status=1"; if ($catid > 0) { $where .= " AND catid=$catid"; } $list = $this->db->table('content') ->where($where) ->order('inputtime desc') ->limit(($page - 1) * $pagesize, $pagesize) ->select(); $this->json(array( 'code' => 0, 'msg' => 'success', 'data' => array( 'list' => $list, 'total' => $this->db->count(), ) )); }小程序的 wx.request 拿到 JSON 后,data.list 就是文章数组,total 用于分页判断。这里有一个值得借鉴的规范:所有接口统一返回{code, msg, data}三字段结构,code 为 0 表示成功,非 0 表示各类业务错误。App 端和小程序端都按这个结构解析,后续加新接口时保持一致,前端就不需要为每个接口写不同的异常分支。
实际开发中我在这层加过一个通用处理:把列表里的 inputtime 时间戳统一格式化成Y-m-d返回,省得前端每个页面都写一遍日期格式化函数。这类小改动对前后端协作非常省事,属于投入小收益高的优化。接口层对前端来说经常像个黑匣子,把响应结构统一了,联调效率能提升一大截。
5. 五合一部署避坑指南:伪静态 404、上传失败、小程序白屏
这套源码我自己部署过不止一次,也在开发群里见过别人反复问同样的问题。这一章把出现频率最高的五个坑列出来,每条都按「现象 → 原因 → 解决」的顺序写,希望能帮你少走弯路。
5.1 栏目页全部 404,首页却正常
现象:首页能打开,但点进任何一个栏目页、文章详情页都是 404。
原因:Nginx 环境没有配置伪静态规则,index.php 的参数路由没有被正确重写。Apache 环境则可能是 .htaccess 文件缺失或 mod_rewrite 没开启。
解决:按 3.1 节的 Nginx 配置加上 location 重写规则,改完执行nginx -s reload。Apache 环境检查根目录的 .htaccess 是否存在,并确认 httpd.conf 里 LoadModule rewrite_module 已启用。九成以上的多端部署 404 都出在这一条,排查顺序先看重写规则,再看文件权限。
5.2 后台图片上传失败,提示「上传失败」或直接无响应
现象:后台编辑器里传图片,进度条走完但提示失败;有时小图成功、大图失败。
原因:分两种情况。小图失败通常是 data/upload 目录没有写权限;大图失败是 PHP 上传大小限制,php.ini 里 upload_max_filesize 默认只有 2M,而后台编辑器默认允许 10M 以内的图片。
解决:先给 data 目录加写权限,再修改 php.ini 的upload_max_filesize和post_max_size,一般调到 20M,重启 PHP 生效。我见过有人只改 upload_max_filesize 不改 post_max_size,结果表单数据整体超限,连文章都提交不了——这两个值必须一起调。改完到后台重新上传一次,就能验证是否解决。
5.3 小程序请求接口报「不在合法域名列表」或直接白屏
现象:小程序开发者工具里请求接口报错,真机预览时页面白屏,后台日志里能看到请求根本没发出去。
原因:微信小程序有域名白名单机制,所有 wx.request 的 URL 必须是已备案且开启 HTTPS 的域名,并添加到小程序后台的 request 合法域名列表。本地联调用http://localhost,开发者工具默认会拦截。
解决:本地调试时在开发者工具里勾选「不校验合法域名」即可联调;上线前必须准备 HTTPS 域名并配置合法域名。开发阶段最省事的流程是:后端先把接口跑通,前端用开发工具的跳过校验开关联调,最后统一切到正式域名。这条是五合一里最容易卡住人的环节,因为它不是代码问题,是平台规则问题——App 端没有这个限制,小程序端有,别在两端联调上重复踩坑。
5.4 改完模板前台没变化,手机端还是旧样式
现象:替换了 template/mobile 下的模板文件,前台手机端刷新还是老样式,缓存目录里能看到新文件,但页面不生效。
原因:这套源码的模板有缓存机制,模板文件首次访问后会生成编译后的缓存文件,模板更新后缓存不会自动失效。PC 端和手机端模板缓存是分开的,所以会出现 PC 更新了、手机没更新的中间状态。
解决:去「后台 → 系统设置 → 清除缓存」,把 data/cache 目录下的编译缓存清掉。我实际操作中习惯每次模板改动后都清一次缓存,避免客户看到新旧混搭的页面。顺手说一句,这个缓存机制在生产环境是好事,它让模板渲染速度提升不少,代价只是开发期需要多点一次「清缓存」按钮。
5.5 安装完成后台白屏,PHP 8 环境下最常见
现象:安装向导正常走完,进入后台时页面全白,前台可能正常,只有后台挂掉。
原因:后台代码里使用了部分在 PHP 8 中行为变更的函数或特性,比如某些字符串函数的参数顺序变化,旧代码在 PHP 8 下直接报错,而错误显示被关闭时就表现为白屏,连排查入口都没有。
解决:最直接的办法是把 PHP 版本切回 7.4,这也是这套源码推荐运行的环境。如果必须用 PHP 8,可以临时在配置里开启 display_errors 把真实报错打出来,逐行修复报错点——但这属于吃力不讨好的活,项目本身是为 7.x 优化的,没必要为了版本号硬升。这条属于典型的环境兼容问题,血泪经验是:跑这类老牌 PHP CMS 之前先确认版本,不要在 8.x 上折腾。
6. 二次开发实战:给企业官网加一个「产品库」自定义模型
最后落到改造。企业官网最常见的需求就是产品展示,而默认的「文章」模型没有价格、型号这些字段,所以二次开发的第一步通常就是新增一个产品模型。这套流程我已经走过好几轮,按下面的顺序做基本不会返工。
6.1 新增模型与字段
在后台「模型管理」里新增模型,模型名填「产品」,然后添加字段:价格(price,数字类型)、型号(model,文本类型)、参数链接(param_url,文本类型)。系统会自动在数据库里建出对应的扩展表结构,并在模型配置里记录字段清单。之后在「内容管理」里发产品时,表单会自动多出这三个输入框,存储时走 4.1 节那个扩展字段写入分支,不需要改一行后台代码。
6.2 API 层透出新增字段,小程序直接展示
默认的文章列表接口只查主表,返回的 data.list 里不带 price、model 这些扩展字段。小程序端要展示产品价格,就得在接口层做一次关联查询:
$list = $this->db->table('content c') ->join('content_ext e', 'c.id = e.contentid', 'left') ->where($where) ->order('c.inputtime desc') ->limit(($page - 1) * $pagesize, $pagesize) ->select();join 之后,每条记录的 price、model 字段就会出现在 JSON 里,小程序端直接用item.price渲染,不需要额外改动。注意这里用的是 left join,主表有记录但扩展表暂时没数据时,字段值会是 null,前端模板要做空值兜底。如果不想动接口代码,还有一个更偷懒的方案:在新增模型时把 price 等字段设置为「附加到主表」,系统会把字段同步写入主表——代价是主表字段越堆越多,后期不好维护,不推荐这么干。
从那以后我每次给这类老牌 PHP CMS 做定制,都强制走一遍「先加模型和字段 → 再验证模板标签取数 → 最后检查接口输出」的流程,三个环节按顺序验证,基本不会出现前台有数据、小程序拿不到字段的尴尬。这套五合一源码的拆解和避坑记录到这里就结束了,希望帮到你。
本文还有配套的精品资源,点击获取