ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于原生PHP的新闻宣传审核考评系统开发实战

基于原生PHP的新闻宣传审核考评系统开发实战 我以前在单位坐班的时候最头疼的不是写稿而是月底那堆考核表。宣传稿件发了多少、被上级平台转载几篇、通报批评多少次、各科室报上来的统计口径对不对全得靠人肉核对。后来我用 PHP 写了这套新闻宣传审核考评系统把“投稿—审核—登记—计分—汇总”整条链路从 Excel 和微信聊天记录里解放出来源码也整理成了完整版本交给同事直接部署。这篇文章就是这套系统从需求拆解、数据表设计到部署避坑的完整复盘适合刚接触 PHP 项目开发、或者手里正压着类似行政事务需求的朋友直接参考。1. 需求拆解与整体设计思路1.1 新闻宣传审核考评到底卡在哪些环节别一听“新闻宣传审核考评系统”就觉得是个内容管理系统实际上它比普通 CMS 多出来的那部分才是开发时真正需要花心思的地方。普通 CMS 管的是“内容能不能发”考评系统管的是“发了之后怎么算账”。我把需求拆成四块投稿登记、审核流、考评计分、统计报表。投稿登记对应宣传干事上报稿件的日常动作包含标题、作者、单位、投稿渠道、稿件类型、刊发标记等字段。这部分看着简单但如果想直接抄源码用字段设计必须提前留好扩展位否则后面加一个“是否被领导批示”都要动表结构。审核流不是简单的“一级审完再审”在实际业务里往往存在“科室初审—分管领导复审—最终发布”三条分支。有些稿件需要走加急通道有些直接退稿有些要退回修改。这一块不能只给一个 status 字段得设计成可以记录审批意见和操作时间的形式。考评计分是最容易出矛盾的地方。不同级别的媒体权重不一样市级、省级、国家级平台的积分差距很大同一个稿件被多个平台转载算最高分还是累加负面通报扣分规则怎么定义。这些规则如果不做成可配置项每季度调整一次就得改一次代码谁维护谁崩溃。统计报表是领导最关心的也是普通开发最容易忽略的。系统不能只输出“总量、排名”还要能支持按部门、按时间、按稿件类型筛选最好能导出成 Excel。很多人拿到源码第一件事就是改报表导出这一点我建议直接用 PHP 的 Spreadsheet 类库别自己拼 CSV编码问题能把人逼疯。1.2 为什么用原生 PHP 而不上框架做过类似系统的人可能都会问用 Laravel 或 ThinkPHP 不香吗香但如果这个系统要部署在单位内网的老服务器上框架反而是负担。我接手的时候目标服务器还是 Windows Server 2012PHP 版本固定在 5.6MySQL 是 5.5连 Composer 都不一定装得上。用框架意味着要处理一堆依赖包、路由配置、环境变量任何一个环节不一致系统就起不来。原生 PHP 的好处是一个目录拷过去改一下数据库连接配置浏览器一开就能跑。这不是说框架不好而是提醒大家选型要看实际环境。如果这套系统是给你自己长期迭代用的服务器环境又是你自己说了算那上框架完全没问题。但如果是要交付给其他单位、其他同事去部署环境不可控才是最大的风险原生 PHP 反而是最稳妥的选择。这套系统最终的形态就是原生 PHP MySQL 少量 JavaScript前端没有用复杂框架。所有公共操作封装成函数文件业务逻辑按模块拆成独立 PHP 文件模板层用简单的 include 方式组合。代码谈不上多优雅但任何一个有一点 PHP 基础的人都能看懂、能改。2. 数据表设计、权限控制与考评规则2.1 三张核心数据表的关系设计数据库是这套系统的地基。我最终设计了两张主表和一张中间表稿件表article、考评表score_log、部门表department。作者信息没有单独建表而是直接冗余在稿件表里因为一个作者可能同时属于多个部门单独建表反而要处理多对多关联。稿件表的核心字段长这样CREATE TABLE article ( id int(11) NOT NULL AUTO_INCREMENT, title varchar(200) NOT NULL COMMENT 稿件标题, author varchar(50) NOT NULL COMMENT 作者, department_id int(11) NOT NULL COMMENT 所属部门, type tinyint(1) NOT NULL DEFAULT 1 COMMENT 1图文 2视频 3新媒体, channel varchar(50) NOT NULL COMMENT 投稿渠道, status tinyint(1) NOT NULL DEFAULT 1 COMMENT 1待审 2通过 3退回 4已发布, media_level tinyint(1) NOT NULL DEFAULT 1 COMMENT 媒体级别 1市级 2省级 3国家级, is_reprinted tinyint(1) NOT NULL DEFAULT 0 COMMENT 是否被转载, publish_time datetime DEFAULT NULL, create_time datetime DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有个很关键的细节utf8mb4一定不能省。老项目里很多人用utf8遇到生僻字、特殊符号就会乱码更别说 emoji 了。新闻稿件里作者名字带“”这种生僻字的情况我真实遇到过用utf8当时就直接插入失败换成utf8mb4才解决。考评表不单独记录总分而是每一条操作记录单独存一行需要总分时再用 SUM 临时计算。这样做的好处是规则调整时不用改历史数据。比如原来市级媒体计 2 分后来调整成 3 分新数据按新规则算老数据仍保留原始记录报表里额外展示“调整后得分”即可。如果直接把分数字段覆盖掉后续审计时就会说不清楚。2.2 审核状态机与权限控制怎么写审核流我推荐用一个简单的状态机不要写一堆 if-else 嵌套。每个状态定义成常量操作函数里先判断当前状态和操作类型是否匹配不匹配直接返回错误码。const STATUS_PENDING 1; const STATUS_APPROVED 2; const STATUS_REJECTED 3; const STATUS_PUBLISHED 4; function transition($currentStatus, $action) { $rules [ STATUS_PENDING [approve STATUS_APPROVED, reject STATUS_REJECTED], STATUS_APPROVED [publish STATUS_PUBLISHED, reject STATUS_REJECTED], STATUS_REJECTED [resubmit STATUS_PENDING], ]; return $rules[$currentStatus][$action] ?? null; }实际调用时只传当前状态和动作返回下一个状态不合法就返回 null。这套逻辑既避免了操作漏洞又让代码分支容易理解。后来同事要增加一层“终审”我只要在规则数组里加一行就行。权限控制我没有用复杂的 RBAC 组件用一个user_role字段区分管理员、审核员和普通干事然后在每个操作入口加一个权限检查函数function check_permission($needRole) { session_start(); if (!isset($_SESSION[user]) || $_SESSION[user][role] $needRole) { header(Location: login.php?msgpermission_denied); exit; } }对这类内网管理后台来说这种简单方案完全够用。真要去引一套完整的权限组件反而把简单问题复杂化了。安全方面要做到的是所有涉及数据库的操作必须用 PDO 预处理不能直接把$_POST拼进 SQL。2.3 考评计分规则与月度汇总实现考评规则我做成了一张独立配置表而不是硬编码。配置表里有媒体级别、计分基数、转载加分、负面扣分等字段。后台管理页提供增删改保存后立即生效。每个稿件发布成功时会写入一条考评记录。月度汇总时按部门分组聚合SELECT d.name AS department, SUM(IF(s.score 0, s.score, 0)) AS total_score, COUNT(s.id) AS total_count FROM score_log s LEFT JOIN department d ON s.department_id d.id WHERE DATE_FORMAT(s.create_time, %Y-%m) ? GROUP BY s.department_id ORDER BY total_score DESC;这里注意一个坑DATE_FORMAT用在WHERE里会导致索引失效。表数据量小的时候没感觉等积压到几万条记录这个查询会明显变慢。我后来改成传开始日期和结束日期用BETWEEN比较省去了格式化消耗。计分规则这里要重点提醒负面扣分不能简单乘一个负数就完事。有些单位要求负面通报单独列一栏不计入总分但影响排名。我在报表里用两个字段分别存储“正面得分”和“负面扣分”排名规则可配置为“先比总分总分相同比正面得分”。这种细节才是考评系统的灵魂写代码之前一定要和业务方确认清楚。3. 实操部署环境搭建、调试和难点代码3.1 Windows Server 下 PHP 环境搭建与调试如果你要在 Windows Server 上部署这套系统最省心的方式是使用 PHPStudy 这类集成环境或者手动下载 PHP 解压包并配置系统环境变量。手动安装时记得把php.ini-development改成php.ini然后打开这几个扩展extensionmysqli extensionmbstring extensioncurl extensiongd2 extensionopenssl之前有个同事部署时一直报 Call to undefined function问题就是 php.ini 里的扩展没开。另外还要检查extension_dir路径对不对PHP 解压目录移动过之后这个路径经常对不上。开发调试阶段我强烈建议配一下 VSCode 的 PHP Debug。在 VSCode 里安装 PHP IntelliSense 和 PHP Debug 扩展再用xdebug扩展配合 launch.json 做断点调试。xdebug 的配置放在 php.ini 末尾[Xdebug] zend_extensionC:/php8/ext/php_xdebug-3.2.0.dll xdebug.modedebug xdebug.client_host127.0.0.1 xdebug.client_port9003 xdebug.start_with_requestyes这套配置在本地开发时非常好用能看到每个变量当前值比var_dump一次次刷新页面高效太多了。上线部署时记得把start_with_request关掉或者在生产环境里不加载 xdebug否则每个请求都会尝试建立调试连接拖慢访问速度。3.2 图片上传、验证码识别和编码转换三个硬骨头新闻宣传系统里图片上传是逃不掉的。稿件配图、封面图都需要传。写上传逻辑时文件类型校验不能只看$_FILES[file][type]这个值客户端可以伪造。需要结合getimagesize()检查真实文件头。另外务必改掉默认上传目录权限把uploads/目录设置为禁止执行 PHP 脚本避免有人传一个带后门的图片马进来。系统登录页我做了图形验证码。原本想用现成 OCR 识别库做自动登录测试后来发现大多数 PHP 验证码库生成的干扰线准确率都不理想。实际上如果你拿到一套源码想做接口自动化测试更靠谱的方案是直接调验证码类库里的生成函数读取$_SESSION里存的值把登录接口的验证码校验关闭或改成固定值。生产环境千万别这么干这只是测试阶段的手段。编码转换问题也很容易踩坑。新闻稿件有时候从 Word 复制过来自带各种特殊字符。入库前统一做一次mb_convert_encoding($text, UTF-8, auto)能解决大部分乱码问题。导出 Excel 时也要注意直接用fputcsv输出的 CSV 用 Excel 打开会乱码建议在文件开头加一行\xEF\xBB\xBFBOM 头或者直接用 PHPSpreadsheet 写真正的.xlsx文件。还有个小坑PHP 二维数组处理时很多时候需要把某个字段的值取出来当新数组的键。比如按部门分组汇总代码经常这么写$result []; foreach ($list as $row) { $result[$row[department_name]][] $row; }这个操作本身没问题但要注意数字字符串键会自动转成整数比如表单里1、2这种值放到数组键里就变成int。如果你后面要做array_key_exists(1, ...)这种判断会莫名失败。建议需要保字符串键时统一在前面拼一个前缀比如dept_ . $row[id]。3.3 源码目录结构说明与二次开发入口拿到完整源码之后第一件事不是急着往服务器上传而是先摸清目录结构。这套系统的源码目录大致如下/ ├── admin/ # 后台管理模块 │ ├── login.php # 登录 │ ├── dashboard.php # 首页统计 │ ├── article.php # 稿件管理 │ ├── score.php # 考评配置 │ └── report.php # 汇总报表 ├── include/ │ ├── config.php # 数据库配置 │ ├── db.php # PDO 封装 │ ├── auth.php # 权限校验 │ └── function.php # 公共函数 ├── uploads/ # 上传目录 ├── index.php # 前台入口 └── install.sql # 数据库初始化脚本二次开发时最先要看的是include/config.php和include/function.php。前者是数据库连接信息后者是全局函数集合。大部分新增业务逻辑只需要在admin/目录建一个新 PHP 文件然后在auth.php里加上对应权限判断就行。不要改动install.sql里的历史建表语句建议新建一个upgrade.sql记录每次表结构变更。我在这个项目上吃过亏原表里article.title长度不够后来某篇标题超过 200 字直接报错好在那次改动只是 ALTER 语句记录清楚后十分钟就处理完了。4. 常见问题、安全加固与二次开发避坑4.1 运行报错速查表这套系统我在不同环境下部署过十来次总结下来这么几个高频报错基本覆盖了 90% 的情况报错信息原因解决办法Call to undefined function mysqli_connect()mysqli 扩展未开启到 php.ini 里打开extensionmysqliClass PDO not foundPDO 扩展未开启打开extensionpdo_mysqlNo package libzip foundLinux 编译安装时缺依赖安装 libzip 后再编译Warning: date() expects parameter 2 to be integer时间戳传了字符串先用strtotime()转换Headers already sent输出内容后才调header()检查文件开头是否有 BOM 或空格中文乱码字符集不一致统一utf8mb4连接串加charsetutf8mb4还有个之前频繁遇到的exec()执行外部命令完成后无法中断导致页面卡死。这个一般是因为调用了解压或视频转码命令但没设置超时。PHP 里可以用proc_open配合proc_terminate控制超时或者干脆用前端轮询任务状态把耗时操作丢到后台队列里执行不阻塞同步请求。对于新闻宣传这种低并发系统用 MySQL 表模拟一个简单任务队列完全够用。4.2 “带完整源码”项目的安全加固建议既然是带完整源码的项目开源是好事但安全这一步不能省。尤其这种带审核功能的系统一旦被攻击者拿下后台影响面很大。我拿到这类源码后通常会先做四件事第一把默认数据库前缀改掉。如果源码里所有表名都是article、score_log这种攻击者一眼就能猜出表结构。创建表时加一个前缀如news_article然后把 SQL 语句和模型层代码同步改掉。第二密码不能明文存储。哪怕源码里写的是md5($password)也要改成password_hash($password, PASSWORD_DEFAULT)登录时用password_verify()校验。这是一个老生常谈的问题但很多源码项目确实用的是老式 MD5一定要改。第三上传目录单独配置为无脚本执行权限。Nginx 里这样配location ~ \.(php|php5)$ { deny all; }如果是 Apache在 uploads 目录下放一个.htaccess内容只写php_flag engine off。再在后台检查文件扩展名白名单图片只允许jpg、png、gif、webp。就算攻击者绕过前端传了个shell.php也没法在这个目录里执行。第四所有后台操作记录操作日志。我在 score_log 表之外加了一张admin_log表记录后台用户每次增删改操作的时间、IP、请求参数。有人误删稿件时能快速知道是谁动的、改了什么省得每天当扯皮调解员。4.3 二次开发时的几个高频修改点拿到这套系统大多数人做的第一件事就是改单位名称、改部门列表。部门列表在数据库的department表里直接在后台加个“部门管理”页面更省事。第二个高频需求是调整计分规则。我建议在后台增加一个“规则预览”功能把所有稿件打分逻辑列出来保存前可以试算一条测试稿件看分值是否符合预期。这比改完代码直接上生产要安全得多。第三个需求是导出报表。很多单位要的是每个月份的汇总表并且要求 Excel 格式。我前期用 CSV 导出被吐槽无数次后来换成了 PHPSpreadsheet代码量增加不多但用户体验提升非常明显。尤其当报表数据里带“部门名称”和“稿件标题”这类中文字段时用真正 Excel 格式能避免很多编码问题。第四个需求是短信或邮件通知。审核通过后要让作者知道。原生 PHP 实现这个也不难封装一个send_notify()函数内部用腾讯云短信接口或 SMTP 邮件接口。但我建议默认做成可选配置毕竟很多单位内网没外网短信接口根本调不通。5. 从实际使用中总结出来的几条经验我自己在这套系统上线后最明显的感受是月底统计报表从原来的一整天缩短到一秒钟。原来同事们的统计方式是把每篇稿件的推送记录截图、粘贴到 Excel 里手工计数现在打开后台报表页点一下筛选就出来了。还有一点系统上线不代表万事大吉数据初始化才是重活。历史稿件要不要导入年份怎么界定同一个稿件在不同时间被多个平台转载算不算不同记录这些问题都必须在上线前跟业务方说清楚。技术实现再完美业务边界没谈好上线后还是会闹矛盾。源码项目交付的时候我一般会额外附一个README.md写清楚服务器要求、数据库导入方式、默认账号和修改密码的方法。这些文档看似琐碎但能帮你少接无数个“这个系统怎么部署”的咨询电话。最后再分享一个小技巧这类 PHP 项目特别适合把config.php里的数据库连接信息改成从环境变量读取。本地开发用自己的库服务器上配置不同的环境变量同一个源码包不用改代码就能在多个环境间切换。虽然原生 PHP 写起来稍微麻烦点但一次配置好后面升级、迁移都能省下大把时间。如果你现在正准备接手一个类似的 PHP 内部管理系统我的建议是从数据表和业务规则入手先别看代码细节。把考评规则跟业务方逐条确认清楚了再谈字段和页面设计。规则没定清楚再漂亮的界面也是白搭。
返回列表