增加后台会话撤销并统一拼单业务状态

This commit is contained in:
yml2213
2026-08-18 18:36:48 +08:00
parent 4867861b8b
commit 323b9ea34c
7 changed files with 164 additions and 13 deletions
@@ -10,6 +10,12 @@ import {
listAdminUsers,
updateAdminUser,
} from '../../repositories/admin-user-repo.js'
import {
createAdminSession as createAdminSessionRecord,
getActiveAdminSession,
revokeAdminSession,
touchAdminSession,
} from '../../repositories/admin-session-repo.js'
import { addHours, nowIso } from '../../utils/time.js'
import { createHttpError } from '../../utils/http.js'
import { normalizePage, normalizePageSize } from './admin-query-utils.js'
@@ -113,6 +119,19 @@ export async function loginAdmin(
}
const session = createAdminSession(user)
const createdSession = await createAdminSessionRecord({
sessionId: String(session.sessionId || ''),
userId: Number(user.id),
issuedAt: String(session.issuedAt),
expiresAt: String(session.expiresAt),
now: nowIso(),
})
if (!createdSession) {
throw createHttpError('后台登录会话创建失败,请重试', {
statusCode: 503,
errorCode: 'admin_session_create_failed',
})
}
await recordAdminLoginLog({
userId: Number(user.id),
username: String(user.username || normalizedUsername),
@@ -172,6 +191,21 @@ export async function verifyAdminSessionToken(token: unknown): Promise<AdminSess
}
const userId = Number(payload?.uid || 0)
const sessionId = String(payload?.sid || '').trim()
if (!sessionId) {
throw createHttpError('后台登录态无效', {
statusCode: 401,
errorCode: 'admin_auth_invalid',
})
}
const activeSession = await getActiveAdminSession(sessionId, nowIso())
if (!activeSession || Number(activeSession.user_id) !== userId) {
throw createHttpError('后台登录态已失效,请重新登录', {
statusCode: 401,
errorCode: 'admin_auth_revoked',
})
}
await touchAdminSession(sessionId, nowIso())
const user = await getAdminUserById(userId)
if (!user || user.status !== 'active') {
throw createHttpError('后台账号已不可用,请重新登录', {
@@ -190,7 +224,7 @@ export async function verifyAdminSessionToken(token: unknown): Promise<AdminSess
}
return {
sessionId: String(payload?.sid || '').trim(),
sessionId,
userId: Number(user.id),
username: String(user.username || ''),
role: normalizeAdminRole(user.role),
@@ -199,6 +233,12 @@ export async function verifyAdminSessionToken(token: unknown): Promise<AdminSess
}
}
export async function logoutAdmin(token: unknown): Promise<{ success: boolean; revoked: boolean }> {
const session = await verifyAdminSessionToken(token)
const revoked = await revokeAdminSession(session.sessionId, nowIso())
return { success: true, revoked }
}
export async function getAdminSessionSummary(token: unknown): Promise<JsonObject> {
const session = await verifyAdminSessionToken(token)
@@ -421,6 +461,8 @@ function createAdminSession(user: AdminUserRow): JsonObject {
const signature = signPayload(encodedPayload)
return {
sessionId: payload.sid,
issuedAt,
token: `${encodedPayload}.${signature}`,
expiresAt,
user: {
@@ -136,6 +136,7 @@ import {
resolveWorkOrderRulePricing,
resolveWorkOrderSharingEnabled,
resolveWorkerPermissions,
resolveWorkOrderOperationalStatus,
} from './mappers.js'
import {
ensureWorkerPlatformDefaults,
@@ -2059,7 +2060,8 @@ export async function acceptAdminWorkOrders(payload: JsonObject = {}, actorName
)
if (
workOrders.some(
(workOrder) => mapWorkOrderAdmin(workOrder).status !== WORK_ORDER_STATUS.PENDING_ACCEPTANCE,
(workOrder) =>
resolveWorkOrderOperationalStatus(workOrder) !== WORK_ORDER_STATUS.PENDING_ACCEPTANCE,
)
) {
throw createHttpError('选中的订单包含非待验收状态,请刷新后重试', {
@@ -324,6 +324,23 @@ export function mapWorkOrderMaterial(value: unknown) {
}
}
/** 统一计算后台使用的工单业务状态,避免拼单订单继续直接暴露母工单 open。 */
export function resolveWorkOrderOperationalStatus(workOrder: WorkOrderRow): string {
const activeShareQuantity = Math.max(0, Number(workOrder.active_share_quantity || 0))
const pendingShareCount = Math.max(0, Number(workOrder.pending_share_count || 0))
const sharingTotalQuantity = Math.max(1, Number(workOrder.sharing_total_quantity || 1))
const hasActiveShares = activeShareQuantity > 0
const sharingFilled =
workOrder.sharing_enabled === true && activeShareQuantity >= sharingTotalQuantity
if (workOrder.status === WORK_ORDER_STATUS.OPEN && workOrder.sharing_enabled && hasActiveShares) {
return sharingFilled && pendingShareCount === 0
? WORK_ORDER_STATUS.PENDING_ACCEPTANCE
: WORK_ORDER_STATUS.IN_PROGRESS
}
return workOrder.status
}
export function mapWorkOrderAdmin(workOrder: WorkOrderRow) {
const activeShareQuantity = Math.max(0, Number(workOrder.active_share_quantity || 0))
const pendingShareCount = Math.max(0, Number(workOrder.pending_share_count || 0))
@@ -331,14 +348,7 @@ export function mapWorkOrderAdmin(workOrder: WorkOrderRow) {
const hasActiveShares = activeShareQuantity > 0
const sharingFilled =
workOrder.sharing_enabled === true && activeShareQuantity >= sharingTotalQuantity
const status =
workOrder.status === WORK_ORDER_STATUS.OPEN &&
workOrder.sharing_enabled === true &&
hasActiveShares
? sharingFilled && pendingShareCount === 0
? WORK_ORDER_STATUS.PENDING_ACCEPTANCE
: WORK_ORDER_STATUS.IN_PROGRESS
: workOrder.status
const status = resolveWorkOrderOperationalStatus(workOrder)
const hallAvailable =
workOrder.status === WORK_ORDER_STATUS.OPEN &&
(!workOrder.sharing_enabled || activeShareQuantity < sharingTotalQuantity)