Files
cmautobuy/admin/handler/web/user.go
T

153 lines
5.0 KiB
Go
Raw Normal View History

package web
import (
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/gin-gonic/gin"
"cmautobuy/admin/model"
"cmautobuy/admin/repository"
"cmautobuy/admin/service"
)
// AdminRequired 叠加在已认证路由上。采购员能使用业务页,但不能执行管理操作。
func AdminRequired() gin.HandlerFunc {
return func(c *gin.Context) {
user := currentUser(c)
if user != nil && user.IsAdmin() {
c.Next()
return
}
fail(c, http.StatusForbidden, "只有管理员可以执行此管理操作。当前账号仍可使用其他业务页面。")
c.Abort()
}
}
// UserList 显示用户搜索、分页和管理操作。
func (h *Handler) UserList(c *gin.Context) {
h.renderUserList(c, http.StatusOK, c.Query("msg"), c.Query("error"), false, "")
}
func (h *Handler) renderUserList(c *gin.Context, httpStatus int, message, errorMessage string, needCreate bool, createUsername string) {
2026-08-09 21:51:20 +08:00
keyword := strings.TrimSpace(userListValue(c, "q"))
listStatus := userListValue(c, "status")
pageSize := service.ParsePageSize(userListValue(c, "page_size"))
2026-08-09 21:51:20 +08:00
if c.Request.Method == http.MethodPost {
listStatus = c.PostForm("list_status")
}
result, err := service.ListUsersWithPageSize(h.db, currentUser(c), keyword,
model.UserStatus(listStatus), service.ParsePage(userListValue(c, "page")), pageSize)
if err != nil {
fail(c, http.StatusInternalServerError, "读取用户列表失败,账号数据没有被改动。刷新后重试。")
return
}
values := url.Values{}
if keyword != "" {
values.Set("q", keyword)
}
if result.Status != "" {
values.Set("status", string(result.Status))
}
status := fmt.Sprintf("共 %d 个用户 · 第 %d/%d 页", result.Total, result.Page, result.TotalPages)
c.HTML(httpStatus, "user/list", page(c, "users", "用户管理", gin.H{
"Rows": result.Rows, "Keyword": result.Keyword, "Status": status,
"Pagination": service.NewPaginationView(result.Page, pageSize, result.TotalPages, values.Encode()),
"StatusFilter": result.Status, "Message": message, "Error": errorMessage,
"NeedCreate": needCreate, "CreateUsername": createUsername,
"CurrentPage": result.Page,
"CurrentPageSize": pageSize,
}))
}
2026-08-09 21:51:20 +08:00
func userListValue(c *gin.Context, key string) string {
if c.Request.Method == http.MethodPost {
return c.PostForm(key)
}
return c.Query(key)
}
// UserCreate 只创建固定 purchaser 角色,密码不写入 URL、不回显。
func (h *Handler) UserCreate(c *gin.Context) {
err := service.CreatePurchaser(h.db, currentUser(c), c.PostForm("username"),
c.PostForm("password"), c.PostForm("password_confirm"), time.Now())
if err != nil {
if isExpectedUserError(err) {
h.renderUserList(c, http.StatusBadRequest, "", err.Error(), true,
strings.TrimSpace(c.PostForm("username")))
return
}
fail(c, http.StatusInternalServerError, "创建采购员失败,已有账号数据保持不变。刷新后重试。")
return
}
redirectUsers(c, "采购员创建成功", "")
}
// UserSetStatus 启用或禁用账号,并由 Repository 在同一事务撤销全部 Session。
func (h *Handler) UserSetStatus(c *gin.Context) {
status := model.UserStatus(c.PostForm("status"))
err := service.ChangeUserStatus(h.db, currentUser(c), c.PostForm("user_id"), status, time.Now())
if err != nil {
h.userActionFailure(c, err, "修改账号状态失败")
return
}
message := "账号已启用"
if status == model.UserDisabled {
message = "账号已禁用,原有登录已撤销"
}
redirectUsers(c, message, "")
}
// UserResetPassword 重置密码并撤销目标用户全部 Session。密码不会回显。
func (h *Handler) UserResetPassword(c *gin.Context) {
err := service.ResetUserPassword(h.db, currentUser(c), c.PostForm("user_id"),
c.PostForm("password"), c.PostForm("password_confirm"), time.Now())
if err != nil {
h.userActionFailure(c, err, "重置密码失败")
return
}
redirectUsers(c, "密码已重置,原有登录已撤销", "")
}
func (h *Handler) userActionFailure(c *gin.Context, err error, fallback string) {
if isExpectedUserError(err) {
redirectUsers(c, "", err.Error())
return
}
fail(c, http.StatusInternalServerError, fallback+",已有账号数据保持不变。刷新后重试。")
}
func isExpectedUserError(err error) bool {
return service.IsValidationError(err) || errors.Is(err, repository.ErrUsernameExists) ||
errors.Is(err, repository.ErrUserNotFound) || errors.Is(err, repository.ErrLastActiveAdmin)
}
func redirectUsers(c *gin.Context, message, errorMessage string) {
query := url.Values{}
2026-08-09 21:51:20 +08:00
if keyword := c.PostForm("q"); keyword != "" {
query.Set("q", keyword)
}
if status := c.PostForm("list_status"); status != "" {
query.Set("status", status)
}
if page := c.PostForm("page"); page != "" {
query.Set("page", page)
}
query.Set("page_size", fmt.Sprintf("%d", service.ParsePageSize(c.PostForm("page_size"))))
if message != "" {
query.Set("msg", message)
}
if errorMessage != "" {
query.Set("error", errorMessage)
}
location := "/users"
if encoded := query.Encode(); encoded != "" {
location += "?" + encoded
}
c.Redirect(http.StatusSeeOther, location)
}