当前位置: 首页 > news >正文

DiscuzX3.5发帖json api

参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客

简单改造了一下,适配我自己的需求

有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容

使用到的sql如下

CREATE TABLE `pre_forum_post_sync`  (`id` int(0) NOT NULL AUTO_INCREMENT,`foreign_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,`foreign2_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`foreign3_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`tid` int(0) NOT NULL,`pid` int(0) NOT NULL,`update_time` int(0) NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `foreign_id`(`foreign_id`) USING BTREE
) ENGINE = InnoDB  CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;CREATE TABLE `pre_wechat_push_log` (`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,`tid` INT NOT NULL COMMENT '主题ID',`pid` INT NOT NULL COMMENT '帖子ID',`push_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '推送时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

php api代码

同步主站标题api

topic_only.php

<?php
date_default_timezone_set('Asia/Shanghai');
header('Content-Type: application/json');// 定义允许的 accesskey 和 accesssecret
define('ACCESS_KEY', 'my-access-key');
define('ACCESS_SECRET', 'my-access-secret');// 获取并统一转换请求头为小写
$headers = array_change_key_case(getallheaders(), CASE_LOWER);// 检查 accesskey 和 accesssecret 是否存在且匹配
if (!isset($headers['accesskey']) || !isset($headers['accesssecret'])) {http_response_code(403);echo json_encode(['status' => 'error', 'message' => 'Missing AccessKey or AccessSecret']);exit;
}if ($headers['accesskey'] !== ACCESS_KEY || $headers['accesssecret'] !== ACCESS_SECRET) {http_response_code(403);echo json_encode(['status' => 'error', 'message' => 'Invalid AccessKey or AccessSecret']);exit;
}// 接收并解析 JSON 输入
$input = file_get_contents('php://input');
$data = json_decode($input, true);// 检查必要字段
if (!isset($data['fid'], $data['title'], $data['content'], $data['foreign_id'])) {echo json_encode(['status' => 'error', 'message' => 'Missing required parameters']);exit;
}$fid = $data['fid'];
$title = $data['title'];
$content = $data['content'];
$foreign_id = $data['foreign_id'];
$post_time = isset($data['post_time']) ? strtotime($data['post_time']) : time(); // 新增行try {$test = new insert_content('1', 'admin', $post_time); // 使用传入时间$result = $test->sync_post($fid, $title, $content, $foreign_id);echo json_encode(['status' => 'success', 'data' => $result]);
} catch (Exception $e) {echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}class insert_content {private $_prefix = 'pre'; // Discuz 表前缀private $_con;private $_tid;private $_fid;private $_pid;private $_authorid;private $_author;private $_time;private $_title;private $_content;public function __construct($authorid, $author, $time = null) {$this->_authorid = $authorid;$this->_author = $author;$this->_time = $time ?? time();$this->_con = mysqli_connect('mysql', 'root', 'pass', 'ultrax', 3306);if (!$this->_con) {throw new Exception("Database connection failed");}}/*** 同步发帖方法** @param $fid* @param $title* @param $content* @param $foreign_id* @return array* @throws Exception*/public function sync_post($fid, $title, $content, $foreign_id) {$this->_fid = $fid;$this->_title = $title;$this->_content = $content;// 先检查是否已同步$sql = "SELECT tid, pid FROM {$this->_prefix}_forum_post_sync WHERE foreign_id='{$foreign_id}'";$res = mysqli_query($this->_con, $sql);if ($row = mysqli_fetch_assoc($res)) {// 已存在,仅更新标题$this->_tid = $row['tid'];$this->_pid = $row['pid'];// 仅更新主题表中的标题$sql = "UPDATE {$this->_prefix}_forum_thread SET subject='{$this->_title}', lastpost='{$this->_time}' WHERE tid='{$this->_tid}'";if (!mysqli_query($this->_con, $sql)) {throw new Exception("Update thread failed: " . mysqli_error($this->_con));}// 可选:更新同步记录时间$sql = "UPDATE {$this->_prefix}_forum_post_sync SET update_time='{$this->_time}' WHERE foreign_id='{$foreign_id}'";if (!mysqli_query($this->_con, $sql)) {throw new Exception("Update sync record failed: " . mysqli_error($this->_con));}return ['action' => 'updated','tid' => $this->_tid,'pid' => $this->_pid];} else {// 不存在,新建帖子和主题return $this->insert_new_post($foreign_id);}}private function insert_new_post($foreign_id) {// 第一步:插入主题表$sql = "INSERT INTO {$this->_prefix}_forum_thread SETfid='{$this->_fid}',authorid='{$this->_authorid}',author='{$this->_author}',subject='{$this->_title}',dateline='{$this->_time}', lastpost='{$this->_time}',lastposter='{$this->_author}'";if (!mysqli_query($this->_con, $sql)) {throw new Exception("Insert thread failed: " . mysqli_error($this->_con));}$this->_tid = mysqli_insert_id($this->_con);// 第二步:插入分表协调表if (!mysqli_query($this->_con, "INSERT INTO {$this->_prefix}_forum_post_tableid VALUES ()")) {throw new Exception("Insert post tableid failed: " . mysqli_error($this->_con));}$this->_pid = mysqli_insert_id($this->_con);// 第三步:获取 position 并插入帖子表$res = mysqli_query($this->_con, "SELECT MAX(position) AS max_pos FROM {$this->_prefix}_forum_post WHERE tid='{$this->_tid}'");$row = mysqli_fetch_assoc($res);$position = $row['max_pos'] ? $row['max_pos'] + 1 : 1;$sql = "INSERT INTO {$this->_prefix}_forum_post SETpid='{$this->_pid}',fid='{$this->_fid}',tid='{$this->_tid}',author='{$this->_author}',authorid='{$this->_authorid}',subject='{$this->_title}',dateline='{$this->_time}',message='{$this->_content}',premsg='',position={$position}";if (!mysqli_query($this->_con, $sql)) {throw new Exception("Insert post failed: " . mysqli_error($this->_con));}// 第四步:更新版块统计$sql = "UPDATE {$this->_prefix}_forum_forum SET posts=posts+1, threads=threads+1 WHERE fid='{$this->_fid}'";if (!mysqli_query($this->_con, $sql)) {throw new Exception("Update forum stats failed: " . mysqli_error($this->_con));}// 第五步:更新用户统计$sql = "UPDATE {$this->_prefix}_common_member_count SET posts=posts+1, threads=threads+1 WHERE uid='{$this->_authorid}'";if (!mysqli_query($this->_con, $sql)) {throw new Exception("Update user stats failed: " . mysqli_error($this->_con));}// 插入同步记录$sql = "INSERT INTO {$this->_prefix}_forum_post_sync SETforeign_id='{$foreign_id}',tid='{$this->_tid}',pid='{$this->_pid}',update_time='{$this->_time}'";if (!mysqli_query($this->_con, $sql)) {throw new Exception("Insert sync record failed: " . mysqli_error($this->_con));}return ['action' => 'created','tid' => $this->_tid,'pid' => $this->_pid];}public function __destruct() {if ($this->_con instanceof mysqli) {$this->_con->close();}}
}

使用方法

curl --location 'http://d.example.com/topic_only.php' \
--header 'AccessKey: my-access-key' \
--header 'AccessSecret: my-access-secret' \
--header 'Content-Type: application/json' \
--data '{"fid": "2","title": "测试标题0816","content": "这是一个测试内容。","foreign_id": "9966","post_time": "2024-08-16 10:00:00"
}
'

跟参考的代码相比,这里做了一个简单的鉴权,AccessKey AccessSecret与php代码里保持一致才可调用。本来像参考oss写个签名方法,还是先不搞这么复杂了。

由于采集站没有源站帖子id,则写一个根据标题更新内容的api

update_by_title.php

<?php
date_default_timezone_set('Asia/Shanghai');
header('Content-Type: application/json');// 定义允许的 accesskey 和 accesssecret
define('ACCESS_KEY', 'my-access-key');
define('ACCESS_SECRET', 'my-access-secret');// 获取并统一转换请求头为小写
$headers = array_change_key_case(getallheaders(), CASE_LOWER);// 检查 accesskey 和 accesssecret 是否存在且匹配
if (!isset($headers['accesskey']) || !isset($headers['accesssecret'])) {http_response_code(403);echo json_encode(['status' => 'error', 'message' => 'Missing AccessKey or AccessSecret']);exit;
}if ($headers['accesskey'] !== ACCESS_KEY || $headers['accesssecret'] !== ACCESS_SECRET) {http_response_code(403);echo json_encode(['status' => 'error', 'message' => 'Invalid AccessKey or AccessSecret']);exit;
}// 接收并解析 JSON 输入
$input = file_get_contents('php://input');
$data = json_decode($input, true);// 检查必要字段if (!isset($data['fid'], $data['title'], $data['content']) ||(!isset($data['foreign2_id']) && !isset($data['foreign3_id']))) {echo json_encode(['status' => 'error', 'message' => 'Missing required parameters: fid, title, content, and either foreign2_id or foreign3_id']);exit;}$fid = $data['fid'];
$title = $data['title'];
$content = $data['content'];
$foreign2_id = $data['foreign2_id'] ?? null;
$foreign3_id = $data['foreign3_id'] ?? null;try {// 初始化数据库连接$con = mysqli_connect('mysql', 'root', 'pass', 'ultrax', 3306);if (!$con) {throw new Exception("Database connection failed");}// 查找 title 对应的主题 ID(tid)$sql = "SELECT tid FROM pre_forum_thread WHERE subject = '{$title}' AND fid = '{$fid}' LIMIT 1";$res = mysqli_query($con, $sql);if (!$res || mysqli_num_rows($res) === 0) {throw new Exception("No thread found with the given title and fid");}$row = mysqli_fetch_assoc($res);$tid = $row['tid'];// 获取主帖 pid(通常 position = 1)$sql = "SELECT pid FROM pre_forum_post WHERE tid = '{$tid}' AND position = 1 ORDER BY dateline DESC LIMIT 1";$res = mysqli_query($con, $sql);if (!$res || mysqli_num_rows($res) === 0) {throw new Exception("Main post not found for the thread");}$row = mysqli_fetch_assoc($res);$pid = $row['pid'];// 更新帖子内容$sql = "UPDATE pre_forum_post SET message = '{$content}' WHERE pid = '{$pid}'";if (!mysqli_query($con, $sql)) {throw new Exception("Update post content failed: " . mysqli_error($con));}// 更新同步记录表中的 foreign2_idif (isset($data['foreign2_id'])) {// 更新 foreign2_id 字段$sql = "UPDATE pre_forum_post_syncSET foreign2_id = '{$foreign2_id}'WHERE tid = '{$tid}' AND pid = '{$pid}'";} elseif (isset($data['foreign3_id'])) {// 新增 foreign3_id 字段更新逻辑$sql = "UPDATE pre_forum_post_syncSET foreign3_id = '{$foreign3_id}'WHERE tid = '{$tid}' AND pid = '{$pid}'";}if (!mysqli_query($con, $sql)) {throw new Exception("Update sync record failed: " . mysqli_error($con));}$updateFields = [];if (isset($data['foreign2_id'])) {$updateFields[] = 'foreign2_id';}if (isset($data['foreign3_id'])) {$updateFields[] = 'foreign3_id';}echo json_encode(['status' => 'success','data' => ['tid' => $tid,'pid' => $pid,'updated_fields' => $updateFields,'message' => 'Post updated successfully']]);} catch (Exception $e) {echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}

使用方法

curl --location 'https://d.example.com/update_by_title.php' \
--header 'AccessKey: my-access-key' \
--header 'AccessSecret: my-access-secret' \
--header 'Content-Type: application/json' \
--data '{"fid": "2","title": "测试标题","content": "这是一个测试内容8888872","foreign3_id": "123456"
}
'

 这里 "foreign3_id": "123456"也可以改成  "foreign2_id": "123456",当有多个采集站时扩充数据库字段修改代码即可。

推送新帖到企微群api

wechat_pusher.php

<?php
date_default_timezone_set('Asia/Shanghai');
header('Content-Type: application/json');// 获取并统一转换请求头为小写
$headers = array_change_key_case(getallheaders(), CASE_LOWER);// 检查 accesskey 和 accesssecret 是否存在且匹配
if (!isset($headers['accesskey']) || !isset($headers['accesssecret'])) {http_response_code(403);die(json_encode(['status' => 'error', 'message' => 'Missing AccessKey or AccessSecret']));
}define('ACCESS_KEY', 'my-access-key');
define('ACCESS_SECRET', 'my-access-secret');if ($headers['accesskey'] !== ACCESS_KEY || $headers['accesssecret'] !== ACCESS_SECRET) {http_response_code(403);die(json_encode(['status' => 'error', 'message' => 'Invalid AccessKey or AccessSecret']));
}// 微信机器人 Webhook 地址(替换为你的)
$webhook_url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=youruuid';// 数据库连接配置
$con = mysqli_connect('mysql', 'root', 'pass', 'ultrax', 3306);
if (!$con) {die(json_encode(['status' => 'error', 'message' => 'Database connection failed']));
}// 获取当前时间和30分钟前的时间
$now = date('Y-m-d H:i:s');
$thirty_minutes_ago = date('Y-m-d H:i:s', strtotime('-30 minutes'));// 查询最近30分钟发布的帖子(只取主帖 position = 1)
$sql = "SELECTp.tid,p.pid,t.subject AS title,p.message AS contentFROMpre_forum_post pJOINpre_forum_thread t ON p.tid = t.tidWHEREp.dateline >= UNIX_TIMESTAMP('$thirty_minutes_ago')AND p.position = 1AND NOT EXISTS (SELECT 1 FROM pre_wechat_push_log lWHERE l.tid = p.tid AND l.pid = p.pid)
";$res = mysqli_query($con, $sql);if (!$res) {die(json_encode(['status' => 'error', 'message' => 'Query failed: ' . mysqli_error($con)]));
}while ($row = mysqli_fetch_assoc($res)) {$title = trim($row['title']);$content = trim($row['content']);// 屏蔽以“这是主题内容:”开头的帖子if (mb_strpos($content, '这是主题内容:') === 0) {continue; // 跳过该帖子}// 替换由反引号包裹的 [img] 标签,并转换为 [图片1](url) 的 Markdown 链接格式$imgCount = 0;$content = preg_replace_callback('/`\[img\](.+?)\[\/img\]`/is',  // 匹配反引号内的 [img]标签[/img]function ($matches) use (&$imgCount) {$imgCount++;$url = htmlspecialchars($matches[1]);return "[图片{$imgCount}]({$url})";},$content);// 构造微信消息体(Markdown 格式)$message = "### 新帖子通知\n\n**标题:** {$title}\n\n**内容预览:**\n\n" . mb_substr($content, 0, 200);$postData = json_encode(['msgtype' => 'markdown','markdown' => ['content' => $message]]);// 发送请求$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $webhook_url);curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);$response = curl_exec($ch);curl_close($ch);// 记录推送日志$insert_sql = "INSERT INTO pre_wechat_push_log (tid, pid)VALUES ({$row['tid']}, {$row['pid']})";if (!mysqli_query($con, $insert_sql)) {error_log("Failed to log push record for tid={$row['tid']} pid={$row['pid']}: " . mysqli_error($con));}
}echo json_encode(['status' => 'success', 'message' => 'Push job completed']);
?>

调用

curl --location --request POST 'https://d.example.com/wechat_pusher.php' \
--header 'AccessKey: my-access-key' \
--header 'AccessSecret: my-access-secret' \
--header 'Content-Type: application/json'

这里用post get都可以。

把这3个php文件防弹discuz安装目录下,后续就可以通过调用API实现帖子同步和新帖通知操作了。

相关文章:

  • QEMU源码全解析 —— 块设备虚拟化(24)
  • eBPF系列--BCC中提供的BPF maps高级抽象如何映射到内核的BPF maps?
  • 第23讲、Odoo18 二开常见陷阱
  • 如何思考?思维篇
  • 数学:”度量空间”了解一下?
  • STM标准库-TIM旋转编码器
  • Spark流水线+Gravitino+Marquez数据血缘采集
  • 1 Studying《蓝牙核心规范5.3》
  • MyBatis原理剖析(二)
  • DeepSeek10-RAG相关模型知识说明
  • 编程实验篇--线性探测哈希表
  • 5.子网划分及分片相关计算
  • Apache Spark详解
  • 三十五、面向对象底层逻辑-Spring MVC中AbstractXlsxStreamingView的设计
  • Java求职者面试:微服务技术与源码原理深度解析
  • Spring Cloud Alibaba Seata安装+微服务实战
  • SpringCloud——微服务
  • 微服务体系下将环境流量路由到开发本机
  • (五)Linux性能优化-CPU-性能优化
  • 正点原子[第三期]Arm(iMX6U)Linux移植学习笔记-12.1 Linux内核启动流程简介
  • 网站动态图怎么做/宁波seo在线优化方案
  • 做网站联盟要多少钱/百度最怕哪个部门去投诉
  • 做pc端网站服务/哔哩哔哩b站在线看免费
  • 请求php网站数据库/企业网站建设推广
  • 濉溪建设投资网站/重庆店铺整站优化
  • 专业做外贸网站/网络营销策略分析