package httpapi import ( "encoding/json" "strings" "time" "git.118im.com/im_team/im_workbench/internal/store" ) func workbenchModulePayloads(modules []store.WorkbenchModule) []map[string]any { items := make([]map[string]any, 0, len(modules)) for _, module := range modules { items = append(items, map[string]any{ "code": module.Code, "name": module.Name, "icon_file_id": module.IconFileID, "route": module.Route, "sort_order": module.SortOrder, "unread_count": module.UnreadCount, "enabled": module.Enabled, "permission": firstNonEmpty(module.Permission, "enabled"), }) } return items } func organizationDepartmentPayloads(departments []store.OrganizationDepartment) []map[string]any { items := make([]map[string]any, 0, len(departments)) for _, department := range departments { items = append(items, organizationDepartmentPayload(department)) } return items } func organizationDepartmentPayload(department store.OrganizationDepartment) map[string]any { return map[string]any{ "department_id": department.DepartmentID, "name": department.Name, "parent_id": department.ParentID, "child_count": department.ChildCount, "member_count": department.MemberCount, "version": department.Version, } } func organizationMemberPayloads(members []store.OrganizationMember) []map[string]any { items := make([]map[string]any, 0, len(members)) for _, member := range members { items = append(items, organizationMemberPayload(member)) } return items } func organizationMemberPayload(member store.OrganizationMember) map[string]any { payload := map[string]any{ "user_id": member.UserID, "department_id": member.DepartmentID, "display_name": member.DisplayName, "position": member.Position, "avatar_file_id": member.AvatarFileID, "status": member.Status, "contactable": member.Contactable, "version": member.Version, } if member.Mobile != "" { payload["mobile"] = member.Mobile } if member.Email != "" { payload["email"] = member.Email } return payload } func attendanceConfigPayload(config *store.AttendanceConfig) map[string]any { return map[string]any{ "date": config.Date.Format("2006-01-02"), "timezone": config.Timezone, "rule": attendanceRulePayload(config.Rule), "shifts": attendanceShiftPayloads(config.Shifts), "allowed_actions": []string{"check_in", "check_out"}, "today_record": attendanceRecordPayload(config.TodayRecord), "permissions": map[string]any{ "can_checkin": true, "can_view_managed": true, "can_appeal": config.Rule.AppealAllowed, }, } } func attendanceRulePayload(rule store.AttendanceRule) map[string]any { data := map[string]any{ "rule_id": rule.RuleID, "name": rule.Name, "workday": rule.Workday, "location_required": rule.LocationRequired, "wifi_required": rule.WifiRequired, "allow_outside_checkin": rule.AllowOutsideCheckin, "appeal_allowed": rule.AppealAllowed, } if rule.AppealDeadline != nil { data["appeal_deadline"] = rule.AppealDeadline.UTC().Format(time.RFC3339Nano) } return data } func attendanceShiftPayloads(shifts []store.AttendanceShift) []map[string]any { items := make([]map[string]any, 0, len(shifts)) for _, shift := range shifts { items = append(items, map[string]any{ "shift_id": shift.ShiftID, "name": shift.Name, "work_start": shift.WorkStart, "work_end": shift.WorkEnd, "checkin_start": shift.CheckinStart, "checkout_end": shift.CheckoutEnd, }) } return items } func attendanceRecordPayloads(records []store.AttendanceRecord) []map[string]any { items := make([]map[string]any, 0, len(records)) for i := range records { items = append(items, attendanceRecordPayload(&records[i])) } return items } func attendanceRecordPayload(record *store.AttendanceRecord) map[string]any { if record == nil { return nil } return map[string]any{ "record_id": record.RecordID, "user_id": record.UserID, "date": record.Date.Format("2006-01-02"), "rule_id": record.RuleID, "shift_id": record.ShiftID, "status": record.Status, "checkins": attendanceCheckinPayloads(record.Checkins), "exceptions": jsonList(record.Exceptions), "appeal_status": record.AppealStatus, "version": record.Version, "created_at": record.CreatedAt.UTC().Format(time.RFC3339Nano), "updated_at": record.UpdatedAt.UTC().Format(time.RFC3339Nano), } } func attendanceCheckinPayloads(checkins []store.AttendanceCheckin) []map[string]any { items := make([]map[string]any, 0, len(checkins)) for _, checkin := range checkins { items = append(items, attendanceCheckinPayload(checkin)) } return items } func attendanceCheckinPayload(checkin store.AttendanceCheckin) map[string]any { return map[string]any{ "checkin_id": checkin.CheckinID, "action": checkin.Action, "client_time": checkin.ClientTime.UTC().Format(time.RFC3339Nano), "server_time": checkin.ServerTime.UTC().Format(time.RFC3339Nano), "result": checkin.Result, "location": checkin.Location, "wifi": checkin.Wifi, "device": checkin.Device, } } func attendanceStatsPayload(stats *store.AttendanceStats) map[string]any { if stats == nil { return map[string]any{"expected_count": 0, "checked_count": 0, "exception_count": 0, "not_checked_count": 0} } return map[string]any{ "expected_count": stats.ExpectedCount, "checked_count": stats.CheckedCount, "exception_count": stats.ExceptionCount, "not_checked_count": stats.NotCheckedCount, } } func attendanceAppealPayloads(appeals []store.AttendanceAppeal) []map[string]any { items := make([]map[string]any, 0, len(appeals)) for i := range appeals { items = append(items, attendanceAppealPayload(&appeals[i])) } return items } func attendanceAppealPayload(appeal *store.AttendanceAppeal) map[string]any { if appeal == nil { return nil } data := map[string]any{ "appeal_id": appeal.AppealID, "record_id": appeal.RecordID, "user_id": appeal.UserID, "appeal_type": appeal.AppealType, "reason": appeal.Reason, "target_action": appeal.TargetAction, "attachment_file_ids": jsonList(appeal.AttachmentFileIDs), "status": appeal.Status, "version": appeal.Version, "submitted_at": appeal.SubmittedAt.UTC().Format(time.RFC3339Nano), "created_at": appeal.CreatedAt.UTC().Format(time.RFC3339Nano), "updated_at": appeal.UpdatedAt.UTC().Format(time.RFC3339Nano), } if appeal.TargetTime != nil { data["target_time"] = appeal.TargetTime.UTC().Format(time.RFC3339Nano) } return data } func meetingPayloads(meetings []store.Meeting, viewer int64) []map[string]any { items := make([]map[string]any, 0, len(meetings)) for _, meeting := range meetings { items = append(items, meetingPayload(meeting, viewer)) } return items } func meetingPayload(meeting store.Meeting, viewer int64) map[string]any { participants := meetingParticipantPayloads(meeting.Participants) recorderID := meeting.RecorderID if recorderID == 0 { recorderID = meeting.OrganizerID } data := map[string]any{ "meeting_id": meeting.MeetingID, "title": meeting.Title, "organizer_id": meeting.OrganizerID, "organizer": userSummary(meeting.OrganizerID), "recorder_user_id": recorderID, "recorder": userSummary(recorderID), "start_at": meeting.StartAt.UTC().Format(time.RFC3339Nano), "end_at": meeting.EndAt.UTC().Format(time.RFC3339Nano), "meeting_time": timeRange(meeting.StartAt, meeting.EndAt), "time_range": timeRange(meeting.StartAt, meeting.EndAt), "location": meeting.Location, "state": meeting.State, "reminder_minutes": jsonList(meeting.ReminderMinutes), "version": meeting.Version, "participants": participants, "participant_count": len(participants), "participant_names": []string{}, "permissions": meetingPermissions(meeting, viewer), "created_at": meeting.CreatedAt.UTC().Format(time.RFC3339Nano), "updated_at": meeting.UpdatedAt.UTC().Format(time.RFC3339Nano), } if meeting.CanceledAt != nil { data["canceled_at"] = meeting.CanceledAt.UTC().Format(time.RFC3339Nano) } for _, participant := range meeting.Participants { if participant.UserID == viewer { data["my_response"] = participant.Response break } } return data } func meetingParticipantPayloads(participants []store.MeetingParticipant) []map[string]any { items := make([]map[string]any, 0, len(participants)) for _, participant := range participants { item := map[string]any{"user_id": participant.UserID, "response": participant.Response} if participant.ResponseAt != nil { item["response_at"] = participant.ResponseAt.UTC().Format(time.RFC3339Nano) } items = append(items, item) } return items } func meetingNoteListPayloads(meetings []store.Meeting, viewer int64) []map[string]any { items := make([]map[string]any, 0, len(meetings)) for _, meeting := range meetings { item := meetingPayload(meeting, viewer) item["note_status"] = "empty" item["file_count"] = 0 item["permissions"] = meetingNotePermissions(meeting, viewer) items = append(items, item) } return items } func meetingNotesDetailPayload(meeting store.Meeting, files []store.MeetingNoteFile, viewer int64) map[string]any { meetingData := meetingPayload(meeting, viewer) fileItems := make([]map[string]any, 0, len(files)) for _, file := range files { fileItems = append(fileItems, meetingNoteFilePayload(file, viewer)) } data := map[string]any{} for k, v := range meetingData { data[k] = v } data["meeting"] = meetingData data["note_status"] = "empty" if len(files) > 0 { data["note_status"] = "available" } data["file_count"] = len(files) data["files"] = fileItems data["permissions"] = meetingNotePermissions(meeting, viewer) return data } func meetingNoteFilePayload(file store.MeetingNoteFile, viewer int64) map[string]any { return map[string]any{ "note_file_id": file.NoteFileID, "meeting_id": file.MeetingID, "file_id": file.FileID, "file_name": file.FileName, "mime_type": file.MIMEType, "file_size": file.FileSize, "file_etag": file.FileETag, "status": file.Status, "preview_status": file.PreviewStatus, "preview_mode": file.PreviewMode, "preview_file_id": file.PreviewFileID, "uploader_user_id": file.UploaderID, "uploader": userSummary(file.UploaderID), "version": file.Version, "permissions": map[string]any{ "can_preview": file.Status == "active", "can_download": file.Status == "active", "can_replace": file.Status == "active" && file.UploaderID == viewer, "can_delete": file.Status == "active" && file.UploaderID == viewer, }, "created_at": file.CreatedAt.UTC().Format(time.RFC3339Nano), "updated_at": file.UpdatedAt.UTC().Format(time.RFC3339Nano), } } func dailyReportPayloads(reports []store.DailyReport) []map[string]any { items := make([]map[string]any, 0, len(reports)) for _, report := range reports { items = append(items, dailyReportPayload(report)) } return items } func dailyReportPayload(report store.DailyReport) map[string]any { data := map[string]any{ "report_id": report.ReportID, "author_id": report.AuthorID, "report_date": report.ReportDate.Format("2006-01-02"), "content": report.Content, "content_short": preview(report.Content), "attachments": jsonList(report.Attachments), "status": report.Status, "read_state": report.ReadState, "version": report.Version, "draft_updated_at": report.UpdatedAt.UTC().Format(time.RFC3339Nano), "created_at": report.CreatedAt.UTC().Format(time.RFC3339Nano), "updated_at": report.UpdatedAt.UTC().Format(time.RFC3339Nano), } if report.SubmittedAt != nil { data["submitted_at"] = report.SubmittedAt.UTC().Format(time.RFC3339Nano) } return data } func monthlyReportPayloads(reports []store.MonthlyReport) []map[string]any { items := make([]map[string]any, 0, len(reports)) for _, report := range reports { items = append(items, monthlyReportPayload(report)) } return items } func monthlyReportPayload(report store.MonthlyReport) map[string]any { data := map[string]any{ "report_id": report.ReportID, "author_id": report.AuthorID, "month": report.Month, "work_summary": report.WorkSummary, "next_plan": report.NextPlan, "attachments": jsonList(report.Attachments), "status": report.Status, "read_state": report.ReadState, "version": report.Version, "draft_updated_at": report.UpdatedAt.UTC().Format(time.RFC3339Nano), "created_at": report.CreatedAt.UTC().Format(time.RFC3339Nano), "updated_at": report.UpdatedAt.UTC().Format(time.RFC3339Nano), } if report.SubmittedAt != nil { data["submitted_at"] = report.SubmittedAt.UTC().Format(time.RFC3339Nano) } return data } func mailboxConfigPayload(config store.MailboxConfig) map[string]any { return map[string]any{ "enabled": config.Enabled, "allow_anonymous": config.AllowAnonymous, "required_fields": jsonList(config.RequiredFields), "max_length": config.MaxLength, "attachment_policy": jsonObject(config.AttachmentPolicy), "notice": map[string]any{ "title": config.NoticeTitle, "content": config.NoticeContent, "version": config.Version, }, } } func mailboxSubmissionPayload(submission store.MailboxSubmission) map[string]any { return map[string]any{ "submission_id": submission.SubmissionID, "sender_id": submission.SenderID, "subject": submission.Subject, "anonymous": submission.Anonymous, "attachment_file_ids": jsonList(submission.AttachmentFileIDs), "notice_version": submission.NoticeVersion, "status": submission.Status, "submitted_at": submission.SubmittedAt.UTC().Format(time.RFC3339Nano), } } func meetingPermissions(meeting store.Meeting, viewer int64) map[string]any { isOrganizer := meeting.OrganizerID == viewer isScheduled := meeting.State == "scheduled" return map[string]any{ "can_update": isOrganizer && isScheduled, "can_cancel": isOrganizer && isScheduled, "can_remind": isOrganizer && isScheduled, "can_respond": !isOrganizer && isScheduled, } } func meetingNotePermissions(meeting store.Meeting, viewer int64) map[string]any { recorderID := meeting.RecorderID if recorderID == 0 { recorderID = meeting.OrganizerID } canWrite := recorderID == viewer || meeting.OrganizerID == viewer return map[string]any{"can_view": true, "can_upload": canWrite, "can_replace": canWrite, "can_delete": canWrite, "can_download": true} } func userSummary(userID int64) map[string]any { return map[string]any{"user_id": userID, "display_name": userID} } func jsonList(raw string) []any { var items []any if err := json.Unmarshal([]byte(strings.TrimSpace(raw)), &items); err != nil { return []any{} } return items } func jsonObject(raw string) map[string]any { var item map[string]any if err := json.Unmarshal([]byte(strings.TrimSpace(raw)), &item); err != nil { return map[string]any{} } return item } func preview(value string) string { runes := []rune(strings.TrimSpace(value)) if len(runes) <= 40 { return string(runes) } return string(runes[:40]) } func timeRange(startAt, endAt time.Time) string { if startAt.Format("2006-01-02") == endAt.Format("2006-01-02") { return startAt.Format("2006-01-02 15:04") + "-" + endAt.Format("15:04") } return startAt.Format("2006-01-02 15:04") + "-" + endAt.Format("2006-01-02 15:04") }