增加工单首次查看标识

This commit is contained in:
yml2213
2026-08-17 20:11:35 +08:00
parent 1b4bdec56d
commit 370acdba60
12 changed files with 228 additions and 18 deletions
@@ -5,6 +5,7 @@ import {
getWorkerUserById,
getWorkOrderById,
listWorkerWorkOrderNotes,
listWorkerWorkOrderViews,
listWorkOrderShares,
sumPendingUnfreezeByOrderIds,
} from '../../repositories/worker-platform/index.js'
@@ -283,15 +284,26 @@ async function publishWorkOrderSnapshots(input: {
)) {
const worker = await getWorkerUserById(workerId)
if (!worker) continue
const notes = await listWorkerWorkOrderNotes(workerId, [workOrderId])
const [notes, views] = await Promise.all([
listWorkerWorkOrderNotes(workerId, [workOrderId]),
listWorkerWorkOrderViews(workerId, [workOrderId]),
])
const note = notes.find((item) => Number(item.work_order_id) === workOrderId)?.note || ''
const viewedAt =
views.find((item) => Number(item.work_order_id) === workOrderId)?.viewed_at || null
const share = shares.find((item) => Number(item.worker_id) === workerId)
publishRealtimeEvent({
type: 'work_order.changed',
entityId: workOrderId,
scopes: ['worker_my_orders'],
operation: 'upsert',
data: mapWorkOrderForWorker(workOrder, resolveWorkerPermissions(worker), share, note),
data: mapWorkOrderForWorker(
workOrder,
resolveWorkerPermissions(worker),
share,
note,
viewedAt,
),
workerIds: [workerId],
})
}
@@ -365,6 +365,7 @@ export function mapWorkOrderForWorker(
permissions: { depositFreeAmount: number },
myShare?: WorkOrderShareRow | null,
workerNote = '',
viewedAt?: string | null,
) {
const mapped = mapWorkOrderAdmin(workOrder)
const {
@@ -380,6 +381,7 @@ export function mapWorkOrderForWorker(
freezeDepositAmount: resolveFreezeDepositAmount(workOrder, permissions),
workerNote,
myShare: myShare ? mapWorkOrderShare(myShare) : null,
...(viewedAt === undefined ? {} : { isUnread: !viewedAt }),
}
}
@@ -8,6 +8,7 @@ import {
isWorkerPasswordStrong,
normalizeWithdrawChannel,
normalizeRequirementFields,
mapWorkOrderForWorker,
resolveCollectSubmitTargetWorkOrder,
resolveWorkerPermissions,
resolveSkuNameQuantity,
@@ -197,6 +198,17 @@ function buildWorkOrderRow(overrides: Partial<WorkOrderRow> = {}): WorkOrderRow
}
}
test('mapWorkOrderForWorker reflects the personal first-view state', () => {
const workOrder = buildWorkOrderRow({ assigned_worker_id: 1 })
const permissions = resolveWorkerPermissions(buildWorkerUserRow())
assert.equal(mapWorkOrderForWorker(workOrder, permissions, null, '', null).isUnread, true)
assert.equal(
mapWorkOrderForWorker(workOrder, permissions, null, '', '2026-08-17T00:00:00.000Z').isUnread,
false,
)
assert.equal(mapWorkOrderForWorker(workOrder, permissions).isUnread, undefined)
})
test('resolveCollectSubmitTargetWorkOrder returns the only pending work order when no id is provided', () => {
const workOrder = buildWorkOrderRow()
const selected = resolveCollectSubmitTargetWorkOrder('ORDER-001', [workOrder], '')
@@ -43,6 +43,7 @@ import {
listWorkerSharesByWorker,
listWorkerWalletLedgers,
listWorkerWorkOrderNotes,
listWorkerWorkOrderViews,
listWorkerWithdrawalAccounts,
listWorkCategories,
listWorkOrders,
@@ -60,6 +61,7 @@ import {
updateWorkOrderShareAcceptanceRecord,
updateWorkOrderShareDraftAcceptance,
upsertWorkerWorkOrderNote,
markWorkerWorkOrderViewed,
updateWorkerPassword,
upsertWorkCategory,
upsertWorkerLevel,
@@ -1335,13 +1337,17 @@ export async function listWorkerMyOrders(query: JsonObject = {}, session: Worker
const permissions = resolveWorkerPermissions(worker)
const myShares = await listWorkerSharesByWorker(worker.id)
const myShareByOrderId = new Map(myShares.map((share) => [Number(share.work_order_id), share]))
const notes = await listWorkerWorkOrderNotes(
worker.id,
items.map((item) => Number(item.id)),
)
const workOrderIds = items.map((item) => Number(item.id))
const [notes, views] = await Promise.all([
listWorkerWorkOrderNotes(worker.id, workOrderIds),
listWorkerWorkOrderViews(worker.id, workOrderIds),
])
const noteByOrderId = new Map(
notes.map((item) => [Number(item.work_order_id), String(item.note || '')]),
)
const viewedAtByOrderId = new Map(
views.map((item) => [Number(item.work_order_id), String(item.viewed_at || '')]),
)
return {
items: items.map((item) =>
mapWorkOrderForWorker(
@@ -1349,6 +1355,7 @@ export async function listWorkerMyOrders(query: JsonObject = {}, session: Worker
permissions,
myShareByOrderId.get(Number(item.id)),
noteByOrderId.get(Number(item.id)) || '',
viewedAtByOrderId.get(Number(item.id)) || null,
),
),
pagination: { page, pageSize, total },
@@ -1456,6 +1463,34 @@ export async function saveWorkerOrderNote(
}
}
/** 标记打手本人已查看工单,首次操作后不再展示“新”标识。 */
export async function markWorkerOrderRead(workOrderId: number | string, session: WorkerSession) {
requireActiveWorkerSession(session)
const workOrder = await getRequiredWorkOrder(workOrderId)
const workerId = Number(session.workerId)
const sharingShare = await getWorkOrderShare(workOrder.id, workerId)
const isOrderOwner =
Number(workOrder.assigned_worker_id || 0) === workerId ||
Number(workOrder.last_assigned_worker_id || 0) === workerId
if (!sharingShare && !isOrderOwner) {
throw createHttpError('只能标记自己的订单为已读', {
statusCode: 403,
errorCode: 'work_order_read_owner_required',
})
}
const viewedAt = await markWorkerWorkOrderViewed({
workOrderId: Number(workOrder.id),
workerId,
now: nowIso(),
})
publishWorkOrderRealtimeChange({
workOrderId: Number(workOrder.id),
workerIds: [workerId],
})
return { viewedAt, isUnread: false }
}
export async function joinWorkerSharingOrder(
workOrderId: number | string,
payload: JsonObject = {},