package controllers import ( "encoding/json" "io" "server/middleware" "server/models" "server/pkg/identity" "server/pkg/jwtutil" "server/services/gueststore" "sort" "strconv" "strings" "time" "github.com/beego/beego/v2/client/orm" beego "github.com/beego/beego/v2/server/web" ) type AppNotebookController struct { beego.Controller } // 放行移动端、租户端与游客令牌:游客调用时不进业务表,分流到 gueststore。 func (c *AppNotebookController) appNotebookClaims() (*jwtutil.Claims, error) { return identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndApp, identity.EndBackend, identity.EndGuest) } func (c *AppNotebookController) nbJsonErr(httpStatus, bizCode int, msg string) { c.Ctx.Output.SetStatus(httpStatus) c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} _ = c.ServeJSON() } func (c *AppNotebookController) nbOk(data interface{}) { c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data} _ = c.ServeJSON() } // GetList GET /app/notebook/list func (c *AppNotebookController) GetList() { claims, err := c.appNotebookClaims() if err != nil { c.nbJsonErr(identity.Status(err), identity.Status(err), err.Error()) return } keyword := strings.TrimSpace(c.GetString("keyword")) if identity.IsGuest(claims) { c.guestList(claims, keyword) return } qs := models.Orm.QueryTable(new(models.BackendNotebook)). Filter("is_deleted", 0). Filter("user_id", claims.UserID) if keyword != "" { qs = qs.Filter("title__icontains", keyword) } var list []models.BackendNotebook _, err = qs.OrderBy("-pinned", "-update_time").All(&list) if err != nil && err != orm.ErrNoRows { c.nbJsonErr(500, 500, "查询失败") return } if list == nil { list = []models.BackendNotebook{} } type noteItem struct { ID uint64 `json:"id"` Title string `json:"title"` Content string `json:"content"` Pinned bool `json:"pinned"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } result := make([]noteItem, 0, len(list)) for _, n := range list { item := noteItem{ ID: n.ID, Title: n.Title, Content: n.Content, Pinned: n.Pinned == 1, } item.CreatedAt = n.CreateTime.Format("2006-01-02 15:04:05") if n.UpdateTime != nil { item.UpdatedAt = n.UpdateTime.Format("2006-01-02 15:04:05") } else { item.UpdatedAt = item.CreatedAt } result = append(result, item) } c.nbOk(map[string]interface{}{ "list": result, "total": len(result), }) } // GetDetail GET /app/notebook/:id func (c *AppNotebookController) GetDetail() { claims, err := c.appNotebookClaims() if err != nil { c.nbJsonErr(identity.Status(err), identity.Status(err), err.Error()) return } id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) if err != nil || id == 0 { c.nbJsonErr(400, 400, "无效ID") return } if identity.IsGuest(claims) { c.guestDetail(claims, id) return } var note models.BackendNotebook err = models.Orm.QueryTable(new(models.BackendNotebook)). Filter("id", id). Filter("is_deleted", 0). Filter("user_id", claims.UserID). One(¬e) if err != nil { c.nbJsonErr(404, 404, "笔记不存在") return } createdAt := note.CreateTime.Format("2006-01-02 15:04:05") updatedAt := createdAt if note.UpdateTime != nil { updatedAt = note.UpdateTime.Format("2006-01-02 15:04:05") } c.nbOk(map[string]interface{}{ "id": note.ID, "title": note.Title, "content": note.Content, "pinned": note.Pinned == 1, "created_at": createdAt, "updated_at": updatedAt, }) } // Create POST /app/notebook func (c *AppNotebookController) Create() { claims, err := c.appNotebookClaims() if err != nil { c.nbJsonErr(identity.Status(err), identity.Status(err), err.Error()) return } raw, err := io.ReadAll(c.Ctx.Request.Body) if err != nil { c.nbJsonErr(400, 400, "参数错误") return } var payload struct { Title string `json:"title"` Content string `json:"content"` Pinned bool `json:"pinned"` } if err := json.Unmarshal(raw, &payload); err != nil { c.nbJsonErr(400, 400, "参数错误") return } title := strings.TrimSpace(payload.Title) if title == "" { title = "无标题" } if identity.IsGuest(claims) { c.guestCreateItem(claims, title, payload.Content, payload.Pinned) return } userID := uint64(claims.UserID) pinned := int8(0) if payload.Pinned { pinned = 1 } note := &models.BackendNotebook{ Tid: claims.TenantId, Title: title, Content: payload.Content, Pinned: pinned, UserID: &userID, UserName: &claims.Username, IsDeleted: 0, } id, err := models.Orm.Insert(note) if err != nil { c.nbJsonErr(500, 500, "创建失败") return } note.ID = uint64(id) c.nbOk(map[string]interface{}{ "id": note.ID, "title": note.Title, "content": note.Content, "pinned": note.Pinned == 1, }) } // Update PUT /app/notebook/:id func (c *AppNotebookController) Update() { claims, err := c.appNotebookClaims() if err != nil { c.nbJsonErr(identity.Status(err), identity.Status(err), err.Error()) return } id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) if err != nil || id == 0 { c.nbJsonErr(400, 400, "无效ID") return } raw, err := io.ReadAll(c.Ctx.Request.Body) if err != nil { c.nbJsonErr(400, 400, "参数错误") return } var payload struct { Title string `json:"title"` Content string `json:"content"` Pinned *bool `json:"pinned"` } if err := json.Unmarshal(raw, &payload); err != nil { c.nbJsonErr(400, 400, "参数错误") return } if identity.IsGuest(claims) { c.guestUpdateItem(claims, id, payload.Title, payload.Content, payload.Pinned) return } var note models.BackendNotebook err = models.Orm.QueryTable(new(models.BackendNotebook)). Filter("id", id). Filter("is_deleted", 0). Filter("user_id", claims.UserID). One(¬e) if err != nil { c.nbJsonErr(404, 404, "笔记不存在") return } updateFields := map[string]interface{}{ "update_time": time.Now(), } if payload.Title != "" { updateFields["title"] = strings.TrimSpace(payload.Title) } updateFields["content"] = payload.Content if payload.Pinned != nil { if *payload.Pinned { updateFields["pinned"] = int8(1) } else { updateFields["pinned"] = int8(0) } } _, err = models.Orm.QueryTable(new(models.BackendNotebook)). Filter("id", id). Update(updateFields) if err != nil { c.nbJsonErr(500, 500, "更新失败") return } now := time.Now() createdAt := note.CreateTime.Format("2006-01-02 15:04:05") c.nbOk(map[string]interface{}{ "id": note.ID, "title": updateFields["title"], "content": payload.Content, "pinned": payload.Pinned != nil && *payload.Pinned, "created_at": createdAt, "updated_at": now.Format("2006-01-02 15:04:05"), }) } // Delete DELETE /app/notebook/:id func (c *AppNotebookController) Delete() { claims, err := c.appNotebookClaims() if err != nil { c.nbJsonErr(identity.Status(err), identity.Status(err), err.Error()) return } id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) if err != nil || id == 0 { c.nbJsonErr(400, 400, "无效ID") return } if identity.IsGuest(claims) { if err := gueststore.Delete(uint64(claims.UserID), guestModuleNotebook, id); err != nil { if err == gueststore.ErrNotFound { c.nbJsonErr(404, 404, "笔记不存在") return } c.nbJsonErr(500, 500, "删除失败") return } c.nbOk(nil) return } var note models.BackendNotebook err = models.Orm.QueryTable(new(models.BackendNotebook)). Filter("id", id). Filter("is_deleted", 0). Filter("user_id", claims.UserID). One(¬e) if err != nil { c.nbJsonErr(404, 404, "笔记不存在") return } now := time.Now() _, err = models.Orm.QueryTable(new(models.BackendNotebook)). Filter("id", id). Update(map[string]interface{}{ "is_deleted": 1, "delete_time": now, }) if err != nil { c.nbJsonErr(500, 500, "删除失败") return } middleware.WriteDeleteLog(uint64(claims.UserID), claims.TenantId, "notebook", note.Title) c.nbOk(nil) } // TogglePin POST /app/notebook/:id/togglePin func (c *AppNotebookController) TogglePin() { claims, err := c.appNotebookClaims() if err != nil { c.nbJsonErr(identity.Status(err), identity.Status(err), err.Error()) return } id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) if err != nil || id == 0 { c.nbJsonErr(400, 400, "无效ID") return } if identity.IsGuest(claims) { c.guestTogglePin(claims, id) return } var note models.BackendNotebook err = models.Orm.QueryTable(new(models.BackendNotebook)). Filter("id", id). Filter("is_deleted", 0). Filter("user_id", claims.UserID). One(¬e) if err != nil { c.nbJsonErr(404, 404, "笔记不存在") return } var newPinned int8 if note.Pinned == 1 { newPinned = 0 } else { newPinned = 1 } now := time.Now() _, err = models.Orm.QueryTable(new(models.BackendNotebook)). Filter("id", id). Update(map[string]interface{}{ "pinned": newPinned, "update_time": now, }) if err != nil { c.nbJsonErr(500, 500, "操作失败") return } c.nbOk(map[string]interface{}{ "pinned": newPinned == 1, }) } // ---------- 游客分支(免登录态):数据存 yz_guest_data,响应结构与登录态一致 ---------- // guestNoteFull 读取并补全一条游客笔记(ID/时间以存储行为准)。 func guestNoteFull(r gueststore.Row) guestNoteItem { var it guestNoteItem if json.Unmarshal(r.Payload, &it) != nil { it = guestNoteItem{Title: "无标题"} } it.ID = r.ID it.CreatedAt = r.CreateTime.Format("2006-01-02 15:04:05") it.UpdatedAt = it.CreatedAt if r.UpdateTime != nil { it.UpdatedAt = r.UpdateTime.Format("2006-01-02 15:04:05") } return it } func (c *AppNotebookController) guestList(claims *jwtutil.Claims, keyword string) { rows, err := gueststore.List(uint64(claims.UserID), guestModuleNotebook) if err != nil { c.nbJsonErr(500, 500, "查询失败") return } items := make([]guestNoteItem, 0, len(rows)) for _, r := range rows { it := guestNoteFull(r) if keyword != "" && !strings.Contains(it.Title, keyword) { continue } items = append(items, it) } // 与登录态一致:置顶优先,再按更新时间倒序 sort.SliceStable(items, func(i, j int) bool { if items[i].Pinned != items[j].Pinned { return items[i].Pinned } return items[i].UpdatedAt > items[j].UpdatedAt }) c.nbOk(map[string]interface{}{"list": items, "total": len(items)}) } func (c *AppNotebookController) guestDetail(claims *jwtutil.Claims, id uint64) { r, err := gueststore.Get(uint64(claims.UserID), guestModuleNotebook, id) if err != nil { c.nbJsonErr(404, 404, "笔记不存在") return } c.nbOk(guestNoteFull(r)) } func (c *AppNotebookController) guestCreateItem(claims *jwtutil.Claims, title, content string, pinned bool) { now := time.Now().Format("2006-01-02 15:04:05") it := guestNoteItem{Title: title, Content: content, Pinned: pinned, CreatedAt: now, UpdatedAt: now} newID, err := gueststore.Insert(uint64(claims.UserID), guestModuleNotebook, it) if err != nil { c.nbJsonErr(500, 500, "创建失败") return } c.nbOk(map[string]interface{}{ "id": newID, "title": title, "content": content, "pinned": pinned, }) } func (c *AppNotebookController) guestUpdateItem(claims *jwtutil.Claims, id uint64, title, content string, pinned *bool) { r, err := gueststore.Get(uint64(claims.UserID), guestModuleNotebook, id) if err != nil { c.nbJsonErr(404, 404, "笔记不存在") return } it := guestNoteFull(r) if t := strings.TrimSpace(title); t != "" { it.Title = t } it.Content = content if pinned != nil { it.Pinned = *pinned } it.UpdatedAt = time.Now().Format("2006-01-02 15:04:05") if err = gueststore.Update(uint64(claims.UserID), guestModuleNotebook, id, it); err != nil { c.nbJsonErr(500, 500, "更新失败") return } c.nbOk(map[string]interface{}{ "id": it.ID, "title": it.Title, "content": it.Content, "pinned": it.Pinned, "created_at": it.CreatedAt, "updated_at": it.UpdatedAt, }) } func (c *AppNotebookController) guestTogglePin(claims *jwtutil.Claims, id uint64) { r, err := gueststore.Get(uint64(claims.UserID), guestModuleNotebook, id) if err != nil { c.nbJsonErr(404, 404, "笔记不存在") return } it := guestNoteFull(r) it.Pinned = !it.Pinned it.UpdatedAt = time.Now().Format("2006-01-02 15:04:05") if err = gueststore.Update(uint64(claims.UserID), guestModuleNotebook, id, it); err != nil { c.nbJsonErr(500, 500, "操作失败") return } c.nbOk(map[string]interface{}{"pinned": it.Pinned}) }