修复鉴权:401拦截器加refresh重试 + admin refresh接口 + 路由守卫完善
根因:access_token每2小时过期,前端收到401直接清token跳登录,没有用refresh_token续期 后端修复: - adminauth模块新增 POST /admin/auth/refresh 接口 - Service 注入 JWTManager,支持 admin refresh token 换新 token pair - Refresh 方法验证 subjectType=admin + tokenType=refresh 前端修复: - 401 拦截器核心改造:收到401先调 refresh 接口续期 - 加 isRefreshing 锁 + pendingRequests 队列防止并发刷新 - refresh 用原生 axios.post 避免拦截器递归 - 成功则更新 localStorage + 重试原请求,失败才清 token 跳登录 - 排除 /auth/refresh 自身避免死循环 - 支持 /admin/ 请求独立 token 管理 - auth.ts/adminAuth.ts 新增手动 refreshUserToken/refreshAdminSession - 路由守卫给所有需登录路由添加 meta.requiresAuth - 守卫同时支持 PC 端 /login 和移动端 /m/login
This commit is contained in:
@@ -78,6 +78,29 @@ type HandoffRecordDTO struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type OrderListItemDTO struct {
|
||||
ID uint64 `json:"id"`
|
||||
OrderNo string `json:"order_no"`
|
||||
ListingTitle string `json:"listing_title"`
|
||||
GameName string `json:"game_name"`
|
||||
AccountTitle string `json:"account_title"`
|
||||
RenterNickname string `json:"renter_nickname"`
|
||||
OwnerNickname string `json:"owner_nickname"`
|
||||
Status string `json:"status"`
|
||||
HandoffStatus string `json:"handoff_status"`
|
||||
SettlementStatus string `json:"settlement_status"`
|
||||
RentAmount float64 `json:"rent_amount"`
|
||||
DepositAmount float64 `json:"deposit_amount"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type PaginatedOrders struct {
|
||||
Items []OrderListItemDTO `json:"items"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
}
|
||||
|
||||
type CheckoutDTO struct {
|
||||
ID uint64 `json:"id"`
|
||||
OrderID uint64 `json:"order_id"`
|
||||
|
||||
@@ -44,21 +44,43 @@ func (h *Handler) List(c *gin.Context) {
|
||||
response.Unauthorized(c, "缺少用户上下文")
|
||||
return
|
||||
}
|
||||
items, err := h.service.ListForUser(userID)
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 {
|
||||
pageSize = 20
|
||||
}
|
||||
if pageSize > 100 {
|
||||
pageSize = 100
|
||||
}
|
||||
result, err := h.service.ListForUser(userID, page, pageSize)
|
||||
if err != nil {
|
||||
writeOrderError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
response.OK(c, gin.H{"items": result.Items, "total": result.Total, "page": result.Page, "page_size": result.PageSize})
|
||||
}
|
||||
|
||||
func (h *Handler) AdminList(c *gin.Context) {
|
||||
items, err := h.service.ListAdmin()
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 {
|
||||
pageSize = 20
|
||||
}
|
||||
if pageSize > 100 {
|
||||
pageSize = 100
|
||||
}
|
||||
result, err := h.service.ListAdmin(page, pageSize)
|
||||
if err != nil {
|
||||
writeOrderError(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items})
|
||||
response.OK(c, gin.H{"items": result.Items, "total": result.Total, "page": result.Page, "page_size": result.PageSize})
|
||||
}
|
||||
|
||||
func (h *Handler) AdminDetail(c *gin.Context) {
|
||||
|
||||
@@ -488,36 +488,45 @@ func (r *Repository) AcceptCheckout(userID uint64, orderID uint64) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Repository) ListForUser(userID uint64) ([]OrderDTO, error) {
|
||||
var rows []orderRow
|
||||
err := r.baseQuery().
|
||||
Where("o.renter_id = ?", userID).
|
||||
func (r *Repository) ListForUser(userID uint64, page int, pageSize int) (*PaginatedOrders, error) {
|
||||
var total int64
|
||||
r.db.Model(&model.RentalOrder{}).Where("renter_id = ? OR owner_id = ?", userID, userID).Count(&total)
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
var rows []orderListRow
|
||||
err := r.listItemBaseQuery().
|
||||
Where("o.renter_id = ? OR o.owner_id = ?", userID, userID).
|
||||
Order("o.id DESC").
|
||||
Offset(offset).Limit(pageSize).
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]OrderDTO, 0, len(rows))
|
||||
items := make([]OrderListItemDTO, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
items = append(items, row.toDTO())
|
||||
items = append(items, row.toListItemDTO())
|
||||
}
|
||||
return items, nil
|
||||
return &PaginatedOrders{Items: items, Total: total, Page: page, PageSize: pageSize}, nil
|
||||
}
|
||||
|
||||
func (r *Repository) ListAdmin() ([]OrderDTO, error) {
|
||||
var rows []orderRow
|
||||
err := r.adminQuery().
|
||||
func (r *Repository) ListAdmin(page int, pageSize int) (*PaginatedOrders, error) {
|
||||
var total int64
|
||||
r.db.Model(&model.RentalOrder{}).Count(&total)
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
var rows []orderListRow
|
||||
err := r.listItemBaseQuery().
|
||||
Order("o.id DESC").
|
||||
Limit(200).
|
||||
Offset(offset).Limit(pageSize).
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]OrderDTO, 0, len(rows))
|
||||
items := make([]OrderListItemDTO, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
items = append(items, row.toDTO())
|
||||
items = append(items, row.toListItemDTO())
|
||||
}
|
||||
return items, nil
|
||||
return &PaginatedOrders{Items: items, Total: total, Page: page, PageSize: pageSize}, nil
|
||||
}
|
||||
|
||||
func (r *Repository) FindAdmin(orderID uint64) (*OrderDTO, error) {
|
||||
@@ -898,6 +907,16 @@ func (r *Repository) findHandoffRecord(id uint64) (*HandoffRecordDTO, error) {
|
||||
return &dto, nil
|
||||
}
|
||||
|
||||
func (r *Repository) listItemBaseQuery() *gorm.DB {
|
||||
return r.db.Table("rental_orders AS o").
|
||||
Select("o.id, o.order_no, o.status, o.handoff_status, o.settlement_status, o.rent_amount, o.deposit_amount, o.created_at, "+
|
||||
"a.title AS account_title, a.game_name, "+
|
||||
"owner.nickname AS owner_nickname, renter.nickname AS renter_nickname").
|
||||
Joins("JOIN game_accounts AS a ON a.id = o.account_id").
|
||||
Joins("JOIN users AS owner ON owner.id = o.owner_id").
|
||||
Joins("JOIN users AS renter ON renter.id = o.renter_id")
|
||||
}
|
||||
|
||||
func (r *Repository) baseQuery() *gorm.DB {
|
||||
return r.db.Table("rental_orders AS o").
|
||||
Select("o.*, a.title, a.server_region, a.login_platform").
|
||||
@@ -948,6 +967,39 @@ func (row orderRow) toDTO() OrderDTO {
|
||||
}
|
||||
}
|
||||
|
||||
type orderListRow struct {
|
||||
ID uint64
|
||||
OrderNo string
|
||||
Status string
|
||||
HandoffStatus string
|
||||
SettlementStatus string
|
||||
RentAmount float64
|
||||
DepositAmount float64
|
||||
CreatedAt time.Time
|
||||
AccountTitle string
|
||||
GameName string
|
||||
OwnerNickname string
|
||||
RenterNickname string
|
||||
}
|
||||
|
||||
func (row orderListRow) toListItemDTO() OrderListItemDTO {
|
||||
return OrderListItemDTO{
|
||||
ID: row.ID,
|
||||
OrderNo: row.OrderNo,
|
||||
ListingTitle: row.AccountTitle,
|
||||
GameName: row.GameName,
|
||||
AccountTitle: row.AccountTitle,
|
||||
RenterNickname: row.RenterNickname,
|
||||
OwnerNickname: row.OwnerNickname,
|
||||
Status: row.Status,
|
||||
HandoffStatus: row.HandoffStatus,
|
||||
SettlementStatus: row.SettlementStatus,
|
||||
RentAmount: row.RentAmount,
|
||||
DepositAmount: row.DepositAmount,
|
||||
CreatedAt: row.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func toHandoffDTO(record model.HandoffRecord) HandoffRecordDTO {
|
||||
return HandoffRecordDTO{
|
||||
ID: record.ID,
|
||||
|
||||
@@ -103,18 +103,18 @@ func (s *Service) AcceptCheckout(userID uint64, orderID uint64) error {
|
||||
return s.repo.AcceptCheckout(userID, orderID)
|
||||
}
|
||||
|
||||
func (s *Service) ListForUser(userID uint64) ([]OrderDTO, error) {
|
||||
func (s *Service) ListForUser(userID uint64, page int, pageSize int) (*PaginatedOrders, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.ListForUser(userID)
|
||||
return s.repo.ListForUser(userID, page, pageSize)
|
||||
}
|
||||
|
||||
func (s *Service) ListAdmin() ([]OrderDTO, error) {
|
||||
func (s *Service) ListAdmin(page int, pageSize int) (*PaginatedOrders, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.ListAdmin()
|
||||
return s.repo.ListAdmin(page, pageSize)
|
||||
}
|
||||
|
||||
func (s *Service) FindAdmin(orderID uint64) (*OrderDTO, error) {
|
||||
|
||||
Reference in New Issue
Block a user