package controllers import ( "encoding/json" "fmt" "server/models" "server/pkg/jwtutil" "strings" "github.com/beego/beego/v2/client/orm" beego "github.com/beego/beego/v2/server/web" ) type AppActivityController struct { beego.Controller } func (c *AppActivityController) activityClaims() (*jwtutil.Claims, error) { auth := c.Ctx.Request.Header.Get("Authorization") if auth == "" { return nil, orm.ErrNoRows } parts := strings.SplitN(auth, " ", 2) if len(parts) != 2 || parts[0] != "Bearer" { return nil, orm.ErrNoRows } claims, err := jwtutil.ParseToken(parts[1]) if err != nil { return nil, err } if claims.UserType != "backend" && claims.UserType != "app" && claims.UserType != "platform" { return nil, orm.ErrNoRows } return claims, nil } // GetList GET /app/activity/list func (c *AppActivityController) GetList() { claims, err := c.activityClaims() if err != nil { c.Ctx.Output.SetStatus(401) c.Data["json"] = map[string]interface{}{"code": 401, "msg": "未登录"} _ = c.ServeJSON() return } limit, _ := c.GetInt("limit", 10) if limit < 1 || limit > 50 { limit = 10 } var logs []models.SystemOperationLog qs := models.Orm.QueryTable(new(models.SystemOperationLog)). Filter("user_id", claims.UserID). Exclude("action__in", "登录", "退出", "查询"). OrderBy("-create_time"). Limit(limit) _, err = qs.All(&logs) if err != nil && err != orm.ErrNoRows { c.Ctx.Output.SetStatus(500) c.Data["json"] = map[string]interface{}{"code": 500, "msg": "查询失败"} _ = c.ServeJSON() return } type activityItem struct { ID uint64 `json:"id"` Action string `json:"action"` TargetType string `json:"target_type"` Title string `json:"title"` URL string `json:"url"` CreatedAt string `json:"created_at"` } list := make([]activityItem, 0, len(logs)) for _, l := range logs { module := l.Module action := l.Action // 从请求体中提取标题 title := extractTitle(l.Module, l.Action, l.RequestData) if title == "" { title = fmt.Sprintf("您%s了:%s", actionLabel(action), moduleLabel(module)) } list = append(list, activityItem{ ID: l.ID, Action: action, TargetType: module, Title: title, URL: l.URL, CreatedAt: l.CreateTime.Format("2006-01-02 15:04:05"), }) } c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": list} _ = c.ServeJSON() } // extractTitle 从请求数据中提取可读标题 func extractTitle(module, action string, reqData *string) string { if reqData == nil { return "" } data := strings.TrimSpace(*reqData) if data == "" { return "" } // 解析 JSON 请求体 var body map[string]interface{} if err := json.Unmarshal([]byte(data), &body); err != nil { return "" } moduleCN := moduleLabel(module) actionCN := actionLabel(action) // 笔记本:取 title if module == "notebook" { if t, ok := body["title"].(string); ok && t != "" { return fmt.Sprintf("您%s了%s:【%s】", actionCN, moduleCN, truncateStr(t, 20)) } } // 日程:取 content 第一行 if module == "schedule" { if c, ok := body["content"].(string); ok && c != "" { firstLine := strings.SplitN(c, "\n", 2)[0] return fmt.Sprintf("您%s了%s:【%s】", actionCN, moduleCN, truncateStr(firstLine, 20)) } } return fmt.Sprintf("您%s了:%s", actionCN, moduleCN) } func truncateStr(s string, maxLen int) string { s = strings.TrimSpace(s) if len([]rune(s)) > maxLen { return string([]rune(s)[:maxLen]) + "..." } return s } func moduleLabel(m string) string { switch m { case "notebook": return "记事本" case "schedule": return "日程提醒" case "erp": return "ERP" case "file": return "文件" case "article": return "文章" default: if m == "" { return "系统" } return m } } func actionLabel(a string) string { switch a { case "新增": return "新增" case "编辑": return "编辑" case "删除": return "删除" case "提交": return "操作" default: return a } }