From 0213db84da2f8084a86821001453131342cd9b00 Mon Sep 17 00:00:00 2001
From: Tiaotiao <65841827@qq.com>
Date: Mon, 27 Apr 2026 12:21:41 +0800
Subject: [PATCH 1/8] Add follow API endpoint for user actions
---
api/follow.php | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
create mode 100644 api/follow.php
diff --git a/api/follow.php b/api/follow.php
new file mode 100644
index 0000000..81e627d
--- /dev/null
+++ b/api/follow.php
@@ -0,0 +1,52 @@
+ false, 'Message' => 'Not logged in']);
+ exit;
+}
+
+$token = $_SESSION['token'];
+$authCode = $_SESSION['authCode'];
+
+$targetId = $_GET['id'] ?? '';
+$action = isset($_GET['action']) ? (int)$_GET['action'] : 1; // 1=关注, 0=取消
+
+if (empty($targetId)) {
+ echo json_encode(['Success' => false, 'Message' => 'Missing user id']);
+ exit;
+}
+
+$url = 'http://nlm-api-cn.turtlesim.com/Users/Follow';
+$body = json_encode([
+ 'TargetID' => $targetId,
+ 'Action' => $action
+]);
+$headers = [
+ 'Content-Type: application/json',
+ 'x-API-Token: ' . $token,
+ 'x-API-AuthCode: ' . $authCode,
+];
+
+$ch = curl_init($url);
+curl_setopt_array($ch, [
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HEADER => true,
+ CURLOPT_POST => true,
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_POSTFIELDS => $body,
+ CURLOPT_TIMEOUT => 10,
+]);
+
+$response = curl_exec($ch);
+$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+$bodyResp = substr($response, $headerSize);
+$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+curl_close($ch);
+
+$data = json_decode($bodyResp, true);
+$success = ($httpCode == 200 && isset($data['Status']) && $data['Status'] == 200);
+
+echo json_encode(['Success' => $success]);
+?>
From e178c75dcf932d688c1452807fb9734d32fb82b4 Mon Sep 17 00:00:00 2001
From: Tiaotiao <65841827@qq.com>
Date: Mon, 27 Apr 2026 12:22:33 +0800
Subject: [PATCH 2/8] Add get_comments.php for fetching comments
---
api/get_comments.php | 57 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 api/get_comments.php
diff --git a/api/get_comments.php b/api/get_comments.php
new file mode 100644
index 0000000..02e6818
--- /dev/null
+++ b/api/get_comments.php
@@ -0,0 +1,57 @@
+ false, 'Message' => 'Not logged in']);
+ exit;
+}
+
+$token = $_SESSION['token'];
+$authCode = $_SESSION['authCode'];
+
+$targetId = $_GET['id'] ?? '';
+$targetType = $_GET['type'] ?? 'User';
+$skip = (int)($_GET['skip'] ?? 0);
+$take = (int)($_GET['take'] ?? 20);
+
+if (empty($targetId)) {
+ echo json_encode(['Success' => false, 'Message' => 'Missing target id']);
+ exit;
+}
+
+$url = 'http://nlm-api-cn.turtlesim.com/Messages/GetComments';
+$body = json_encode([
+ 'TargetID' => $targetId,
+ 'TargetType' => $targetType,
+ 'Skip' => $skip,
+ 'Take' => $take
+]);
+
+$headers = [
+ 'Content-Type: application/json',
+ 'x-API-Token: ' . $token,
+ 'x-API-AuthCode: ' . $authCode,
+];
+
+$ch = curl_init($url);
+curl_setopt_array($ch, [
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HEADER => true,
+ CURLOPT_POST => true,
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_POSTFIELDS => $body,
+ CURLOPT_TIMEOUT => 10,
+]);
+
+$response = curl_exec($ch);
+$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+$bodyResp = substr($response, $headerSize);
+$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+curl_close($ch);
+
+$data = json_decode($bodyResp, true);
+$success = ($httpCode == 200 && isset($data['Status']) && $data['Status'] == 200);
+
+echo json_encode(['Success' => $success, 'Data' => $data['Data'] ?? null, 'Message' => $data['Message'] ?? '']);
+?>
From cf19e26a90c61af1969402e9831993dff821eba2 Mon Sep 17 00:00:00 2001
From: Tiaotiao <65841827@qq.com>
Date: Mon, 27 Apr 2026 12:57:17 +0800
Subject: [PATCH 3/8] =?UTF-8?q?=E5=88=9B=E5=BB=BA=20get=5Frelations.php?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
api/get_relations.php | 57 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 api/get_relations.php
diff --git a/api/get_relations.php b/api/get_relations.php
new file mode 100644
index 0000000..466a18c
--- /dev/null
+++ b/api/get_relations.php
@@ -0,0 +1,57 @@
+ false, 'Message' => 'Not logged in']);
+ exit;
+}
+
+$token = $_SESSION['token'];
+$authCode = $_SESSION['authCode'];
+
+$userId = $_GET['id'] ?? '';
+$displayType = (int)($_GET['displayType'] ?? 1); // 1=Follower, 2=Following
+$skip = (int)($_GET['skip'] ?? 0);
+$take = (int)($_GET['take'] ?? 20);
+
+if (empty($userId)) {
+ echo json_encode(['Success' => false, 'Message' => 'Missing user id']);
+ exit;
+}
+
+$url = 'http://nlm-api-cn.turtlesim.com/Users/GetRelations';
+$body = json_encode([
+ 'UserID' => $userId,
+ 'DisplayType' => $displayType,
+ 'Skip' => $skip,
+ 'Take' => $take
+]);
+
+$headers = [
+ 'Content-Type: application/json',
+ 'x-API-Token: ' . $token,
+ 'x-API-AuthCode: ' . $authCode,
+];
+
+$ch = curl_init($url);
+curl_setopt_array($ch, [
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HEADER => true,
+ CURLOPT_POST => true,
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_POSTFIELDS => $body,
+ CURLOPT_TIMEOUT => 10,
+]);
+
+$response = curl_exec($ch);
+$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+$bodyResp = substr($response, $headerSize);
+$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+curl_close($ch);
+
+$data = json_decode($bodyResp, true);
+$success = ($httpCode == 200 && isset($data['Status']) && $data['Status'] == 200);
+
+echo json_encode(['Success' => $success, 'Data' => $data['Data'] ?? null, 'Message' => $data['Message'] ?? '']);
+?>
\ No newline at end of file
From 59a5cb41d3e49096916d6fac1ba424fc66adc4a3 Mon Sep 17 00:00:00 2001
From: Tiaotiao <65841827@qq.com>
Date: Mon, 27 Apr 2026 12:58:13 +0800
Subject: [PATCH 4/8] =?UTF-8?q?=E5=88=9B=E5=BB=BA=20visit.php?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
api/visit.php | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
create mode 100644 api/visit.php
diff --git a/api/visit.php b/api/visit.php
new file mode 100644
index 0000000..15abe1b
--- /dev/null
+++ b/api/visit.php
@@ -0,0 +1,49 @@
+ false]);
+ exit;
+}
+
+$token = $_SESSION['token'];
+$authCode = $_SESSION['authCode'];
+
+$contentId = $_GET['id'] ?? '';
+$category = $_GET['category'] ?? '';
+
+if (empty($contentId) || empty($category)) {
+ echo json_encode(['Success' => false]);
+ exit;
+}
+
+$baseUrl = 'http://nlm-api-cn.turtlesim.com/';
+$url = $baseUrl . 'Contents/VisitExperiment';
+
+$requestData = [
+ 'SummaryID' => $contentId,
+ 'Category' => $category
+];
+
+$headers = [
+ "Content-Type: application/json",
+ "x-API-Token: " . $token,
+ "x-API-AuthCode: " . $authCode,
+];
+
+$ch = curl_init($url);
+curl_setopt_array($ch, [
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HEADER => true,
+ CURLOPT_POST => true,
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_POSTFIELDS => json_encode($requestData),
+ CURLOPT_TIMEOUT => 5,
+]);
+
+curl_exec($ch);
+curl_close($ch);
+
+echo json_encode(['Success' => true]);
+?>
\ No newline at end of file
From b3fb7c1b1781f0987e493756e4f16817c517f95d Mon Sep 17 00:00:00 2001
From: Tiaotiao <65841827@qq.com>
Date: Mon, 27 Apr 2026 12:58:49 +0800
Subject: [PATCH 5/8] =?UTF-8?q?=E5=88=9B=E5=BB=BA=20star.php?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
api/star.php | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
create mode 100644 api/star.php
diff --git a/api/star.php b/api/star.php
new file mode 100644
index 0000000..ce44740
--- /dev/null
+++ b/api/star.php
@@ -0,0 +1,65 @@
+ false, 'Message' => 'Not logged in']);
+ exit;
+}
+
+$token = $_SESSION['token'];
+$authCode = $_SESSION['authCode'];
+
+$contentId = $_GET['id'] ?? '';
+$category = $_GET['category'] ?? '';
+$action = $_GET['action'] ?? '';
+$status = isset($_GET['status']) ? (bool)$_GET['status'] : true;
+
+if (empty($contentId) || empty($category) || !in_array($action, ['star', 'support'])) {
+ echo json_encode(['Success' => false, 'Message' => 'Invalid parameters']);
+ exit;
+}
+
+$baseUrl = 'http://nlm-api-cn.turtlesim.com/';
+$url = $baseUrl . 'Contents/StarContent';
+
+$starType = $action === 'star' ? 0 : 1; // 0=Star, 1=Support
+
+$requestData = [
+ 'ContentID' => $contentId,
+ 'Category' => $category,
+ 'Type' => $starType,
+ 'Status' => $status
+];
+
+$headers = [
+ "Content-Type: application/json",
+ "x-API-Token: " . $token,
+ "x-API-AuthCode: " . $authCode,
+];
+
+$ch = curl_init($url);
+curl_setopt_array($ch, [
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HEADER => true,
+ CURLOPT_POST => true,
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_POSTFIELDS => json_encode($requestData),
+ CURLOPT_TIMEOUT => 10,
+]);
+
+$response = curl_exec($ch);
+$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+$body = substr($response, $headerSize);
+$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+curl_close($ch);
+
+$data = json_decode($body, true);
+$success = ($httpCode == 200 && isset($data['Status']) && $data['Status'] == 200);
+
+echo json_encode([
+ 'Success' => $success,
+ 'Data' => $data
+]);
+?>
\ No newline at end of file
From 7e2acbef955ee5f89d0ab246c95cd5d766a32e62 Mon Sep 17 00:00:00 2001
From: Tiaotiao <65841827@qq.com>
Date: Mon, 27 Apr 2026 12:59:38 +0800
Subject: [PATCH 6/8] =?UTF-8?q?=E5=88=9B=E5=BB=BA=20post=5Fcomment.php?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
api/post_comment.php | 60 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
create mode 100644 api/post_comment.php
diff --git a/api/post_comment.php b/api/post_comment.php
new file mode 100644
index 0000000..5ad4a89
--- /dev/null
+++ b/api/post_comment.php
@@ -0,0 +1,60 @@
+ false, 'Message' => 'Not logged in']);
+ exit;
+}
+
+$token = $_SESSION['token'];
+$authCode = $_SESSION['authCode'];
+
+// 获取 POST 请求体
+$input = json_decode(file_get_contents('php://input'), true);
+$targetId = $input['TargetID'] ?? '';
+$targetType = $input['TargetType'] ?? 'User';
+$content = $input['Content'] ?? '';
+$replyId = $input['ReplyID'] ?? '';
+
+if (empty($targetId) || empty($content)) {
+ echo json_encode(['Success' => false, 'Message' => 'Missing required fields']);
+ exit;
+}
+
+$url = 'http://nlm-api-cn.turtlesim.com/Messages/PostComment';
+$body = json_encode([
+ 'TargetID' => $targetId,
+ 'TargetType' => $targetType,
+ 'Content' => $content,
+ 'ReplyID' => $replyId,
+ 'Language' => 'Chinese'
+]);
+
+$headers = [
+ 'Content-Type: application/json',
+ 'x-API-Token: ' . $token,
+ 'x-API-AuthCode: ' . $authCode,
+];
+
+$ch = curl_init($url);
+curl_setopt_array($ch, [
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HEADER => true,
+ CURLOPT_POST => true,
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_POSTFIELDS => $body,
+ CURLOPT_TIMEOUT => 10,
+]);
+
+$response = curl_exec($ch);
+$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+$bodyResp = substr($response, $headerSize);
+$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+curl_close($ch);
+
+$data = json_decode($bodyResp, true);
+$success = ($httpCode == 200 && isset($data['Status']) && $data['Status'] == 200);
+
+echo json_encode(['Success' => $success, 'Data' => $data['Data'] ?? null, 'Message' => $data['Message'] ?? '']);
+?>
\ No newline at end of file
From bf559b1652f304442a981f74830620bd250ba540 Mon Sep 17 00:00:00 2001
From: Tiaotiao <65841827@qq.com>
Date: Mon, 27 Apr 2026 13:00:28 +0800
Subject: [PATCH 7/8] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20med.php?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
med.php | 981 +++++++++++++++++++++-----------------------------------
1 file changed, 370 insertions(+), 611 deletions(-)
diff --git a/med.php b/med.php
index a846cf4..cf38c52 100644
--- a/med.php
+++ b/med.php
@@ -11,12 +11,12 @@
if (isset($_SESSION['responseBody'])) {
$dt = is_string($_SESSION['responseBody']) ? json_decode($_SESSION['responseBody'], true) : $_SESSION['responseBody'];
} else {
- $redirectUrl = '/getv.php?r=' . urlencode('med.php?category=' . $category . '&id=' . $contentId . '&type=' . $type);
- header('Location: ' . $redirectUrl);
- exit;
+ $redirectUrl = '/getv.php?r=' . urlencode('med.php?category=' . $category . '&id=' . $contentId . '&type=' . $type);
+ header('Location: ' . $redirectUrl);
+ exit;
}
-// 获取作品详情的函数
+// 获取作品详情
function getContentSummary($contentId, $category, $token, $authCode) {
$baseUrl = 'http://nlm-api-cn.turtlesim.com/';
$url = $baseUrl . 'Contents/GetSummary';
@@ -48,7 +48,6 @@ function getContentSummary($contentId, $category, $token, $authCode) {
]);
$response = curl_exec($ch);
-
if ($response === false) {
curl_close($ch);
return null;
@@ -57,13 +56,90 @@ function getContentSummary($contentId, $category, $token, $authCode) {
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$body = substr($response, $headerSize);
-
curl_close($ch);
$responseData = json_decode($body, true);
return json_last_error() === JSON_ERROR_NONE ? $responseData : null;
}
+// 获取改编列表
+function getDerivatives($contentId, $category, $token, $authCode) {
+ $baseUrl = 'http://nlm-api-cn.turtlesim.com/';
+ $url = $baseUrl . 'Contents/GetDerivatives';
+
+ $requestData = [
+ 'ContentID' => $contentId,
+ 'Category' => $category,
+ 'Language' => 'Chinese',
+ 'WithSummary' => true
+ ];
+
+ $headers = [
+ "Content-Type: application/json",
+ "Accept: application/json",
+ "Accept-Language: zh-CN",
+ "x-API-Token: " . $token,
+ "x-API-AuthCode: " . $authCode,
+ ];
+
+ $ch = curl_init($url);
+ curl_setopt_array($ch, [
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HEADER => true,
+ CURLOPT_POST => true,
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_POSTFIELDS => json_encode($requestData),
+ CURLOPT_FOLLOWLOCATION => true,
+ CURLOPT_TIMEOUT => 15,
+ ]);
+
+ $response = curl_exec($ch);
+ $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+ $body = substr($response, $headerSize);
+ curl_close($ch);
+
+ $data = json_decode($body, true);
+ return json_last_error() === JSON_ERROR_NONE ? $data : null;
+}
+
+// 检查是否已收藏(Star)
+function isContentStarred($contentId, $category, $token, $authCode) {
+ $baseUrl = 'http://nlm-api-cn.turtlesim.com/';
+ $url = $baseUrl . 'Contents/IsStarred';
+
+ $requestData = [
+ 'ContentID' => $contentId,
+ 'Category' => $category,
+ ];
+
+ $headers = [
+ "Content-Type: application/json",
+ "x-API-Token: " . $token,
+ "x-API-AuthCode: " . $authCode,
+ ];
+
+ $ch = curl_init($url);
+ curl_setopt_array($ch, [
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_HEADER => true,
+ CURLOPT_POST => true,
+ CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_POSTFIELDS => json_encode($requestData),
+ CURLOPT_TIMEOUT => 10,
+ ]);
+
+ $response = curl_exec($ch);
+ $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
+ $body = substr($response, $headerSize);
+ curl_close($ch);
+
+ $data = json_decode($body, true);
+ if ($data && isset($data['Data']) && is_bool($data['Data'])) {
+ return $data['Data'];
+ }
+ return false;
+}
+
// 获取作品详情
$contentData = null;
if (!empty($contentId) && !empty($category)) {
@@ -76,6 +152,19 @@ function getContentSummary($contentId, $category, $token, $authCode) {
}
$content = $contentData['Data'] ?? null;
+
+// 获取改编列表
+$remixes = [];
+$isStarred = false;
+if ($content && $contentId && $category) {
+ $derivativesData = getDerivatives($contentId, $category, $token, $authCode);
+ if ($derivativesData && isset($derivativesData['Data']['Experiments']['Children-Models'])) {
+ $remixes = $derivativesData['Data']['Experiments']['Children-Models'];
+ }
+ // 获取初始收藏状态
+ $isStarred = isContentStarred($contentId, $category, $token, $authCode);
+}
+
// 处理日期格式
function formatDate($timestamp) {
return date('Y年m月d日', $timestamp / 1000);
@@ -91,125 +180,66 @@ function formatDate($timestamp) {
'visual' => '可视化编程',
'knowledge' => '实验知识库'
];
-
$pageTitle = $pageTitles[$type] ?? '作品详情';
+
// 自定义标签解析器类
class CustomTagParser {
-
- /**
- * 解析包含自定义标签的文本
- */
public static function parse(string $text): string {
- if (empty($text)) {
- return '';
- }
-
- // 先转义HTML特殊字符,防止XSS攻击
+ if (empty($text)) return '';
$text = htmlspecialchars($text);
-
- // 先解析Markdown标题(在换行转换之前)
$text = self::parseMarkdownHeadings($text);
-
- // 将换行符转换为
$text = nl2br($text);
- // 解析自定义标签
$patterns = [
- // user标签 - 保持原有格式
'/<user=([a-f0-9]+)>(.*?)<\/user>/i',
- // experiment标签 - 跳转链接
'/<experiment=([a-f0-9]+)>(.*?)<\/experiment>/i',
- // discussion标签 - 跳转链接
'/<discussion=([a-f0-9]+)>(.*?)<\/discussion>/i',
- // model标签 - 跳转链接
'/<model=([a-f0-9]+)>(.*?)<\/model>/i',
- // external标签 - 外部链接
'/<external=([^&]+)>(.*?)<\/external>/i',
- // size标签
'/<size=([^&]+)>(.*?)<\/size>/i',
- // color标签 - 新增
'/<color=([^&]+)>(.*?)<\/color>/i',
- // b标签
'/<b>(.*?)<\/b>/i',
- // i标签
'/<i>(.*?)<\/i>/i',
- // a标签 - 深蓝色标签
'/<a>(.*?)<\/a>/i'
];
$replacements = [
- // user标签
'$2',
- // experiment标签
'$2',
- // discussion标签
'$2',
- // model标签
'$2',
- // external标签
'$2',
- // size标签
'$2',
- // color标签 - 新增
'$2',
- // b标签
'$1',
- // i标签
'$1',
- // a标签
'$1'
];
$text = preg_replace($patterns, $replacements, $text);
-
- // 解析简单的Markdown格式
$text = self::parseSimpleMarkdown($text);
-
return $text;
}
- /**
- * 解析Markdown标题
- */
private static function parseMarkdownHeadings(string $text): string {
- // 支持1-6级标题
- // 一级标题: # 标题
$text = preg_replace('/^# (.+)$/m', '$1', $text);
-
- // 二级标题: ## 标题
$text = preg_replace('/^## (.+)$/m', '$1', $text);
-
- // 三级标题: ### 标题
- $text = preg_replace('/^### (.+)$/m', '$1', $text);
-
- // 四级标题: #### 标题
+ $text = preg_replace('/^### (.+)$/m', '$1', $text);
$text = preg_replace('/^#### (.+)$/m', '$1', $text);
-
return $text;
}
- /**
- * 解析简单的Markdown格式
- */
private static function parseSimpleMarkdown(string $text): string {
- // 粗体
$text = preg_replace('/\*\*(.*?)\*\*/', '$1', $text);
-
- // 斜体
$text = preg_replace('/\*(.*?)\*/', '$1', $text);
-
- // 删除线
$text = preg_replace('/~~(.*?)~~/', '$1', $text);
-
- // 行内代码
$text = preg_replace('/`([^`]+)`/', '$1', $text);
-
return $text;
}
}
?>
-
+