package controllers import ( "encoding/json" "strconv" "strings" "time" "server/middleware" "server/models" "server/pkg/identity" "server/pkg/jwtutil" "server/services/gueststore" "github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/core/logs" beego "github.com/beego/beego/v2/server/web" ) // ==================== 移动端 PasswordStore ==================== // // 移动端「账号管理」(密码库)接口。与 Web 端 /backend/passwordStore 同一套数据 // (yz_backend_password_store,一个平台下挂多个账号),按 JWT 中的 tid + user_id // 隔离,小程序「账号管理」页面调用。 type AppPasswordStoreController struct { beego.Controller } // 放行移动端、租户端与游客令牌:游客 tid=0 合法,数据进 gueststore 而非业务表。 func (c *AppPasswordStoreController) appPwdClaims() (*jwtutil.Claims, error) { claims, err := identity.Claims(c.Ctx.Request.Header.Get("Authorization"), identity.EndApp, identity.EndBackend, identity.EndGuest) if err != nil { return nil, err } if identity.IsGuest(claims) { if claims.UserID <= 0 { return nil, orm.ErrNoRows } return claims, nil } if claims.UserID <= 0 || claims.TenantId <= 0 { return nil, orm.ErrNoRows } return claims, nil } // GetList GET /api/passwordStore/list?keyword= func (c *AppPasswordStoreController) GetList() { cl, e := c.appPwdClaims() if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } if identity.IsGuest(cl) { c.guestList(cl, strings.TrimSpace(c.GetString("keyword"))) return } qs := models.Orm.QueryTable(new(models.BackendPasswordStore)). Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID) if k := strings.TrimSpace(c.GetString("keyword")); k != "" { cond := orm.NewCondition(). Or("platform__icontains", k). Or("url__icontains", k). Or("accounts__icontains", k). Or("remark__icontains", k) qs = qs.SetCond(orm.NewCondition().And("is_deleted", 0).And("tid", cl.TenantId).And("user_id", cl.UserID).AndCond(cond)) } var rows []models.BackendPasswordStore if _, e = qs.OrderBy("-id").All(&rows); e != nil && e != orm.ErrNoRows { logs.Error("[passwordStore] app list failed: %v", e) passwordReply(&c.Controller, 500, "查询失败: "+e.Error(), nil) return } res := make([]PasswordStoreItemDTO, 0, len(rows)) for _, row := range rows { res = append(res, PasswordStoreItemDTO{ ID: row.ID, Tid: row.Tid, Platform: row.Platform, URL: row.URL, Accounts: accountsFromJSON(row.Accounts), Remark: row.Remark, UserID: row.UserID, CreateTime: row.CreateTime, UpdateTime: row.UpdateTime, }) } passwordReply(&c.Controller, 200, "success", map[string]interface{}{"list": res, "total": len(res)}) } // GetDetail GET /api/passwordStore/detail/:id func (c *AppPasswordStoreController) GetDetail() { cl, e := c.appPwdClaims() if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } id, e := strconv.Atoi(c.Ctx.Input.Param(":id")) if e != nil { passwordReply(&c.Controller, 400, "参数错误", nil) return } if identity.IsGuest(cl) { c.guestDetail(cl, uint64(id)) return } var m models.BackendPasswordStore e = models.Orm.QueryTable(new(models.BackendPasswordStore)). Filter("id", id).Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID).One(&m) if e == orm.ErrNoRows { passwordReply(&c.Controller, 404, "记录不存在", nil) return } if e != nil { logs.Error("[passwordStore] app detail failed: %v", e) passwordReply(&c.Controller, 500, "查询失败: "+e.Error(), nil) return } passwordReply(&c.Controller, 200, "success", PasswordStoreItemDTO{ ID: m.ID, Tid: m.Tid, Platform: m.Platform, URL: m.URL, Accounts: accountsFromJSON(m.Accounts), Remark: m.Remark, UserID: m.UserID, CreateTime: m.CreateTime, UpdateTime: m.UpdateTime, }) } // Create POST /api/passwordStore/create func (c *AppPasswordStoreController) Create() { cl, e := c.appPwdClaims() if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } p, e := parsePassword(&c.Controller) if e != nil { passwordReply(&c.Controller, 400, e.Error(), nil) return } if !validPassword(p) { passwordReply(&c.Controller, 400, "请输入平台名称", nil) return } if e := validateAccountsUnique(p.Accounts); e != nil { passwordReply(&c.Controller, 400, e.Error(), nil) return } if identity.IsGuest(cl) { c.guestCreate(cl, p) return } qs := models.Orm.QueryTable(new(models.BackendPasswordStore)). Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID) if existing, e := findSamePlatformRecord(qs, 0, p.Platform, p.URL); e != nil { passwordReply(&c.Controller, 500, "查询失败: "+e.Error(), nil) return } else if existing > 0 { passwordReply(&c.Controller, 400, "同平台同网址记录已存在,请勿重复添加", nil) return } m := models.BackendPasswordStore{ Tid: cl.TenantId, UserID: uint64(cl.UserID), Platform: p.Platform, URL: p.URL, Accounts: accountsToJSON(p.Accounts), Remark: p.Remark, } if _, e = models.Orm.Insert(&m); e != nil { logs.Error("[passwordStore] app create failed: %v", e) passwordReply(&c.Controller, 500, "创建失败: "+e.Error(), nil) return } passwordReply(&c.Controller, 200, "success", PasswordStoreItemDTO{ ID: m.ID, Tid: m.Tid, Platform: m.Platform, URL: m.URL, Accounts: p.Accounts, Remark: m.Remark, UserID: m.UserID, CreateTime: m.CreateTime, UpdateTime: m.UpdateTime, }) } // Update POST /api/passwordStore/update/:id func (c *AppPasswordStoreController) Update() { cl, e := c.appPwdClaims() if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } id, e := strconv.Atoi(c.Ctx.Input.Param(":id")) if e != nil { passwordReply(&c.Controller, 400, "参数错误", nil) return } p, e := parsePassword(&c.Controller) if e != nil { passwordReply(&c.Controller, 400, e.Error(), nil) return } if !validPassword(p) { passwordReply(&c.Controller, 400, "请输入平台名称", nil) return } if e := validateAccountsUnique(p.Accounts); e != nil { passwordReply(&c.Controller, 400, e.Error(), nil) return } if identity.IsGuest(cl) { c.guestUpdate(cl, uint64(id), p) return } var m models.BackendPasswordStore e = models.Orm.QueryTable(new(models.BackendPasswordStore)). Filter("id", id).Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID).One(&m) if e == orm.ErrNoRows { passwordReply(&c.Controller, 404, "记录不存在", nil) return } if e != nil { logs.Error("[passwordStore] app update query failed: %v", e) passwordReply(&c.Controller, 500, "查询失败: "+e.Error(), nil) return } qs := models.Orm.QueryTable(new(models.BackendPasswordStore)). Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID) if existing, e := findSamePlatformRecord(qs, m.ID, p.Platform, p.URL); e != nil { passwordReply(&c.Controller, 500, "查询失败: "+e.Error(), nil) return } else if existing > 0 { passwordReply(&c.Controller, 400, "同平台同网址记录已存在,请勿重复添加", nil) return } m.Platform = p.Platform m.URL = p.URL m.Remark = p.Remark m.Accounts = accountsToJSON(p.Accounts) if _, e = models.Orm.Update(&m); e != nil { logs.Error("[passwordStore] app update failed: %v", e) passwordReply(&c.Controller, 500, "更新失败: "+e.Error(), nil) return } passwordReply(&c.Controller, 200, "success", PasswordStoreItemDTO{ ID: m.ID, Tid: m.Tid, Platform: m.Platform, URL: m.URL, Accounts: p.Accounts, Remark: m.Remark, UserID: m.UserID, CreateTime: m.CreateTime, UpdateTime: m.UpdateTime, }) } // Delete DELETE /api/passwordStore/delete/:id func (c *AppPasswordStoreController) Delete() { cl, e := c.appPwdClaims() if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } id, e := strconv.Atoi(c.Ctx.Input.Param(":id")) if e != nil { passwordReply(&c.Controller, 400, "参数错误", nil) return } if identity.IsGuest(cl) { if err := gueststore.Delete(uint64(cl.UserID), guestModulePasswordStore, uint64(id)); err != nil { if err == gueststore.ErrNotFound { passwordReply(&c.Controller, 404, "记录不存在", nil) return } passwordReply(&c.Controller, 500, "删除失败", nil) return } passwordReply(&c.Controller, 200, "success", nil) return } var m models.BackendPasswordStore e = models.Orm.QueryTable(new(models.BackendPasswordStore)). Filter("id", id).Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID).One(&m) if e == orm.ErrNoRows { passwordReply(&c.Controller, 404, "记录不存在", nil) return } if e != nil { logs.Error("[passwordStore] app delete query failed: %v", e) passwordReply(&c.Controller, 500, "查询失败: "+e.Error(), nil) return } if _, e = models.Orm.Delete(&m); e != nil { logs.Error("[passwordStore] app delete failed: %v", e) passwordReply(&c.Controller, 500, "删除失败: "+e.Error(), nil) return } middleware.WriteDeleteLog(uint64(cl.UserID), cl.TenantId, "passwordStore", m.Platform) passwordReply(&c.Controller, 200, "success", nil) } // ---------- 游客分支(免登录态):数据存 yz_guest_data,响应结构与登录态一致 ---------- func guestPwdFull(r gueststore.Row) PasswordStoreItemDTO { var it PasswordStoreItemDTO if json.Unmarshal(r.Payload, &it) != nil { it = PasswordStoreItemDTO{} } it.ID = r.ID it.Tid = 0 it.UserID = 0 it.CreateTime = r.CreateTime it.UpdateTime = r.UpdateTime if it.Accounts == nil { it.Accounts = []models.PasswordAccountItem{} } return it } // guestPwdSamePlatform 与登录态 findSamePlatformRecord 同语义:平台+网址重复。 func guestPwdSamePlatform(it PasswordStoreItemDTO, platform, url string, excludeID uint64) bool { if it.ID == excludeID { return false } return strings.EqualFold(strings.TrimSpace(it.Platform), strings.TrimSpace(platform)) && strings.TrimSpace(it.URL) == strings.TrimSpace(url) } func guestPwdKeywordHit(it PasswordStoreItemDTO, k string) bool { k = strings.ToLower(k) if strings.Contains(strings.ToLower(it.Platform), k) || strings.Contains(strings.ToLower(it.URL), k) || strings.Contains(strings.ToLower(it.Remark), k) { return true } for _, a := range it.Accounts { if strings.Contains(strings.ToLower(a.Username), k) { return true } } return false } func (c *AppPasswordStoreController) guestList(cl *jwtutil.Claims, keyword string) { rows, err := gueststore.List(uint64(cl.UserID), guestModulePasswordStore) if err != nil { passwordReply(&c.Controller, 500, "查询失败", nil) return } res := make([]PasswordStoreItemDTO, 0, len(rows)) for _, r := range rows { it := guestPwdFull(r) if keyword != "" && !guestPwdKeywordHit(it, keyword) { continue } res = append(res, it) } passwordReply(&c.Controller, 200, "success", map[string]interface{}{"list": res, "total": len(res)}) } func (c *AppPasswordStoreController) guestDetail(cl *jwtutil.Claims, id uint64) { r, err := gueststore.Get(uint64(cl.UserID), guestModulePasswordStore, id) if err != nil { passwordReply(&c.Controller, 404, "记录不存在", nil) return } passwordReply(&c.Controller, 200, "success", guestPwdFull(r)) } func (c *AppPasswordStoreController) guestCreate(cl *jwtutil.Claims, p passwordPayload) { rows, err := gueststore.List(uint64(cl.UserID), guestModulePasswordStore) if err != nil { passwordReply(&c.Controller, 500, "查询失败", nil) return } for _, r := range rows { if guestPwdSamePlatform(guestPwdFull(r), p.Platform, p.URL, 0) { passwordReply(&c.Controller, 400, "同平台同网址记录已存在,请勿重复添加", nil) return } } it := PasswordStoreItemDTO{ Platform: p.Platform, URL: p.URL, Accounts: p.Accounts, Remark: p.Remark, CreateTime: time.Now(), } newID, err := gueststore.Insert(uint64(cl.UserID), guestModulePasswordStore, it) if err != nil { passwordReply(&c.Controller, 500, "创建失败", nil) return } it.ID = newID passwordReply(&c.Controller, 200, "success", it) } func (c *AppPasswordStoreController) guestUpdate(cl *jwtutil.Claims, id uint64, p passwordPayload) { r, err := gueststore.Get(uint64(cl.UserID), guestModulePasswordStore, id) if err != nil { passwordReply(&c.Controller, 404, "记录不存在", nil) return } rows, err := gueststore.List(uint64(cl.UserID), guestModulePasswordStore) if err != nil { passwordReply(&c.Controller, 500, "查询失败", nil) return } for _, rr := range rows { if guestPwdSamePlatform(guestPwdFull(rr), p.Platform, p.URL, id) { passwordReply(&c.Controller, 400, "同平台同网址记录已存在,请勿重复添加", nil) return } } it := guestPwdFull(r) it.Platform = p.Platform it.URL = p.URL it.Accounts = p.Accounts it.Remark = p.Remark if err = gueststore.Update(uint64(cl.UserID), guestModulePasswordStore, id, it); err != nil { passwordReply(&c.Controller, 500, "更新失败", nil) return } passwordReply(&c.Controller, 200, "success", it) }