完善个人中心资金功能
This commit is contained in:
@@ -7,17 +7,22 @@ import { findLatestOrderByAnyPlatformOrderId, getOrderById } from '../../reposit
|
||||
import {
|
||||
acceptWorkOrderAndSettle,
|
||||
addWorkerWalletCredit,
|
||||
countWorkerAcceptedOrders,
|
||||
countWorkerActiveOrders,
|
||||
createWorkOrder,
|
||||
createWorkOrderEvent,
|
||||
createWorkerFinanceRequest,
|
||||
createWorkerUser,
|
||||
getWorkerFinanceRequestSummary,
|
||||
incrementWorkerSessionVersion,
|
||||
getWorkOrderById,
|
||||
getWorkOrderByOrderItemId,
|
||||
getWorkerUserById,
|
||||
getWorkerUserByUsername,
|
||||
listAllWorkCategories,
|
||||
listWorkerFinanceRequests,
|
||||
listPendingMaterialWorkOrdersByPlatformOrderId,
|
||||
listWorkerWalletLedgers,
|
||||
grabWorkOrder,
|
||||
listWorkOrders,
|
||||
listWorkProductRules,
|
||||
@@ -25,14 +30,17 @@ import {
|
||||
listWorkerUsers,
|
||||
resolveProblemWorkOrder,
|
||||
updateWorkOrder,
|
||||
updateWorkerPassword,
|
||||
updateWorkerUser,
|
||||
upsertWorkCategory,
|
||||
upsertWorkProductRule,
|
||||
upsertWorkerLevel,
|
||||
type WorkCategoryRow,
|
||||
type WorkerFinanceRequestRow,
|
||||
type WorkOrderRow,
|
||||
type WorkProductRuleRow,
|
||||
type WorkerLevelRow,
|
||||
type WorkerWalletLedgerRow,
|
||||
type WorkerUserRow,
|
||||
type GrabWorkOrderFailureReason,
|
||||
type ProblemWorkOrderResolutionAction,
|
||||
@@ -278,9 +286,226 @@ export function requireActiveWorkerSession(session: WorkerSession | null | undef
|
||||
|
||||
export async function getWorkerProfile(session: WorkerSession) {
|
||||
const worker = await getRequiredWorker(session.workerId)
|
||||
const [financeSummary, acceptedOrderCount] = await Promise.all([
|
||||
getWorkerFinanceRequestSummary(session.workerId),
|
||||
countWorkerAcceptedOrders(session.workerId),
|
||||
])
|
||||
return {
|
||||
worker: mapWorkerUser(worker),
|
||||
permissions: resolveWorkerPermissions(worker),
|
||||
summary: {
|
||||
acceptedOrderCount,
|
||||
pendingWithdrawAmount: financeSummary.pendingWithdrawAmount,
|
||||
approvedWithdrawAmount: financeSummary.approvedWithdrawAmount,
|
||||
pendingRechargeAmount: financeSummary.pendingRechargeAmount,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function listWorkerProfileWalletLedgers(
|
||||
query: JsonObject = {},
|
||||
session: WorkerSession,
|
||||
) {
|
||||
const page = normalizePage(query.page)
|
||||
const pageSize = normalizePageSize(query.pageSize)
|
||||
const ledgerType = normalizeWalletLedgerType(query.ledgerType)
|
||||
const { items, total } = await listWorkerWalletLedgers({
|
||||
page,
|
||||
pageSize,
|
||||
workerId: session.workerId,
|
||||
ledgerType,
|
||||
})
|
||||
return {
|
||||
items: items.map(mapWalletLedger),
|
||||
pagination: { page, pageSize, total },
|
||||
}
|
||||
}
|
||||
|
||||
export async function listWorkerProfileFinanceRequests(
|
||||
query: JsonObject = {},
|
||||
session: WorkerSession,
|
||||
) {
|
||||
const page = normalizePage(query.page)
|
||||
const pageSize = normalizePageSize(query.pageSize)
|
||||
const status = normalizeFinanceRequestStatus(query.status)
|
||||
const requestType = normalizeFinanceRequestType(query.requestType)
|
||||
const { items, total } = await listWorkerFinanceRequests({
|
||||
page,
|
||||
pageSize,
|
||||
workerId: session.workerId,
|
||||
status,
|
||||
requestType,
|
||||
})
|
||||
return {
|
||||
items: items.map(mapFinanceRequest),
|
||||
pagination: { page, pageSize, total },
|
||||
}
|
||||
}
|
||||
|
||||
export async function createWorkerRechargeRequest(
|
||||
payload: JsonObject = {},
|
||||
session: WorkerSession,
|
||||
) {
|
||||
requireActiveWorkerSession(session)
|
||||
const worker = await getRequiredWorker(session.workerId)
|
||||
const amount = normalizeAmountFen(payload.amount ?? payload.amountYuan, 0)
|
||||
if (amount <= 0) {
|
||||
throw createHttpError('充值金额必须大于 0', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_recharge_amount_invalid',
|
||||
})
|
||||
}
|
||||
|
||||
const note = String(payload.note || payload.remark || '').trim()
|
||||
const created = await createWorkerFinanceRequest({
|
||||
workerId: worker.id,
|
||||
requestType: 'recharge',
|
||||
amount,
|
||||
accountChannel: 'manual',
|
||||
accountName: worker.display_name || worker.username,
|
||||
accountNo: worker.phone || '',
|
||||
note: note || '个人中心充值申请',
|
||||
payloadJson: JSON.stringify({
|
||||
source: 'worker_profile_recharge',
|
||||
username: worker.username,
|
||||
phone: worker.phone || '',
|
||||
}),
|
||||
now: nowIso(),
|
||||
})
|
||||
if (!created) {
|
||||
throw createHttpError('充值申请创建失败', {
|
||||
statusCode: 500,
|
||||
errorCode: 'worker_recharge_request_create_failed',
|
||||
})
|
||||
}
|
||||
return {
|
||||
request: mapFinanceRequest(created),
|
||||
}
|
||||
}
|
||||
|
||||
export async function createWorkerWithdrawRequest(
|
||||
payload: JsonObject = {},
|
||||
session: WorkerSession,
|
||||
) {
|
||||
requireActiveWorkerSession(session)
|
||||
const worker = await getRequiredWorker(session.workerId)
|
||||
const amount = normalizeAmountFen(payload.amount ?? payload.amountYuan, 0)
|
||||
if (amount <= 0) {
|
||||
throw createHttpError('提现金额必须大于 0', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_withdraw_amount_invalid',
|
||||
})
|
||||
}
|
||||
|
||||
const accountChannel = normalizeWithdrawChannel(
|
||||
payload.accountChannel || payload.channel,
|
||||
)
|
||||
const accountName = String(payload.accountName || payload.realName || '').trim()
|
||||
const accountNo = String(payload.accountNo || payload.account || '').trim()
|
||||
if (!accountName) {
|
||||
throw createHttpError('请填写收款人姓名', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_withdraw_account_name_required',
|
||||
})
|
||||
}
|
||||
if (!accountNo) {
|
||||
throw createHttpError('请填写收款账号', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_withdraw_account_no_required',
|
||||
})
|
||||
}
|
||||
|
||||
const financeSummary = await getWorkerFinanceRequestSummary(worker.id)
|
||||
const availableAmount = Number(worker.available_amount || 0)
|
||||
const availableForWithdraw = Math.max(
|
||||
0,
|
||||
availableAmount - financeSummary.pendingWithdrawAmount,
|
||||
)
|
||||
if (amount > availableForWithdraw) {
|
||||
throw createHttpError('可提现余额不足,请先减少提现金额或等待已提交申请处理', {
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_withdraw_amount_insufficient',
|
||||
})
|
||||
}
|
||||
|
||||
const note = String(payload.note || payload.remark || '').trim()
|
||||
const created = await createWorkerFinanceRequest({
|
||||
workerId: worker.id,
|
||||
requestType: 'withdraw',
|
||||
amount,
|
||||
accountChannel,
|
||||
accountName,
|
||||
accountNo,
|
||||
note: note || '个人中心提现申请',
|
||||
payloadJson: JSON.stringify({
|
||||
source: 'worker_profile_withdraw',
|
||||
username: worker.username,
|
||||
phone: worker.phone || '',
|
||||
}),
|
||||
now: nowIso(),
|
||||
})
|
||||
if (!created) {
|
||||
throw createHttpError('提现申请创建失败', {
|
||||
statusCode: 500,
|
||||
errorCode: 'worker_withdraw_request_create_failed',
|
||||
})
|
||||
}
|
||||
return {
|
||||
request: mapFinanceRequest(created),
|
||||
availableForWithdraw,
|
||||
}
|
||||
}
|
||||
|
||||
export async function changeWorkerPassword(
|
||||
payload: JsonObject = {},
|
||||
session: WorkerSession,
|
||||
) {
|
||||
const worker = await getRequiredWorker(session.workerId)
|
||||
const currentPassword = normalizePassword(
|
||||
payload.currentPassword || payload.oldPassword,
|
||||
)
|
||||
const nextPassword = normalizePassword(payload.newPassword || payload.password)
|
||||
|
||||
if (!currentPassword) {
|
||||
throw createHttpError('请输入当前密码', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_password_current_required',
|
||||
})
|
||||
}
|
||||
|
||||
if (!verifyWorkerPassword(currentPassword, worker.password_hash)) {
|
||||
throw createHttpError('当前密码不正确', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_password_current_invalid',
|
||||
})
|
||||
}
|
||||
|
||||
if (!nextPassword) {
|
||||
throw createHttpError('请输入新密码', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_password_new_required',
|
||||
})
|
||||
}
|
||||
|
||||
validateWorkerPassword(nextPassword)
|
||||
|
||||
if (currentPassword === nextPassword) {
|
||||
throw createHttpError('新密码不能与当前密码一致', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_password_unchanged',
|
||||
})
|
||||
}
|
||||
|
||||
const updated = await updateWorkerPassword({
|
||||
workerId: worker.id,
|
||||
passwordHash: hashWorkerPassword(nextPassword),
|
||||
sessionVersion: normalizeSessionVersion(worker.session_version) + 1,
|
||||
now: nowIso(),
|
||||
})
|
||||
|
||||
return {
|
||||
worker: mapWorkerUser(updated || worker),
|
||||
reloginRequired: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1291,6 +1516,8 @@ function mapWorkerUser(worker: WorkerUserRow) {
|
||||
wallet: {
|
||||
availableAmount: Number(worker.available_amount || 0),
|
||||
frozenDepositAmount: Number(worker.frozen_deposit_amount || 0),
|
||||
totalCreditedAmount: Number(worker.total_credited_amount || 0),
|
||||
totalSettledAmount: Number(worker.total_settled_amount || 0),
|
||||
},
|
||||
createdAt: worker.created_at,
|
||||
updatedAt: worker.updated_at,
|
||||
@@ -1298,6 +1525,43 @@ function mapWorkerUser(worker: WorkerUserRow) {
|
||||
}
|
||||
}
|
||||
|
||||
function mapWalletLedger(ledger: WorkerWalletLedgerRow | null | undefined) {
|
||||
if (!ledger) return null
|
||||
return {
|
||||
ledgerId: Number(ledger.id),
|
||||
ledgerType: ledger.ledger_type,
|
||||
amount: Number(ledger.amount || 0),
|
||||
balanceAfter: Number(ledger.balance_after || 0),
|
||||
frozenAfter: Number(ledger.frozen_after || 0),
|
||||
relatedWorkOrderId: ledger.related_work_order_id
|
||||
? Number(ledger.related_work_order_id)
|
||||
: null,
|
||||
auditStatus: ledger.audit_status || '',
|
||||
note: ledger.note || '',
|
||||
payload: safeParseJson(ledger.payload_json),
|
||||
createdAt: ledger.created_at,
|
||||
}
|
||||
}
|
||||
|
||||
function mapFinanceRequest(request: WorkerFinanceRequestRow | null | undefined) {
|
||||
if (!request) return null
|
||||
return {
|
||||
requestId: Number(request.id),
|
||||
requestType: request.request_type || '',
|
||||
status: request.status || '',
|
||||
amount: Number(request.amount || 0),
|
||||
accountChannel: request.account_channel || '',
|
||||
accountName: request.account_name || '',
|
||||
accountNo: request.account_no || '',
|
||||
note: request.note || '',
|
||||
reviewedNote: request.reviewed_note || '',
|
||||
payload: safeParseJson(request.payload_json),
|
||||
createdAt: request.created_at,
|
||||
updatedAt: request.updated_at,
|
||||
reviewedAt: request.reviewed_at,
|
||||
}
|
||||
}
|
||||
|
||||
function mapWorkOrderAdmin(workOrder: WorkOrderRow) {
|
||||
return {
|
||||
workOrderId: Number(workOrder.id),
|
||||
@@ -1632,6 +1896,61 @@ function normalizeEnabledStatus(value: unknown) {
|
||||
return String(value || 'active').trim() === 'disabled' ? 'disabled' : 'active'
|
||||
}
|
||||
|
||||
function normalizeWalletLedgerType(value: unknown) {
|
||||
const ledgerType = String(value || '').trim()
|
||||
if (!ledgerType) return ''
|
||||
if (
|
||||
[
|
||||
'manual_credit',
|
||||
'deposit_freeze',
|
||||
'deposit_release',
|
||||
'deposit_deduction',
|
||||
'reward_settlement',
|
||||
].includes(ledgerType)
|
||||
) {
|
||||
return ledgerType
|
||||
}
|
||||
throw createHttpError('钱包流水类型不正确', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_wallet_ledger_type_invalid',
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeFinanceRequestType(value: unknown) {
|
||||
const requestType = String(value || '').trim()
|
||||
if (!requestType) return ''
|
||||
if (['recharge', 'withdraw'].includes(requestType)) {
|
||||
return requestType
|
||||
}
|
||||
throw createHttpError('资金申请类型不正确', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_finance_request_type_invalid',
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeFinanceRequestStatus(value: unknown) {
|
||||
const status = String(value || '').trim()
|
||||
if (!status) return ''
|
||||
if (['pending', 'approved', 'rejected', 'cancelled'].includes(status)) {
|
||||
return status
|
||||
}
|
||||
throw createHttpError('资金申请状态不正确', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_finance_request_status_invalid',
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeWithdrawChannel(value: unknown) {
|
||||
const channel = String(value || '').trim()
|
||||
if (['alipay', 'wechat', 'bank'].includes(channel)) {
|
||||
return channel
|
||||
}
|
||||
throw createHttpError('提现方式不正确', {
|
||||
statusCode: 400,
|
||||
errorCode: 'worker_withdraw_channel_invalid',
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeMatchType(value: unknown) {
|
||||
const matchType = String(value || '').trim()
|
||||
if (matchType === 'exact') return 'exact'
|
||||
|
||||
Reference in New Issue
Block a user