---
# System prepended metadata

title: 執行環境

---

# 執行環境


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);
        }

    }
}
```
