Felix
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 執行環境 1. 安裝 Nodejs [v18.14.1](https://nodejs.org/en/blog/release/v18.14.1) 2. Clone repository ```shell git clone https://never-mind@dev.azure.com/never-mind/smart-chatbot-with-gpt/_git/smart-chatbot-with-gpt ``` 3. Install package ```shell npm install ``` 4. Run ```shell npm run dev ``` 5. Run production ```shell npm run build npm run preview ``` ### Build images ```shell docker build -t vue-ai-chatbot . docker run -p 3000:3000 -it --name vue-ai-chatbot vue-ai-chatbot ``` ### 產品首頁 http://localhost:3000/introduction.html [Azure 測試網址](https://customer-chatbot.azurewebsites.net/introduction.html) ### 後台登入頁 http://localhost:3000/enterprises/login [Azure 測試網址](https://customer-chatbot.azurewebsites.net) - 測試帳號 - 客服角色 帳/密 - employee1@test.com / 1234 - employee2@test.com / 1234 - 管理者 帳/密 - admin1@test.com / 1234 ### API Swagger http://220.134.67.237:8080/swagger/index.html <hr /> # ChatBot API | 版本 | 最後變更日 | 修改人 | 描述 | | :--------- | --------------- | ------ | ----------------------------------------------------------- | | Alpha v1.0 | 2023/3/3 23:25 | Felix | 初版 條列前端部份功能 API | | - | 2023/3/5 22:10 | Felix | 初版 修改部份 API | | - | 2023/3/7 20:10 | Felix | 變更文字描述 customer->guest | | - | 2023/3/19 16:40 | Felix | 新增產品首頁範例, 新增回覆滿意度 API, 修改部份 api 回傳參數 | | - | 2023/3/20 21:10 | Felix | 變動 Signalr api, 提供範本 | | - | 2023/3/22 22:04 | Felix | 新增, 變動工單和顧客 api | | - | 2023/3/26 11:48 | Felix | 新增, 訊息紀錄查詢所需 api | | - | 2023/4/15 23:30 | Steve | 新增, 中止聊天室對話 api<br />POST /api/v1/chatroom/cancel<br /><br />設定工單標籤與優先權 api<br />POST /api/v1/workorder/tagpriority<br /><br />新增管理/客服人員 Api<br />POST /api/v1/employee/create | || 2023/04/24 | Steve | 新增前端顧客註冊API<br />POST /api/v1/guest/register | | | 2023/05/02 | Steve | 新增工作量報表API<br />POST /api/v1/report/workload<br /><br />新增滿意度調查報表API<br />POST /api/v1/report/satisfaction<br /><br />新增查詢客服人員明細API<br />GET /api/v1/employee/detail<br /><br />新增編輯客服人員API<br />POST /api/v1/employee/edit | | | 2023/05/6 | Steve | 新增工作量報表-儀錶板-每小時在線聊天室數量<br />GET /api/v1/report/workload/dashbord/hourofday<br /><br />新增工作量報表-儀錶板-平均一周在線聊天室數量<br />GET /api/v1/report/workload/dashbord/weekofmonth<br /><br />新增工作量報表-儀錶板-每個月/年在線聊天室數量<br />GET /api/v1/report/workload/dashbord/monthofyear<br /><br />新增工作量報表-儀錶板-top 10聊天類型種類(工單的標籤)區分 一個月資料<br />GET /api/v1/report/workload/dashbord/tag<br /><br />新增工作量報表-儀錶板-平均一天/周聊天優先等級區分 一個月資料<br />GET /api/v1/report/workload/dashbord/priority<br /><br />新增滿意度調查報表-儀錶板-當天工單分數<br />GET /api/v1/report/satisfaction/dashboard/hourofday<br /><br />新增滿意度調查報表-儀錶板-一周工單分數<br />GET /api/v1/report/satisfaction/dashboard/weekofmonth<br /><br />新增滿意度調查報表-儀錶板-每個月工單數<br />GET /api/v1/report/satisfaction/dashboard/monthofyear | <hr/> > ## 帳號相關 | 角色(roleId) | 描述 | | ------------ | -------- | | 0 | 顧客 | | 1 | 客服 | | 2 | 企業帳號 | > ## SignalR訂閱/解訂閱 & Hub方法 Hub端點: /hubs/chat | 事件 | 描述 | | -------- | ------- | | SubscribeChatroom(string roomId) | 訂閱聊天室 | | UnsubscribeChatroom(string roomId) | 解訂閱聊天室 | | SubscribeCustomerService() | 訂閱客服人員群組, 所有客服人員都要訂閱 | | UnsubscribeCustomerService() | 解訂閱客服人員群組 | | Join(JoinRequest request) | 加入聊天室, 參數"同聊天 Hub 端點" | | Chat(ChatRequest request) | 訊息聊天, 參數"同聊天 Hub 端點" | | Readed(ReadedRequest request) | 訊息被已讀, 參數"同聊天 Hub 端點" | | Leave(JoinRequest request) | 離開聊天室, 參數"同聊天 Hub 端點" | 列舉對應文字 ```C# public enum MessageType { [Description("text")] Text, [Description("file")] File, //特殊命令, 參照CommandType [Description("command")] Command } public enum CommandType { //一般聊天 [Description("chat")] Chat, //完成對話 [Description("complete")] Complete, //取消對話 [Description("cancel")] Cancel, [Description("like")] Like } ``` ### C#聊天室發話範例 ```C# internal class Program { private static HubConnection connection; private static void Main(string[] args) { try { //連線設定 connection = new HubConnectionBuilder() .WithUrl("http://220.134.67.237:8080/hubs/chat") .Build(); //監聽事件 connection.On<ChatResponse>("ChatReceive", message => { Console.WriteLine(JsonConvert.SerializeObject(message)); }); //建立SignalR連線 connection.StartAsync().Wait(); if (connection.State == HubConnectionState.Connected) { Console.WriteLine("連線成功!!"); } var roomId = Guid.NewGuid().ToString(); var senderId = Guid.NewGuid().ToString(); //訂閱聊天室 connection.InvokeAsync("SubscribeChatroom", roomId); //發送Chat事件 connection.InvokeAsync("Chat", new { RoomId = roomId, SenderId = senderId, Name = "Steve", RoleId = 0, Message = "Hello Alex", MessageType = "text", CommandType = "chat" }); Console.Read(); //解訂閱 connection.InvokeAsync("UnsubscribeChatroom", roomId); } catch (Exception ex) { Console.WriteLine(ex); } } } ``` ### 報表 #### 工作量報表 POST /api/v1/report/workload - [x] 完成 ```json // Request Authorize Header: Bearer token { "startDate": "2023-04-02", //can't null "endDate": "2023-05-02", //can't null, 日期區間預設帶入1個月 "deptId": 1, "employeeName": "阿夫", //模糊搜尋 "tag": "重要", //模糊搜尋 "priority": 2, //1:最優先, 2:緊急, 3:一般, 4:低, 5:最低 "like": 2 //1:差(1到3讚), 2:滿意(4到6讚), 3:非常滿意(7到10讚) } //Response { "status": "ok", "message": "", "data": [ { "employeeId": "0b2bbd39-4992-493e-af0b-9d59b5b31f50", "employeeName": "測試帳號3", "online": true, "averageFirstResponseTime": "6天1時17分10秒", //第一次平均回覆時間 "averageResponseTime": "6天1時17分10秒", //平均回覆時間 "maxResponseTime": "16天2時10分34秒", //最久回覆時間 "unprocess": 0, //未處理工單數 "processing": 4, //處理中工單數 "finished": 2, //完成工單數 "abandon": 0 //中止工單數 }, ... ] } ``` #### 工作量報表-儀錶板-每小時在線聊天室數量 GET /api/v1/report/workload/dashbord/hourofday - [x] 完成 ```json // Request Authorize Header: Bearer token { "employeeId": "7661e585-35dc-4787-baac-e2c3d3674319" } // Response { "status": "ok", "message": "", "data": [ { "x": "1", "y": "0" }, { "x": "2", "y": "0" }, { "x": "3", "y": "0" }, { "x": "4", "y": "0" }, { "x": "5", "y": "0" }, { "x": "6", "y": "0" }, { "x": "7", "y": "0" }, { "x": "8", "y": "0" }, { "x": "9", "y": "0" }, { "x": "10", "y": "0" }, { "x": "11", "y": "0" }, { "x": "12", "y": "0" }, { "x": "13", "y": "0" }, { "x": "14", "y": "0" }, { "x": "15", "y": "0" }, { "x": "16", "y": "0" }, { "x": "17", "y": "0" }, { "x": "18", "y": "0" }, { "x": "19", "y": "0" }, { "x": "20", "y": "0" }, { "x": "21", "y": "0" }, { "x": "22", "y": "0" }, { "x": "23", "y": "0" }, { "x": "24", "y": "0" } ] } ``` #### 工作量報表-儀錶板-平均一周在線聊天室數量 GET /api/v1/report/workload/dashbord/weekofmonth - [x] 完成 ```json // Request Authorize Header: Bearer token { "employeeId": "7661e585-35dc-4787-baac-e2c3d3674319", "startDate": "2023-04-17", //一周的第一天是星期一 "endDate": "2023-04-23" } // Response { "status": "ok", "message": "", "data": [ { "x": "2023-04-17", "y": "0" }, { "x": "2023-04-18", "y": "1" }, { "x": "2023-04-19", "y": "6" }, { "x": "2023-04-20", "y": "0" }, { "x": "2023-04-21", "y": "0" }, { "x": "2023-04-22", "y": "5" }, { "x": "2023-04-23", "y": "0" } ] } ``` #### 工作量報表-儀錶板-每個月/年在線聊天室數量 GET /api/v1/report/workload/dashbord/monthofyear - [x] 完成 ```json // Request Authorize Header: Bearer token { "employeeId": "7661e585-35dc-4787-baac-e2c3d3674319" } // Response { "status": "ok", "message": "", "data": [ { "x": "1", "y": "0" }, { "x": "2", "y": "0" }, { "x": "3", "y": "0" }, { "x": "4", "y": "24" }, { "x": "5", "y": "3" }, { "x": "6", "y": "0" }, { "x": "7", "y": "0" }, { "x": "8", "y": "0" }, { "x": "9", "y": "0" }, { "x": "10", "y": "0" }, { "x": "11", "y": "0" }, { "x": "12", "y": "0" } ] } ``` #### 工作量報表-儀錶板-top 10聊天類型種類(工單的標籤)區分 一個月資料 GET /api/v1/report/workload/dashbord/tag - [x] 完成 ```json //Request Authorize Header: Bearer token { "employeeId": "7661e585-35dc-4787-baac-e2c3d3674319" } // Response { "status": "ok", "message": "", "data": [ { "x": "奧客", "y": "1" }, { "x": "重要客戶", "y": "1" }, { "x": "ok,gg", "y": "1" }, { "x": "ddd", "y": "1" }, { "x": "6666", "y": "1" } ] } ``` #### 工作量報表-儀錶板-平均一天/周聊天優先等級區分 一個月資料 GET /api/v1/report/workload/dashbord/priority - [x] 完成 ```json // Request Authorize Header: Bearer token { "employeeId": "7661e585-35dc-4787-baac-e2c3d3674319" } // Response { "status": "ok", "message": "", "data": [ { "x": "最優先", "y": "1" }, { "x": "緊急", "y": "4" }, { "x": "一般", "y": "20" }, { "x": "低", "y": "2" }, { "x": "最低", "y": "0" } ] } ``` #### 滿意度調查報表 POST /api/v1/report/satisfaction - [x] 完成 ```json // Request Authorize Header: Bearer token { "startDate": "2023-04-02", //can't null "endDate": "2023-05-02", //can't null, 日期區間預設帶入1個月 "deptId": 1, "employeeName": "阿夫", //模糊搜尋 "like": 2 //1:差(1到3讚), 2:滿意(4到6讚), 3:非常滿意(7到10讚) } //Response { "status": "ok", "message": "", "data": [ { "employeeId": "0b2bbd39-4992-493e-af0b-9d59b5b31f50", "employeeName": "測試帳號3", "deptId": 1, "issues": 2, //聊天總數 "points": 0, //總讚數 "percentage": 9.09, //佔整體聊天數百分比 "great": 0, //非常滿意聊天數 "good": 0, //滿意聊天數 "bad": 0 //待改善聊天數 }, ... ] } ``` #### 滿意度調查報表-儀錶板-當天工單分數 GET /api/v1/report/satisfaction/dashboard/hourofday - [x] 完成 ```json // Request Authorize Header: Bearer token { "employeeId": "7661e585-35dc-4787-baac-e2c3d3674319" } // Response { "status": "ok", "message": "", "data": [ { "x": "1", "y": "0" }, { "x": "2", "y": "0" }, { "x": "3", "y": "0" }, { "x": "4", "y": "0" }, { "x": "5", "y": "0" }, { "x": "6", "y": "0" }, { "x": "7", "y": "0" }, { "x": "8", "y": "0" }, { "x": "9", "y": "0" }, { "x": "10", "y": "0" }, { "x": "11", "y": "0" }, { "x": "12", "y": "0" }, { "x": "13", "y": "0" }, { "x": "14", "y": "0" }, { "x": "15", "y": "0" }, { "x": "16", "y": "0" }, { "x": "17", "y": "0" }, { "x": "18", "y": "0" }, { "x": "19", "y": "0" }, { "x": "20", "y": "0" }, { "x": "21", "y": "0" }, { "x": "22", "y": "0" }, { "x": "23", "y": "0" }, { "x": "24", "y": "0" } ] } ``` #### 滿意度調查報表-儀錶板-一周工單分數 GET /api/v1/report/satisfaction/dashboard/weekofmonth - [x] 完成 ```json // Request Authorize Header: Bearer token { "employeeId": "7661e585-35dc-4787-baac-e2c3d3674319", "startDate": "2023-04-17", //一周的第一天是星期一 "endDate": "2023-04-23" } // Response { "status": "ok", "message": "", "data": [ { "x": "2023-04-17", "y": "0" }, { "x": "2023-04-18", "y": "0" }, { "x": "2023-04-19", "y": "19" }, { "x": "2023-04-20", "y": "0" }, { "x": "2023-04-21", "y": "0" }, { "x": "2023-04-22", "y": "24" }, { "x": "2023-04-23", "y": "0" } ] } ``` #### 滿意度調查報表-儀錶板-每個月工單數 GET /api/v1/report/satisfaction/dashboard/monthofyear - [x] 完成 ```json // Request Authorize Header: Bearer token { "employeeId": "7661e585-35dc-4787-baac-e2c3d3674319" } // Response { "status": "ok", "message": "", "data": [ { "x": "1", "y": "0" }, { "x": "2", "y": "0" }, { "x": "3", "y": "0" }, { "x": "4", "y": "44" }, { "x": "5", "y": "14" }, { "x": "6", "y": "0" }, { "x": "7", "y": "0" }, { "x": "8", "y": "0" }, { "x": "9", "y": "0" }, { "x": "10", "y": "0" }, { "x": "11", "y": "0" }, { "x": "12", "y": "0" } ] } ``` ### 客服 / 企業端登入 #### POST /api/v1/enterprise/authenticate - [x] 完成 - 登入後Bearer token將存放user資訊, 包含roleId, name, dept, organizeId... ```json // Request: { "account": "0910231995", // email or mobileNum "password": "1qaz@WSX" } // Response: // 登入成功 { "status": "ok", "message": "登入成功", "user":{ "id": 1, "roleId": 1, // 1: 客服角色, 2: 企業管理者角色 "name": "大雄", "dept": "技術部", "deptId": 2, "organize": "良心企業股份有限公司", "organizeId": 20 }, "loginDate": "2023-03-02T00:00:00Z" "token": "xxx-xxx-xxx-xxx-xxx", // jwt 可以用來解析使用者資訊 "tokenExpiredDate": "2030-10-10T00:00:00Z" } // 登入失敗:帳號或密碼錯誤 { "status": "failed", "message": "帳號或密碼錯誤" } ``` ### 企業帳號註冊 #### POST /api/v1/enterprise/register - [x] 完成 ```json // Request: { "email": "alex001212@gmail.com", "mobileNum": "0910231995", // 非必填 "organize": "良心企業股份有限公司", "password": "1qaz@WSX" } // Response: // 註冊成功 { "status": "ok", "message": "註冊成功" } // 註冊失敗 { "status": "failed", "errorMsg": "註冊失敗" } ``` ### 忘記密碼 #### POST /api/v1/enterprise/forget - [ ] 完成 ```json // Request: { "email": "alex001212@gmail.com" } // Response: E-mail 透過連結導回前端 https://localhost:3000/enterprise/resetpwd?email=alex001212@gmail.com&resetTOP=50125 ``` ### 重設密碼 #### POST /api/v1/enterprise/resetpwd - [x] 完成 ```json // Request: { "email": "alex001212@gmail.com", "resetOTP": "50125" "password": "1qaz@WSX" } ``` > ## 顧客端聊天相關 > ![](https://i.imgur.com/bImMUCo.png) ### 取得可用的客服清單 #### GET /api/v1/employee/list - [x] 完成 ```json // Response: [ { "employeeId": "91d2a971-8ea2-43ba-98b1-d8f979200019", "employeeName": "Mr.J", "avatar": "https://domain.com/assets/images/avater001.jpg", "isOnline": true, "lastTimeOnline": "2023-03-02T00:00:00Z" //最後上線的時間 } ] ``` ### 取得最近的訊息 (最後一筆) #### POST /api/v1/chatroom/recently - [x] 完成 - 會先按下傳送訊息過才會顯示, 並實際傳送訊息後 ```json // Request: { "guestId": "8068537f-e2ad-4023-900c-b3c0824456ec" } // Response: | 回傳參數 | 說明 | | ------------ | ------------------------- | | roomId | 聊天室id | | employeeId | 承接訊息的客服 | | employeeName | 承接訊息客服的名稱 | | name | 發話人 | | roleId | 角色 | | avatar | 發話人頭像 | | mType | 訊息類型(file,image,text) | | messageId | 最後的訊息ID | | message | 最後的訊息 | | messageTime | 最後訊息時間 | { "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "employee": { "employeeId": "91d2a971-8ea2-43ba-98b1-d8f979200019", "employeeName": "Mr.J", "avatar":"https://domain.com/assets/images/avater001.jpg", }, "last": { "name": "Alex", "roleId": "0", "avatar":"https://domain.com/assets/images/avater001.jpg", "mType": "text", "messageId": 10222, "message": "xxx...", "messageTime": "2023-03-02T00:00:00Z" } } ``` ### 傳送訊息給我們 (產生聊天室) #### POST /api/v1/chatroom/create - [x] 完成 - 瀏覽器判斷 localStorage 沒有 uuidv4 時, 會產生一組隨機 uuid, 用來當作匿名聊天的 guestId - 若舊訊息已存在, 則不必再產生新的聊天室 id - 若工單已被按下完成, 則產生新的工單 ```json // Request: { "guestId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name":"xxx", "email": "xxx@xxx.ccc", "tel": "0912345678" } // Response: { "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a" } ``` ![](https://i.imgur.com/Jsyr35S.png) ### 載入聊天室的所有訊息 #### POST /api/v1/chatroom/messages - [x] 完成 - 先前若有傳送過訊息才須載入 ```json // Request Authorize Header: Bearer token { "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a" } // Response | 回傳參數 | 說明 | | ----------- | --------------------------------- | | messageId | 訊息Id | | senderId | 發話人ID | | name | 暱稱 | | roleId | 角色 | | avatar | 發話人頭像 | | mType | 訊息類型(file,image,text,command) | | message | 訊息 | | messageTime | 訊息時間 | | readed | 是否已讀 | { "messages": [ { "messageId": 10351, "senderId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "Alex", "roleId": "0", "avatar":"https://domain.com/assets/images/avater001.jpg", "mType": "text", "message": "xxx...", "messageTime": "2023-03-02T00:00:00Z", "readed": true, }, ... ] } ``` > ## 客服端聊天相關 (客服權限) ![](https://i.imgur.com/F76f5ID.png) ### 取得被分配聊天室清單 #### POST /api/v1/chatroom/mylist - [x] 完成 - 就是客服與顧客的聊天對話紀錄 - 顯示最後的訊息 ```json // Request: Authorize Header: Bearer token // Response: | 回傳參數 | 說明 | | ----------- | --------------------------------- | | roomId | 聊天室id | | senderId | 發話人ID | | name | 發話人 | | roleId | 角色 | | avatar | 發話人頭像 | | mType | 訊息類型(file,image,text,command) | | messageId | 最後的訊息ID | | message | 最後的訊息 | | unread | 未讀訊息數 | | messageTime | 最後訊息時間 | [ { "workOrderId":1, "tag":"abc", "priority":3, "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "guest": { "guestId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "Alex" }, "last": { "senderId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "Alex", "roleId":"0", "avatar":"https://domain.com/assets/images/avater001.jpg", "mType": "text", "messageId": 10222, "message": "xxx...", "unread": 2, "messageTime": "2023-03-02T00:00:00Z" } } ] ``` ### 載入聊天室的所有訊息 - (同顧客聊天相關 - 載入聊天室的所有訊息) ### 結束聊天室對話 #### POST /api/v1/chatroom/end - [x] 完成 - 紀錄為完成的工單 - 結束後會推送給顧客一則訊息 command:like, 要求填寫滿意度 ```json // Request Authorize Header: Bearer token { "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a" } // Response { "status": "ok", "message": "成功" } ``` ### 中止聊天室對話 #### POST /api/v1/chatroom/cancel - [x] 完成 - 紀錄為中止的工單 - 結束後會推送給顧客一則訊息 command:cancel, 要求填寫滿意度 ```json // Request Authorize Header: Bearer token { "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a" } // Response { "status": "ok", "message": "成功" } ``` > ## 聊天連線 ### 聊天 Hub 端點 #### /ws/v1/hubs/chat - SinganR 的 ConnectionId 要與 senderId 做綁定 - 參考備註 [1. C# hub 範例](#sample-1) #### 加入聊天室 (WS) - [x] 完成 ```json // JoinSend // JoinSend { "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "senderId": "8068537f-e2ad-4023-900c-b3c0824456ec", // 傳送者id 依照roleId判斷是guestId或employeeId "name": "Alex", "roleId": 0 } // JoinReceive (推給其他人) { "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "senderId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "Alex", "roleId": 0 } ``` #### 訊息聊天 (WS) - [x] 完成 ```json // ChatSend // 1. 一般訊息 { "senderId": "8068537f-e2ad-4023-900c-b3c0824456ec", "roleId": 1, "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "mType": "text", "message": "哈嘍!" } // 2. 一般訊息 (檔案) (需要搭配另一檔案上傳的API) { "senderId": "8068537f-e2ad-4023-900c-b3c0824456ec", "roleId": 1, "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "messageType": "file", "commandType": "chat" "message": "xxxxx-xxxxx-xxxx.jpeg" } // 3. 指令-按讚 (由客服結束對話時發送) { "senderId": "8068537f-e2ad-4023-900c-b3c0824456ec", "roleId": 1, "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "mType": "command", "message": "is_like" } // ChatReceive (推給其他人) { "senderId": "91d2a971-8ea2-43ba-98b1-d8f979200019", "roleId": 1, "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "name": "Mr.J", "avatar":"https://domain.com/assets/images/avater001.jpg", "messageId": 102051, "mType": "text", "message": "哈嘍!", "messageTime": "2023-03-02T00:00:00Z" } ``` #### 訊息被已讀 (WS) - [x] 完成 ```json // ReadSend { "senderId": "91d2a971-8ea2-43ba-98b1-d8f979200019", "roleId": 1, "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "messageId": 102051 } // ReadReceive (推給其他人) { "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "messageId": 102051 } ``` #### 離開聊天室 (WS) - [x] 完成 ```json // LeaveReceive (推給其他人) { "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "senderId": 1, "name": "Mr.J", "roleId": 1 } ``` ### 回覆滿意度 #### POST /api/v1/chatroom/comment - [x] 完成 ```json // Request { "point": 2, "comment": "客服人員態度很差" } // Response { "status": "ok", "message": "完成" } ``` > ### 檔案上傳的 API ### 檔案上傳 #### POST /api/v1/file/upload - [x] 完成 ```json // Request Authorize Header: Bearer token { "fileName": "xxxx-xxxx-xxxx.jpg", "fileBase64": "xaffxxxxxx=z-" } ``` > ## 顧客管理相關 ### 註冊顧客帳號 #### POST /api/v1/guest/register - [x] 完成 ```json // Request { "guestId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "顧客1", "eMail": "dddd@gmail.com", "tel": "0933000123" } //Response { "status": "ok", "message": "帳戶註冊完成" } ``` ### 取得顧客清單 #### GET /api/v1/guest - [x] 完成 ```json // Request Authorize Header: Bearer token // Response [ { "guestId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "Alex" } ... ] ``` > ## 工單相關 ### 聊天 Hub 端點 #### /ws/v1/hubs/chat #### 新工單產生 (WS) - [x] 完成 ```json // WorkOrderReceive (推給所有客服) { "workOrderId": 1, "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "guestId": "8068537f-e2ad-4023-900c-b3c0824456ec", "guestName": "Alex", // 顧客名稱 "message": "我需要訂位", "messageTime": "2023-03-02T00:00:00Z" } ``` ![](https://i.imgur.com/I40AZSO.png) ### 取得工單統計 #### GET /api/v1/workorder/status - [ ] 完成 - 邏輯 - roleId=1 - 待處理的工單數顯示所有的 - 處理中的工單數顯示自己承接的 - 處理完成工單數(24H)顯示自己完成的 - 中止的工單數顯示自己承接後中止的 - roleId=2 看到所有的統計 ```json // Request: Authorize Header: Bearer token // Response: { unprocess: 0, // 待處理的工單數 processing: 1, // 處理中的工單數 finished: 1, // 處理完成的工單數 (24h) abandon: 0 // 中止的工單數 } ``` ### 取得未被分配的工單 #### GET /api/v1/workorder/unassignment - [x] 完成 ```json // Request: Authorize Header: Bearer token // Response: [ { "workOrderId": 1, "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "guestId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "Alex", // 顧客名稱 "mType": "text", // 最後的訊息 "message": "我需要訂位", // 最後的訊息 "messageTime": "2023-03-02T00:00:00Z", "created": "2023-03-02T00:00:00Z" } ... ] ``` ### 承接工單 #### POST /api/v1/workorder/undertake/ - [x] 完成 - 接了工單之後, 會顯示於聊天列表(/chatroom/mylist) ```json // Request: Authorize Header: Bearer token { "workOrderId": 1 } // Response: { "status": "ok", "message": "接單成功", "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a" } ``` ![](https://i.imgur.com/QMHb4Bj.png) ### 所有工單清單 #### POST /api/v1/workorder/list - [x] 完成 - 邏輯 - roleId=1 看到客服自己承接的工單 - roleId=2 看到所有的 ```json // Request Authorize Header: Bearer token { "state": 1, // 篩選工單狀態 (0:未處理, 1:處理中, 2:已完成, 3:中止) "guestName": "Alex", // 篩選顧客名稱 (需求者) } // Response [ { "id": 1, "state": "assignment", "tag": "", "guestName": "Alex", // 顧客名稱 (需求者) "guestId": "8068537f-e2ad-4023-900c-b3c0824456ec", "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "type": "?", // 工單類型 (未提供) "priority": "1", // 優先等級 (固定回傳1) "created": "2023-03-02T00:00:00Z", // 工單建立時間 "modified": "2023-03-02T00:00:00Z", "reason": "", // 狀態原因 "employee":{ "employeeId": "91d2a971-8ea2-43ba-98b1-d8f979200019", // 工單承接人 "employeeName": "Mr. J" } } ] ``` ### 設定工單標籤與優先權 #### POST /api/v1/workorder/tagpriority - [x] 完成 ```json // Request Authorize Header: Bearer token { "workOrderId": 1, // can't null "tag": "奧客", "priority": 4, // 優先權 1:最優先, 2:緊急, 3:一般, 4:低, 5:最低 can't null } // Response { "status": "ok", "message": "成功" } ``` > ## 顧客管理相關 > ### 所有顧客清單 #### POST /api/v1/guest/list - [x] 完成 ```json // Request Authorize Header: Bearer token { "name": "Alex", // 篩選名稱 "email": null, "mobile": null } // Response [ { "guestId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "Alex", "email": "alex00123bb@gmail.com", "mobile": "0912323111", "tag": "", "workOrderStatus": "assignment", "created": "2023-03-02T00:00:00Z", "modified": "2023-03-02T00:00:00Z" }, { "guestId": "1118537f-e2ad-4023-900c-b3c0824456ec", "name": "葉師父", "email": "yayaya@gmail.com", "mobile": "0933000111", "tag": "", "workOrderStatus": "assignment", "created": "2023-03-03T00:00:00Z", "modified": "2023-03-04T00:00:00Z" }, ... ] ``` > ## 客服管理相關 > TODO > ## ChatGPT 相關 > TODO > ## 部門相關 ### 取得部門清單 #### GET /api/v1/dept/list - [x] 完成 ```json // Response [ { deptId:1, deptName: "客服部" } ... ] ``` ### 新增部門 #### POST /api/v1/dept/create - [x] 完成 ### 修改部門 #### POST /api/v1/dept/update - [x] 完成 ### 修改部門人員 #### POST /api/v1/dept/employee - [x] 完成 ```json // Request { "deptId": 9, "employeeIds": [ "7661e585-35dc-4787-baac-e2c3d3674319", "d31b60f4-33bd-470b-b6f8-4df16ce833c1" ] } // Response { "status": "ok", "message": "修改部門人員成功" } ``` ### 刪除部門 #### POST /api/v1/dept/delete - [x] 完成 > ## 訊息紀錄查詢相關 > ### 取得客服人員清單 #### POST /api/v1/employee/dept - [x] 完成 ```json // Request: { "deptId": 1 // can be null } // Response: [ { "employeeId": "91d2a971-8ea2-43ba-98b1-d8f979200019", "employeeName": "客服1號", "deptId": 1, "avatar": "https://domain.com/assets/images/avater001.jpg", "isOnline": true, "lastTimeOnline": "2023-03-02T00:00:00Z" //最後上線的時間 } ] ``` ### 取得客服人員所有訊息 #### POST /api/v1/employee/chatroomlist - [x] 完成 ```json // Request: { "employeeId": "91d2a971-8ea2-43ba-98b1-d8f979200019", "startDate": "2023-03-01T00:00:00Z", // can be null "endDate": "2023-03-31T00:00:00Z" // can be null } // Response: [ { "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "guest": { "guestId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "Alex" }, "last": { "senderId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "Alex", "roleId":"0", "avatar":"https://domain.com/assets/images/avater001.jpg", "mType": "text", "messageId": 10222, "message": "xxx...", "unread": 2, "messageTime": "2023-03-02T00:00:00Z" } } ] ``` ### 新增管理/客服人員帳號 POST /api/v1/employee/chatroomlist - [x] 完成 // Request: ```json { "deptId": 1, "roleId": 1, //1:客服人員, 2:管理員 "name": "約瑟夫", "email": "abc@gmail.com", "tel":, "1234567890" "password": "123456" } ``` // Response: ```json [ { "roomId": "69727d6d-c8a4-455a-aa22-903d485e524a", "guest": { "guestId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "Alex" }, "last": { "senderId": "8068537f-e2ad-4023-900c-b3c0824456ec", "name": "Alex", "roleId":"0", "avatar":"https://domain.com/assets/images/avater001.jpg", "mType": "text", "messageId": 10222, "message": "xxx...", "unread": 2, "messageTime": "2023-03-02T00:00:00Z" } } ] ``` #### 取得客服人員明細 GET /api/v1/employee/detail - [x] 完成 ```json // Request Authorize Header: Bearer token { "employeeId": "7661e585-35dc-4787-baac-e2c3d3674319" } //Response { "employeeId": "7661e585-35dc-4787-baac-e2c3d3674319", "employeeName": "阿夫", "roleId": 1, "deptId": 1, "eMail": "joepopxp@gmail.com", "mobile": "0963052383", "password": "123456", "avatar": "" } ``` #### 編輯客服人員 POST /api/v1/employee/edit - [x] 完成 ```json // Request Authorize Header: Bearer token { "employeeId": "7661e585-35dc-4787-baac-e2c3d3674319", "deptId": 1, "roleId": 1, "name": "阿夫", "eMail": "joepopxp@gmail.com", "tel": "0963052777", "password": "123456" } //Response { "status": "ok", "message": "編輯客服人員帳號成功" } ``` ## 備註 ##### sample-1 - ChatHub.cs ```csharp using System.Text.Json; using Microsoft.AspNetCore.SignalR; namespace SignalRChat.Hubs { public class ChatHub : Hub { public async Task JoinSend(string message) { var data = JsonSerializer.Deserialize<object>(message); await Clients.Others.SendAsync("JoinReceive", data); } public async Task ChatSend(string message) { var httpCtx = Context.GetHttpContext(); Console.WriteLine(String.Format("Client {0} send message", Context.ConnectionId)); var data = JsonSerializer.Deserialize<object>(message); await Clients.Others.SendAsync("ChatReceive", data); } public async Task ReadSend(string message) { var httpCtx = Context.GetHttpContext(); Console.WriteLine(String.Format("Client {0} send message", Context.ConnectionId)); var data = JsonSerializer.Deserialize<object>(message); await Clients.Others.SendAsync("ReadReceive", data); } // 連線 public override Task OnConnectedAsync() { Console.WriteLine(String.Format("Client {0} connected", Context.ConnectionId)); return base.OnConnectedAsync(); } // 離線 public override async Task OnDisconnectedAsync(Exception? exception) { Console.WriteLine(String.Format("Client {0} dis-connected", Context.ConnectionId)); await Clients.Others.SendAsync("LeaveReceive", null); } } } ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully