拆分打手会话仓储
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
export * from './types.js'
|
||||
export * from './shared.js'
|
||||
export * from './worker-repo.js'
|
||||
export * from './worker-session-repo.js'
|
||||
export * from './work-order-repo.js'
|
||||
export * from './work-order-event-repo.js'
|
||||
export * from './work-order-share-repo.js'
|
||||
|
||||
@@ -8,7 +8,6 @@ import type {
|
||||
WorkerFinanceRequestRow,
|
||||
WorkerLevelRow,
|
||||
WorkerUserRow,
|
||||
WorkerSessionRow,
|
||||
WorkerWithdrawalAccountRow,
|
||||
WorkerWalletLedgerRow,
|
||||
WorkerWalletRow,
|
||||
@@ -58,164 +57,16 @@ const WORKER_FINANCE_REQUEST_SELECT = `
|
||||
) withdraw_stats ON TRUE
|
||||
`
|
||||
|
||||
export type CreateWorkerSessionInput = {
|
||||
sessionId: string
|
||||
workerId: number
|
||||
deviceId: string
|
||||
deviceType: string
|
||||
deviceName: string
|
||||
userAgent: string
|
||||
ipAddress: string
|
||||
issuedAt: string
|
||||
expiresAt: string
|
||||
now: string
|
||||
maxDevices: number
|
||||
}
|
||||
|
||||
export type CreateWorkerSessionResult = {
|
||||
created: boolean
|
||||
activeSessions: WorkerSessionRow[]
|
||||
session: WorkerSessionRow | null
|
||||
}
|
||||
|
||||
export async function createWorkerSessionRecord(
|
||||
input: CreateWorkerSessionInput,
|
||||
): Promise<CreateWorkerSessionResult> {
|
||||
return withTransaction(async (client) => {
|
||||
// 对账号加行锁,避免并发登录同时绕过设备数量限制。
|
||||
await client.query('SELECT id FROM worker_users WHERE id = $1 FOR UPDATE', [input.workerId])
|
||||
await client.query(
|
||||
`
|
||||
UPDATE worker_sessions
|
||||
SET status = 'revoked', revoked_at = $2, updated_at = $2
|
||||
WHERE worker_id = $1 AND status = 'active' AND expires_at <= $2
|
||||
`,
|
||||
[input.workerId, input.now],
|
||||
)
|
||||
|
||||
await client.query(
|
||||
`
|
||||
UPDATE worker_sessions
|
||||
SET status = 'revoked', revoked_at = $3, updated_at = $3
|
||||
WHERE worker_id = $1 AND device_id = $2 AND status = 'active'
|
||||
`,
|
||||
[input.workerId, input.deviceId, input.now],
|
||||
)
|
||||
|
||||
const activeResult = await client.query<WorkerSessionRow>(
|
||||
`
|
||||
SELECT * FROM worker_sessions
|
||||
WHERE worker_id = $1 AND status = 'active' AND expires_at > $2
|
||||
ORDER BY last_seen_at DESC, id DESC
|
||||
`,
|
||||
[input.workerId, input.now],
|
||||
)
|
||||
const activeSessions = activeResult.rows
|
||||
if (activeSessions.length >= input.maxDevices) {
|
||||
return { created: false, activeSessions, session: null }
|
||||
}
|
||||
|
||||
const result = await client.query<WorkerSessionRow>(
|
||||
`
|
||||
INSERT INTO worker_sessions (
|
||||
session_id, worker_id, device_id, device_type, device_name,
|
||||
user_agent, ip_address, status, issued_at, last_seen_at,
|
||||
expires_at, created_at, updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, 'active', $8, $8, $9, $10, $10)
|
||||
RETURNING *
|
||||
`,
|
||||
[
|
||||
input.sessionId,
|
||||
input.workerId,
|
||||
input.deviceId,
|
||||
input.deviceType,
|
||||
input.deviceName,
|
||||
input.userAgent,
|
||||
input.ipAddress,
|
||||
input.issuedAt,
|
||||
input.expiresAt,
|
||||
input.now,
|
||||
],
|
||||
)
|
||||
return { created: true, activeSessions, session: result.rows[0] || null }
|
||||
})
|
||||
}
|
||||
|
||||
export async function getWorkerSessionBySessionId(
|
||||
sessionId: string,
|
||||
): Promise<WorkerSessionRow | null> {
|
||||
const result = await query<WorkerSessionRow>(
|
||||
'SELECT * FROM worker_sessions WHERE session_id = $1 LIMIT 1',
|
||||
[String(sessionId || '').trim()],
|
||||
)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function touchWorkerSession(sessionId: string, now: string): Promise<boolean> {
|
||||
const result = await query(
|
||||
`
|
||||
UPDATE worker_sessions
|
||||
SET last_seen_at = $2, updated_at = $2
|
||||
WHERE session_id = $1 AND status = 'active' AND expires_at > $2
|
||||
`,
|
||||
[String(sessionId || '').trim(), now],
|
||||
)
|
||||
return (result.rowCount || 0) > 0
|
||||
}
|
||||
|
||||
export async function revokeWorkerSession(
|
||||
workerId: number | string,
|
||||
sessionId: string,
|
||||
now: string,
|
||||
): Promise<boolean> {
|
||||
const result = await query(
|
||||
`
|
||||
UPDATE worker_sessions
|
||||
SET status = 'revoked', revoked_at = $3, updated_at = $3
|
||||
WHERE worker_id = $1 AND session_id = $2 AND status = 'active'
|
||||
`,
|
||||
[Number(workerId), String(sessionId || '').trim(), now],
|
||||
)
|
||||
return (result.rowCount || 0) > 0
|
||||
}
|
||||
|
||||
export async function revokeAllWorkerSessions(
|
||||
workerId: number | string,
|
||||
now: string,
|
||||
): Promise<number> {
|
||||
const result = await query(
|
||||
`
|
||||
UPDATE worker_sessions
|
||||
SET status = 'revoked', revoked_at = $2, updated_at = $2
|
||||
WHERE worker_id = $1 AND status = 'active'
|
||||
`,
|
||||
[Number(workerId), now],
|
||||
)
|
||||
return result.rowCount || 0
|
||||
}
|
||||
|
||||
export async function listWorkerSessions(
|
||||
workerId: number | string,
|
||||
now: string,
|
||||
): Promise<WorkerSessionRow[]> {
|
||||
await query(
|
||||
`
|
||||
UPDATE worker_sessions
|
||||
SET status = 'revoked', revoked_at = $2, updated_at = $2
|
||||
WHERE worker_id = $1 AND status = 'active' AND expires_at <= $2
|
||||
`,
|
||||
[Number(workerId), now],
|
||||
)
|
||||
const result = await query<WorkerSessionRow>(
|
||||
`
|
||||
SELECT * FROM worker_sessions
|
||||
WHERE worker_id = $1 AND status = 'active' AND expires_at > $2
|
||||
ORDER BY device_type ASC, last_seen_at DESC, id DESC
|
||||
`,
|
||||
[Number(workerId), now],
|
||||
)
|
||||
return result.rows
|
||||
}
|
||||
export {
|
||||
createWorkerSessionRecord,
|
||||
getWorkerSessionBySessionId,
|
||||
listWorkerSessions,
|
||||
revokeAllWorkerSessions,
|
||||
revokeWorkerSession,
|
||||
touchWorkerSession,
|
||||
type CreateWorkerSessionInput,
|
||||
type CreateWorkerSessionResult,
|
||||
} from './worker-session-repo.js'
|
||||
|
||||
export async function getWorkerLevelByKey(levelKey: string): Promise<WorkerLevelRow | null> {
|
||||
const result = await query<WorkerLevelRow>(
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import { query, withTransaction } from '../../db/client.js'
|
||||
import type { WorkerSessionRow } from './types.js'
|
||||
|
||||
export type CreateWorkerSessionInput = {
|
||||
sessionId: string
|
||||
workerId: number
|
||||
deviceId: string
|
||||
deviceType: string
|
||||
deviceName: string
|
||||
userAgent: string
|
||||
ipAddress: string
|
||||
issuedAt: string
|
||||
expiresAt: string
|
||||
now: string
|
||||
maxDevices: number
|
||||
}
|
||||
|
||||
export type CreateWorkerSessionResult = {
|
||||
created: boolean
|
||||
activeSessions: WorkerSessionRow[]
|
||||
session: WorkerSessionRow | null
|
||||
}
|
||||
|
||||
export async function createWorkerSessionRecord(
|
||||
input: CreateWorkerSessionInput,
|
||||
): Promise<CreateWorkerSessionResult> {
|
||||
return withTransaction(async (client) => {
|
||||
// 对账号加行锁,避免并发登录同时绕过设备数量限制。
|
||||
await client.query('SELECT id FROM worker_users WHERE id = $1 FOR UPDATE', [input.workerId])
|
||||
await client.query(
|
||||
`UPDATE worker_sessions
|
||||
SET status = 'revoked', revoked_at = $2, updated_at = $2
|
||||
WHERE worker_id = $1 AND status = 'active' AND expires_at <= $2`,
|
||||
[input.workerId, input.now],
|
||||
)
|
||||
await client.query(
|
||||
`UPDATE worker_sessions
|
||||
SET status = 'revoked', revoked_at = $3, updated_at = $3
|
||||
WHERE worker_id = $1 AND device_id = $2 AND status = 'active'`,
|
||||
[input.workerId, input.deviceId, input.now],
|
||||
)
|
||||
const activeResult = await client.query<WorkerSessionRow>(
|
||||
`SELECT * FROM worker_sessions
|
||||
WHERE worker_id = $1 AND status = 'active' AND expires_at > $2
|
||||
ORDER BY last_seen_at DESC, id DESC`,
|
||||
[input.workerId, input.now],
|
||||
)
|
||||
const activeSessions = activeResult.rows
|
||||
if (activeSessions.length >= input.maxDevices) {
|
||||
return { created: false, activeSessions, session: null }
|
||||
}
|
||||
const result = await client.query<WorkerSessionRow>(
|
||||
`INSERT INTO worker_sessions (
|
||||
session_id, worker_id, device_id, device_type, device_name,
|
||||
user_agent, ip_address, status, issued_at, last_seen_at,
|
||||
expires_at, created_at, updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, 'active', $8, $8, $9, $10, $10)
|
||||
RETURNING *`,
|
||||
[
|
||||
input.sessionId,
|
||||
input.workerId,
|
||||
input.deviceId,
|
||||
input.deviceType,
|
||||
input.deviceName,
|
||||
input.userAgent,
|
||||
input.ipAddress,
|
||||
input.issuedAt,
|
||||
input.expiresAt,
|
||||
input.now,
|
||||
],
|
||||
)
|
||||
return { created: true, activeSessions, session: result.rows[0] || null }
|
||||
})
|
||||
}
|
||||
|
||||
export async function getWorkerSessionBySessionId(
|
||||
sessionId: string,
|
||||
): Promise<WorkerSessionRow | null> {
|
||||
const result = await query<WorkerSessionRow>(
|
||||
'SELECT * FROM worker_sessions WHERE session_id = $1 LIMIT 1',
|
||||
[String(sessionId || '').trim()],
|
||||
)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function touchWorkerSession(sessionId: string, now: string): Promise<boolean> {
|
||||
const result = await query(
|
||||
`UPDATE worker_sessions
|
||||
SET last_seen_at = $2, updated_at = $2
|
||||
WHERE session_id = $1 AND status = 'active' AND expires_at > $2`,
|
||||
[String(sessionId || '').trim(), now],
|
||||
)
|
||||
return (result.rowCount || 0) > 0
|
||||
}
|
||||
|
||||
export async function revokeWorkerSession(
|
||||
workerId: number | string,
|
||||
sessionId: string,
|
||||
now: string,
|
||||
): Promise<boolean> {
|
||||
const result = await query(
|
||||
`UPDATE worker_sessions
|
||||
SET status = 'revoked', revoked_at = $3, updated_at = $3
|
||||
WHERE worker_id = $1 AND session_id = $2 AND status = 'active'`,
|
||||
[Number(workerId), String(sessionId || '').trim(), now],
|
||||
)
|
||||
return (result.rowCount || 0) > 0
|
||||
}
|
||||
|
||||
export async function revokeAllWorkerSessions(
|
||||
workerId: number | string,
|
||||
now: string,
|
||||
): Promise<number> {
|
||||
const result = await query(
|
||||
`UPDATE worker_sessions
|
||||
SET status = 'revoked', revoked_at = $2, updated_at = $2
|
||||
WHERE worker_id = $1 AND status = 'active'`,
|
||||
[Number(workerId), now],
|
||||
)
|
||||
return result.rowCount || 0
|
||||
}
|
||||
|
||||
export async function listWorkerSessions(
|
||||
workerId: number | string,
|
||||
now: string,
|
||||
): Promise<WorkerSessionRow[]> {
|
||||
await query(
|
||||
`UPDATE worker_sessions
|
||||
SET status = 'revoked', revoked_at = $2, updated_at = $2
|
||||
WHERE worker_id = $1 AND status = 'active' AND expires_at <= $2`,
|
||||
[Number(workerId), now],
|
||||
)
|
||||
const result = await query<WorkerSessionRow>(
|
||||
`SELECT * FROM worker_sessions
|
||||
WHERE worker_id = $1 AND status = 'active' AND expires_at > $2
|
||||
ORDER BY device_type ASC, last_seen_at DESC, id DESC`,
|
||||
[Number(workerId), now],
|
||||
)
|
||||
return result.rows
|
||||
}
|
||||
Reference in New Issue
Block a user