messages表必须设status TINYINT NOT NULL DEFAULT 0和created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,否则新消息status为NULL导致查询遗漏、created_at为0000-00-00使排序失效。

messages 表必须带 status 和 created_at 默认值
不设 status TINYINT NOT NULL DEFAULT 0,新消息插入后 status 为 NULL,查 WHERE status = 0 就漏数据;前端“未读数”要么不准,要么得加 OR status IS NULL,逻辑越补越乱。
不设 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,MySQL 5.7+ 会报错,5.6 会填 0000-00-00 00:00:00,新消息永远排在最底下,分页和倒序展示全崩。
最小可用字段集必须包含:
-
id(主键,AUTO_INCREMENT) -
sender_id和receiver_id(都设NOT NULL;若支持系统公告或群发,receiver_id改为允许NULL) -
status(TINYINT NOT NULL DEFAULT 0,0=未读,1=已读,2=删除) -
created_at(TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP) -
type(TINYINT DEFAULT 1,区分私信/公告/群发)
查收件箱必须用预处理 + session 校验
写 where('to_id = '.session('id')) 是 SQL 注入温床——session 被劫持或伪造时,攻击者能拖走全表消息。更隐蔽的是:当 session('id') 为空、非数字或类型错乱,查询可能返回空或报错,前端只显示空白,根本看不出哪出问题。
正确做法:
- 先校验:
if (!session('id') || !is_numeric(session('id'))) { $this->error('登录失效'); } - 再用数组条件预处理:
$messages = M('Email')->where(['to_id' => session('id')])->order('created_at DESC')->select(); - 关联用户真名别手写
ON t1.to_id = t2.id,ThinkPHP 3.2+ 支持数组条件,更安全
标记已读不能只改 status,得同步更新未读缓存
每次点开一条消息都去 SELECT COUNT(*) FROM messages WHERE receiver_id = ? AND status = 0,用户量过万,顶部小红点就变慢;多个标签页同时打开同一封信,还可能因并发导致未读数扣成负数。
必须同步维护缓存:
- 用 Redis 存
user:123:unread,增删改status时一起操作 - “全部标为已读”用
UPDATE ... WHERE to_id = ? AND status = 0,避免重复更新 - 批量操作后,记得重置缓存值,不要只靠
INCR/DECR
发信时 sender_id 必须强制取自 session,禁用前端传参
如果接口接收 $_POST['sender_id'] 并直接入库,恶意用户改个 ID 就能冒充管理员发系统通知——前端隐藏域、JS 校验全无效,HTTP 请求本身毫无可信度。
控制器里一律写:
$data['sender_id'] = session('id');- 删掉所有接收
sender_id参数的逻辑 - 如需支持系统公告(
sender_id = 0),则receiver_id字段不能设NOT NULL,且加type字段区分 - 发信前检查接收者是否存在:
if (!M('User')->find($receiverId)) { $this->error('用户不存在'); }
status 和 created_at 的默认值不是可选项,是上线即崩的临界点;缓存不同步的问题往往在压测或并发高峰才暴露,但修复成本远高于建表时多敲两个单词。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











