package controllers import ( "encoding/json" "fmt" "io" "strconv" "strings" "time" "server/models" "server/pkg/jwtutil" "github.com/beego/beego/v2/client/orm" "github.com/beego/beego/v2/core/logs" beego "github.com/beego/beego/v2/server/web" ) type passwordPayload struct { Platform string `json:"platform"` URL string `json:"url"` Accounts []models.PasswordAccountItem `json:"accounts"` Remark string `json:"remark"` } // PasswordStoreItemDTO 统一返回给前端的结构体,包含解析后的账号列表 type PasswordStoreItemDTO struct { ID uint64 `json:"id"` Tid int `json:"tid,omitempty"` Platform string `json:"platform"` URL string `json:"url"` Accounts []models.PasswordAccountItem `json:"accounts"` Remark string `json:"remark"` UserID uint64 `json:"user_id"` CreateTime time.Time `json:"create_time"` UpdateTime *time.Time `json:"update_time"` } func accountsToJSON(list []models.PasswordAccountItem) string { cleaned := make([]models.PasswordAccountItem, 0, len(list)) for _, item := range list { u := strings.TrimSpace(item.Username) p := strings.TrimSpace(item.Password) reg := strings.TrimSpace(item.RegistrationInfo) rem := strings.TrimSpace(item.Remark) if u == "" && p == "" && reg == "" && rem == "" { continue } cleaned = append(cleaned, models.PasswordAccountItem{ Username: u, Password: p, RegistrationInfo: reg, Remark: rem, }) } b, err := json.Marshal(cleaned) if err != nil { return "[]" } return string(b) } func accountsFromJSON(raw string) []models.PasswordAccountItem { raw = strings.TrimSpace(raw) if raw == "" { return []models.PasswordAccountItem{} } var list []models.PasswordAccountItem if err := json.Unmarshal([]byte(raw), &list); err == nil && list != nil { return list } return []models.PasswordAccountItem{} } func passwordClaims(c *beego.Controller, kind string) (*jwtutil.Claims, error) { p := strings.SplitN(c.Ctx.Request.Header.Get("Authorization"), " ", 2) if len(p) != 2 || p[0] != "Bearer" { return nil, orm.ErrNoRows } cl, e := jwtutil.ParseToken(p[1]) if e != nil || cl.UserType != kind || cl.UserID <= 0 { return nil, orm.ErrNoRows } return cl, nil } func parsePassword(c *beego.Controller) (passwordPayload, error) { var p passwordPayload b, e := io.ReadAll(c.Ctx.Request.Body) if e == nil { e = json.Unmarshal(b, &p) } p.Platform = strings.TrimSpace(p.Platform) p.URL = strings.TrimSpace(p.URL) p.Remark = strings.TrimSpace(p.Remark) return p, e } func passwordReply(c *beego.Controller, status int, msg string, data interface{}) { c.Ctx.Output.SetStatus(status) c.Data["json"] = map[string]interface{}{"code": status, "msg": msg, "data": data} _ = c.ServeJSON() } func validPassword(p passwordPayload) bool { return p.Platform != "" } // sameAccount 判定两个账号是否重复: // 登录账号都非空时按账号比较(忽略大小写);账号为空时按密码比较。 func sameAccount(a, b models.PasswordAccountItem) bool { u1 := strings.TrimSpace(a.Username) u2 := strings.TrimSpace(b.Username) if u1 != "" && u2 != "" { return strings.EqualFold(u1, u2) } if u1 != u2 { return false } return strings.TrimSpace(a.Password) == strings.TrimSpace(b.Password) } // validateAccountsUnique 校验同一平台下的登录账号不重复 func validateAccountsUnique(list []models.PasswordAccountItem) error { seen := map[string]bool{} for _, item := range list { u := strings.TrimSpace(item.Username) if u == "" { continue } key := strings.ToLower(u) if seen[key] { return fmt.Errorf("同一平台下登录账号「%s」重复,请合并后再保存", u) } seen[key] = true } return nil } // dedupeAccounts 去掉同组内的重复账号(保留首次出现的一条),返回去重后的列表与去重数量 func dedupeAccounts(list []models.PasswordAccountItem) ([]models.PasswordAccountItem, int) { out := make([]models.PasswordAccountItem, 0, len(list)) removed := 0 for _, item := range list { dup := false for _, keep := range out { if sameAccount(keep, item) { dup = true break } } if dup { removed++ continue } out = append(out, item) } return out, removed } // findSamePlatformRecord 查询同用户(同租户)下是否已存在「平台名称 + 网址」相同的记录 func findSamePlatformRecord(qs orm.QuerySeter, excludeID uint64, platform, url string) (uint64, error) { q := qs.Filter("is_deleted", 0).Filter("platform", platform).Filter("url", url) if excludeID > 0 { q = q.Exclude("id", excludeID) } // 用 Values 只取 ID,避免不同模型的 One() 类型差异 var rows []orm.Params if _, err := q.OrderBy("id").Limit(1).Values(&rows, "id"); err != nil { return 0, err } if len(rows) == 0 { return 0, orm.ErrNoRows } id, _ := strconv.ParseUint(fmt.Sprintf("%v", rows[0]["Id"]), 10, 64) if id == 0 { id, _ = strconv.ParseUint(fmt.Sprintf("%v", rows[0]["id"]), 10, 64) } if id == 0 { return 0, orm.ErrNoRows } return id, nil } // passwordPageParams 解析分页参数:page 默认 1,pageSize 默认 20、上限 200 func passwordPageParams(c *beego.Controller) (page, pageSize int) { page, _ = c.GetInt("page", 1) pageSize, _ = c.GetInt("pageSize", 20) if page < 1 { page = 1 } if pageSize < 1 || pageSize > 200 { pageSize = 20 } return page, pageSize } // passwordPageReply 分页列表统一响应(list + total + 当前分页信息) func passwordPageReply(c *beego.Controller, msg string, list interface{}, total int64, page, pageSize int) { passwordReply(c, 200, msg, map[string]interface{}{ "list": list, "total": total, "page": page, "page_size": pageSize, }) } // ==================== 平台端 Password Store ==================== type PlatformPasswordStoreController struct{ beego.Controller } func (c *PlatformPasswordStoreController) List() { cl, e := passwordClaims(&c.Controller, "platform") if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } qs := models.Orm.QueryTable(new(models.PlatformPasswordStore)).Filter("is_deleted", 0).Filter("user_id", cl.UserID) k := strings.TrimSpace(c.GetString("keyword")) if 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("user_id", cl.UserID).AndCond(cond)) } // 分页:默认每页 20 条,单页最多 200 条 page, pageSize := passwordPageParams(&c.Controller) total, _ := qs.Count() var list []models.PlatformPasswordStore _, e = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&list) if e != nil && e != orm.ErrNoRows { logs.Error("[passwordStore] platform list failed: %v", e) passwordReply(&c.Controller, 500, "查询失败: "+e.Error(), nil) return } res := make([]PasswordStoreItemDTO, 0, len(list)) for _, row := range list { res = append(res, PasswordStoreItemDTO{ ID: row.ID, Platform: row.Platform, URL: row.URL, Accounts: accountsFromJSON(row.Accounts), Remark: row.Remark, UserID: row.UserID, CreateTime: row.CreateTime, UpdateTime: row.UpdateTime, }) } passwordPageReply(&c.Controller, "success", res, total, page, pageSize) } func (c *PlatformPasswordStoreController) Detail() { cl, e := passwordClaims(&c.Controller, "platform") if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } id, _ := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) var v models.PlatformPasswordStore e = models.Orm.QueryTable(new(models.PlatformPasswordStore)).Filter("id", id).Filter("is_deleted", 0).Filter("user_id", cl.UserID).One(&v) if e != nil { passwordReply(&c.Controller, 404, "记录不存在", nil) return } dto := PasswordStoreItemDTO{ ID: v.ID, Platform: v.Platform, URL: v.URL, Accounts: accountsFromJSON(v.Accounts), Remark: v.Remark, UserID: v.UserID, CreateTime: v.CreateTime, UpdateTime: v.UpdateTime, } passwordReply(&c.Controller, 200, "success", dto) } func (c *PlatformPasswordStoreController) Create() { cl, e := passwordClaims(&c.Controller, "platform") if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } p, e := parsePassword(&c.Controller) if e != nil || !validPassword(p) { passwordReply(&c.Controller, 400, "平台名称不能为空", nil) return } // 重复检测:平台名称 + 网址 已存在则拒绝新增 if existID, ferr := findSamePlatformRecord( models.Orm.QueryTable(new(models.PlatformPasswordStore)).Filter("user_id", cl.UserID), 0, p.Platform, p.URL); ferr == nil && existID > 0 { passwordReply(&c.Controller, 400, fmt.Sprintf("已存在同名同网址的平台「%s」(ID %d),请直接编辑该记录", p.Platform, existID), nil) return } // 重复检测:同一平台下的登录账号不能重复 if verr := validateAccountsUnique(p.Accounts); verr != nil { passwordReply(&c.Controller, 400, verr.Error(), nil) return } accJSON := accountsToJSON(p.Accounts) v := &models.PlatformPasswordStore{ Platform: p.Platform, URL: p.URL, Accounts: accJSON, Remark: p.Remark, UserID: uint64(cl.UserID), } id, e := models.Orm.Insert(v) if e != nil { logs.Error("[passwordStore] platform create failed: %v", e) passwordReply(&c.Controller, 500, "创建失败: "+e.Error(), nil) return } v.ID = uint64(id) dto := PasswordStoreItemDTO{ ID: v.ID, Platform: v.Platform, URL: v.URL, Accounts: accountsFromJSON(v.Accounts), Remark: v.Remark, UserID: v.UserID, CreateTime: v.CreateTime, UpdateTime: v.UpdateTime, } passwordReply(&c.Controller, 200, "创建成功", dto) } func (c *PlatformPasswordStoreController) Update() { cl, e := passwordClaims(&c.Controller, "platform") if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } id, _ := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) p, e := parsePassword(&c.Controller) if e != nil || !validPassword(p) { passwordReply(&c.Controller, 400, "平台名称不能为空", nil) return } var v models.PlatformPasswordStore e = models.Orm.QueryTable(new(models.PlatformPasswordStore)).Filter("id", id).Filter("is_deleted", 0).Filter("user_id", cl.UserID).One(&v) if e != nil { passwordReply(&c.Controller, 404, "记录不存在", nil) return } // 重复检测:改成的平台名称 + 网址 不能与自己的其它记录冲突 if existID, ferr := findSamePlatformRecord( models.Orm.QueryTable(new(models.PlatformPasswordStore)).Filter("user_id", cl.UserID), id, p.Platform, p.URL); ferr == nil && existID > 0 { passwordReply(&c.Controller, 400, fmt.Sprintf("已存在同名同网址的平台「%s」(ID %d),请先合并或改名", p.Platform, existID), nil) return } // 重复检测:同一平台下的登录账号不能重复 if verr := validateAccountsUnique(p.Accounts); verr != nil { passwordReply(&c.Controller, 400, verr.Error(), nil) return } now := time.Now() accJSON := accountsToJSON(p.Accounts) _, e = models.Orm.QueryTable(new(models.PlatformPasswordStore)).Filter("id", id).Update(map[string]interface{}{ "platform": p.Platform, "url": p.URL, "accounts": accJSON, "remark": p.Remark, "update_time": now, }) if e != nil { logs.Error("[passwordStore] platform update failed: %v", e) passwordReply(&c.Controller, 500, "更新失败: "+e.Error(), nil) return } v.Platform = p.Platform v.URL = p.URL v.Accounts = accJSON v.Remark = p.Remark v.UpdateTime = &now dto := PasswordStoreItemDTO{ ID: v.ID, Platform: v.Platform, URL: v.URL, Accounts: accountsFromJSON(v.Accounts), Remark: v.Remark, UserID: v.UserID, CreateTime: v.CreateTime, UpdateTime: v.UpdateTime, } passwordReply(&c.Controller, 200, "更新成功", dto) } func (c *PlatformPasswordStoreController) Delete() { cl, e := passwordClaims(&c.Controller, "platform") if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } id, _ := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) n, e := models.Orm.QueryTable(new(models.PlatformPasswordStore)).Filter("id", id).Filter("user_id", cl.UserID).Update(map[string]interface{}{"is_deleted": 1, "delete_time": time.Now()}) if e != nil || n == 0 { passwordReply(&c.Controller, 404, "记录不存在", nil) return } passwordReply(&c.Controller, 200, "删除成功", nil) } // ==================== 租户端 Password Store ==================== type BackendPasswordStoreController struct{ beego.Controller } func (c *BackendPasswordStoreController) List() { cl, e := passwordClaims(&c.Controller, "backend") if e != nil || cl.TenantId <= 0 { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } qs := models.Orm.QueryTable(new(models.BackendPasswordStore)).Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID) k := strings.TrimSpace(c.GetString("keyword")) if 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)) } // 分页:默认每页 20 条,单页最多 200 条 page, pageSize := passwordPageParams(&c.Controller) total, _ := qs.Count() var list []models.BackendPasswordStore _, e = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&list) if e != nil && e != orm.ErrNoRows { logs.Error("[passwordStore] backend list failed: %v", e) passwordReply(&c.Controller, 500, "查询失败: "+e.Error(), nil) return } res := make([]PasswordStoreItemDTO, 0, len(list)) for _, row := range list { 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, }) } passwordPageReply(&c.Controller, "success", res, total, page, pageSize) } func (c *BackendPasswordStoreController) Detail() { cl, e := passwordClaims(&c.Controller, "backend") if e != nil || cl.TenantId <= 0 { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } id, _ := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) var v models.BackendPasswordStore e = models.Orm.QueryTable(new(models.BackendPasswordStore)).Filter("id", id).Filter("tid", cl.TenantId).Filter("is_deleted", 0).Filter("user_id", cl.UserID).One(&v) if e != nil { passwordReply(&c.Controller, 404, "记录不存在", nil) return } dto := PasswordStoreItemDTO{ ID: v.ID, Tid: v.Tid, Platform: v.Platform, URL: v.URL, Accounts: accountsFromJSON(v.Accounts), Remark: v.Remark, UserID: v.UserID, CreateTime: v.CreateTime, UpdateTime: v.UpdateTime, } passwordReply(&c.Controller, 200, "success", dto) } func (c *BackendPasswordStoreController) Create() { cl, e := passwordClaims(&c.Controller, "backend") if e != nil || cl.TenantId <= 0 { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } p, e := parsePassword(&c.Controller) if e != nil || !validPassword(p) { passwordReply(&c.Controller, 400, "平台名称不能为空", nil) return } // 重复检测:同租户下平台名称 + 网址 已存在则拒绝新增 if existID, ferr := findSamePlatformRecord( models.Orm.QueryTable(new(models.BackendPasswordStore)).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID), 0, p.Platform, p.URL); ferr == nil && existID > 0 { passwordReply(&c.Controller, 400, fmt.Sprintf("已存在同名同网址的平台「%s」(ID %d),请直接编辑该记录", p.Platform, existID), nil) return } // 重复检测:同一平台下的登录账号不能重复 if verr := validateAccountsUnique(p.Accounts); verr != nil { passwordReply(&c.Controller, 400, verr.Error(), nil) return } accJSON := accountsToJSON(p.Accounts) v := &models.BackendPasswordStore{ Tid: cl.TenantId, Platform: p.Platform, URL: p.URL, Accounts: accJSON, Remark: p.Remark, UserID: uint64(cl.UserID), } id, e := models.Orm.Insert(v) if e != nil { logs.Error("[passwordStore] backend create failed: %v", e) passwordReply(&c.Controller, 500, "创建失败: "+e.Error(), nil) return } v.ID = uint64(id) dto := PasswordStoreItemDTO{ ID: v.ID, Tid: v.Tid, Platform: v.Platform, URL: v.URL, Accounts: accountsFromJSON(v.Accounts), Remark: v.Remark, UserID: v.UserID, CreateTime: v.CreateTime, UpdateTime: v.UpdateTime, } passwordReply(&c.Controller, 200, "创建成功", dto) } func (c *BackendPasswordStoreController) Update() { cl, e := passwordClaims(&c.Controller, "backend") if e != nil || cl.TenantId <= 0 { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } id, _ := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) p, e := parsePassword(&c.Controller) if e != nil || !validPassword(p) { passwordReply(&c.Controller, 400, "平台名称不能为空", nil) return } var v models.BackendPasswordStore e = models.Orm.QueryTable(new(models.BackendPasswordStore)).Filter("id", id).Filter("tid", cl.TenantId).Filter("is_deleted", 0).Filter("user_id", cl.UserID).One(&v) if e != nil { passwordReply(&c.Controller, 404, "记录不存在", nil) return } // 重复检测:改成的平台名称 + 网址 不能与自己的其它记录冲突 if existID, ferr := findSamePlatformRecord( models.Orm.QueryTable(new(models.BackendPasswordStore)).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID), id, p.Platform, p.URL); ferr == nil && existID > 0 { passwordReply(&c.Controller, 400, fmt.Sprintf("已存在同名同网址的平台「%s」(ID %d),请先合并或改名", p.Platform, existID), nil) return } // 重复检测:同一平台下的登录账号不能重复 if verr := validateAccountsUnique(p.Accounts); verr != nil { passwordReply(&c.Controller, 400, verr.Error(), nil) return } now := time.Now() accJSON := accountsToJSON(p.Accounts) _, e = models.Orm.QueryTable(new(models.BackendPasswordStore)).Filter("id", id).Update(map[string]interface{}{ "platform": p.Platform, "url": p.URL, "accounts": accJSON, "remark": p.Remark, "update_time": now, }) if e != nil { logs.Error("[passwordStore] backend update failed: %v", e) passwordReply(&c.Controller, 500, "更新失败: "+e.Error(), nil) return } v.Platform = p.Platform v.URL = p.URL v.Accounts = accJSON v.Remark = p.Remark v.UpdateTime = &now dto := PasswordStoreItemDTO{ ID: v.ID, Tid: v.Tid, Platform: v.Platform, URL: v.URL, Accounts: accountsFromJSON(v.Accounts), Remark: v.Remark, UserID: v.UserID, CreateTime: v.CreateTime, UpdateTime: v.UpdateTime, } passwordReply(&c.Controller, 200, "更新成功", dto) } func (c *BackendPasswordStoreController) Delete() { cl, e := passwordClaims(&c.Controller, "backend") if e != nil || cl.TenantId <= 0 { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } id, _ := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) n, e := models.Orm.QueryTable(new(models.BackendPasswordStore)).Filter("id", id).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID).Update(map[string]interface{}{"is_deleted": 1, "delete_time": time.Now()}) if e != nil || n == 0 { passwordReply(&c.Controller, 404, "记录不存在", nil) return } passwordReply(&c.Controller, 200, "删除成功", nil) } // ==================== 批量导入 / 导出 ==================== // // Excel 列顺序(与数据库字段一一对应): // 平台名称 platform | 网址 url | 账号 username | 密码 password | // 注册信息 registration_info | 账号备注 account_remark | 备注 remark // 一个平台可挂多个账号:多行「平台名称 + 网址」相同即合并为一条记录的多个账号; // 已存在的同平台同网址记录按「追加账号(跳过完全重复项)」处理。 type passwordImportRow struct { Platform string `json:"platform"` URL string `json:"url"` Username string `json:"username"` Password string `json:"password"` RegistrationInfo string `json:"registration_info"` AccountRemark string `json:"account_remark"` Remark string `json:"remark"` } type passwordImportResult struct { Created int `json:"created"` Updated int `json:"updated"` Skipped int `json:"skipped"` Failed int `json:"failed"` Failures []string `json:"failures"` } type passwordImportGroup struct { Platform string URL string Remark string Accounts []models.PasswordAccountItem } func parsePasswordImportRows(c *beego.Controller) ([]passwordImportRow, error) { b, e := io.ReadAll(c.Ctx.Request.Body) var p struct { Rows []passwordImportRow `json:"rows"` } if e == nil { e = json.Unmarshal(b, &p) } return p.Rows, e } // groupPasswordImportRows 按「平台名称 + 网址」聚合成记录,返回有序分组与行级错误 func groupPasswordImportRows(rows []passwordImportRow) ([]*passwordImportGroup, []string) { order := make([]string, 0, len(rows)) index := map[string]*passwordImportGroup{} failures := make([]string, 0) for i, r := range rows { platform := strings.TrimSpace(r.Platform) if platform == "" { failures = append(failures, fmt.Sprintf("第%d行:平台名称不能为空", i+2)) continue } url := strings.TrimSpace(r.URL) username := strings.TrimSpace(r.Username) pwd := strings.TrimSpace(r.Password) reg := strings.TrimSpace(r.RegistrationInfo) accRemark := strings.TrimSpace(r.AccountRemark) remark := strings.TrimSpace(r.Remark) key := platform + "\n" + url g, ok := index[key] if !ok { g = &passwordImportGroup{Platform: platform, URL: url} index[key] = g order = append(order, key) } if username != "" || pwd != "" || reg != "" || accRemark != "" { item := models.PasswordAccountItem{ Username: username, Password: pwd, RegistrationInfo: reg, Remark: accRemark, } // 文件内重复检测:同一「平台名称 + 网址」下登录账号重复则跳过 dup := false for _, keep := range g.Accounts { if sameAccount(keep, item) { dup = true break } } if dup { label := username if label == "" { label = "(空账号)" } failures = append(failures, fmt.Sprintf("第%d行:平台「%s」下账号「%s」与前面某行重复,已跳过", i+2, platform, label)) continue } g.Accounts = append(g.Accounts, item) } if remark != "" { g.Remark = remark } } out := make([]*passwordImportGroup, 0, len(order)) for _, k := range order { out = append(out, index[k]) } return out, failures } // mergeAccounts 追加导入账号到已有账号之后,返回合并结果与「实际新增条数」。 // 重复判定:登录账号相同(忽略大小写)即视为重复,账号为空时按密码比较。 func mergeAccounts(exist, incoming []models.PasswordAccountItem) ([]models.PasswordAccountItem, int) { out := make([]models.PasswordAccountItem, 0, len(exist)+len(incoming)) out = append(out, exist...) added := 0 for _, in := range incoming { dup := false for _, e := range exist { if sameAccount(e, in) { dup = true break } } if dup { continue } out = append(out, in) added++ } return out, added } // Import POST /platform/passwordStore/import { rows: [...] } func (c *PlatformPasswordStoreController) Import() { cl, e := passwordClaims(&c.Controller, "platform") if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } rows, e := parsePasswordImportRows(&c.Controller) if e != nil { passwordReply(&c.Controller, 400, "参数错误", nil) return } uid := uint64(cl.UserID) groups, failures := groupPasswordImportRows(rows) res := passwordImportResult{Failed: len(failures), Failures: failures} now := time.Now() for _, g := range groups { var exist models.PlatformPasswordStore err := models.Orm.QueryTable(new(models.PlatformPasswordStore)). Filter("is_deleted", 0).Filter("user_id", uid). Filter("platform", g.Platform).Filter("url", g.URL).One(&exist) if err == nil { merged, added := mergeAccounts(accountsFromJSON(exist.Accounts), g.Accounts) if added == 0 { res.Skipped++ res.Failures = append(res.Failures, fmt.Sprintf("平台「%s」的登录账号已全部存在,已跳过", g.Platform)) continue } accounts := accountsToJSON(merged) remark := exist.Remark if g.Remark != "" { remark = g.Remark } if _, uerr := models.Orm.QueryTable(new(models.PlatformPasswordStore)).Filter("id", exist.ID). Update(map[string]interface{}{"accounts": accounts, "remark": remark, "update_time": now}); uerr != nil { res.Failed++ res.Failures = append(res.Failures, fmt.Sprintf("平台「%s」更新失败:%s", g.Platform, uerr.Error())) continue } res.Updated++ continue } v := &models.PlatformPasswordStore{ Platform: g.Platform, URL: g.URL, Accounts: accountsToJSON(g.Accounts), Remark: g.Remark, UserID: uid, } if _, ierr := models.Orm.Insert(v); ierr != nil { res.Failed++ res.Failures = append(res.Failures, fmt.Sprintf("平台「%s」新增失败:%s", g.Platform, ierr.Error())) continue } res.Created++ } passwordReply(&c.Controller, 200, "导入完成", res) } // Export GET /platform/passwordStore/export 导出全部(不受列表 200 条限制) func (c *PlatformPasswordStoreController) Export() { cl, e := passwordClaims(&c.Controller, "platform") if e != nil { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } var list []models.PlatformPasswordStore if _, e = models.Orm.QueryTable(new(models.PlatformPasswordStore)). Filter("is_deleted", 0).Filter("user_id", cl.UserID). OrderBy("-id").All(&list); e != nil && e != orm.ErrNoRows { passwordReply(&c.Controller, 500, "查询失败: "+e.Error(), nil) return } res := make([]PasswordStoreItemDTO, 0, len(list)) for _, row := range list { res = append(res, PasswordStoreItemDTO{ ID: row.ID, 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)}) } // Import POST /backend/passwordStore/import { rows: [...] } func (c *BackendPasswordStoreController) Import() { cl, e := passwordClaims(&c.Controller, "backend") if e != nil || cl.TenantId <= 0 { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } rows, e := parsePasswordImportRows(&c.Controller) if e != nil { passwordReply(&c.Controller, 400, "参数错误", nil) return } uid := uint64(cl.UserID) tid := cl.TenantId groups, failures := groupPasswordImportRows(rows) res := passwordImportResult{Failed: len(failures), Failures: failures} now := time.Now() for _, g := range groups { var exist models.BackendPasswordStore err := models.Orm.QueryTable(new(models.BackendPasswordStore)). Filter("is_deleted", 0).Filter("tid", tid).Filter("user_id", uid). Filter("platform", g.Platform).Filter("url", g.URL).One(&exist) if err == nil { merged, added := mergeAccounts(accountsFromJSON(exist.Accounts), g.Accounts) if added == 0 { res.Skipped++ res.Failures = append(res.Failures, fmt.Sprintf("平台「%s」的登录账号已全部存在,已跳过", g.Platform)) continue } accounts := accountsToJSON(merged) remark := exist.Remark if g.Remark != "" { remark = g.Remark } if _, uerr := models.Orm.QueryTable(new(models.BackendPasswordStore)).Filter("id", exist.ID). Update(map[string]interface{}{"accounts": accounts, "remark": remark, "update_time": now}); uerr != nil { res.Failed++ res.Failures = append(res.Failures, fmt.Sprintf("平台「%s」更新失败:%s", g.Platform, uerr.Error())) continue } res.Updated++ continue } v := &models.BackendPasswordStore{ Tid: tid, Platform: g.Platform, URL: g.URL, Accounts: accountsToJSON(g.Accounts), Remark: g.Remark, UserID: uid, } if _, ierr := models.Orm.Insert(v); ierr != nil { res.Failed++ res.Failures = append(res.Failures, fmt.Sprintf("平台「%s」新增失败:%s", g.Platform, ierr.Error())) continue } res.Created++ } passwordReply(&c.Controller, 200, "导入完成", res) } // Export GET /backend/passwordStore/export 导出当前租户下本人全部记录 func (c *BackendPasswordStoreController) Export() { cl, e := passwordClaims(&c.Controller, "backend") if e != nil || cl.TenantId <= 0 { passwordReply(&c.Controller, 401, "未登录或无权限", nil) return } var list []models.BackendPasswordStore if _, e = models.Orm.QueryTable(new(models.BackendPasswordStore)). Filter("is_deleted", 0).Filter("tid", cl.TenantId).Filter("user_id", cl.UserID). OrderBy("-id").All(&list); e != nil && e != orm.ErrNoRows { passwordReply(&c.Controller, 500, "查询失败: "+e.Error(), nil) return } res := make([]PasswordStoreItemDTO, 0, len(list)) for _, row := range list { 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)}) }