package handlers import ( "encoding/json" "errors" "net/http" "strconv" "strings" "github.com/go-chi/chi/v5" "trade/web/internal/auth" "trade/web/internal/middleware" "trade/web/internal/store" ) type createUserReq struct { Username string `json:"username"` Password string `json:"password"` Role string `json:"role"` } type patchUserReq struct { Password *string `json:"password,omitempty"` Disabled *bool `json:"disabled,omitempty"` } func (d *Deps) AdminListUsers(w http.ResponseWriter, r *http.Request) { users, err := d.Auth.ListUsers() if err != nil { writeErr(w, http.StatusInternalServerError, err.Error()) return } out := make([]map[string]any, 0, len(users)) for i := range users { out = append(out, sanitize(&users[i])) } writeJSON(w, http.StatusOK, out) } func (d *Deps) AdminCreateUser(w http.ResponseWriter, r *http.Request) { var req createUserReq if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeErr(w, http.StatusBadRequest, "invalid json") return } req.Username = strings.TrimSpace(req.Username) if req.Username == "" || len(req.Password) < 6 { writeErr(w, http.StatusBadRequest, "用户名必填,密码至少 6 位") return } role := strings.TrimSpace(req.Role) if role == "" { role = store.RoleUser } if role != store.RoleAdmin && role != store.RoleUser { writeErr(w, http.StatusBadRequest, "role 取值必须是 admin 或 user") return } hash, err := auth.HashPassword(req.Password) if err != nil { writeErr(w, http.StatusInternalServerError, "hash failed") return } u, err := d.Auth.CreateUser(req.Username, hash, role) if err != nil { // UNIQUE 冲突等 writeErr(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusOK, sanitize(u)) } func (d *Deps) AdminPatchUser(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { writeErr(w, http.StatusBadRequest, "invalid id") return } var req patchUserReq if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeErr(w, http.StatusBadRequest, "invalid json") return } if req.Password == nil && req.Disabled == nil { writeErr(w, http.StatusBadRequest, "无可更新字段") return } if req.Password != nil { if len(*req.Password) < 6 { writeErr(w, http.StatusBadRequest, "新密码至少 6 位") return } hash, err := auth.HashPassword(*req.Password) if err != nil { writeErr(w, http.StatusInternalServerError, "hash failed") return } if err := d.Auth.UpdatePassword(id, hash); err != nil { writeErr(w, statusForErr(err), err.Error()) return } } if req.Disabled != nil { // 禁止禁用自己,避免管理员锁死自己 me, _ := middleware.FromContext(r.Context()) if *req.Disabled && me.ID == id { writeErr(w, http.StatusBadRequest, "不能禁用自己") return } if err := d.Auth.SetDisabled(id, *req.Disabled); err != nil { writeErr(w, statusForErr(err), err.Error()) return } } u, err := d.Auth.GetByID(id) if err != nil { writeErr(w, statusForErr(err), err.Error()) return } writeJSON(w, http.StatusOK, sanitize(u)) } func (d *Deps) AdminDeleteUser(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { writeErr(w, http.StatusBadRequest, "invalid id") return } me, _ := middleware.FromContext(r.Context()) if me.ID == id { writeErr(w, http.StatusBadRequest, "不能删除自己") return } if err := d.Auth.DeleteUser(id); err != nil { writeErr(w, statusForErr(err), err.Error()) return } writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) } func statusForErr(err error) int { if errors.Is(err, store.ErrNotFound) { return http.StatusNotFound } return http.StatusInternalServerError }