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

@@ -38,12 +38,12 @@ func parseGroupID(c *gin.Context) string {
// parseUserIDFromPath 从路径参数获取用户IDUUID格式
func parseUserIDFromPath(c *gin.Context) string {
return c.Param("userId")
return c.Param("user_id")
}
// parseAnnouncementID 从路径参数获取公告ID
func parseAnnouncementID(c *gin.Context) string {
return c.Param("announcementId")
return c.Param("announcement_id")
}
// ==================== 群组管理 ====================
@@ -454,7 +454,7 @@ func (h *GroupHandler) GetMembers(c *gin.Context) {
// ==================== RESTful Action 端点 ====================
// HandleCreateGroup 创建群组
// POST /api/v1/groups/create
// POST /api/v1/groups
func (h *GroupHandler) HandleCreateGroup(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -478,7 +478,7 @@ func (h *GroupHandler) HandleCreateGroup(c *gin.Context) {
}
// HandleGetUserGroups 获取用户群组列表
// GET /api/v1/groups/list
// GET /api/v1/groups
func (h *GroupHandler) HandleGetUserGroups(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -499,7 +499,6 @@ func (h *GroupHandler) HandleGetUserGroups(c *gin.Context) {
}
// HandleGetMyMemberInfo 获取我在群组中的成员信息
// GET /api/v1/groups/get_my_info?group_id=xxx
// GET /api/v1/groups/:id/me
func (h *GroupHandler) HandleGetMyMemberInfo(c *gin.Context) {
userID := parseUserID(c)
@@ -551,7 +550,7 @@ func (h *GroupHandler) HandleGetMyMemberInfo(c *gin.Context) {
}
// HandleDissolveGroup 解散群组
// POST /api/v1/groups/dissolve
// DELETE /api/v1/groups/:id
func (h *GroupHandler) HandleDissolveGroup(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -559,18 +558,13 @@ func (h *GroupHandler) HandleDissolveGroup(c *gin.Context) {
return
}
var params dto.DissolveGroupParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if err := h.groupService.DissolveGroup(userID, params.GroupID); err != nil {
if err := h.groupService.DissolveGroup(userID, groupID); err != nil {
if err == service.ErrNotGroupOwner {
response.Forbidden(c, "只有群主可以解散群组")
return
@@ -587,7 +581,7 @@ func (h *GroupHandler) HandleDissolveGroup(c *gin.Context) {
}
// HandleTransferOwner 转让群主
// POST /api/v1/groups/transfer
// POST /api/v1/groups/:id/transfer
func (h *GroupHandler) HandleTransferOwner(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -595,22 +589,24 @@ func (h *GroupHandler) HandleTransferOwner(c *gin.Context) {
return
}
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
var params dto.TransferOwnerParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if params.NewOwnerID == "" {
response.BadRequest(c, "new_owner_id is required")
return
}
if err := h.groupService.TransferOwner(userID, params.GroupID, params.NewOwnerID); err != nil {
if err := h.groupService.TransferOwner(userID, groupID, params.NewOwnerID); err != nil {
if err == service.ErrNotGroupOwner {
response.Forbidden(c, "只有群主可以转让群主")
return
@@ -631,7 +627,7 @@ func (h *GroupHandler) HandleTransferOwner(c *gin.Context) {
}
// HandleInviteMembers 邀请成员加入群组
// POST /api/v1/groups/invite_members
// POST /api/v1/groups/:id/invitations
func (h *GroupHandler) HandleInviteMembers(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -639,18 +635,19 @@ func (h *GroupHandler) HandleInviteMembers(c *gin.Context) {
return
}
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
var params dto.InviteMembersParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if err := h.groupService.InviteMembers(userID, params.GroupID, params.MemberIDs); err != nil {
if err := h.groupService.InviteMembers(userID, groupID, params.MemberIDs); err != nil {
if err == service.ErrNotGroupMember {
response.Forbidden(c, "只有群成员可以邀请他人")
return
@@ -675,7 +672,7 @@ func (h *GroupHandler) HandleInviteMembers(c *gin.Context) {
}
// HandleJoinGroup 加入群组
// POST /api/v1/groups/join
// POST /api/v1/groups/:id/join-requests
func (h *GroupHandler) HandleJoinGroup(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -683,18 +680,13 @@ func (h *GroupHandler) HandleJoinGroup(c *gin.Context) {
return
}
var params dto.JoinGroupParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if err := h.groupService.JoinGroup(userID, params.GroupID); err != nil {
if err := h.groupService.JoinGroup(userID, groupID); err != nil {
if err == service.ErrJoinRequestPending {
response.SuccessWithMessage(c, "申请已提交,等待群主/管理员审批", nil)
return
@@ -723,7 +715,7 @@ func (h *GroupHandler) HandleJoinGroup(c *gin.Context) {
}
// HandleSetNickname 设置群内昵称
// POST /api/v1/groups/set_nickname
// PUT /api/v1/groups/:id/members/me/nickname
func (h *GroupHandler) HandleSetNickname(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -731,18 +723,19 @@ func (h *GroupHandler) HandleSetNickname(c *gin.Context) {
return
}
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
var params dto.SetNicknameParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if err := h.groupService.SetMemberNickname(userID, params.GroupID, params.Nickname); err != nil {
if err := h.groupService.SetMemberNickname(userID, groupID, params.Nickname); err != nil {
if err == service.ErrNotGroupMember {
response.BadRequest(c, "不是群成员")
return
@@ -759,7 +752,7 @@ func (h *GroupHandler) HandleSetNickname(c *gin.Context) {
}
// HandleSetJoinType 设置加群方式
// POST /api/v1/groups/set_join_type
// PUT /api/v1/groups/:id/join-type
func (h *GroupHandler) HandleSetJoinType(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -767,18 +760,19 @@ func (h *GroupHandler) HandleSetJoinType(c *gin.Context) {
return
}
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
var params dto.SetJoinTypeParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if err := h.groupService.SetJoinType(userID, params.GroupID, params.JoinType); err != nil {
if err := h.groupService.SetJoinType(userID, groupID, params.JoinType); err != nil {
if err == service.ErrNotGroupOwner {
response.Forbidden(c, "只有群主可以设置加群方式")
return
@@ -803,7 +797,7 @@ func (h *GroupHandler) HandleSetJoinType(c *gin.Context) {
}
// HandleCreateAnnouncement 创建群公告
// POST /api/v1/groups/create_announcement
// POST /api/v1/groups/:id/announcements
func (h *GroupHandler) HandleCreateAnnouncement(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -811,18 +805,19 @@ func (h *GroupHandler) HandleCreateAnnouncement(c *gin.Context) {
return
}
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
var params dto.CreateAnnouncementParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
response.BadRequest(c, "group_id is required")
return
}
announcement, err := h.groupService.CreateAnnouncement(userID, params.GroupID, params.Content)
announcement, err := h.groupService.CreateAnnouncement(userID, groupID, params.Content)
if err != nil {
if err == service.ErrNotGroupAdmin {
response.Forbidden(c, "只有群主或管理员可以发布公告")
@@ -840,7 +835,6 @@ func (h *GroupHandler) HandleCreateAnnouncement(c *gin.Context) {
}
// HandleGetAnnouncements 获取群公告列表
// GET /api/v1/groups/get_announcements?group_id=xxx
// GET /api/v1/groups/:id/announcements
func (h *GroupHandler) HandleGetAnnouncements(c *gin.Context) {
userID := parseUserID(c)
@@ -872,7 +866,7 @@ func (h *GroupHandler) HandleGetAnnouncements(c *gin.Context) {
}
// HandleDeleteAnnouncement 删除群公告
// POST /api/v1/groups/delete_announcement
// DELETE /api/v1/groups/:id/announcements/:announcement_id
func (h *GroupHandler) HandleDeleteAnnouncement(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -880,22 +874,18 @@ func (h *GroupHandler) HandleDeleteAnnouncement(c *gin.Context) {
return
}
var params dto.DeleteAnnouncementParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if params.AnnouncementID == "" {
announcementID := parseAnnouncementID(c)
if announcementID == "" {
response.BadRequest(c, "announcement_id is required")
return
}
if err := h.groupService.DeleteAnnouncement(userID, params.AnnouncementID); err != nil {
if err := h.groupService.DeleteAnnouncement(userID, announcementID); err != nil {
if err == service.ErrNotGroupAdmin {
response.Forbidden(c, "只有群主或管理员可以删除公告")
return
@@ -1292,7 +1282,7 @@ func (h *GroupHandler) DeleteAnnouncement(c *gin.Context) {
// ==================== RESTful Action 端点 ====================
// HandleSetGroupKick 群组踢人
// POST /api/v1/groups/set_group_kick
// POST /api/v1/groups/:id/members/kick
func (h *GroupHandler) HandleSetGroupKick(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -1300,23 +1290,25 @@ func (h *GroupHandler) HandleSetGroupKick(c *gin.Context) {
return
}
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
var params dto.SetGroupKickParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if params.UserID == "" {
response.BadRequest(c, "user_id is required")
return
}
// 使用 RemoveMember 方法
err := h.groupService.RemoveMember(userID, params.GroupID, params.UserID)
err := h.groupService.RemoveMember(userID, groupID, params.UserID)
if err != nil {
if err == service.ErrNotGroupAdmin {
response.Forbidden(c, "只有群主或管理员可以移除成员")
@@ -1342,7 +1334,7 @@ func (h *GroupHandler) HandleSetGroupKick(c *gin.Context) {
}
// HandleSetGroupBan 群组单人禁言
// POST /api/v1/groups/set_group_ban
// POST /api/v1/groups/:id/members/ban
func (h *GroupHandler) HandleSetGroupBan(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -1350,16 +1342,18 @@ func (h *GroupHandler) HandleSetGroupBan(c *gin.Context) {
return
}
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
var params dto.SetGroupBanParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if params.UserID == "" {
response.BadRequest(c, "user_id is required")
return
@@ -1367,8 +1361,8 @@ func (h *GroupHandler) HandleSetGroupBan(c *gin.Context) {
// duration > 0 或 duration = -1 表示禁言duration = 0 表示解除禁言
muted := params.Duration != 0
log.Printf("[HandleSetGroupBan] 开始禁言操作: userID=%s, groupID=%s, targetUserID=%s, duration=%d, muted=%v", userID, params.GroupID, params.UserID, params.Duration, muted)
err := h.groupService.MuteMember(userID, params.GroupID, params.UserID, muted)
log.Printf("[HandleSetGroupBan] 开始禁言操作: userID=%s, groupID=%s, targetUserID=%s, duration=%d, muted=%v", userID, groupID, params.UserID, params.Duration, muted)
err := h.groupService.MuteMember(userID, groupID, params.UserID, muted)
if err != nil {
log.Printf("[HandleSetGroupBan] 禁言操作失败: %v", err)
} else {
@@ -1403,7 +1397,7 @@ func (h *GroupHandler) HandleSetGroupBan(c *gin.Context) {
}
// HandleSetGroupWholeBan 群组全员禁言
// POST /api/v1/groups/set_group_whole_ban
// PUT /api/v1/groups/:id/ban
func (h *GroupHandler) HandleSetGroupWholeBan(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -1411,18 +1405,19 @@ func (h *GroupHandler) HandleSetGroupWholeBan(c *gin.Context) {
return
}
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
var params dto.SetGroupWholeBanParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
response.BadRequest(c, "group_id is required")
return
}
err := h.groupService.SetMuteAll(userID, params.GroupID, params.Enable)
err := h.groupService.SetMuteAll(userID, groupID, params.Enable)
if err != nil {
if err == service.ErrNotGroupOwner {
response.Forbidden(c, "只有群主可以设置全员禁言")
@@ -1444,7 +1439,7 @@ func (h *GroupHandler) HandleSetGroupWholeBan(c *gin.Context) {
}
// HandleSetGroupAdmin 群组设置管理员
// POST /api/v1/groups/set_group_admin
// PUT /api/v1/groups/:id/members/:user_id/admin
func (h *GroupHandler) HandleSetGroupAdmin(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -1452,28 +1447,30 @@ func (h *GroupHandler) HandleSetGroupAdmin(c *gin.Context) {
return
}
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
targetUserID := parseUserIDFromPath(c)
if targetUserID == "" {
response.BadRequest(c, "user_id is required")
return
}
var params dto.SetGroupAdminParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if params.UserID == "" {
response.BadRequest(c, "user_id is required")
return
}
// 根据 enable 参数设置角色
role := model.GroupRoleMember
if params.Enable {
role = model.GroupRoleAdmin
}
err := h.groupService.SetMemberRole(userID, params.GroupID, params.UserID, role)
err := h.groupService.SetMemberRole(userID, groupID, targetUserID, role)
if err != nil {
if err == service.ErrNotGroupOwner {
response.Forbidden(c, "只有群主可以设置管理员")
@@ -1499,7 +1496,7 @@ func (h *GroupHandler) HandleSetGroupAdmin(c *gin.Context) {
}
// HandleSetGroupName 设置群名
// POST /api/v1/groups/set_group_name
// PUT /api/v1/groups/:id/name
func (h *GroupHandler) HandleSetGroupName(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -1507,16 +1504,18 @@ func (h *GroupHandler) HandleSetGroupName(c *gin.Context) {
return
}
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
var params dto.SetGroupNameParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if params.GroupName == "" {
response.BadRequest(c, "group_name is required")
return
@@ -1526,7 +1525,7 @@ func (h *GroupHandler) HandleSetGroupName(c *gin.Context) {
"name": params.GroupName,
}
err := h.groupService.UpdateGroup(userID, params.GroupID, updates)
err := h.groupService.UpdateGroup(userID, groupID, updates)
if err != nil {
if err == service.ErrNotGroupAdmin {
response.Forbidden(c, "没有权限修改群组信息")
@@ -1541,12 +1540,12 @@ func (h *GroupHandler) HandleSetGroupName(c *gin.Context) {
}
// 获取更新后的群组信息
group, _ := h.groupService.GetGroupByID(params.GroupID)
group, _ := h.groupService.GetGroupByID(groupID)
response.Success(c, dto.GroupToResponse(group))
}
// HandleSetGroupAvatar 设置群头像
// POST /api/v1/groups/set_group_avatar
// PUT /api/v1/groups/:id/avatar
func (h *GroupHandler) HandleSetGroupAvatar(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -1554,16 +1553,18 @@ func (h *GroupHandler) HandleSetGroupAvatar(c *gin.Context) {
return
}
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
var params dto.SetGroupAvatarParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
response.BadRequest(c, "group_id is required")
return
}
if params.Avatar == "" {
response.BadRequest(c, "avatar is required")
return
@@ -1573,7 +1574,7 @@ func (h *GroupHandler) HandleSetGroupAvatar(c *gin.Context) {
"avatar": params.Avatar,
}
err := h.groupService.UpdateGroup(userID, params.GroupID, updates)
err := h.groupService.UpdateGroup(userID, groupID, updates)
if err != nil {
if err == service.ErrNotGroupAdmin {
response.Forbidden(c, "没有权限修改群组信息")
@@ -1588,12 +1589,12 @@ func (h *GroupHandler) HandleSetGroupAvatar(c *gin.Context) {
}
// 获取更新后的群组信息
group, _ := h.groupService.GetGroupByID(params.GroupID)
group, _ := h.groupService.GetGroupByID(groupID)
response.Success(c, dto.GroupToResponse(group))
}
// HandleSetGroupLeave 退出群组
// POST /api/v1/groups/set_group_leave
// POST /api/v1/groups/:id/leave
func (h *GroupHandler) HandleSetGroupLeave(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -1601,18 +1602,13 @@ func (h *GroupHandler) HandleSetGroupLeave(c *gin.Context) {
return
}
var params dto.SetGroupLeaveParams
if err := c.ShouldBindJSON(&params); err != nil {
response.BadRequest(c, err.Error())
return
}
if params.GroupID == "" {
groupID := parseGroupID(c)
if groupID == "" {
response.BadRequest(c, "group_id is required")
return
}
err := h.groupService.LeaveGroup(userID, params.GroupID)
err := h.groupService.LeaveGroup(userID, groupID)
if err != nil {
if err == service.ErrNotGroupMember {
response.BadRequest(c, "不是群成员")
@@ -1630,7 +1626,7 @@ func (h *GroupHandler) HandleSetGroupLeave(c *gin.Context) {
}
// HandleSetGroupAddRequest 处理加群审批
// POST /api/v1/groups/set_group_add_request
// POST /api/v1/groups/:id/join-requests/handle
func (h *GroupHandler) HandleSetGroupAddRequest(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -1678,7 +1674,7 @@ func (h *GroupHandler) HandleSetGroupAddRequest(c *gin.Context) {
}
// HandleRespondInvite 处理群邀请响应
// POST /api/v1/groups/respond_invite
// POST /api/v1/groups/:id/join-requests/respond
func (h *GroupHandler) HandleRespondInvite(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
@@ -1725,7 +1721,6 @@ func (h *GroupHandler) HandleRespondInvite(c *gin.Context) {
}
// HandleGetGroupInfo 获取群信息
// GET /api/v1/groups/get?group_id=xxx
// GET /api/v1/groups/:id
func (h *GroupHandler) HandleGetGroupInfo(c *gin.Context) {
userID := parseUserID(c)
@@ -1761,7 +1756,6 @@ func (h *GroupHandler) HandleGetGroupInfo(c *gin.Context) {
}
// HandleGetGroupMemberList 获取群成员列表
// GET /api/v1/groups/get_members?group_id=xxx
// GET /api/v1/groups/:id/members
func (h *GroupHandler) HandleGetGroupMemberList(c *gin.Context) {
userID := parseUserID(c)

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

View File

@@ -0,0 +1,140 @@
package handler
import (
"strconv"
"github.com/gin-gonic/gin"
"carrot_bbs/internal/pkg/response"
"carrot_bbs/internal/service"
)
type ScheduleHandler struct {
scheduleService service.ScheduleService
}
func NewScheduleHandler(scheduleService service.ScheduleService) *ScheduleHandler {
return &ScheduleHandler{scheduleService: scheduleService}
}
type createScheduleCourseRequest struct {
Name string `json:"name" binding:"required"`
Teacher string `json:"teacher"`
Location string `json:"location"`
DayOfWeek int `json:"day_of_week" binding:"required"`
StartSection int `json:"start_section" binding:"required"`
EndSection int `json:"end_section" binding:"required"`
Weeks []int `json:"weeks" binding:"required,min=1"`
Color string `json:"color"`
}
type updateScheduleCourseRequest = createScheduleCourseRequest
func (h *ScheduleHandler) ListCourses(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
response.Unauthorized(c, "")
return
}
week := 0
if rawWeek := c.Query("week"); rawWeek != "" {
parsed, err := strconv.Atoi(rawWeek)
if err != nil {
response.BadRequest(c, "invalid week")
return
}
week = parsed
}
list, err := h.scheduleService.ListCourses(userID, week)
if err != nil {
response.HandleError(c, err, "failed to list schedule courses")
return
}
response.Success(c, gin.H{"list": list})
}
func (h *ScheduleHandler) CreateCourse(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
response.Unauthorized(c, "")
return
}
var req createScheduleCourseRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, err.Error())
return
}
created, err := h.scheduleService.CreateCourse(userID, service.CreateScheduleCourseInput{
Name: req.Name,
Teacher: req.Teacher,
Location: req.Location,
DayOfWeek: req.DayOfWeek,
StartSection: req.StartSection,
EndSection: req.EndSection,
Weeks: req.Weeks,
Color: req.Color,
})
if err != nil {
response.HandleError(c, err, "failed to create schedule course")
return
}
response.SuccessWithMessage(c, "course created", gin.H{"course": created})
}
func (h *ScheduleHandler) UpdateCourse(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
response.Unauthorized(c, "")
return
}
courseID := c.Param("id")
if courseID == "" {
response.BadRequest(c, "invalid course id")
return
}
var req updateScheduleCourseRequest
if err := c.ShouldBindJSON(&req); err != nil {
response.BadRequest(c, err.Error())
return
}
updated, err := h.scheduleService.UpdateCourse(userID, courseID, service.CreateScheduleCourseInput{
Name: req.Name,
Teacher: req.Teacher,
Location: req.Location,
DayOfWeek: req.DayOfWeek,
StartSection: req.StartSection,
EndSection: req.EndSection,
Weeks: req.Weeks,
Color: req.Color,
})
if err != nil {
response.HandleError(c, err, "failed to update schedule course")
return
}
response.SuccessWithMessage(c, "course updated", gin.H{"course": updated})
}
func (h *ScheduleHandler) DeleteCourse(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
response.Unauthorized(c, "")
return
}
courseID := c.Param("id")
if courseID == "" {
response.BadRequest(c, "invalid course id")
return
}
if err := h.scheduleService.DeleteCourse(userID, courseID); err != nil {
response.HandleError(c, err, "failed to delete schedule course")
return
}
response.SuccessWithMessage(c, "course deleted", nil)
}