feat(schedule): add course table screens and navigation

Add complete schedule functionality including:
- Schedule screen with weekly course table view
- Course detail screen with transparent modal presentation
- New ScheduleStack navigator integrated into main tab bar
- Schedule service for API interactions
- Type definitions for course entities

Also includes bug fixes for group invite/request handlers
to include required groupId parameter.
This commit is contained in:
2026-03-12 08:38:14 +08:00
parent 21293644b8
commit 0a0cbacbcc
25 changed files with 3050 additions and 260 deletions

View File

@@ -116,14 +116,14 @@ func (h *MessageHandler) HandleTyping(c *gin.Context) {
response.Unauthorized(c, "")
return
}
var params struct {
ConversationID string `json:"conversation_id" binding:"required"`
}
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
conversationID := getIDParam(c, "id")
if conversationID == "" {
response.BadRequest(c, "conversation id is required")
return
}
h.chatService.SendTyping(c.Request.Context(), userID, params.ConversationID)
h.chatService.SendTyping(c.Request.Context(), userID, conversationID)
response.SuccessWithMessage(c, "typing sent", nil)
}
@@ -397,8 +397,8 @@ func (h *MessageHandler) SendMessage(c *gin.Context) {
}
// HandleSendMessage RESTful 风格的发送消息端点
// POST /api/v1/conversations/send_message
// 请求体格式: {"detail_type": "private", "conversation_id": "123445667", "segments": [{"type": "text", "data": {"text": "嗨~"}}]}
// POST /api/v1/conversations/:id/messages
// 请求体格式: {"detail_type": "private", "segments": [{"type": "text", "data": {"text": "嗨~"}}]}
func (h *MessageHandler) HandleSendMessage(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
@@ -406,6 +406,12 @@ func (h *MessageHandler) HandleSendMessage(c *gin.Context) {
return
}
conversationID := getIDParam(c, "id")
if conversationID == "" {
response.BadRequest(c, "conversation id is required")
return
}
var params dto.SendMessageParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
@@ -413,10 +419,6 @@ func (h *MessageHandler) HandleSendMessage(c *gin.Context) {
}
// 验证参数
if params.ConversationID == "" {
response.BadRequest(c, "conversation_id is required")
return
}
if params.DetailType == "" {
response.BadRequest(c, "detail_type is required")
return
@@ -427,7 +429,7 @@ func (h *MessageHandler) HandleSendMessage(c *gin.Context) {
}
// 发送消息
msg, err := h.chatService.SendMessage(c.Request.Context(), userID, params.ConversationID, params.Segments, params.ReplyToID)
msg, err := h.chatService.SendMessage(c.Request.Context(), userID, conversationID, params.Segments, params.ReplyToID)
if err != nil {
response.BadRequest(c, err.Error())
return
@@ -480,7 +482,7 @@ func (h *MessageHandler) HandleDeleteMsg(c *gin.Context) {
}
// HandleGetConversationList 获取会话列表
// GET /api/v1/conversations/list
// GET /api/v1/conversations
func (h *MessageHandler) HandleGetConversationList(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
@@ -780,7 +782,6 @@ func (h *MessageHandler) HandleCreateConversation(c *gin.Context) {
}
// HandleGetConversation 获取会话详情
// GET /api/v1/conversations/get?conversation_id=xxx
// GET /api/v1/conversations/:id
func (h *MessageHandler) HandleGetConversation(c *gin.Context) {
userID := c.GetString("user_id")
@@ -825,7 +826,6 @@ func (h *MessageHandler) HandleGetConversation(c *gin.Context) {
}
// HandleGetMessages 获取会话消息
// GET /api/v1/conversations/get_messages?conversation_id=xxx
// GET /api/v1/conversations/:id/messages
func (h *MessageHandler) HandleGetMessages(c *gin.Context) {
userID := c.GetString("user_id")
@@ -913,7 +913,7 @@ func (h *MessageHandler) HandleGetMessages(c *gin.Context) {
}
// HandleMarkRead 标记已读
// POST /api/v1/conversations/mark_read
// POST /api/v1/conversations/:id/read
func (h *MessageHandler) HandleMarkRead(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
@@ -921,18 +921,19 @@ func (h *MessageHandler) HandleMarkRead(c *gin.Context) {
return
}
var params dto.MarkReadParams
if err := c.ShouldBindJSON(&params); err != nil {
conversationID := getIDParam(c, "id")
if conversationID == "" {
response.BadRequest(c, "conversation id is required")
return
}
var req dto.MarkReadRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.ConversationID == "" {
response.BadRequest(c, "conversation_id is required")
return
}
err := h.chatService.MarkAsRead(c.Request.Context(), params.ConversationID, userID, params.LastReadSeq)
err := h.chatService.MarkAsRead(c.Request.Context(), conversationID, userID, req.LastReadSeq)
if err != nil {
response.BadRequest(c, err.Error())
return
@@ -942,7 +943,7 @@ func (h *MessageHandler) HandleMarkRead(c *gin.Context) {
}
// HandleSetConversationPinned 设置会话置顶
// POST /api/v1/conversations/set_pinned
// PUT /api/v1/conversations/:id/pinned
func (h *MessageHandler) HandleSetConversationPinned(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
@@ -950,24 +951,27 @@ func (h *MessageHandler) HandleSetConversationPinned(c *gin.Context) {
return
}
var params dto.SetConversationPinnedParams
if err := c.ShouldBindJSON(&params); err != nil {
conversationID := getIDParam(c, "id")
if conversationID == "" {
response.BadRequest(c, "conversation id is required")
return
}
var req struct {
IsPinned bool `json:"is_pinned"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.ConversationID == "" {
response.BadRequest(c, "conversation_id is required")
return
}
if err := h.chatService.SetConversationPinned(c.Request.Context(), params.ConversationID, userID, params.IsPinned); err != nil {
if err := h.chatService.SetConversationPinned(c.Request.Context(), conversationID, userID, req.IsPinned); err != nil {
response.BadRequest(c, err.Error())
return
}
response.SuccessWithMessage(c, "conversation pinned status updated", gin.H{
"conversation_id": params.ConversationID,
"is_pinned": params.IsPinned,
"conversation_id": conversationID,
"is_pinned": req.IsPinned,
})
}