Преглед изворни кода

Merge pull request #2342 from visuddhinanda/development

Development
visuddhinanda пре 8 месеци
родитељ
комит
411c0d21b8

+ 4 - 1
api-v8/app/Http/Api/AuthApi.php

@@ -31,7 +31,10 @@ class AuthApi
                 return ['user_uid' => $jwt->uid, 'user_id' => $jwt->id];
             }
         } else if (isset($_COOKIE['user_uid'])) {
-            return ['user_uid' => $_COOKIE['user_uid'], 'user_id' => $_COOKIE['user_id']];
+            return [
+                'user_uid' => $_COOKIE['user_uid'],
+                'user_id' => $_COOKIE['user_id']
+            ];
         } else {
             return false;
         }

+ 1 - 1
api-v8/app/Http/Controllers/DiscussionController.php

@@ -210,7 +210,7 @@ class DiscussionController extends Controller
 
         $table = $table->orderBy($request->get('order', 'created_at'), $request->get('dir', 'desc'));
         $table = $table->skip($request->get("offset", 0))
-            ->take($request->get('limit', 1000));
+            ->take($request->get('limit', 100));
 
         $result = $table->get();
 

+ 36 - 15
api-v8/app/Http/Controllers/TaskStatusController.php

@@ -4,16 +4,18 @@ declare(strict_types=1);
 
 namespace App\Http\Controllers;
 
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Log;
+
 use App\Models\Task;
 use App\Models\TaskRelation;
 use App\Models\TaskAssignee;
-use Illuminate\Http\Request;
-use Illuminate\Support\Facades\Log;
+use App\Models\AiModel;
 use App\Http\Resources\TaskResource;
 use App\Http\Api\AuthApi;
 use App\Http\Api\WatchApi;
-use App\Models\AiModel;
-use App\Services\AiTranslateService;
+use App\Http\Api\UserApi;
+
 
 
 class TaskStatusController extends Controller
@@ -126,6 +128,9 @@ class TaskStatusController extends Controller
             case 'restarted':
                 $this->pushChange('restarted', $task->id);
                 break;
+            case 'quit':
+                $this->pushChange('quit', $task->id);
+                break;
             case 'done':
                 $this->pushChange('done', $task->id);
                 $task->finished_at = now();
@@ -190,8 +195,6 @@ class TaskStatusController extends Controller
             if ($aiAssistant) {
                 $aiTask = Task::find($taskId);
                 try {
-                    //$ai = app(AiTranslateService::class);
-                    //$params = $ai->makeByTask($taskId, $aiAssistant->uid, true);
                     \App\Jobs\ProcessAITranslateJob::publish($taskId, $aiAssistant->uid);
                     $aiTask->executor_id = $aiAssistant->uid;
                     $aiTask->status = 'queue';
@@ -209,6 +212,7 @@ class TaskStatusController extends Controller
         }
 
         $allChanged = [];
+        $discussion = app(\App\Services\DiscussionService::class);
         foreach ($this->changeTasks as $key => $tasksId) {
             $allChanged = array_merge($allChanged, $tasksId);
             #change status in related
@@ -226,17 +230,34 @@ class TaskStatusController extends Controller
             if ($key === 'restart') {
                 $data['finished_at'] = null;
             }
+            if ($key === 'quit') {
+                $data['executor_id'] = null;
+            }
             Task::whereIn('id', $tasksId)
                 ->update($data);
-            //发送站内信
-            $send = WatchApi::change(
-                resId: $tasksId,
-                from: $user['user_uid'],
-                message: "任务状态变为 {$key}",
-            );
-            Log::debug('watch message', [
-                'send-to' => $send,
-            ]);
+
+            try {
+                //发送站内信
+                $send = WatchApi::change(
+                    resId: $tasksId,
+                    from: $user['user_uid'],
+                    message: "任务状态变为 {$key}",
+                );
+                Log::debug('watch message', [
+                    'send-to' => $send,
+                ]);
+                $editor = UserApi::getByUuid($user['user_uid']);
+                foreach ($tasksId as $taskId) {
+                    $discussion->create([
+                        'res_id' => $taskId,
+                        'res_type' => 'task',
+                        'title' => "{$editor['nickName']} 将任务状态变为 {$key}",
+                        'editor_uid' => $user['user_uid'],
+                    ]);
+                }
+            } catch (\Throwable $th) {
+                Log::error($th->getMessage());
+            }
         }
 
         //changed tasks

+ 48 - 0
api-v8/app/Http/Requests/StoreDiscussionRequest.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace App\Http\Requests;
+
+use Illuminate\Foundation\Http\FormRequest;
+use App\Http\Api\AuthApi;
+use Illuminate\Support\Facades\Log;
+
+class StoreDiscussionRequest extends FormRequest
+{
+    private $user;
+    /**
+     * Determine if the user is authorized to make this request.
+     *
+     * @return bool
+     */
+    public function authorize()
+    {
+        $user = AuthApi::current($this);
+        if (!$user) {
+            Log::warning('discussion store auth failed', ['request' => $this]);
+            return false;
+        }
+        $this->user = $user;
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array
+     */
+    public function rules(): array
+    {
+        return [
+            'res_id' => 'required|string',
+            'res_type' => 'required|string',
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'res_id.required' => 'res_type是必填项',
+            'res_type.required' => 'type是必填项',
+        ];
+    }
+}

+ 22 - 3
api-v8/app/Models/Discussion.php

@@ -9,7 +9,26 @@ class Discussion extends Model
 {
     use HasFactory;
     protected $primaryKey = 'id';
-	protected $casts = [
-		'id' => 'string'
-	];
+    protected $casts = [
+        'id' => 'string'
+    ];
+
+    //批量填充
+    protected $fillable = [
+        'res_id',
+        'res_type',
+        'type',
+        'tpl_id',
+        'title',
+        'content',
+        'content_type',
+        'parent',
+        'editor_uid',
+    ];
+
+    // 设置默认值
+    protected $attributes = [
+        'content_type' => 'markdown',
+        'type' => 'discussion'
+    ];
 }

+ 33 - 0
api-v8/app/Services/DiscussionService.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Services;
+
+use App\Models\Discussion;
+use Illuminate\Support\Facades\Hash;
+use App\Http\Api\Mq;
+use App\Http\Resources\DiscussionResource;
+
+class DiscussionService
+{
+    public function create(array $data): Discussion
+    {
+        if (isset($data['parent'])) {
+            $parentInfo = Discussion::find($data['parent']);
+            if (!$parentInfo) {
+                throw new \Exception('没有找到parent', 500);
+            }
+            $data['res_id '] = $parentInfo->res_id;
+            $data['res_type'] = $parentInfo->res_type;
+        }
+        $discussion = Discussion::create($data);
+        //更新parent children_count
+        if (isset($data['parent'])) {
+            $parentInfo->increment('children_count', 1);
+            $parentInfo->save();
+        }
+        if (isset($data['notification']) && $data['notification'] == 'true') {
+            Mq::publish('discussion', new DiscussionResource($discussion));
+        }
+        return $discussion;
+    }
+}

+ 3 - 1
dashboard-v4/dashboard/src/components/api/task.ts

@@ -27,7 +27,8 @@ export type TTaskStatus =
   | "canceled"
   | "expired"
   | "queue"
-  | "stop";
+  | "stop"
+  | "quit";
 export const StatusButtons: TTaskStatus[] = [
   "pending",
   "published",
@@ -35,6 +36,7 @@ export const StatusButtons: TTaskStatus[] = [
   "done",
   "restarted",
   "requested_restart",
+  "quit",
 ];
 export type TTaskType = "instance" | "workflow" | "group";
 

+ 13 - 12
dashboard-v4/dashboard/src/components/chat/PromptButtonGroup.tsx

@@ -84,8 +84,8 @@ const PromptButtonGroup = ({ onText }: IWidget) => {
     if (!user) {
       return;
     }
-    const getPrompt = async (studio: string) => {
-      const urlTpl = `/v2/article?view=template&studio_name=${user?.realName}&subtitle=_template_prompt_&content=true`;
+    const getPromptOne = async (studio: string) => {
+      const urlTpl = `/v2/article?view=template&studio_name=${studio}&subtitle=_template_prompt_&content=true`;
       const json = await get<IArticleListResponse>(urlTpl);
       if (json.ok) {
         if (json.data.rows.length > 0) {
@@ -97,17 +97,18 @@ const PromptButtonGroup = ({ onText }: IWidget) => {
       return false;
     };
 
-    getPrompt(user?.realName).then((value) => {
-      if (value) {
-        setData(parseMarkdownToPromptNodes(value));
-      } else {
-        getPrompt("admin").then((value) => {
-          if (value) {
-            setData(parseMarkdownToPromptNodes(value));
-          }
-        });
+    const getPrompt = async () => {
+      const my = await getPromptOne(user.realName);
+      if (my) {
+        setData(parseMarkdownToPromptNodes(my));
+        return;
       }
-    });
+      const system = await getPromptOne("admin");
+      if (system) {
+        setData(parseMarkdownToPromptNodes(system));
+      }
+    };
+    getPrompt().catch((e) => console.error(e));
   }, [user, user?.realName]);
 
   return (

+ 1 - 17
dashboard-v4/dashboard/src/components/task/Description.tsx

@@ -22,17 +22,10 @@ const Description = ({ task, onChange, onDiscussion }: IWidget) => {
   const [mode, setMode] = useState<"read" | "edit">("read");
   const [content, setContent] = useState(task?.description);
   const [loading, setLoading] = useState(false);
-  const [open, setOpen] = useState(false);
 
   useEffect(() => setContent(task?.description), [task]);
   return (
     <div>
-      <DiscussionDrawer
-        open={open}
-        onClose={() => setOpen(false)}
-        resId={task?.id}
-        resType="task"
-      />
       <div
         style={{
           display: "flex",
@@ -46,16 +39,7 @@ const Description = ({ task, onChange, onDiscussion }: IWidget) => {
         <span>
           {mode === "read" ? (
             <Space>
-              <Button
-                key={1}
-                onClick={() => {
-                  if (typeof onDiscussion === "undefined") {
-                    setOpen(true);
-                  } else {
-                    onDiscussion();
-                  }
-                }}
-              >
+              <Button key={1} onClick={onDiscussion}>
                 {intl.formatMessage({ id: "buttons.discussion" })}
               </Button>
               <Button

+ 72 - 0
dashboard-v4/dashboard/src/components/task/TaskLog.tsx

@@ -0,0 +1,72 @@
+import { Button, Timeline } from "antd";
+import React, { useEffect, useState } from "react";
+import { get } from "../../request";
+import { ICommentApiData, ICommentListResponse } from "../api/Comment";
+import TimeShow from "../general/TimeShow";
+import { StatusButtons, TTaskStatus } from "../api/task";
+import { TaskStatusColor } from "./TaskStatus";
+
+interface IWidget {
+  taskId?: string;
+  onMore?: () => void;
+}
+const TaskLog = ({ taskId, onMore }: IWidget) => {
+  const [data, setData] = useState<ICommentApiData[]>();
+  useEffect(() => {
+    const url: string = `/v2/discussion?view=res_id&id=${taskId}&limit=5`;
+    console.info("api request", url);
+    get<ICommentListResponse>(url).then((json) => {
+      if (json.ok) {
+        console.debug("discussion api response", json);
+        setData(json.data.rows);
+      }
+    });
+  }, [taskId]);
+
+  function findKeywordInTitle(title?: string): string | undefined {
+    if (!title) {
+      return undefined;
+    }
+    const keywords = StatusButtons;
+
+    for (const keyword of keywords) {
+      if (title.includes(keyword)) {
+        return keyword;
+      }
+    }
+
+    return undefined;
+  }
+
+  return (
+    <>
+      <Timeline>
+        {data?.map((item, id) => {
+          const status = findKeywordInTitle(item.title);
+          return (
+            <Timeline.Item
+              key={id}
+              color={TaskStatusColor(status as TTaskStatus)}
+            >
+              <div>
+                <TimeShow
+                  showLabel={false}
+                  showIcon={false}
+                  createdAt={item.created_at}
+                />
+              </div>
+              <div>{item.title}</div>
+            </Timeline.Item>
+          );
+        })}
+        <Timeline.Item>
+          <Button type="link" onClick={onMore}>
+            更多
+          </Button>
+        </Timeline.Item>
+      </Timeline>
+    </>
+  );
+};
+
+export default TaskLog;

+ 12 - 1
dashboard-v4/dashboard/src/components/task/TaskReader.tsx

@@ -17,6 +17,8 @@ import TaskStatus from "./TaskStatus";
 import Description from "./Description";
 import Category from "./Category";
 import { useIntl } from "react-intl";
+import TaskLog from "./TaskLog";
+import DiscussionDrawer from "../discussion/DiscussionDrawer";
 
 const { Text } = Typography;
 
@@ -40,6 +42,8 @@ const TaskReader = ({ taskId, onChange, onDiscussion }: IWidget) => {
   const [openNextTask, setOpenNextTask] = useState(false);
   const [task, setTask] = useState<ITaskData>();
   const [loading, setLoading] = useState(true);
+  const [open, setOpen] = useState(false);
+
   useEffect(() => {
     const url = `/v2/task/${taskId}`;
     console.info("task api request", url);
@@ -191,13 +195,20 @@ const TaskReader = ({ taskId, onChange, onDiscussion }: IWidget) => {
         </Space>
       </div>
       <Divider />
+      <TaskLog taskId={taskId} onMore={() => setOpen(true)} />
       <Description
         task={task}
         onChange={(data) => {
           setTask(data[0]);
           onChange && onChange(data);
         }}
-        onDiscussion={onDiscussion}
+        onDiscussion={() => setOpen(true)}
+      />
+      <DiscussionDrawer
+        open={open}
+        onClose={() => setOpen(false)}
+        resId={taskId}
+        resType="task"
       />
     </div>
   );

+ 30 - 26
dashboard-v4/dashboard/src/components/task/TaskStatus.tsx

@@ -1,9 +1,37 @@
 import { Progress, Tag, Tooltip } from "antd";
-import { ITaskData, ITaskResponse } from "../api/task";
+import { ITaskData, ITaskResponse, TTaskStatus } from "../api/task";
 import { useIntl } from "react-intl";
 import { useEffect, useState } from "react";
 import { get } from "../../request";
 
+export const TaskStatusColor = (status?: TTaskStatus) => {
+  let color = "";
+  switch (status) {
+    case "pending":
+      color = "default";
+      break;
+    case "published":
+      color = "orange";
+      break;
+    case "running":
+      color = "processing";
+      break;
+    case "done":
+      color = "success";
+      break;
+    case "restarted":
+      color = "error";
+      break;
+    case "requested_restart":
+      color = "warning";
+      break;
+    case "stop":
+      color = "error";
+      break;
+  }
+  return color;
+};
+
 interface IWidget {
   task?: ITaskData;
 }
@@ -35,31 +63,7 @@ const TaskStatus = ({ task }: IWidget) => {
     };
   }, [task]);
 
-  let color = "";
-  switch (task?.status) {
-    case "pending":
-      color = "default";
-      break;
-    case "published":
-      color = "orange";
-      break;
-    case "running":
-      color = "processing";
-      break;
-    case "done":
-      color = "success";
-      break;
-    case "restarted":
-      color = "error";
-      break;
-    case "requested_restart":
-      color = "warning";
-      break;
-    case "stop":
-      color = "error";
-      break;
-  }
-
+  const color = TaskStatusColor(task?.status);
   return (
     <>
       <Tag color={color}>

+ 4 - 0
dashboard-v4/dashboard/src/components/task/TaskStatusButton.tsx

@@ -90,6 +90,7 @@ const TaskStatusButton = ({
       menuEnable = [
         "done",
         "stop",
+        "quit",
         requested_restart_enable ? "requested_restart" : "done",
       ];
       break;
@@ -108,6 +109,9 @@ const TaskStatusButton = ({
     case "stop":
       menuEnable = ["restarted"];
       break;
+    case "quit":
+      menuEnable = ["published"];
+      break;
   }
 
   const items: IStatusMenu[] = StatusButtons.map((item) => {

+ 5 - 4
dashboard-v4/dashboard/src/locales/en-US/buttons.ts

@@ -95,11 +95,12 @@ const items = {
   "buttons.manage": "Manage",
   "buttons.delete.wbw.sentence": "Delete Wbw",
   "buttons.ai.translate": "AI Translate",
-  "buttons.task.status.change.to.published": "发布",
+  "buttons.task.status.change.to.published": "publish",
   "buttons.task.status.change.to.running": "领取",
-  "buttons.task.status.change.to.done": "完成任务",
-  "buttons.task.status.change.to.restarted": "重做",
-  "buttons.task.status.change.to.requested_restart": "请求重做",
+  "buttons.task.status.change.to.done": "done",
+  "buttons.task.status.change.to.restarted": "restart",
+  "buttons.task.status.change.to.requested_restart": "request restart",
+  "buttons.task.status.change.to.quit": "quit",
   "buttons.access-token.get": "access token",
   "buttons.task.add.pre-task": "pre task",
   "buttons.task.add.next-task": "next task",

+ 1 - 0
dashboard-v4/dashboard/src/locales/en-US/label.ts

@@ -69,6 +69,7 @@ const items = {
   "labels.task.status.expired": "expired",
   "labels.task.status.queue": "queue",
   "labels.task.status.stop": "stop",
+  "labels.task.status.quit": "quit",
   "labels.filter": "filter",
   "labels.participants": "participants",
   "labels.task.category": "task category",

+ 1 - 0
dashboard-v4/dashboard/src/locales/zh-Hans/buttons.ts

@@ -101,6 +101,7 @@ const items = {
   "buttons.task.status.change.to.done": "完成任务",
   "buttons.task.status.change.to.restarted": "重做",
   "buttons.task.status.change.to.requested_restart": "请求重做",
+  "buttons.task.status.change.to.quit": "放弃任务",
   "buttons.access-token.get": "获取访问口令",
   "buttons.task.add.pre-task": "添加前置任务",
   "buttons.task.add.next-task": "添加后置任务",

+ 1 - 0
dashboard-v4/dashboard/src/locales/zh-Hans/label.ts

@@ -77,6 +77,7 @@ const items = {
   "labels.task.status.expired": "已过期",
   "labels.task.status.queue": "排队中",
   "labels.task.status.stop": "停止",
+  "labels.task.status.quit": "放弃",
   "labels.filter": "过滤器",
   "labels.participants": "参与者",
   "labels.task.category": "任务类型",

+ 3 - 3
dashboard-v4/dashboard/src/pages/library/search/search.tsx

@@ -25,7 +25,6 @@ const Widget = () => {
   const [pageType, setPageType] = useState("P");
   const [view, setView] = useState<ISearchView | undefined>("pali");
   const [caseWord, setCaseWord] = useState<string[]>();
-  const [prompt, setPrompt] = useState<string>();
   const [sysPrompt, setSysPrompt] = useState<string>();
 
   const [ftsData, setFtsData] = useState<IFtsItem[]>();
@@ -117,7 +116,9 @@ const Widget = () => {
                 return `## ${item.title}-${item.paliTitle} \n\n${item.content}\n\n`;
               })
               .join("");
-            setSysPrompt(`${chat}\n\n请根据上述巴利文本内容,回答用户的问题`);
+            setSysPrompt(
+              `# 搜索词:${key}\n\n# 搜索结果:\n\n${chat}\n\n请根据上述巴利文本内容,回答用户的问题`
+            );
           }
         } else {
           console.error(json.message);
@@ -273,7 +274,6 @@ const Widget = () => {
                   ]}
                 />
                 <AIChatComponent
-                  initMessage={prompt}
                   systemPrompt={sysPrompt}
                   onChat={() => setChat(true)}
                 />