From a1e8ffcfd0fb8f759c0c1754d6e1dfe40a539d1e Mon Sep 17 00:00:00 2001
From: tianya <yanghuxiao@vip.qq.com>
Date: Wed, 26 Mar 2025 21:23:22 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AF=8C=E6=96=87=E6=9C=AC=E7=BC=96=E8=BE=91?=
 =?UTF-8?q?=E5=99=A8=E6=94=AF=E6=8C=81AI=E6=93=8D=E4=BD=9C=EF=BC=8C?=
 =?UTF-8?q?=E9=9C=80=E8=A6=81AI=E5=8A=A9=E6=89=8B1.0.3=E7=89=88=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/admin/ai_dialog.php                       | 231 ++++++++++++++++++
 src/admin/api.php                             |  28 ++-
 src/admin/archives_do.php                     |   4 +-
 src/admin/inc/inc_archives_functions.php      |   4 +-
 .../plugins/dedebizai/icons/dedebizai.png     | Bin 0 -> 469 bytes
 .../ckeditor/plugins/dedebizai/plugin.js      |  28 +++
 src/system/inc/inc_fun_funAdmin.php           |   2 +-
 7 files changed, 290 insertions(+), 7 deletions(-)
 create mode 100644 src/admin/ai_dialog.php
 create mode 100644 src/static/ckeditor/plugins/dedebizai/icons/dedebizai.png
 create mode 100644 src/static/ckeditor/plugins/dedebizai/plugin.js

diff --git a/src/admin/ai_dialog.php b/src/admin/ai_dialog.php
new file mode 100644
index 00000000..2ebbc971
--- /dev/null
+++ b/src/admin/ai_dialog.php
@@ -0,0 +1,231 @@
+<?php
+/**
+ * 富文本AI对话框
+ *
+ * @version        $id:ai_dialog.php 2025 tianya $
+ * @package        DedeBIZ.Dialog
+ * @copyright      Copyright (c) 2022 DedeBIZ.COM
+ * @license        GNU GPL v2 (https://www.dedebiz.com/license)
+ * @link           https://www.dedebiz.com
+ */
+require_once(dirname(__FILE__) . "/config.php");
+
+if (empty($f)) {
+    $f = 'form1.enclosure';
+}
+if (empty($comeback)) {
+    $comeback = '';
+}
+$addparm = '';
+if (!empty($CKEditor)) {
+    $addparm = '&CKEditor=' . $CKEditor;
+}
+if (!empty($CKEditorFuncNum)) {
+    $addparm .= '&CKEditorFuncNum=' . $CKEditorFuncNum;
+}
+if (!empty($noeditor)) {
+    $addparm .= '&noeditor=yes';
+}
+?>
+<!DOCTYPE html>
+<html>
+
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
+    <title>AI提示词对话框</title>
+    <link rel="stylesheet" href="/static/web/css/font-awesome.min.css">
+    <link rel="stylesheet" href="/static/web/css/bootstrap.min.css">
+    <link rel="stylesheet" href="/static/web/css/admin.css">
+    <script src="/static/web/js/jquery.min.js"></script>
+    <script src="/static/web/js/bootstrap.min.js"></script>
+    <script src="/static/web/js/admin.main.js"></script>
+</head>
+
+<body class="p-3">
+    <div class="card shadow-sm">
+        <div class="card-header">小德AI助手:内容处理</div>
+        <div class="card-body">
+            <div class="form-group">
+                <div class="alert alert-warning mb-0" role="alert">
+                    <span>处理过程中请勿关闭当前对话框,否则富文本处理将终止</span>
+                </div>
+            </div>
+            <div class="form-group">
+                <textarea id="prompt" class="form-control" style="height:160px" placeholder="请输入内容处理要求,例如:我需要将内容润色下,希望更专业"></textarea>
+            </div>
+            <div class="form-group">
+                <label for="modelid" class="form-label">选择模型</label>
+                <select id="modelid" class="form-control">
+                    <?php
+                    $dsql->SetQuery("SELECT AM.*,A.title as aititle FROM `#@__ai_model` AM LEFT JOIN `#@__ai` A ON A.id = AM.aiid ORDER BY AM.sortrank ASC,AM.id DESC");
+                    $dsql->Execute();
+                    while ($row = $dsql->GetObject()) {
+                    ?>
+                        <option value="<?php echo $row->id; ?>" <?php echo $row->isdefault == 1 ? ' selected' : ''; ?>><?php echo $row->model; ?> <?php echo $row->aititle; ?></option>
+                    <?php
+                    }
+                    ?>
+                </select>
+            </div>
+            <div class="form-group">
+                <button type="button" id="btnAIAction" class="btn btn-success btn-sm">确定</button>
+            </div>
+        </div>
+    </div>
+    <script>
+        $("#btnAIAction").click(async function() {
+            let body = window.opener.CKEDITOR.instances["<?php echo $f ?>"].getData();
+            console.log(body);
+            let prompt = document.getElementById("prompt").value;
+            let modelid = document.getElementById("modelid").value;
+            let req = await fetch(`api.php?action=get_ai_server&pname=body_edit&modelid=${modelid}&prompt=${prompt}`);
+            let resp = await req.json();
+            if (resp.code !== 0) {
+                ShowMsg("获取服务器地址失败");
+                return
+            }
+
+            let req2 = await fetch(`api.php?action=get_setbody_url`);
+            let resp2 = await req2.json();
+            if (resp2.code !== 0) {
+                ShowMsg("获取服务器地址失败");
+                return
+            }
+            let req3 = await fetch(resp2.data, {
+                method: 'POST',
+                headers: {
+                    'Content-Type': 'application/x-www-form-urlencoded'
+                },
+                body: new URLSearchParams({
+                    body: body
+                })
+            });
+            let resp3 = await req3.json();
+            if (resp3.code !== 0) {
+                ShowMsg("提交原始内容失败");
+                return
+            }
+            let eventSource = new EventSource(resp.data);
+            //新增状态跟踪变量
+            let currentKey = null;
+            let tagBuffer = "";
+            let isClosingTag = false;
+            $("#mdlAI").modal('hide');
+            window.opener.CKEDITOR.instances["<?php echo $f ?>"].getCommand('openDedeBIZAi').disable();
+            $("#btnAIAction").attr("disabled", "disabled");
+            $("#prompt").attr("disabled", "disabled");
+            $("#modelid").attr("disabled", "disabled");
+            prompt = "";
+            let bodyHtml = "";
+            let lastChar = "";
+            eventSource.onmessage = (event) => {
+                const chars = event.data.split('');
+                chars.forEach(char => {
+                    if (lastChar === '\\' && char === 'r') {
+                        char = '<br>'; //替换为br标签
+                        lastChar = ""; //清空追踪字符
+                    } else {
+                        lastChar = char; //记录当前字符
+                    }
+                    if (char === '\\') {
+                        return; //如果是反斜杠,跳过处理
+                    }
+                    if (currentKey) {
+                        if (char === '{') {
+                            isClosingTag = true;
+                            tagBuffer = '{';
+                            return;
+                        }
+                        if (isClosingTag) {
+                            tagBuffer += char;
+                            if (tagBuffer === `{/${currentKey}}`) {
+                                if (currentKey == "content") {
+                                    window.opener.CKEDITOR.instances["<?php echo $f ?>"].setReadOnly(false);
+                                    bodyHtml = "";
+                                } else {
+                                    const input = document.querySelector(`[name="${currentKey}"]`);
+                                    if (input) $(input).prop("disabled", false).removeClass("disabled"); //恢复输入状态
+                                }
+                                currentKey = null;
+                                isClosingTag = false;
+                                tagBuffer = "";
+                                return;
+                            }
+                            if (!`{/${currentKey}}`.startsWith(tagBuffer)) {
+                                if (currentKey == "content") {
+                                    bodyHtml += tagBuffer;
+                                    console.log(bodyHtml);
+                                    window.opener.CKEDITOR.instances["<?php echo $f ?>"].setData(bodyHtml)
+                                } else {
+                                    const input = document.querySelector(`[name="${currentKey}"]`);
+                                    if (input) input.value += tagBuffer;
+                                }
+                                isClosingTag = false;
+                                tagBuffer = "";
+                            }
+                        } else {
+                            if (currentKey == "content") {
+                                bodyHtml += char;
+                                window.opener.CKEDITOR.instances["<?php echo $f ?>"].setData(bodyHtml)
+                            } else {
+                                const input = document.querySelector(`[name="${currentKey}"]`);
+                                if (input) {
+                                    input.value += char;
+                                    input.scrollTop = input.scrollHeight; //滚动到底部
+                                }
+                            }
+                        }
+                    } else {
+                        if (char === '{') {
+                            tagBuffer = '{';
+                        } else if (tagBuffer.startsWith('{')) {
+                            tagBuffer += char;
+                            if (char === '}') {
+                                const match = tagBuffer.match(/{([^>]+)}/);
+                                if (match) {
+                                    currentKey = match[1];
+                                    if (currentKey == "content") {
+                                        window.opener.CKEDITOR.instances["<?php echo $f ?>"].setReadOnly(true);
+                                    } else {
+                                        const input = document.querySelector(`[name="${currentKey}"]`);
+                                        if (input) {
+                                            $(input).prop("disabled", true).addClass("disabled"); //仅禁用当前输入框
+                                            input.value = "";
+                                        }
+                                    }
+                                }
+                                tagBuffer = "";
+                            }
+                        }
+                    }
+                });
+            };
+            eventSource.onerror = (error) => {
+                if (error.target.readyState === EventSource.CONNECTING) {
+                    ShowMsg("连接失败,请确保您已开启并正确配置了DedeBIZ小德AI助手。 <a class='text-success' href='https://www.dedebiz.com/ai?from=dedebiz' target='_blank'>如何配置?</a>");
+                } else if (typeof error.data !== "undefined" && error.data !== "" && error.target.readyState !== EventSource.CLOSED) {
+                    ShowMsg(error.data);
+                }
+                window.opener.CKEDITOR.instances["<?php echo $f ?>"].getCommand('openDedeBIZAi').enable();
+                $("#btnAI").prop("disabled", false);
+                $("#btnAIAction").prop("disabled", false);
+                $("#prompt").prop("disabled", false);
+                $("#modelid").prop("disabled", false);
+                eventSource.close();
+            };
+            //监听特定事件close
+            eventSource.addEventListener('close', (event) => {
+                console.log('SSE connection closed:', event.data);
+                window.opener.CKEDITOR.instances["<?php echo $f ?>"].getCommand('openDedeBIZAi').enable();
+                $("#btnAIAction").prop("disabled", false);
+                $("#prompt").prop("disabled", false);
+                $("#modelid").prop("disabled", false);
+                eventSource.close(); //关闭连接
+                window.opener.CKEDITOR.instances["<?php echo $f ?>"].setReadOnly(false);
+            });
+        });
+    </script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/src/admin/api.php b/src/admin/api.php
index 7843cd86..66d89b33 100644
--- a/src/admin/api.php
+++ b/src/admin/api.php
@@ -16,7 +16,7 @@ require_once(DEDEINC.'/userlogin.class.php');
 @set_time_limit(0);
 AjaxHead();
 helper('cache');
-$action = isset($action) && in_array($action, array('is_need_check_code', 'has_new_version', 'get_changed_files', 'update_backup', 'get_update_versions', 'update', 'upload_image','get_ai_server')) ? $action  : '';
+$action = isset($action) && in_array($action, array('is_need_check_code', 'has_new_version', 'get_changed_files', 'update_backup', 'get_update_versions', 'update', 'upload_image', 'get_ai_server', 'get_setbody_url')) ? $action  : '';
 $curDir = dirname(GetCurUrl());//当前目录
 /**
  * 登录鉴权
@@ -74,7 +74,7 @@ if ($action === 'is_need_check_code') {
         $unQueryVer[] = "6.2.0";
     }
     $row = $dsql->GetOne("SELECT COUNT(*) as dd FROM `#@__sysconfig` WHERE varname = 'cfg_bizcore_api'");
-    if ($row['dd'] == 0) {
+    if (isset($row['dd']) && $row['dd'] == 0) {
         $unQueryVer[] = "6.2.3";
     }
     if (!$dsql->IsTable("#@__sys_payment")) {
@@ -83,6 +83,13 @@ if ($action === 'is_need_check_code') {
     if (!TableHasField("#@__arctype", "apienabled")) {
         $unQueryVer[] = "6.2.7";
     }
+    if (!$dsql->IsTable("#@__ai")) {
+        $unQueryVer[] = "6.5.0";
+    }
+    $row = $dsql->GetOne("SELECT COUNT(*) as dd FROM `#@__ai_prompt` WHERE pname = 'body_edit'");
+    if (isset($row['dd']) && $row['dd'] == 0) {
+        $unQueryVer[] = "6.5.1";
+    }
     if (count($unQueryVer) > 0) {
         $upsqls = GetUpdateSQL();
         foreach ($unQueryVer as $vv) {
@@ -459,5 +466,22 @@ if ($action === 'is_need_check_code') {
         "code" => 0,
         "data" => $url,
     ));
+} else if($action === 'get_setbody_url') {
+    $params = $_GET;
+    unset($params['action']);
+    checkLogin();
+    $params['timestamp'] = time(); // 加入时间戳
+    $cuserLogin = new userLogin();
+    $params['adminid'] = $cuserLogin->getUserID(); // 加入时间戳
+    $params['ip'] = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; // 获取客户端IP
+
+    ksort($params); // 按字典序排序
+    $queryString = http_build_query($params); // 生成查询字符串
+    $params['sign'] = md5($queryString . $cfg_ai_apikey); // 计算MD5签名
+    $url = $cfg_ai_server . '/api/setbody?' . http_build_query($params);
+    echo json_encode(array(
+        "code" => 0,
+        "data" => $url,
+    ));
 }
 ?>
\ No newline at end of file
diff --git a/src/admin/archives_do.php b/src/admin/archives_do.php
index 8fdb41f7..a2a416a1 100644
--- a/src/admin/archives_do.php
+++ b/src/admin/archives_do.php
@@ -658,7 +658,7 @@ else if ($dopost == "makekw") {
                     if (strlen($keywords.$k) >= 60) {
                         break;
                     } else {
-                        if (strlen($k) <= 2) continue;
+                        if (strlen($k) <= 2 || $length == 3) continue;
                         $keywords .= $k.',';
                     }
                 }
@@ -666,7 +666,7 @@ else if ($dopost == "makekw") {
                     if (strlen($keywords.$k) >= 60) {
                         break;
                     } else if (!in_array($k, $titleindexs)) {
-                        if (strlen($k) <= 2) continue;
+                        if (strlen($k) <= 2 || $length == 3) continue;
                         $keywords .= $k.',';
                     }
                 }
diff --git a/src/admin/inc/inc_archives_functions.php b/src/admin/inc/inc_archives_functions.php
index d2c14263..5336edfa 100644
--- a/src/admin/inc/inc_archives_functions.php
+++ b/src/admin/inc/inc_archives_functions.php
@@ -560,7 +560,7 @@ function AnalyseHtmlBody($body, &$description, &$litpic, &$keywords, $dtype = ''
                     if (strlen($keywords.$k) >= 60) {
                         break;
                     } else {
-                        if (strlen($k) <= 2) continue;
+                        if (strlen($k) <= 2 || $length == 3) continue;
                         $keywords .= $k.',';
                     }
                 }
@@ -568,7 +568,7 @@ function AnalyseHtmlBody($body, &$description, &$litpic, &$keywords, $dtype = ''
                     if (strlen($keywords.$k) >= 60) {
                         break;
                     } else if (!in_array($k, $titleindexs)) {
-                        if (strlen($k) <= 2) continue;
+                        if (strlen($k) <= 2 || $length == 3) continue;
                         $keywords .= $k.',';
                     }
                 }
diff --git a/src/static/ckeditor/plugins/dedebizai/icons/dedebizai.png b/src/static/ckeditor/plugins/dedebizai/icons/dedebizai.png
new file mode 100644
index 0000000000000000000000000000000000000000..a6f845c6f8087571515184d36845bbb4a4498e25
GIT binary patch
literal 469
zcmV;`0V@89P)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00001b5ch_0olnc
ze*gdg32;bRa{vGf6951U69E94oEQKA0LD;ER7L;)|M2ke;Nal&^z>|OYzqqu0000K
z6cmbzit_UE3=9lUPfwqppP-<io}QjOJUkyCANctA_4W1q{QQ4^e*pmjg@uJ@XJ>VF
zb<NGqfPjF@%gaGQK}t$W>FMcIR8+yi!J3+yR#sMge0;C3ulxJ^TU%SErlwh0S=QFp
zJ3Bj}p`jNS7nYWmn3$NWtE*sOV7<M)r>CcCYHIQE@#^a8?(XhOOH0?+*K~Aretv!v
z6BB)XeJw35DJdx-At3?+0;Hs*VAC`m0001}Nkl<Zc$`hoy$ga+6b0~e>SeE?K_Lht
zBB`OFp@xe7`M|XyS|TVS8iX>`$RP!xCq6&UbopJl9|TAgVG2Ujfj@T0QdAYPA&dwx
z=>wJmPeJ<&OYjNw$#;^&K=mFNF)3iI;97$z8FkM!n31t`{R-x>L|`3DForx}nXS}d
zx{EzlYVJK>D_dcjojJMLy^F`@CU_b8benVtxjua~Y*VqTKGpdH%?dsdA>}Xg00000
LNkvXXu0mjfq*KRP

literal 0
HcmV?d00001

diff --git a/src/static/ckeditor/plugins/dedebizai/plugin.js b/src/static/ckeditor/plugins/dedebizai/plugin.js
new file mode 100644
index 00000000..45387826
--- /dev/null
+++ b/src/static/ckeditor/plugins/dedebizai/plugin.js
@@ -0,0 +1,28 @@
+CKEDITOR.plugins.add("dedebizai", {
+    icons: "dedebizai",
+    init: function (a) {
+        a.addCommand("openDedeBIZAi",
+            {
+                exec: function (a) {
+                    var w = 800;
+                    var h = 600;
+                    var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX;
+                    var dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screenY;
+                
+                    var width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
+                    var height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;
+                
+                    var systemZoom = width / window.screen.availWidth;
+                    var posLeft = (width - w) / 2 / systemZoom + dualScreenLeft;
+                    var posTop = (height - h) / 2 / systemZoom + dualScreenTop;
+                    window.open("./ai_dialog.php?f=" + a.name + "&noeditor=yes", "popUpImagesWin", "scrollbars=yes,resizable=yes,statebar=no,width=800,height=460,left=" + posLeft + ", top=" + posTop);
+                }
+            });
+        a.ui.addButton("DedeBIZAi",
+            {
+                label: "AI助手",
+                command: "openDedeBIZAi",
+                toolbar: "insert"
+            })
+    }
+});
\ No newline at end of file
diff --git a/src/system/inc/inc_fun_funAdmin.php b/src/system/inc/inc_fun_funAdmin.php
index 61d07f38..2a65efcf 100755
--- a/src/system/inc/inc_fun_funAdmin.php
+++ b/src/system/inc/inc_fun_funAdmin.php
@@ -146,7 +146,7 @@ function SpGetEditor($fname, $fvalue, $nheight = "350", $etype = "Basic", $gtype
             if ($GLOBALS['cfg_db_language'] == "utf8mb4") {
                 $emoji = ",emoji";
             }
-            $addConfig = ",{allowedContent:true,pasteFilter:null,filebrowserImageUploadUrl:'./dialog/select_images_post.php',filebrowserUploadUrl:'./dialog/select_media_post.php?ck=1',extraPlugins:'html5video,html5audio,dedepagebreak,ddfilebrowser,mimage,textindent,tabletools,tableresize,tableselection,codesnippet{$emoji}',codeSnippet_theme: 'default'}";
+            $addConfig = ",{allowedContent:true,pasteFilter:null,filebrowserImageUploadUrl:'./dialog/select_images_post.php',filebrowserUploadUrl:'./dialog/select_media_post.php?ck=1',extraPlugins:'html5video,html5audio,dedepagebreak,ddfilebrowser,mimage,dedebizai,textindent,tabletools,tableresize,tableselection,codesnippet{$emoji}',codeSnippet_theme: 'default'}";
         }
         if (defined('DEDEUSER')) {
             $addConfig = ",{filebrowserImageUploadUrl:'api.php?action=upload&type=litpic&ck=1',filebrowserUploadUrl:'api.php?action=upload&type=media&ck=1',extraPlugins:'html5video,html5audio,textindent',filebrowserImageBrowseDisabled:true}";