add room usecase
This commit is contained in:
parent
377b52515d
commit
beaf6841cc
|
|
@ -76,3 +76,14 @@ AmazonS3Settings:
|
||||||
AccessKey: AKIAVRUVVY4IJOBFOY42
|
AccessKey: AKIAVRUVVY4IJOBFOY42
|
||||||
SecretKey: sSpml0h3k0y2hU5A+Fxlhcv+QGt4ddobttvvlxm+
|
SecretKey: sSpml0h3k0y2hU5A+Fxlhcv+QGt4ddobttvvlxm+
|
||||||
CloudFrontID: E3UMOQ0CGBOBAE
|
CloudFrontID: E3UMOQ0CGBOBAE
|
||||||
|
Centrifugo:
|
||||||
|
APIURL: http://localhost:8000/api
|
||||||
|
APIKey: "api-key"
|
||||||
|
Cassandra:
|
||||||
|
Hosts:
|
||||||
|
- localhost
|
||||||
|
Port: 9042
|
||||||
|
Keyspace: chat
|
||||||
|
Username: "cassandra"
|
||||||
|
Password: "cassandra"
|
||||||
|
UseAuth: false
|
||||||
|
|
@ -0,0 +1,351 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Type: 聊天室 (Chat Room)
|
||||||
|
// =================================================================
|
||||||
|
type (
|
||||||
|
// CreateRoomReq 創建聊天室請求
|
||||||
|
CreateRoomReq {
|
||||||
|
Authorization
|
||||||
|
Name string `json:"name" validate:"required,min=1,max=100"` // 聊天室名稱
|
||||||
|
Status string `json:"status,optional" validate:"omitempty,oneof=active archived"` // 狀態,預設為 active
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRoomReq 更新聊天室請求
|
||||||
|
UpdateRoomReq {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id" validate:"required"`
|
||||||
|
Name *string `json:"name,optional" validate:"omitempty,min=1,max=100"`
|
||||||
|
Status *string `json:"status,optional" validate:"omitempty,oneof=active archived"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoomResp 聊天室回應
|
||||||
|
RoomResp {
|
||||||
|
RoomID string `json:"room_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRoomsReq 查詢聊天室列表請求
|
||||||
|
ListRoomsReq {
|
||||||
|
Authorization
|
||||||
|
Status string `json:"status,optional" validate:"omitempty,oneof=active archived"` // 狀態篩選
|
||||||
|
PageSize int `json:"page_size,optional" validate:"omitempty,min=1,max=100"` // 每頁大小,預設 20
|
||||||
|
LastID string `json:"last_id,optional"` // 用於 cursor-based pagination
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRoomsResp 聊天室列表回應
|
||||||
|
ListRoomsResp {
|
||||||
|
Rooms []RoomResp `json:"rooms"`
|
||||||
|
LastID string `json:"last_id,optional"` // 用於下一頁查詢
|
||||||
|
Total int64 `json:"total,optional"` // 總數(僅第一頁返回)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMemberReq 添加成員請求
|
||||||
|
AddMemberReq {
|
||||||
|
Authorization
|
||||||
|
RoomID string `json:"room_id" validate:"required"`
|
||||||
|
UID string `json:"uid" validate:"required"` // 要添加的用戶 UID
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveMemberReq 移除成員請求
|
||||||
|
RemoveMemberReq {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id" validate:"required"`
|
||||||
|
UID string `path:"uid" validate:"required"` // 要移除的用戶 UID
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateMemberRoleReq 更新成員角色請求
|
||||||
|
UpdateMemberRoleReq {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id" validate:"required"`
|
||||||
|
UID string `path:"uid" validate:"required"`
|
||||||
|
Role string `json:"role" validate:"required,oneof=admin member"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemberResp 成員回應
|
||||||
|
MemberResp {
|
||||||
|
RoomID string `json:"room_id"`
|
||||||
|
UID string `json:"uid"`
|
||||||
|
Role string `json:"role"`
|
||||||
|
JoinedAt string `json:"joined_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMembersResp 成員列表回應
|
||||||
|
ListMembersResp {
|
||||||
|
Members []Member `json:"members"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemberResp 成員回應
|
||||||
|
Member {
|
||||||
|
UID string `json:"uid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Avatar string `json:"avatar"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserRoomsResp 用戶聊天室列表回應
|
||||||
|
GetUserRoomsResp {
|
||||||
|
Rooms []RoomResp `json:"rooms"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomReq {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Type: 訊息 (Message)
|
||||||
|
// =================================================================
|
||||||
|
type (
|
||||||
|
// SendMessageReq 發送訊息請求
|
||||||
|
SendMessageReq {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id" validate:"required"`
|
||||||
|
Content string `json:"content" validate:"required,min=1,max=5000"` // 訊息內容
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessageResp 發送訊息回應
|
||||||
|
SendMessageResp {
|
||||||
|
RoomID string `json:"room_id"`
|
||||||
|
BucketDay string `json:"bucket_day"` // yyyyMMdd
|
||||||
|
TS int64 `json:"ts"` // timestamp
|
||||||
|
UID string `json:"uid"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMessagesReq 查詢訊息列表請求
|
||||||
|
ListMessagesReq {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id" validate:"required"`
|
||||||
|
BucketDay string `json:"bucket_day,optional"` // yyyyMMdd,不提供則使用今天
|
||||||
|
PageSize int64 `json:"page_size,optional" validate:"omitempty,min=1,max=100"` // 每頁大小,預設 20
|
||||||
|
LastTS int64 `json:"last_ts,optional"` // 用於 cursor-based pagination
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListMessagesResp 訊息列表回應
|
||||||
|
ListMessagesResp {
|
||||||
|
Messages []MessageResp `json:"messages"`
|
||||||
|
Total int64 `json:"total,optional"` // 總數(僅第一頁返回)
|
||||||
|
LastTS int64 `json:"last_ts,optional"` // 用於下一頁查詢
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageResp 訊息回應
|
||||||
|
MessageResp {
|
||||||
|
RoomID string `json:"room_id"`
|
||||||
|
BucketDay string `json:"bucket_day"` // yyyyMMdd
|
||||||
|
TS int64 `json:"ts"` // timestamp
|
||||||
|
UID string `json:"uid"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IsMemberResp {
|
||||||
|
IsMember bool `json:"is_member"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Service: 聊天 API - 需要登入 (Chat Service)
|
||||||
|
// =================================================================
|
||||||
|
@server(
|
||||||
|
group: chat
|
||||||
|
prefix: /api/v1/chat
|
||||||
|
schemes: https
|
||||||
|
timeout: 30s
|
||||||
|
middleware: AuthMiddleware
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
// ==================== 聊天室管理 ====================
|
||||||
|
@doc(
|
||||||
|
summary: "創建聊天室"
|
||||||
|
description: "創建一個新的聊天室,創建者自動成為管理員"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (RoomResp) // 建立成功
|
||||||
|
@respdoc-400 (ErrorResp) "請求參數格式錯誤"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-422 (ErrorResp) "創建太多聊天室了(目前仙梅設定上限)"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler createRoom
|
||||||
|
post /rooms (CreateRoomReq) returns (RoomResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "取得聊天室資訊"
|
||||||
|
description: "根據 room_id 取得聊天室的詳細資訊"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (RoomResp) // 取得聊天室
|
||||||
|
@respdoc-400 (ErrorResp) "請求參數格式錯誤"
|
||||||
|
@respdoc-401 (ErrorResp) "不再房間內的人"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-404 (ErrorResp) "找不到聊天室"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler getRoom
|
||||||
|
get /rooms/:room_id (RoomReq) returns (RoomResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "更新聊天室資訊"
|
||||||
|
description: "更新聊天室的名稱或狀態,需要管理員權限"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (RoomResp) // 取得聊天室
|
||||||
|
@respdoc-400 (ErrorResp) "請求參數格式錯誤"
|
||||||
|
@respdoc-401 (ErrorResp) "不再房間內的人"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-404 (ErrorResp) "找不到聊天室"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler updateRoom
|
||||||
|
put /rooms/:room_id (UpdateRoomReq) returns (RoomResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "刪除聊天室"
|
||||||
|
description: "刪除聊天室及其所有成員和訊息,需要管理員權限"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (RespOK)
|
||||||
|
@respdoc-400 (ErrorResp) "請求參數格式錯誤"
|
||||||
|
@respdoc-401 (ErrorResp) "權限不夠"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-404 (ErrorResp) "找不到聊天室"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler deleteRoom
|
||||||
|
delete /rooms/:room_id (RoomReq) returns (RespOK)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "查詢聊天室列表"
|
||||||
|
description: "查詢聊天室列表,支援狀態篩選和分頁(自己的,之後再支援管理員吃全部)"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (ListRoomsResp) // 取得聊天室列表
|
||||||
|
@respdoc-400 (ErrorResp) "請求參數格式錯誤"
|
||||||
|
@respdoc-401 (ErrorResp) "權限不夠"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-404 (ErrorResp) "找不到聊天室"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler listRooms
|
||||||
|
get /rooms (ListRoomsReq) returns (ListRoomsResp)
|
||||||
|
|
||||||
|
// ==================== 成員管理 ====================
|
||||||
|
@doc(
|
||||||
|
summary: "添加成員到聊天室"
|
||||||
|
description: "將用戶添加到聊天室,需要管理員權限"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (MemberResp) // 取得聊天室列表
|
||||||
|
@respdoc-400 (ErrorResp) "請求參數格式錯誤"
|
||||||
|
@respdoc-401 (ErrorResp) "權限不夠"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-404 (ErrorResp) "找不到聊天室"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler addMember
|
||||||
|
post /rooms/:room_id/members (AddMemberReq) returns (MemberResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "移除聊天室成員"
|
||||||
|
description: "將成員從聊天室中移除,需要管理員權限或本人操作"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (RespOK)
|
||||||
|
@respdoc-401 (ErrorResp) "權限不夠"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-404 (ErrorResp) "找不到聊天室"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler removeMember
|
||||||
|
delete /rooms/:room_id/members/:uid (RemoveMemberReq) returns (RespOK)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "更新成員角色"
|
||||||
|
description: "更新成員在聊天室中的角色,需要管理員權限"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (MemberResp)
|
||||||
|
@respdoc-401 (ErrorResp) "權限不夠"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-404 (ErrorResp) "找不到聊天室"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler updateMemberRole
|
||||||
|
put /rooms/:room_id/members/:uid/role (UpdateMemberRoleReq) returns (MemberResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "查詢聊天室成員列表"
|
||||||
|
description: "查詢指定聊天室的所有成員"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (ListMembersResp)
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler listMembers
|
||||||
|
get /rooms/:room_id/members (RoomReq) returns (ListMembersResp)
|
||||||
|
|
||||||
|
// ==================== 用戶相關 ====================
|
||||||
|
@doc(
|
||||||
|
summary: "查詢用戶所在的聊天室"
|
||||||
|
description: "查詢當前用戶或指定用戶所在的所有聊天室"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (GetUserRoomsResp)
|
||||||
|
@respdoc-401 (ErrorResp) "權限不夠"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler getUserRooms
|
||||||
|
get /users/rooms (Authorization) returns (GetUserRoomsResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "檢查用戶是否在聊天室中"
|
||||||
|
description: "檢查指定用戶是否在某個聊天室中"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (IsMemberResp)
|
||||||
|
@respdoc-401 (ErrorResp) "權限不夠"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler checkUserInRoom
|
||||||
|
get /rooms/:room_id/members/:uid (RemoveMemberReq) returns (IsMemberResp)
|
||||||
|
|
||||||
|
// ==================== 訊息相關 ====================
|
||||||
|
@doc(
|
||||||
|
summary: "發送訊息"
|
||||||
|
description: "在聊天室中發送訊息,需要是聊天室成員"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (RespOK)
|
||||||
|
@respdoc-400 (ErrorResp) "請求參數格式錯誤"
|
||||||
|
@respdoc-401 (ErrorResp) "權限不夠"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler sendMessage
|
||||||
|
post /rooms/:room_id/messages (SendMessageReq) returns (RespOK)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "查詢訊息列表"
|
||||||
|
description: "查詢聊天室中的訊息列表,支援分頁和按日期篩選"
|
||||||
|
)
|
||||||
|
/*
|
||||||
|
@respdoc-200 (ListMessagesResp)
|
||||||
|
@respdoc-400 (ErrorResp) "請求參數格式錯誤"
|
||||||
|
@respdoc-401 (ErrorResp) "權限不夠"
|
||||||
|
@respdoc-403 (ErrorResp) "沒有登入"
|
||||||
|
@respdoc-500 (ErrorResp) // 伺服器內部錯誤
|
||||||
|
*/
|
||||||
|
@handler listMessages
|
||||||
|
get /rooms/:room_id/messages (ListMessagesReq) returns (ListMessagesResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -17,5 +17,6 @@ import (
|
||||||
"ping.api"
|
"ping.api"
|
||||||
"member.api"
|
"member.api"
|
||||||
"file_storage.api"
|
"file_storage.api"
|
||||||
|
"chat.api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,222 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Type: 通知事件 (Notification Event)
|
||||||
|
// =================================================================
|
||||||
|
type (
|
||||||
|
// CreateEventReq 創建通知事件請求
|
||||||
|
CreateEventReq {
|
||||||
|
Authorization
|
||||||
|
EventType string `json:"event_type" validate:"required"` // POST_PUBLISHED, COMMENT_ADDED, MENTIONED 等
|
||||||
|
ActorUID string `json:"actor_uid" validate:"required"` // 觸發者 UID
|
||||||
|
ObjectType string `json:"object_type" validate:"required"` // POST, COMMENT, USER 等
|
||||||
|
ObjectID string `json:"object_id" validate:"required"` // 對應物件 ID
|
||||||
|
Title string `json:"title" validate:"required"` // 顯示用標題
|
||||||
|
Body string `json:"body" validate:"required"` // 顯示用內容/摘要
|
||||||
|
Payload string `json:"payload,optional"` // JSON string(額外欄位)
|
||||||
|
Priority string `json:"priority,optional" validate:"omitempty,oneof=critical high normal low"` // 優先級,預設 normal
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventResp 通知事件回應
|
||||||
|
EventResp {
|
||||||
|
EventID string `json:"event_id"`
|
||||||
|
EventType string `json:"event_type"`
|
||||||
|
ActorUID string `json:"actor_uid"`
|
||||||
|
ObjectType string `json:"object_type"`
|
||||||
|
ObjectID string `json:"object_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
Priority string `json:"priority"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListEventsReq 查詢事件列表請求
|
||||||
|
ListEventsReq {
|
||||||
|
Authorization
|
||||||
|
ObjectID string `json:"object_id,optional"` // 物件 ID 篩選
|
||||||
|
ObjectType string `json:"object_type,optional"` // 物件類型篩選
|
||||||
|
Limit int `json:"limit,optional" validate:"omitempty,min=1,max=100"` // 限制數量,預設 20
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListEventsResp 事件列表回應
|
||||||
|
ListEventsResp {
|
||||||
|
Events []EventResp `json:"events"`
|
||||||
|
Total int64 `json:"total,optional"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Type: 用戶通知 (User Notification)
|
||||||
|
// =================================================================
|
||||||
|
type (
|
||||||
|
// CreateUserNotificationReq 創建用戶通知請求
|
||||||
|
CreateUserNotificationReq {
|
||||||
|
Authorization
|
||||||
|
UserID string `json:"user_id" validate:"required"` // 收通知的人
|
||||||
|
EventID string `json:"event_id" validate:"required"` // 對應 notification_event.event_id
|
||||||
|
TTL int `json:"ttl,optional"` // 過期時間(秒),預設 30 天
|
||||||
|
}
|
||||||
|
|
||||||
|
// BulkCreateNotificationsReq 批量創建通知請求
|
||||||
|
BulkCreateNotificationsReq {
|
||||||
|
Authorization
|
||||||
|
UserIDs []string `json:"user_ids" validate:"required,min=1,max=100"` // 用戶 UID 列表
|
||||||
|
EventID string `json:"event_id" validate:"required"` // 對應 notification_event.event_id
|
||||||
|
TTL int `json:"ttl,optional"` // 過期時間(秒),預設 30 天
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListNotificationsReq 查詢通知列表請求
|
||||||
|
ListNotificationsReq {
|
||||||
|
Authorization
|
||||||
|
Buckets []string `json:"buckets,optional"` // 分桶列表,例如 ["2025-11", "2025-10"],不提供則使用最近 3 個月
|
||||||
|
Limit int `json:"limit,optional" validate:"omitempty,min=1,max=100"` // 限制數量,預設 20
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotificationResp 用戶通知回應
|
||||||
|
NotificationResp {
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
Bucket string `json:"bucket"` // 分桶,例如 '2025-11' 或 '2025-11-17'
|
||||||
|
TS string `json:"ts"` // 通知時間,排序用(UTC0)
|
||||||
|
EventID string `json:"event_id"` // 對應 notification_event.event_id
|
||||||
|
Status string `json:"status"` // UNREAD / READ / ARCHIVED
|
||||||
|
ReadAt *string `json:"read_at,omitempty"` // 已讀時間(非必填)
|
||||||
|
Event EventResp `json:"event,omitempty"` // 關聯的事件資訊(可選)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListNotificationsResp 通知列表回應
|
||||||
|
ListNotificationsResp {
|
||||||
|
Notifications []NotificationResp `json:"notifications"`
|
||||||
|
Total int64 `json:"total,optional"`
|
||||||
|
UnreadCount int64 `json:"unread_count"` // 未讀數量
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkAsReadReq 標記已讀請求
|
||||||
|
MarkAsReadReq {
|
||||||
|
Authorization
|
||||||
|
Bucket string `json:"bucket" validate:"required"` // 分桶,例如 '2025-11-17'
|
||||||
|
TS string `json:"ts" validate:"required"` // 通知時間戳
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkAllAsReadReq 標記全部已讀請求
|
||||||
|
MarkAllAsReadReq {
|
||||||
|
Authorization
|
||||||
|
Buckets []string `json:"buckets,optional"` // 分桶列表,不提供則標記所有
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountUnreadResp 未讀數量回應
|
||||||
|
CountUnreadResp {
|
||||||
|
Count int64 `json:"count"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Type: 通知游標 (Notification Cursor)
|
||||||
|
// =================================================================
|
||||||
|
type (
|
||||||
|
// UpdateCursorReq 更新游標請求
|
||||||
|
UpdateCursorReq {
|
||||||
|
Authorization
|
||||||
|
LastSeenTS string `json:"last_seen_ts" validate:"required"` // 最後看到的時間戳
|
||||||
|
}
|
||||||
|
|
||||||
|
// CursorResp 游標回應
|
||||||
|
CursorResp {
|
||||||
|
UID string `json:"uid"`
|
||||||
|
LastSeenTS string `json:"last_seen_ts"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Service: 通知 API - 需要登入 (Notification Service)
|
||||||
|
// =================================================================
|
||||||
|
@server(
|
||||||
|
group: notification
|
||||||
|
prefix: /api/v1/notifications
|
||||||
|
schemes: https
|
||||||
|
timeout: 30s
|
||||||
|
middleware: AuthMiddleware
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
// ==================== 通知事件 ====================
|
||||||
|
@doc(
|
||||||
|
summary: "創建通知事件"
|
||||||
|
description: "創建一個新的通知事件,通常由系統內部調用"
|
||||||
|
)
|
||||||
|
@handler createEvent
|
||||||
|
post /events (CreateEventReq) returns (EventResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "取得通知事件"
|
||||||
|
description: "根據 event_id 取得通知事件的詳細資訊"
|
||||||
|
)
|
||||||
|
@handler getEvent
|
||||||
|
get /events/:event_id (Authorization) returns (EventResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "查詢通知事件列表"
|
||||||
|
description: "查詢通知事件列表,支援按物件篩選"
|
||||||
|
)
|
||||||
|
@handler listEvents
|
||||||
|
get /events (ListEventsReq) returns (ListEventsResp)
|
||||||
|
|
||||||
|
// ==================== 用戶通知 ====================
|
||||||
|
@doc(
|
||||||
|
summary: "創建用戶通知"
|
||||||
|
description: "為單個用戶創建通知"
|
||||||
|
)
|
||||||
|
@handler createUserNotification
|
||||||
|
post /users/:user_id/notifications (CreateUserNotificationReq) returns (NotificationResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "批量創建用戶通知"
|
||||||
|
description: "為多個用戶批量創建通知"
|
||||||
|
)
|
||||||
|
@handler bulkCreateNotifications
|
||||||
|
post /notifications/bulk (BulkCreateNotificationsReq) returns (RespOK)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "查詢用戶通知列表"
|
||||||
|
description: "查詢當前用戶的通知列表,支援按分桶篩選和分頁"
|
||||||
|
)
|
||||||
|
@handler listNotifications
|
||||||
|
get /me/notifications (ListNotificationsReq) returns (ListNotificationsResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "標記通知為已讀"
|
||||||
|
description: "標記單個通知為已讀狀態"
|
||||||
|
)
|
||||||
|
@handler markAsRead
|
||||||
|
put /me/notifications/:bucket/:ts/read (MarkAsReadReq) returns (RespOK)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "標記所有通知為已讀"
|
||||||
|
description: "標記指定分桶或所有通知為已讀狀態"
|
||||||
|
)
|
||||||
|
@handler markAllAsRead
|
||||||
|
put /me/notifications/read (MarkAllAsReadReq) returns (RespOK)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "查詢未讀通知數量"
|
||||||
|
description: "查詢當前用戶的未讀通知數量"
|
||||||
|
)
|
||||||
|
@handler countUnread
|
||||||
|
get /me/notifications/unread/count (Authorization) returns (CountUnreadResp)
|
||||||
|
|
||||||
|
// ==================== 通知游標 ====================
|
||||||
|
@doc(
|
||||||
|
summary: "取得通知游標"
|
||||||
|
description: "取得當前用戶的通知游標資訊"
|
||||||
|
)
|
||||||
|
@handler getCursor
|
||||||
|
get /me/notifications/cursor (Authorization) returns (CursorResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "更新通知游標"
|
||||||
|
description: "更新當前用戶的通知游標,用於追蹤最後查看的通知位置"
|
||||||
|
)
|
||||||
|
@handler updateCursor
|
||||||
|
put /me/notifications/cursor (UpdateCursorReq) returns (CursorResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,376 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Type: 權限 (Permission)
|
||||||
|
// =================================================================
|
||||||
|
type (
|
||||||
|
// PermissionResp 權限回應
|
||||||
|
PermissionResp {
|
||||||
|
ID string `json:"id"`
|
||||||
|
ParentID string `json:"parent_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
HTTPPath string `json:"http_path,omitempty"`
|
||||||
|
HTTPMethod string `json:"http_method,omitempty"`
|
||||||
|
Status string `json:"status"` // active, inactive
|
||||||
|
Type string `json:"type"` // menu, button, api
|
||||||
|
}
|
||||||
|
|
||||||
|
// PermissionTreeNode 權限樹節點
|
||||||
|
PermissionTreeNode {
|
||||||
|
PermissionResp
|
||||||
|
Children []PermissionTreeNode `json:"children,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPermissionByHTTPReq 根據 HTTP 資訊查詢權限請求
|
||||||
|
GetPermissionByHTTPReq {
|
||||||
|
Authorization
|
||||||
|
Path string `json:"path" validate:"required"` // HTTP 路徑
|
||||||
|
Method string `json:"method" validate:"required"` // HTTP 方法
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpandPermissionsReq 展開權限請求
|
||||||
|
ExpandPermissionsReq {
|
||||||
|
Authorization
|
||||||
|
Permissions []string `json:"permissions" validate:"required,min=1"` // 權限名稱列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpandPermissionsResp 展開權限回應
|
||||||
|
ExpandPermissionsResp {
|
||||||
|
Permissions []string `json:"permissions"` // 展開後的權限列表(包含父權限)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsersByPermissionReq 根據權限查詢用戶請求
|
||||||
|
GetUsersByPermissionReq {
|
||||||
|
Authorization
|
||||||
|
Permissions []string `json:"permissions" validate:"required,min=1"` // 權限名稱列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsersByPermissionResp 根據權限查詢用戶回應
|
||||||
|
GetUsersByPermissionResp {
|
||||||
|
UserUIDs []string `json:"user_uids"` // 擁有指定權限的用戶 UID 列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPermissionsResp 權限列表回應
|
||||||
|
ListPermissionsResp {
|
||||||
|
Permissions []PermissionResp `json:"permissions"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Type: 角色 (Role)
|
||||||
|
// =================================================================
|
||||||
|
type (
|
||||||
|
// CreateRoleReq 創建角色請求
|
||||||
|
CreateRoleReq {
|
||||||
|
Authorization
|
||||||
|
ClientID int `json:"client_id" validate:"required"`
|
||||||
|
Name string `json:"name" validate:"required,min=1,max=100"`
|
||||||
|
Permissions map[string]string `json:"permissions,optional"` // 權限映射,key 為權限名稱,value 為狀態 (open, close)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRoleReq 更新角色請求
|
||||||
|
UpdateRoleReq {
|
||||||
|
Authorization
|
||||||
|
Name *string `json:"name,optional" validate:"omitempty,min=1,max=100"`
|
||||||
|
Status *string `json:"status,optional" validate:"omitempty,oneof=active inactive"`
|
||||||
|
Permissions map[string]string `json:"permissions,optional"` // 權限映射
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleResp 角色回應
|
||||||
|
RoleResp {
|
||||||
|
ID string `json:"id"`
|
||||||
|
UID string `json:"uid"`
|
||||||
|
ClientID int `json:"client_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"` // active, inactive
|
||||||
|
Permissions map[string]string `json:"permissions"` // 權限映射
|
||||||
|
CreateTime string `json:"create_time"`
|
||||||
|
UpdateTime string `json:"update_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleWithUserCountResp 角色回應(含用戶數量)
|
||||||
|
RoleWithUserCountResp {
|
||||||
|
RoleResp
|
||||||
|
UserCount int `json:"user_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRolesReq 查詢角色列表請求
|
||||||
|
ListRolesReq {
|
||||||
|
Authorization
|
||||||
|
ClientID int `json:"client_id,optional"`
|
||||||
|
Name string `json:"name,optional"`
|
||||||
|
Status string `json:"status,optional" validate:"omitempty,oneof=active inactive"`
|
||||||
|
Permissions []string `json:"permissions,optional"` // 權限名稱列表(篩選擁有這些權限的角色)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRolesResp 角色列表回應
|
||||||
|
ListRolesResp {
|
||||||
|
Roles []RoleWithUserCountResp `json:"roles"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageRolesReq 分頁查詢角色請求
|
||||||
|
PageRolesReq {
|
||||||
|
Authorization
|
||||||
|
ClientID int `json:"client_id,optional"`
|
||||||
|
Name string `json:"name,optional"`
|
||||||
|
Status string `json:"status,optional" validate:"omitempty,oneof=active inactive"`
|
||||||
|
Permissions []string `json:"permissions,optional"`
|
||||||
|
Page int `json:"page,optional" validate:"omitempty,min=1"` // 頁碼,從 1 開始
|
||||||
|
Size int `json:"size,optional" validate:"omitempty,min=1,max=100"` // 每頁大小
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageRolesResp 角色分頁回應
|
||||||
|
PageRolesResp {
|
||||||
|
List []RoleWithUserCountResp `json:"list"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Type: 角色權限 (Role Permission)
|
||||||
|
// =================================================================
|
||||||
|
type (
|
||||||
|
// GetRolePermissionsResp 角色權限回應
|
||||||
|
GetRolePermissionsResp {
|
||||||
|
RoleUID string `json:"role_uid"`
|
||||||
|
Permissions map[string]string `json:"permissions"` // 權限映射
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserPermissionsResp 用戶權限回應
|
||||||
|
GetUserPermissionsResp {
|
||||||
|
UserUID string `json:"user_uid"`
|
||||||
|
RoleUID string `json:"role_uid"`
|
||||||
|
RoleName string `json:"role_name"`
|
||||||
|
Permissions map[string]string `json:"permissions"` // 權限映射
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRolePermissionsReq 更新角色權限請求
|
||||||
|
UpdateRolePermissionsReq {
|
||||||
|
Authorization
|
||||||
|
Permissions map[string]string `json:"permissions" validate:"required"` // 權限映射
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckPermissionReq 檢查權限請求
|
||||||
|
CheckPermissionReq {
|
||||||
|
Authorization
|
||||||
|
Path string `json:"path" validate:"required"` // HTTP 路徑
|
||||||
|
Method string `json:"method" validate:"required"` // HTTP 方法
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckPermissionResp 檢查權限回應
|
||||||
|
CheckPermissionResp {
|
||||||
|
Allowed bool `json:"allowed"` // 是否有權限
|
||||||
|
PermissionName string `json:"permission_name,omitempty"` // 權限名稱
|
||||||
|
PlainCode bool `json:"plain_code"` // 是否有 plain_code 權限(特殊邏輯)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Type: 用戶角色 (User Role)
|
||||||
|
// =================================================================
|
||||||
|
type (
|
||||||
|
// AssignRoleReq 指派角色請求
|
||||||
|
AssignRoleReq {
|
||||||
|
Authorization
|
||||||
|
UserUID string `json:"user_uid" validate:"required"`
|
||||||
|
RoleUID string `json:"role_uid" validate:"required"`
|
||||||
|
Brand string `json:"brand,optional"` // 品牌標識
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserRoleResp 用戶角色回應
|
||||||
|
UserRoleResp {
|
||||||
|
UserUID string `json:"user_uid"`
|
||||||
|
RoleUID string `json:"role_uid"`
|
||||||
|
Brand string `json:"brand"`
|
||||||
|
CreateTime string `json:"create_time"`
|
||||||
|
UpdateTime string `json:"update_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUserRolesReq 查詢用戶角色列表請求
|
||||||
|
ListUserRolesReq {
|
||||||
|
Authorization
|
||||||
|
Brand string `json:"brand,optional"`
|
||||||
|
RoleID string `json:"role_id,optional"`
|
||||||
|
Status string `json:"status,optional" validate:"omitempty,oneof=active inactive"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUserRolesResp 用戶角色列表回應
|
||||||
|
ListUserRolesResp {
|
||||||
|
UserRoles []UserRoleResp `json:"user_roles"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsersByRoleResp 角色用戶列表回應
|
||||||
|
GetUsersByRoleResp {
|
||||||
|
UserRoles []UserRoleResp `json:"user_roles"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// Service: 權限管理 API - 需要登入 (Permission Service)
|
||||||
|
// =================================================================
|
||||||
|
@server(
|
||||||
|
group: permission
|
||||||
|
prefix: /api/v1/permissions
|
||||||
|
schemes: https
|
||||||
|
timeout: 30s
|
||||||
|
middleware: AuthMiddleware
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
// ==================== 權限管理 ====================
|
||||||
|
@doc(
|
||||||
|
summary: "取得所有權限"
|
||||||
|
description: "取得系統中所有啟用的權限列表"
|
||||||
|
)
|
||||||
|
@handler getAllPermissions
|
||||||
|
get / (Authorization) returns (ListPermissionsResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "取得權限樹"
|
||||||
|
description: "取得以樹狀結構組織的權限列表"
|
||||||
|
)
|
||||||
|
@handler getPermissionTree
|
||||||
|
get /tree (Authorization) returns (PermissionTreeNode)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "根據 HTTP 資訊取得權限"
|
||||||
|
description: "根據 HTTP 路徑和方法取得對應的權限資訊"
|
||||||
|
)
|
||||||
|
@handler getPermissionByHTTP
|
||||||
|
post /by-http (GetPermissionByHTTPReq) returns (PermissionResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "展開權限"
|
||||||
|
description: "展開權限列表,包含所有父權限"
|
||||||
|
)
|
||||||
|
@handler expandPermissions
|
||||||
|
post /expand (ExpandPermissionsReq) returns (ExpandPermissionsResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "根據權限取得用戶"
|
||||||
|
description: "取得擁有指定權限的所有用戶 UID"
|
||||||
|
)
|
||||||
|
@handler getUsersByPermission
|
||||||
|
post /users (GetUsersByPermissionReq) returns (GetUsersByPermissionResp)
|
||||||
|
|
||||||
|
// ==================== 角色管理 ====================
|
||||||
|
@doc(
|
||||||
|
summary: "創建角色"
|
||||||
|
description: "創建一個新角色並設定權限"
|
||||||
|
)
|
||||||
|
@handler createRole
|
||||||
|
post /roles (CreateRoleReq) returns (RoleResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "更新角色"
|
||||||
|
description: "更新角色的名稱、狀態或權限"
|
||||||
|
)
|
||||||
|
@handler updateRole
|
||||||
|
put /roles/:uid (UpdateRoleReq) returns (RoleResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "刪除角色"
|
||||||
|
description: "刪除指定角色(軟刪除,設為 inactive)"
|
||||||
|
)
|
||||||
|
@handler deleteRole
|
||||||
|
delete /roles/:uid (Authorization) returns (RespOK)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "取得角色"
|
||||||
|
description: "根據 UID 取得角色的詳細資訊"
|
||||||
|
)
|
||||||
|
@handler getRole
|
||||||
|
get /roles/:uid (Authorization) returns (RoleResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "查詢角色列表"
|
||||||
|
description: "查詢角色列表,支援多種篩選條件"
|
||||||
|
)
|
||||||
|
@handler listRoles
|
||||||
|
get /roles (ListRolesReq) returns (ListRolesResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "分頁查詢角色"
|
||||||
|
description: "分頁查詢角色列表,支援多種篩選條件"
|
||||||
|
)
|
||||||
|
@handler pageRoles
|
||||||
|
get /roles/page (PageRolesReq) returns (PageRolesResp)
|
||||||
|
|
||||||
|
// ==================== 角色權限管理 ====================
|
||||||
|
@doc(
|
||||||
|
summary: "取得角色權限"
|
||||||
|
description: "取得指定角色的所有權限"
|
||||||
|
)
|
||||||
|
@handler getRolePermissions
|
||||||
|
get /roles/:role_uid (Authorization) returns (GetRolePermissionsResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "取得用戶權限"
|
||||||
|
description: "取得指定用戶的所有權限(透過角色)"
|
||||||
|
)
|
||||||
|
@handler getUserPermissions
|
||||||
|
get /users/:user_uid (Authorization) returns (GetUserPermissionsResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "更新角色權限"
|
||||||
|
description: "更新指定角色的權限列表"
|
||||||
|
)
|
||||||
|
@handler updateRolePermissions
|
||||||
|
put /roles/:role_uid (UpdateRolePermissionsReq) returns (GetRolePermissionsResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "檢查權限"
|
||||||
|
description: "檢查當前用戶是否有執行指定 HTTP 操作的權限"
|
||||||
|
)
|
||||||
|
@handler checkPermission
|
||||||
|
post /check (CheckPermissionReq) returns (CheckPermissionResp)
|
||||||
|
|
||||||
|
// ==================== 用戶角色管理 ====================
|
||||||
|
@doc(
|
||||||
|
summary: "指派角色給用戶"
|
||||||
|
description: "為用戶指派一個角色"
|
||||||
|
)
|
||||||
|
@handler assignRole
|
||||||
|
post /users/:user_uid/roles (AssignRoleReq) returns (UserRoleResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "更新用戶角色"
|
||||||
|
description: "更新用戶的角色(替換現有角色)"
|
||||||
|
)
|
||||||
|
@handler updateUserRole
|
||||||
|
put /users/:user_uid/roles/:role_uid (Authorization) returns (UserRoleResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "移除用戶角色"
|
||||||
|
description: "移除用戶的角色"
|
||||||
|
)
|
||||||
|
@handler removeUserRole
|
||||||
|
delete /users/:user_uid/roles (Authorization) returns (RespOK)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "取得用戶角色"
|
||||||
|
description: "取得指定用戶的角色資訊"
|
||||||
|
)
|
||||||
|
@handler getUserRole
|
||||||
|
get /users/:user_uid/roles (Authorization) returns (UserRoleResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "取得角色的所有用戶"
|
||||||
|
description: "取得擁有指定角色的所有用戶"
|
||||||
|
)
|
||||||
|
@handler getUsersByRole
|
||||||
|
get /roles/:role_uid/users (Authorization) returns (GetUsersByRoleResp)
|
||||||
|
|
||||||
|
@doc(
|
||||||
|
summary: "查詢用戶角色列表"
|
||||||
|
description: "查詢用戶角色列表,支援多種篩選條件"
|
||||||
|
)
|
||||||
|
@handler listUserRoles
|
||||||
|
get /user-roles (ListUserRolesReq) returns (ListUserRolesResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 添加成員到聊天室
|
||||||
|
func AddMemberHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.AddMemberReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//if err := svcCtx.Validate.ValidateAll(req); err != nil {
|
||||||
|
// e := errs.InvalidFormat(err.Error())
|
||||||
|
// httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Status{
|
||||||
|
// Code: int64(e.FullCode()),
|
||||||
|
// Message: err.Error(),
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
|
||||||
|
l := chat.NewAddMemberLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.AddMember(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 檢查用戶是否在聊天室中
|
||||||
|
func CheckUserInRoomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.RemoveMemberReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewCheckUserInRoomLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.CheckUserInRoom(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 創建聊天室
|
||||||
|
func CreateRoomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.CreateRoomReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewCreateRoomLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.CreateRoom(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 刪除聊天室
|
||||||
|
func DeleteRoomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.RoomReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewDeleteRoomLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.DeleteRoom(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 取得聊天室資訊
|
||||||
|
func GetRoomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.RoomReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewGetRoomLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.GetRoom(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 查詢用戶所在的聊天室
|
||||||
|
func GetUserRoomsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.Authorization
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewGetUserRoomsLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.GetUserRooms(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 查詢聊天室成員列表
|
||||||
|
func ListMembersHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.RoomReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewListMembersLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.ListMembers(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 查詢訊息列表
|
||||||
|
func ListMessagesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ListMessagesReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewListMessagesLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.ListMessages(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 查詢聊天室列表
|
||||||
|
func ListRoomsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ListRoomsReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewListRoomsLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.ListRooms(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 移除聊天室成員
|
||||||
|
func RemoveMemberHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.RemoveMemberReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewRemoveMemberLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.RemoveMember(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 發送訊息
|
||||||
|
func SendMessageHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.SendMessageReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewSendMessageLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.SendMessage(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 更新成員角色
|
||||||
|
func UpdateMemberRoleHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.UpdateMemberRoleReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewUpdateMemberRoleLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.UpdateMemberRole(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"backend/internal/logic/chat"
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 更新聊天室資訊
|
||||||
|
func UpdateRoomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.UpdateRoomReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
e := errs.InputInvalidFormatError(err.Error())
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := chat.NewUpdateRoomLogic(r.Context(), svcCtx)
|
||||||
|
resp, err := l.UpdateRoom(&req)
|
||||||
|
if err != nil {
|
||||||
|
e := errs.FromError(err)
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, e.HTTPStatus(), types.Resp{
|
||||||
|
Code: e.DisplayCode(),
|
||||||
|
Message: e.Error(),
|
||||||
|
Error: e.Unwrap(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
httpx.WriteJsonCtx(r.Context(), w, http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Code generated by goctl. DO NOT EDIT.
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
// goctl 1.9.0
|
// goctl 1.8.5
|
||||||
|
|
||||||
package handler
|
package handler
|
||||||
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
auth "backend/internal/handler/auth"
|
auth "backend/internal/handler/auth"
|
||||||
|
chat "backend/internal/handler/chat"
|
||||||
fileStorage "backend/internal/handler/fileStorage"
|
fileStorage "backend/internal/handler/fileStorage"
|
||||||
ping "backend/internal/handler/ping"
|
ping "backend/internal/handler/ping"
|
||||||
user "backend/internal/handler/user"
|
user "backend/internal/handler/user"
|
||||||
|
|
@ -60,6 +61,94 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||||
rest.WithTimeout(10000*time.Millisecond),
|
rest.WithTimeout(10000*time.Millisecond),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthMiddleware},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
// 創建聊天室
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/rooms",
|
||||||
|
Handler: chat.CreateRoomHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 查詢聊天室列表
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/rooms",
|
||||||
|
Handler: chat.ListRoomsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 取得聊天室資訊
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/rooms/:room_id",
|
||||||
|
Handler: chat.GetRoomHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 更新聊天室資訊
|
||||||
|
Method: http.MethodPut,
|
||||||
|
Path: "/rooms/:room_id",
|
||||||
|
Handler: chat.UpdateRoomHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 刪除聊天室
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/rooms/:room_id",
|
||||||
|
Handler: chat.DeleteRoomHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 添加成員到聊天室
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/rooms/:room_id/members",
|
||||||
|
Handler: chat.AddMemberHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 查詢聊天室成員列表
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/rooms/:room_id/members",
|
||||||
|
Handler: chat.ListMembersHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 移除聊天室成員
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/rooms/:room_id/members/:uid",
|
||||||
|
Handler: chat.RemoveMemberHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 檢查用戶是否在聊天室中
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/rooms/:room_id/members/:uid",
|
||||||
|
Handler: chat.CheckUserInRoomHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 更新成員角色
|
||||||
|
Method: http.MethodPut,
|
||||||
|
Path: "/rooms/:room_id/members/:uid/role",
|
||||||
|
Handler: chat.UpdateMemberRoleHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 發送訊息
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/rooms/:room_id/messages",
|
||||||
|
Handler: chat.SendMessageHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 查詢訊息列表
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/rooms/:room_id/messages",
|
||||||
|
Handler: chat.ListMessagesHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 查詢用戶所在的聊天室
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/users/rooms",
|
||||||
|
Handler: chat.GetUserRoomsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithPrefix("/api/v1/chat"),
|
||||||
|
rest.WithTimeout(30000*time.Millisecond),
|
||||||
|
)
|
||||||
|
|
||||||
server.AddRoutes(
|
server.AddRoutes(
|
||||||
rest.WithMiddlewares(
|
rest.WithMiddlewares(
|
||||||
[]rest.Middleware{serverCtx.AuthMiddleware},
|
[]rest.Middleware{serverCtx.AuthMiddleware},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddMemberLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAddMemberLogic 添加成員到聊天室
|
||||||
|
func NewAddMemberLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddMemberLogic {
|
||||||
|
return &AddMemberLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *AddMemberLogic) AddMember(req *types.AddMemberReq) (resp *types.MemberResp, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CheckUserInRoomLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查用戶是否在聊天室中
|
||||||
|
func NewCheckUserInRoomLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CheckUserInRoomLogic {
|
||||||
|
return &CheckUserInRoomLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *CheckUserInRoomLogic) CheckUserInRoom(req *types.RemoveMemberReq) (resp *types.IsMemberResp, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateRoomLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCreateRoomLogic 創建聊天室
|
||||||
|
func NewCreateRoomLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateRoomLogic {
|
||||||
|
return &CreateRoomLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *CreateRoomLogic) CreateRoom(req *types.CreateRoomReq) (resp *types.RoomResp, err error) {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeleteRoomLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刪除聊天室
|
||||||
|
func NewDeleteRoomLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteRoomLogic {
|
||||||
|
return &DeleteRoomLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *DeleteRoomLogic) DeleteRoom(req *types.RoomReq) (resp *types.RespOK, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetRoomLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取得聊天室資訊
|
||||||
|
func NewGetRoomLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRoomLogic {
|
||||||
|
return &GetRoomLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GetRoomLogic) GetRoom(req *types.RoomReq) (resp *types.RoomResp, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetUserRoomsLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查詢用戶所在的聊天室
|
||||||
|
func NewGetUserRoomsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserRoomsLogic {
|
||||||
|
return &GetUserRoomsLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GetUserRoomsLogic) GetUserRooms(req *types.Authorization) (resp *types.GetUserRoomsResp, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListMembersLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查詢聊天室成員列表
|
||||||
|
func NewListMembersLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListMembersLogic {
|
||||||
|
return &ListMembersLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ListMembersLogic) ListMembers(req *types.RoomReq) (resp *types.ListMembersResp, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListMessagesLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查詢訊息列表
|
||||||
|
func NewListMessagesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListMessagesLogic {
|
||||||
|
return &ListMessagesLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ListMessagesLogic) ListMessages(req *types.ListMessagesReq) (resp *types.ListMessagesResp, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListRoomsLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查詢聊天室列表
|
||||||
|
func NewListRoomsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListRoomsLogic {
|
||||||
|
return &ListRoomsLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ListRoomsLogic) ListRooms(req *types.ListRoomsReq) (resp *types.ListRoomsResp, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RemoveMemberLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除聊天室成員
|
||||||
|
func NewRemoveMemberLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RemoveMemberLogic {
|
||||||
|
return &RemoveMemberLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *RemoveMemberLogic) RemoveMember(req *types.RemoveMemberReq) (resp *types.RespOK, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SendMessageLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 發送訊息
|
||||||
|
func NewSendMessageLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendMessageLogic {
|
||||||
|
return &SendMessageLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SendMessageLogic) SendMessage(req *types.SendMessageReq) (resp *types.RespOK, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateMemberRoleLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新成員角色
|
||||||
|
func NewUpdateMemberRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateMemberRoleLogic {
|
||||||
|
return &UpdateMemberRoleLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *UpdateMemberRoleLogic) UpdateMemberRole(req *types.UpdateMemberRoleReq) (resp *types.MemberResp, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"backend/internal/svc"
|
||||||
|
"backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UpdateRoomLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新聊天室資訊
|
||||||
|
func NewUpdateRoomLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateRoomLogic {
|
||||||
|
return &UpdateRoomLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *UpdateRoomLogic) UpdateRoom(req *types.UpdateRoomReq) (resp *types.RoomResp, err error) {
|
||||||
|
// todo: add your logic here and delete this line
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -65,3 +65,7 @@ func initCassandraDB(c *config.Config) (*cassandra.DB, error) {
|
||||||
|
|
||||||
return cassandra.New(opts...)
|
return cassandra.New(opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MustRoomUseCase(c *config.Config, logger errs.Logger) usecase.R {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
// Code generated by goctl. DO NOT EDIT.
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
// goctl 1.9.0
|
// goctl 1.8.5
|
||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
|
type AddMemberReq struct {
|
||||||
|
Authorization
|
||||||
|
RoomID string `json:"room_id" validate:"required"`
|
||||||
|
UID string `json:"uid" validate:"required"` // 要添加的用戶 UID
|
||||||
|
}
|
||||||
|
|
||||||
type Authorization struct {
|
type Authorization struct {
|
||||||
Authorization string `header:"Authorization" validate:"required"`
|
Authorization string `header:"Authorization" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
@ -10,12 +16,59 @@ type Authorization struct {
|
||||||
type BaseReq struct {
|
type BaseReq struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateRoomReq struct {
|
||||||
|
Authorization
|
||||||
|
Name string `json:"name" validate:"required,min=1,max=100"` // 聊天室名稱
|
||||||
|
Status string `json:"status,optional" validate:"omitempty,oneof=active archived"` // 狀態,預設為 active
|
||||||
|
}
|
||||||
|
|
||||||
type CredentialsPayload struct {
|
type CredentialsPayload struct {
|
||||||
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼 (後端應使用 bcrypt 進行雜湊)
|
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼 (後端應使用 bcrypt 進行雜湊)
|
||||||
PasswordConfirm string `json:"password_confirm" validate:"eqfield=Password"` // 確認密碼
|
PasswordConfirm string `json:"password_confirm" validate:"eqfield=Password"` // 確認密碼
|
||||||
AccountType string `json:"account_type" validate:"required,oneof=email phone any"` // 帳號型別 email phone any
|
AccountType string `json:"account_type" validate:"required,oneof=email phone any"` // 帳號型別 email phone any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetUserRoomsResp struct {
|
||||||
|
Rooms []RoomResp `json:"rooms"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IsMemberResp struct {
|
||||||
|
IsMember bool `json:"is_member"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListMembersResp struct {
|
||||||
|
Members []Member `json:"members"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListMessagesReq struct {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id" validate:"required"`
|
||||||
|
BucketDay string `json:"bucket_day,optional"` // yyyyMMdd,不提供則使用今天
|
||||||
|
PageSize int64 `json:"page_size,optional" validate:"omitempty,min=1,max=100"` // 每頁大小,預設 20
|
||||||
|
LastTS int64 `json:"last_ts,optional"` // 用於 cursor-based pagination
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListMessagesResp struct {
|
||||||
|
Messages []MessageResp `json:"messages"`
|
||||||
|
Total int64 `json:"total,optional"` // 總數(僅第一頁返回)
|
||||||
|
LastTS int64 `json:"last_ts,optional"` // 用於下一頁查詢
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListRoomsReq struct {
|
||||||
|
Authorization
|
||||||
|
Status string `json:"status,optional" validate:"omitempty,oneof=active archived"` // 狀態篩選
|
||||||
|
PageSize int `json:"page_size,optional" validate:"omitempty,min=1,max=100"` // 每頁大小,預設 20
|
||||||
|
LastID string `json:"last_id,optional"` // 用於 cursor-based pagination
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListRoomsResp struct {
|
||||||
|
Rooms []RoomResp `json:"rooms"`
|
||||||
|
LastID string `json:"last_id,optional"` // 用於下一頁查詢
|
||||||
|
Total int64 `json:"total,optional"` // 總數(僅第一頁返回)
|
||||||
|
}
|
||||||
|
|
||||||
type LoginReq struct {
|
type LoginReq struct {
|
||||||
AuthMethod string `json:"auth_method" validate:"required,oneof=credentials platform"` // 驗證類型 credentials platform
|
AuthMethod string `json:"auth_method" validate:"required,oneof=credentials platform"` // 驗證類型 credentials platform
|
||||||
LoginID string `json:"login_id" validate:"required,min=3,max=50"` // 信箱或手機號碼
|
LoginID string `json:"login_id" validate:"required,min=3,max=50"` // 信箱或手機號碼
|
||||||
|
|
@ -30,6 +83,28 @@ type LoginResp struct {
|
||||||
TokenType string `json:"token_type"` // 通常固定為 "Bearer"
|
TokenType string `json:"token_type"` // 通常固定為 "Bearer"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Member struct {
|
||||||
|
UID string `json:"uid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Avatar string `json:"avatar"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemberResp struct {
|
||||||
|
RoomID string `json:"room_id"`
|
||||||
|
UID string `json:"uid"`
|
||||||
|
Role string `json:"role"`
|
||||||
|
JoinedAt string `json:"joined_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageResp struct {
|
||||||
|
RoomID string `json:"room_id"`
|
||||||
|
BucketDay string `json:"bucket_day"` // yyyyMMdd
|
||||||
|
TS int64 `json:"ts"` // timestamp
|
||||||
|
UID string `json:"uid"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
type MyInfo struct {
|
type MyInfo struct {
|
||||||
Platform string `json:"platform"` // 註冊平台
|
Platform string `json:"platform"` // 註冊平台
|
||||||
UID string `json:"uid"` // 用戶 UID
|
UID string `json:"uid"` // 用戶 UID
|
||||||
|
|
@ -76,6 +151,12 @@ type RefreshTokenResp struct {
|
||||||
TokenType string `json:"token_type"`
|
TokenType string `json:"token_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RemoveMemberReq struct {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id" validate:"required"`
|
||||||
|
UID string `path:"uid" validate:"required"` // 要移除的用戶 UID
|
||||||
|
}
|
||||||
|
|
||||||
type RequestPasswordResetReq struct {
|
type RequestPasswordResetReq struct {
|
||||||
Identifier string `json:"identifier" validate:"required"` // 使用者帳號 (信箱或手機)
|
Identifier string `json:"identifier" validate:"required"` // 使用者帳號 (信箱或手機)
|
||||||
AccountType string `json:"account_type" validate:"required,oneof=email phone"`
|
AccountType string `json:"account_type" validate:"required,oneof=email phone"`
|
||||||
|
|
@ -104,6 +185,33 @@ type Resp struct {
|
||||||
type RespOK struct {
|
type RespOK struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RoomReq struct {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoomResp struct {
|
||||||
|
RoomID string `json:"room_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SendMessageReq struct {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id" validate:"required"`
|
||||||
|
Content string `json:"content" validate:"required,min=1,max=5000"` // 訊息內容
|
||||||
|
}
|
||||||
|
|
||||||
|
type SendMessageResp struct {
|
||||||
|
RoomID string `json:"room_id"`
|
||||||
|
BucketDay string `json:"bucket_day"` // yyyyMMdd
|
||||||
|
TS int64 `json:"ts"` // timestamp
|
||||||
|
UID string `json:"uid"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
type SubmitVerificationCodeReq struct {
|
type SubmitVerificationCodeReq struct {
|
||||||
Account string `json:"account" validate:"required`
|
Account string `json:"account" validate:"required`
|
||||||
Purpose string `json:"purpose" validate:"required,oneof=email_verification phone_verification"`
|
Purpose string `json:"purpose" validate:"required,oneof=email_verification phone_verification"`
|
||||||
|
|
@ -111,6 +219,13 @@ type SubmitVerificationCodeReq struct {
|
||||||
Authorization
|
Authorization
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateMemberRoleReq struct {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id" validate:"required"`
|
||||||
|
UID string `path:"uid" validate:"required"`
|
||||||
|
Role string `json:"role" validate:"required,oneof=admin member"`
|
||||||
|
}
|
||||||
|
|
||||||
type UpdatePasswordReq struct {
|
type UpdatePasswordReq struct {
|
||||||
CurrentPassword string `json:"current_password" validate:"required"`
|
CurrentPassword string `json:"current_password" validate:"required"`
|
||||||
NewPassword string `json:"new_password" validate:"required,min=8,max=128"`
|
NewPassword string `json:"new_password" validate:"required,min=8,max=128"`
|
||||||
|
|
@ -118,6 +233,13 @@ type UpdatePasswordReq struct {
|
||||||
Authorization
|
Authorization
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateRoomReq struct {
|
||||||
|
Authorization
|
||||||
|
RoomID string `path:"room_id" validate:"required"`
|
||||||
|
Name *string `json:"name,optional" validate:"omitempty,min=1,max=100"`
|
||||||
|
Status *string `json:"status,optional" validate:"omitempty,oneof=active archived"`
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateUserInfoReq struct {
|
type UpdateUserInfoReq struct {
|
||||||
AvatarURL *string `json:"avatar_url,optional"` // 頭像 URL
|
AvatarURL *string `json:"avatar_url,optional"` // 頭像 URL
|
||||||
FullName *string `json:"full_name,optional"` // 用戶全名
|
FullName *string `json:"full_name,optional"` // 用戶全名
|
||||||
|
|
|
||||||
|
|
@ -18,4 +18,3 @@ type MessageDedup struct {
|
||||||
func (m MessageDedup) TableName() string {
|
func (m MessageDedup) TableName() string {
|
||||||
return "message_dedup"
|
return "message_dedup"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RoomUseCase 定義聊天室相關的業務邏輯介面
|
||||||
|
type RoomUseCase interface {
|
||||||
|
// CreateRoom 創建聊天室
|
||||||
|
CreateRoom(ctx context.Context, req CreateRoomReq) (*Room, error)
|
||||||
|
// GetRoom 取得聊天室資訊
|
||||||
|
GetRoom(ctx context.Context, req GetRoomReq) (*Room, error)
|
||||||
|
// UpdateRoom 更新聊天室資訊
|
||||||
|
UpdateRoom(ctx context.Context, req UpdateRoomReq) (*Room, error)
|
||||||
|
// DeleteRoom 刪除聊天室
|
||||||
|
DeleteRoom(ctx context.Context, req DeleteRoomReq) error
|
||||||
|
// ListRooms 查詢聊天室列表(分頁)
|
||||||
|
ListRooms(ctx context.Context, req ListRoomsReq) ([]Room, string, int64, error)
|
||||||
|
// IsUserInRoom 檢查用戶是否在聊天室中
|
||||||
|
IsUserInRoom(ctx context.Context, req IsUserInRoomReq) (bool, error)
|
||||||
|
|
||||||
|
// AddMember 添加成員到聊天室
|
||||||
|
AddMember(ctx context.Context, req AddMemberReq) (*Member, error)
|
||||||
|
// RemoveMember 移除聊天室成員
|
||||||
|
RemoveMember(ctx context.Context, req RemoveMemberReq) error
|
||||||
|
// UpdateMemberRole 更新成員角色
|
||||||
|
UpdateMemberRole(ctx context.Context, req UpdateMemberRoleReq) (*Member, error)
|
||||||
|
// ListMembers 查詢聊天室成員列表
|
||||||
|
ListMembers(ctx context.Context, req ListMembersReq) ([]Member, int64, error)
|
||||||
|
|
||||||
|
// GetUserRooms 查詢用戶所在的聊天室
|
||||||
|
GetUserRooms(ctx context.Context, req GetUserRoomsReq) ([]Room, int64, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 聊天室管理請求/回應 ====================
|
||||||
|
|
||||||
|
type CreateRoomReq struct {
|
||||||
|
UID string // 創建者 UID
|
||||||
|
Name string // 聊天室名稱
|
||||||
|
Status string // 狀態,預設為 active
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetRoomReq struct {
|
||||||
|
UID string // 請求者 UID
|
||||||
|
RoomID string // 聊天室 ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateRoomReq struct {
|
||||||
|
UID string // 請求者 UID
|
||||||
|
RoomID string // 聊天室 ID
|
||||||
|
Name *string // 聊天室名稱(可選)
|
||||||
|
Status *string // 狀態(可選)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteRoomReq struct {
|
||||||
|
UID string // 請求者 UID
|
||||||
|
RoomID string // 聊天室 ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListRoomsReq struct {
|
||||||
|
UID string // 用戶 UID(查詢該用戶所在的聊天室)
|
||||||
|
Status string // 狀態篩選(可選)
|
||||||
|
PageSize int // 每頁大小,預設 20
|
||||||
|
LastID string // 用於 cursor-based pagination
|
||||||
|
}
|
||||||
|
|
||||||
|
type IsUserInRoomReq struct {
|
||||||
|
UID string // 用戶 UID
|
||||||
|
RoomID string // 聊天室 ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type Room struct {
|
||||||
|
RoomID string `json:"room_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 成員管理請求/回應 ====================
|
||||||
|
|
||||||
|
type AddMemberReq struct {
|
||||||
|
UID string // 操作者 UID
|
||||||
|
RoomID string // 聊天室 ID
|
||||||
|
MemberUID string // 要添加的用戶 UID
|
||||||
|
Role string // 角色,預設為 member
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveMemberReq struct {
|
||||||
|
UID string // 操作者 UID
|
||||||
|
RoomID string // 聊天室 ID
|
||||||
|
MemberUID string // 要移除的用戶 UID
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateMemberRoleReq struct {
|
||||||
|
UID string // 操作者 UID
|
||||||
|
RoomID string // 聊天室 ID
|
||||||
|
MemberUID string // 要更新的用戶 UID
|
||||||
|
Role string // 新角色
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListMembersReq struct {
|
||||||
|
UID string // 請求者 UID
|
||||||
|
RoomID string // 聊天室 ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type Member struct {
|
||||||
|
RoomID string `json:"room_id"`
|
||||||
|
UID string `json:"uid"`
|
||||||
|
Role string `json:"role"`
|
||||||
|
JoinedAt int64 `json:"joined_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 用戶相關請求/回應 ====================
|
||||||
|
|
||||||
|
type GetUserRoomsReq struct {
|
||||||
|
UID string // 用戶 UID
|
||||||
|
}
|
||||||
|
|
@ -950,4 +950,3 @@ func TestRoomRepository_Integration(t *testing.T) {
|
||||||
assert.True(t, inRoom)
|
assert.True(t, inRoom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,790 @@
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"backend/pkg/chat/domain/chat"
|
||||||
|
"backend/pkg/chat/domain/entity"
|
||||||
|
"backend/pkg/chat/domain/repository"
|
||||||
|
"backend/pkg/chat/domain/usecase"
|
||||||
|
"backend/pkg/library/cassandra"
|
||||||
|
errs "backend/pkg/library/errors"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gocql/gocql"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultRoomPageSize = 20
|
||||||
|
maxRoomPageSize = 100
|
||||||
|
defaultRoomStatus = "active"
|
||||||
|
defaultMemberRole = "member"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RoomUseCaseParam struct {
|
||||||
|
RoomRepo repository.RoomRepository
|
||||||
|
Logger errs.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoomUseCase struct {
|
||||||
|
RoomUseCaseParam
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRoomUseCase 創建新的聊天室 UseCase
|
||||||
|
func NewRoomUseCase(param RoomUseCaseParam) usecase.RoomUseCase {
|
||||||
|
return &RoomUseCase{
|
||||||
|
param,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 聊天室管理 ====================
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) CreateRoom(ctx context.Context, req usecase.CreateRoomReq) (*usecase.Room, error) {
|
||||||
|
// 驗證輸入參數
|
||||||
|
if err := uc.validateCreateRoomReq(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 設置預設值
|
||||||
|
status := req.Status
|
||||||
|
if status == "" {
|
||||||
|
status = defaultRoomStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
// 創建聊天室實體
|
||||||
|
now := time.Now().UTC().UnixNano()
|
||||||
|
room := &entity.Room{
|
||||||
|
Name: req.Name,
|
||||||
|
Status: status,
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存聊天室
|
||||||
|
if err := uc.RoomRepo.Create(ctx, room); err != nil {
|
||||||
|
return nil, uc.logError("RoomRepo.Create", req, err, "failed to create room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 將創建者添加為管理員
|
||||||
|
member := &entity.RoomMember{
|
||||||
|
RoomID: room.RoomID,
|
||||||
|
UID: req.UID,
|
||||||
|
Role: chat.RoomRoleAdmin.String(),
|
||||||
|
JoinedAt: now,
|
||||||
|
}
|
||||||
|
if err := uc.RoomRepo.Insert(ctx, member); err != nil {
|
||||||
|
// 如果添加成員失敗,嘗試刪除已創建的聊天室(清理)
|
||||||
|
_ = uc.RoomRepo.RoomDelete(ctx, room.RoomID.String())
|
||||||
|
return nil, uc.logError("RoomRepo.Insert", req, err, "failed to add creator as admin")
|
||||||
|
}
|
||||||
|
|
||||||
|
return uc.entityRoomToUseCase(room), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) GetRoom(ctx context.Context, req usecase.GetRoomReq) (*usecase.Room, error) {
|
||||||
|
// 驗證輸入參數
|
||||||
|
if err := uc.validateGetRoomReq(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查用戶是否在聊天室中
|
||||||
|
isInRoom, err := uc.RoomRepo.IsUserInRoom(ctx, req.UID, req.RoomID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, uc.logError("RoomRepo.IsUserInRoom", req, err, "failed to check user in room")
|
||||||
|
}
|
||||||
|
if !isInRoom {
|
||||||
|
return nil, errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"user is not in the room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取得聊天室資訊
|
||||||
|
room, err := uc.RoomRepo.RoomGet(ctx, req.RoomID)
|
||||||
|
if err != nil {
|
||||||
|
if cassandra.IsNotFound(err) {
|
||||||
|
return nil, errs.ResNotFoundErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"room not found")
|
||||||
|
}
|
||||||
|
return nil, uc.logError("RoomRepo.RoomGet", req, err, "failed to get room")
|
||||||
|
}
|
||||||
|
|
||||||
|
return uc.entityRoomToUseCase(room), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) UpdateRoom(ctx context.Context, req usecase.UpdateRoomReq) (*usecase.Room, error) {
|
||||||
|
// 驗證輸入參數
|
||||||
|
if err := uc.validateUpdateRoomReq(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查用戶是否在聊天室中且為管理員
|
||||||
|
member, err := uc.RoomRepo.Get(ctx, req.RoomID, req.UID)
|
||||||
|
if err != nil {
|
||||||
|
if cassandra.IsNotFound(err) {
|
||||||
|
return nil, errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"user is not in the room")
|
||||||
|
}
|
||||||
|
return nil, uc.logError("RoomRepo.Get", req, err, "failed to get member")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查是否為管理員
|
||||||
|
role := chat.RoomRole(member.Role)
|
||||||
|
if !role.IsAdmin() {
|
||||||
|
return nil, errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
{Key: "role", Val: member.Role},
|
||||||
|
},
|
||||||
|
"only admin can update room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取得現有聊天室資訊
|
||||||
|
room, err := uc.RoomRepo.RoomGet(ctx, req.RoomID)
|
||||||
|
if err != nil {
|
||||||
|
if cassandra.IsNotFound(err) {
|
||||||
|
return nil, errs.ResNotFoundErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"room not found")
|
||||||
|
}
|
||||||
|
return nil, uc.logError("RoomRepo.RoomGet", req, err, "failed to get room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新欄位
|
||||||
|
if req.Name != nil {
|
||||||
|
room.Name = *req.Name
|
||||||
|
}
|
||||||
|
if req.Status != nil {
|
||||||
|
room.Status = *req.Status
|
||||||
|
}
|
||||||
|
room.UpdatedAt = time.Now().UTC().UnixNano()
|
||||||
|
|
||||||
|
// 保存更新
|
||||||
|
if err := uc.RoomRepo.RoomUpdate(ctx, room); err != nil {
|
||||||
|
return nil, uc.logError("RoomRepo.RoomUpdate", req, err, "failed to update room")
|
||||||
|
}
|
||||||
|
|
||||||
|
return uc.entityRoomToUseCase(room), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) DeleteRoom(ctx context.Context, req usecase.DeleteRoomReq) error {
|
||||||
|
// 驗證輸入參數
|
||||||
|
if err := uc.validateDeleteRoomReq(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查用戶是否在聊天室中且為管理員
|
||||||
|
member, err := uc.RoomRepo.Get(ctx, req.RoomID, req.UID)
|
||||||
|
if err != nil {
|
||||||
|
if cassandra.IsNotFound(err) {
|
||||||
|
return errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"user is not in the room")
|
||||||
|
}
|
||||||
|
return uc.logError("RoomRepo.Get", req, err, "failed to get member")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查是否為管理員
|
||||||
|
role := chat.RoomRole(member.Role)
|
||||||
|
if !role.IsAdmin() {
|
||||||
|
return errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
{Key: "role", Val: member.Role},
|
||||||
|
},
|
||||||
|
"only admin can delete room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刪除聊天室(會同時刪除相關的成員和訊息)
|
||||||
|
if err := uc.RoomRepo.RoomDelete(ctx, req.RoomID); err != nil {
|
||||||
|
if cassandra.IsNotFound(err) {
|
||||||
|
return errs.ResNotFoundErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"room not found")
|
||||||
|
}
|
||||||
|
return uc.logError("RoomRepo.RoomDelete", req, err, "failed to delete room")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) ListRooms(ctx context.Context, req usecase.ListRoomsReq) ([]usecase.Room, string, int64, error) {
|
||||||
|
// 驗證輸入參數
|
||||||
|
if err := uc.validateListRoomsReq(req); err != nil {
|
||||||
|
return nil, "", 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 設置預設值
|
||||||
|
pageSize := req.PageSize
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = defaultRoomPageSize
|
||||||
|
}
|
||||||
|
if pageSize > maxRoomPageSize {
|
||||||
|
pageSize = maxRoomPageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查詢用戶所在的聊天室
|
||||||
|
userRooms, err := uc.RoomRepo.GetUserRooms(ctx, req.UID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", 0, uc.logError("RoomRepo.GetUserRooms", req, err, "failed to get user rooms")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果沒有聊天室,直接返回
|
||||||
|
if len(userRooms) == 0 {
|
||||||
|
return []usecase.Room{}, "", 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取得聊天室 ID 列表
|
||||||
|
roomIDs := make([]string, 0, len(userRooms))
|
||||||
|
for _, ur := range userRooms {
|
||||||
|
roomIDs = append(roomIDs, ur.RoomID.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根據 ID 列表查詢聊天室詳細資訊
|
||||||
|
rooms, err := uc.RoomRepo.RoomGetByID(ctx, roomIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", 0, uc.logError("RoomRepo.RoomGetByID", req, err, "failed to get rooms by id")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 篩選狀態
|
||||||
|
if req.Status != "" {
|
||||||
|
filtered := make([]entity.Room, 0, len(rooms))
|
||||||
|
for _, room := range rooms {
|
||||||
|
if room.Status == req.Status {
|
||||||
|
filtered = append(filtered, room)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rooms = filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轉換為 usecase.Room
|
||||||
|
result := make([]usecase.Room, 0, len(rooms))
|
||||||
|
for _, room := range rooms {
|
||||||
|
result = append(result, *uc.entityRoomToUseCase(&room))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 計算總數(僅第一頁)
|
||||||
|
var total int64
|
||||||
|
if req.LastID == "" {
|
||||||
|
total = int64(len(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回最後一個 ID(用於分頁)
|
||||||
|
lastID := ""
|
||||||
|
if len(result) > 0 {
|
||||||
|
lastID = result[len(result)-1].RoomID
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, lastID, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) IsUserInRoom(ctx context.Context, req usecase.IsUserInRoomReq) (bool, error) {
|
||||||
|
// 驗證輸入參數
|
||||||
|
if err := uc.validateIsUserInRoomReq(req); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查用戶是否在聊天室中
|
||||||
|
isInRoom, err := uc.RoomRepo.IsUserInRoom(ctx, req.UID, req.RoomID)
|
||||||
|
if err != nil {
|
||||||
|
return false, uc.logError("RoomRepo.IsUserInRoom", req, err, "failed to check user in room")
|
||||||
|
}
|
||||||
|
|
||||||
|
return isInRoom, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 成員管理 ====================
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) AddMember(ctx context.Context, req usecase.AddMemberReq) (*usecase.Member, error) {
|
||||||
|
// 驗證輸入參數
|
||||||
|
if err := uc.validateAddMemberReq(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查操作者是否在聊天室中且為管理員
|
||||||
|
operator, err := uc.RoomRepo.Get(ctx, req.RoomID, req.UID)
|
||||||
|
if err != nil {
|
||||||
|
if cassandra.IsNotFound(err) {
|
||||||
|
return nil, errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"operator is not in the room")
|
||||||
|
}
|
||||||
|
return nil, uc.logError("RoomRepo.Get", req, err, "failed to get operator")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查是否為管理員
|
||||||
|
operatorRole := chat.RoomRole(operator.Role)
|
||||||
|
if !operatorRole.IsAdmin() {
|
||||||
|
return nil, errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
{Key: "role", Val: operator.Role},
|
||||||
|
},
|
||||||
|
"only admin can add members")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查要添加的用戶是否已在聊天室中
|
||||||
|
existing, err := uc.RoomRepo.Get(ctx, req.RoomID, req.MemberUID)
|
||||||
|
if err == nil && existing != nil {
|
||||||
|
return nil, errs.InputInvalidFormatError("user is already in the room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 設置預設角色
|
||||||
|
role := req.Role
|
||||||
|
if role == "" {
|
||||||
|
role = defaultMemberRole
|
||||||
|
}
|
||||||
|
|
||||||
|
// 驗證角色
|
||||||
|
roleEnum := chat.RoomRole(role)
|
||||||
|
if !roleEnum.IsValid() || roleEnum.IsOwner() {
|
||||||
|
return nil, errs.InputInvalidFormatError("invalid role")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 創建成員實體
|
||||||
|
now := time.Now().UTC().UnixNano()
|
||||||
|
roomUUID, err := gocql.ParseUUID(req.RoomID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.InputInvalidFormatError("invalid room_id format")
|
||||||
|
}
|
||||||
|
|
||||||
|
member := &entity.RoomMember{
|
||||||
|
RoomID: roomUUID,
|
||||||
|
UID: req.MemberUID,
|
||||||
|
Role: role,
|
||||||
|
JoinedAt: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加到聊天室
|
||||||
|
if err := uc.RoomRepo.Insert(ctx, member); err != nil {
|
||||||
|
return nil, uc.logError("RoomRepo.Insert", req, err, "failed to add member")
|
||||||
|
}
|
||||||
|
|
||||||
|
return uc.entityMemberToUseCase(member), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) RemoveMember(ctx context.Context, req usecase.RemoveMemberReq) error {
|
||||||
|
// 驗證輸入參數
|
||||||
|
if err := uc.validateRemoveMemberReq(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查操作者是否在聊天室中
|
||||||
|
operator, err := uc.RoomRepo.Get(ctx, req.RoomID, req.UID)
|
||||||
|
if err != nil {
|
||||||
|
if cassandra.IsNotFound(err) {
|
||||||
|
return errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"operator is not in the room")
|
||||||
|
}
|
||||||
|
return uc.logError("RoomRepo.Get", req, err, "failed to get operator")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查要移除的成員
|
||||||
|
member, err := uc.RoomRepo.Get(ctx, req.RoomID, req.MemberUID)
|
||||||
|
if err != nil {
|
||||||
|
if cassandra.IsNotFound(err) {
|
||||||
|
return errs.ResNotFoundErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "member_uid", Val: req.MemberUID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"member not found")
|
||||||
|
}
|
||||||
|
return uc.logError("RoomRepo.Get", req, err, "failed to get member")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查權限:管理員可以移除任何人,普通成員只能移除自己
|
||||||
|
operatorRole := chat.RoomRole(operator.Role)
|
||||||
|
if !operatorRole.IsAdmin() {
|
||||||
|
if req.UID != req.MemberUID {
|
||||||
|
return errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "member_uid", Val: req.MemberUID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"only admin can remove other members")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不能移除擁有者
|
||||||
|
memberRole := chat.RoomRole(member.Role)
|
||||||
|
if memberRole.IsOwner() {
|
||||||
|
return errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "member_uid", Val: req.MemberUID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"cannot remove owner")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除成員
|
||||||
|
if err := uc.RoomRepo.DeleteMember(ctx, req.RoomID, req.MemberUID); err != nil {
|
||||||
|
return uc.logError("RoomRepo.DeleteMember", req, err, "failed to remove member")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) UpdateMemberRole(ctx context.Context, req usecase.UpdateMemberRoleReq) (*usecase.Member, error) {
|
||||||
|
// 驗證輸入參數
|
||||||
|
if err := uc.validateUpdateMemberRoleReq(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查操作者是否在聊天室中且為管理員
|
||||||
|
operator, err := uc.RoomRepo.Get(ctx, req.RoomID, req.UID)
|
||||||
|
if err != nil {
|
||||||
|
if cassandra.IsNotFound(err) {
|
||||||
|
return nil, errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"operator is not in the room")
|
||||||
|
}
|
||||||
|
return nil, uc.logError("RoomRepo.Get", req, err, "failed to get operator")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查是否為管理員
|
||||||
|
operatorRole := chat.RoomRole(operator.Role)
|
||||||
|
if !operatorRole.IsAdmin() {
|
||||||
|
return nil, errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
{Key: "role", Val: operator.Role},
|
||||||
|
},
|
||||||
|
"only admin can update member role")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查要更新的成員
|
||||||
|
member, err := uc.RoomRepo.Get(ctx, req.RoomID, req.MemberUID)
|
||||||
|
if err != nil {
|
||||||
|
if cassandra.IsNotFound(err) {
|
||||||
|
return nil, errs.ResNotFoundErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "member_uid", Val: req.MemberUID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"member not found")
|
||||||
|
}
|
||||||
|
return nil, uc.logError("RoomRepo.Get", req, err, "failed to get member")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 驗證角色
|
||||||
|
roleEnum := chat.RoomRole(req.Role)
|
||||||
|
if !roleEnum.IsValid() || roleEnum.IsOwner() {
|
||||||
|
return nil, errs.InputInvalidFormatError("invalid role")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不能修改擁有者的角色
|
||||||
|
memberRole := chat.RoomRole(member.Role)
|
||||||
|
if memberRole.IsOwner() {
|
||||||
|
return nil, errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "member_uid", Val: req.MemberUID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"cannot update owner role")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新角色
|
||||||
|
member.Role = req.Role
|
||||||
|
if err := uc.RoomRepo.UpdateRole(ctx, member); err != nil {
|
||||||
|
return nil, uc.logError("RoomRepo.UpdateRole", req, err, "failed to update member role")
|
||||||
|
}
|
||||||
|
|
||||||
|
return uc.entityMemberToUseCase(member), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) ListMembers(ctx context.Context, req usecase.ListMembersReq) ([]usecase.Member, int64, error) {
|
||||||
|
// 驗證輸入參數
|
||||||
|
if err := uc.validateListMembersReq(req); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查用戶是否在聊天室中
|
||||||
|
isInRoom, err := uc.RoomRepo.IsUserInRoom(ctx, req.UID, req.RoomID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, uc.logError("RoomRepo.IsUserInRoom", req, err, "failed to check user in room")
|
||||||
|
}
|
||||||
|
if !isInRoom {
|
||||||
|
return nil, 0, errs.AuthForbiddenErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "uid", Val: req.UID},
|
||||||
|
{Key: "room_id", Val: req.RoomID},
|
||||||
|
},
|
||||||
|
"user is not in the room")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查詢所有成員
|
||||||
|
members, err := uc.RoomRepo.AllMembers(ctx, req.RoomID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, uc.logError("RoomRepo.AllMembers", req, err, "failed to list members")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轉換為 usecase.Member
|
||||||
|
result := make([]usecase.Member, 0, len(members))
|
||||||
|
for _, member := range members {
|
||||||
|
result = append(result, *uc.entityMemberToUseCase(&member))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 計算總數
|
||||||
|
total, err := uc.RoomRepo.Count(ctx, req.RoomID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, uc.logError("RoomRepo.Count", req, err, "failed to count members")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 用戶相關 ====================
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) GetUserRooms(ctx context.Context, req usecase.GetUserRoomsReq) ([]usecase.Room, int64, error) {
|
||||||
|
// 驗證輸入參數
|
||||||
|
if err := uc.validateGetUserRoomsReq(req); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查詢用戶所在的聊天室
|
||||||
|
userRooms, err := uc.RoomRepo.GetUserRooms(ctx, req.UID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, uc.logError("RoomRepo.GetUserRooms", req, err, "failed to get user rooms")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果沒有聊天室,直接返回
|
||||||
|
if len(userRooms) == 0 {
|
||||||
|
return []usecase.Room{}, 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取得聊天室 ID 列表
|
||||||
|
roomIDs := make([]string, 0, len(userRooms))
|
||||||
|
for _, ur := range userRooms {
|
||||||
|
roomIDs = append(roomIDs, ur.RoomID.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根據 ID 列表查詢聊天室詳細資訊
|
||||||
|
rooms, err := uc.RoomRepo.RoomGetByID(ctx, roomIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, uc.logError("RoomRepo.RoomGetByID", req, err, "failed to get rooms by id")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轉換為 usecase.Room
|
||||||
|
result := make([]usecase.Room, 0, len(rooms))
|
||||||
|
for _, room := range rooms {
|
||||||
|
result = append(result, *uc.entityRoomToUseCase(&room))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 計算總數
|
||||||
|
total := int64(len(result))
|
||||||
|
|
||||||
|
return result, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 輔助方法 ====================
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) entityRoomToUseCase(room *entity.Room) *usecase.Room {
|
||||||
|
return &usecase.Room{
|
||||||
|
RoomID: room.RoomID.String(),
|
||||||
|
Name: room.Name,
|
||||||
|
Status: room.Status,
|
||||||
|
CreatedAt: room.CreatedAt,
|
||||||
|
UpdatedAt: room.UpdatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) entityMemberToUseCase(member *entity.RoomMember) *usecase.Member {
|
||||||
|
return &usecase.Member{
|
||||||
|
RoomID: member.RoomID.String(),
|
||||||
|
UID: member.UID,
|
||||||
|
Role: member.Role,
|
||||||
|
JoinedAt: member.JoinedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 驗證方法 ====================
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) validateCreateRoomReq(req usecase.CreateRoomReq) error {
|
||||||
|
if req.Name == "" {
|
||||||
|
return errs.InputInvalidFormatError("name cannot be empty")
|
||||||
|
}
|
||||||
|
if req.UID == "" {
|
||||||
|
return errs.InputInvalidFormatError("uid cannot be empty")
|
||||||
|
}
|
||||||
|
if req.Status != "" && req.Status != "active" && req.Status != "archived" {
|
||||||
|
return errs.InputInvalidFormatError("invalid status")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) validateGetRoomReq(req usecase.GetRoomReq) error {
|
||||||
|
if req.RoomID == "" {
|
||||||
|
return errs.InputInvalidFormatError("room_id cannot be empty")
|
||||||
|
}
|
||||||
|
if req.UID == "" {
|
||||||
|
return errs.InputInvalidFormatError("uid cannot be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) validateUpdateRoomReq(req usecase.UpdateRoomReq) error {
|
||||||
|
if req.RoomID == "" {
|
||||||
|
return errs.InputInvalidFormatError("room_id cannot be empty")
|
||||||
|
}
|
||||||
|
if req.UID == "" {
|
||||||
|
return errs.InputInvalidFormatError("uid cannot be empty")
|
||||||
|
}
|
||||||
|
if req.Name != nil && *req.Name == "" {
|
||||||
|
return errs.InputInvalidFormatError("name cannot be empty")
|
||||||
|
}
|
||||||
|
if req.Status != nil && *req.Status != "active" && *req.Status != "archived" {
|
||||||
|
return errs.InputInvalidFormatError("invalid status")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) validateDeleteRoomReq(req usecase.DeleteRoomReq) error {
|
||||||
|
if req.RoomID == "" {
|
||||||
|
return errs.InputInvalidFormatError("room_id cannot be empty")
|
||||||
|
}
|
||||||
|
if req.UID == "" {
|
||||||
|
return errs.InputInvalidFormatError("uid cannot be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) validateListRoomsReq(req usecase.ListRoomsReq) error {
|
||||||
|
if req.UID == "" {
|
||||||
|
return errs.InputInvalidFormatError("uid cannot be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) validateIsUserInRoomReq(req usecase.IsUserInRoomReq) error {
|
||||||
|
if req.UID == "" {
|
||||||
|
return errs.InputInvalidFormatError("uid cannot be empty")
|
||||||
|
}
|
||||||
|
if req.RoomID == "" {
|
||||||
|
return errs.InputInvalidFormatError("room_id cannot be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) validateAddMemberReq(req usecase.AddMemberReq) error {
|
||||||
|
if req.RoomID == "" {
|
||||||
|
return errs.InputInvalidFormatError("room_id cannot be empty")
|
||||||
|
}
|
||||||
|
if req.UID == "" {
|
||||||
|
return errs.InputInvalidFormatError("uid cannot be empty")
|
||||||
|
}
|
||||||
|
if req.MemberUID == "" {
|
||||||
|
return errs.InputInvalidFormatError("member_uid cannot be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) validateRemoveMemberReq(req usecase.RemoveMemberReq) error {
|
||||||
|
if req.RoomID == "" {
|
||||||
|
return errs.InputInvalidFormatError("room_id cannot be empty")
|
||||||
|
}
|
||||||
|
if req.UID == "" {
|
||||||
|
return errs.InputInvalidFormatError("uid cannot be empty")
|
||||||
|
}
|
||||||
|
if req.MemberUID == "" {
|
||||||
|
return errs.InputInvalidFormatError("member_uid cannot be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) validateUpdateMemberRoleReq(req usecase.UpdateMemberRoleReq) error {
|
||||||
|
if req.RoomID == "" {
|
||||||
|
return errs.InputInvalidFormatError("room_id cannot be empty")
|
||||||
|
}
|
||||||
|
if req.UID == "" {
|
||||||
|
return errs.InputInvalidFormatError("uid cannot be empty")
|
||||||
|
}
|
||||||
|
if req.MemberUID == "" {
|
||||||
|
return errs.InputInvalidFormatError("member_uid cannot be empty")
|
||||||
|
}
|
||||||
|
if req.Role == "" {
|
||||||
|
return errs.InputInvalidFormatError("role cannot be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) validateListMembersReq(req usecase.ListMembersReq) error {
|
||||||
|
if req.RoomID == "" {
|
||||||
|
return errs.InputInvalidFormatError("room_id cannot be empty")
|
||||||
|
}
|
||||||
|
if req.UID == "" {
|
||||||
|
return errs.InputInvalidFormatError("uid cannot be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) validateGetUserRoomsReq(req usecase.GetUserRoomsReq) error {
|
||||||
|
if req.UID == "" {
|
||||||
|
return errs.InputInvalidFormatError("uid cannot be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 錯誤日誌 ====================
|
||||||
|
|
||||||
|
func (uc *RoomUseCase) logError(funcName string, req interface{}, err error, message string) error {
|
||||||
|
return errs.DBErrorErrorL(
|
||||||
|
uc.Logger,
|
||||||
|
[]errs.LogField{
|
||||||
|
{Key: "func", Val: funcName},
|
||||||
|
{Key: "req", Val: fmt.Sprintf("%+v", req)},
|
||||||
|
{Key: "err", Val: err.Error()},
|
||||||
|
},
|
||||||
|
message,
|
||||||
|
).Wrap(err)
|
||||||
|
}
|
||||||
|
|
@ -542,4 +542,3 @@ func TestMultipleOptions(t *testing.T) {
|
||||||
assert.True(t, cfg.UseAuth)
|
assert.True(t, cfg.UseAuth)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -500,4 +500,3 @@ func TestLockOptions_RealWorldScenarios(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -960,4 +960,3 @@ func TestOption_RealWorldScenarios(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -517,4 +517,3 @@ func TestQueryBuilder_Count_ErrorCases(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -544,4 +544,3 @@ func TestBuildUpdateFields(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -137,4 +137,3 @@ func TestOrder_EdgeCases(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -243,4 +243,3 @@ func TestKeyGeneration(t *testing.T) {
|
||||||
assert.Equal(t, "test:token:jti-123", blacklist.tokenKey("jti-123"))
|
assert.Equal(t, "test:token:jti-123", blacklist.tokenKey("jti-123"))
|
||||||
assert.Equal(t, "test:user_version:user-456", blacklist.userVersionKey("user-456"))
|
assert.Equal(t, "test:user_version:user-456", blacklist.userVersionKey("user-456"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -185,4 +185,3 @@ func (s *Service) GetUsersOnlineStatus(ctx context.Context, userIDs []string) (m
|
||||||
}
|
}
|
||||||
return s.online.GetUsersOnlineStatus(ctx, userIDs)
|
return s.online.GetUsersOnlineStatus(ctx, userIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -300,4 +300,3 @@ func TestService_MultipleConnections(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.False(t, online)
|
assert.False(t, online)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,6 @@ func (c *Client) DisconnectWithCode(ctx context.Context, user string, code uint3
|
||||||
return c.callAPI(ctx, "disconnect", req, nil)
|
return c.callAPI(ctx, "disconnect", req, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ==================== 在線狀態方法 ====================
|
// ==================== 在線狀態方法 ====================
|
||||||
|
|
||||||
// Presence 獲取頻道在線用戶
|
// Presence 獲取頻道在線用戶
|
||||||
|
|
|
||||||
|
|
@ -269,4 +269,3 @@ func TestOnlineStore_ImplementsInterface(t *testing.T) {
|
||||||
// 確保 RedisOnlineStore 實現了 OnlineStore 介面
|
// 確保 RedisOnlineStore 實現了 OnlineStore 介面
|
||||||
var _ OnlineStore = store
|
var _ OnlineStore = store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,3 @@ func GetBucketDay(t time.Time) string {
|
||||||
func GetTodayBucketDay() string {
|
func GetTodayBucketDay() string {
|
||||||
return GetBucketDay(time.Now())
|
return GetBucketDay(time.Now())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue