229 lines
12 KiB
PHP
Executable File
229 lines
12 KiB
PHP
Executable File
<?php
|
||
require_once __DIR__ . '/includes/auth.php';
|
||
require_once __DIR__ . '/includes/functions.php';
|
||
requireAdmin();
|
||
|
||
$pageTitle = '工单管理';
|
||
require __DIR__ . '/includes/header.php';
|
||
|
||
$msg = '';
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||
if (!verifyCsrf($_POST['csrf_token'] ?? '')) { die('CSRF token无效'); }
|
||
$id = (int)($_POST['id'] ?? 0);
|
||
if ($_POST['action'] === 'set_done') {
|
||
$pdo->beginTransaction();
|
||
try {
|
||
$stmt = $pdo->prepare("SELECT * FROM work_orders WHERE id = ? AND status = '未处理'");
|
||
$stmt->execute([$id]);
|
||
$order = $stmt->fetch();
|
||
if (!$order) {
|
||
$msg = '<div class="alert alert-danger">工单不存在或状态已变更</div>';
|
||
$pdo->rollBack();
|
||
} else {
|
||
$codes = array_filter(explode("\n", $order['code'] ?? ''));
|
||
$missingClaim = 0;
|
||
$noStock = 0;
|
||
|
||
foreach ($codes as $code) {
|
||
$code = trim($code);
|
||
if ($code === '') continue;
|
||
|
||
$stmt = $pdo->prepare("SELECT * FROM claim_records WHERE code = ? AND user_id = ? AND product_id = ? FOR UPDATE");
|
||
$stmt->execute([$code, $order['creator_id'], $order['product_id']]);
|
||
$cr = $stmt->fetch();
|
||
if (!$cr) { $missingClaim++; continue; }
|
||
|
||
$stmt = $pdo->prepare("SELECT 1 FROM redemption_codes WHERE value = ? AND status = 1 AND (expired_at IS NULL OR expired_at > NOW()) AND product_id = ? LIMIT 1 FOR UPDATE");
|
||
$stmt->execute([$cr['value'], $order['product_id']]);
|
||
if (!$stmt->fetch()) { $noStock++; }
|
||
}
|
||
|
||
if ($missingClaim > 0 || $noStock > 0) {
|
||
$pdo->rollBack();
|
||
$errParts = [];
|
||
if ($missingClaim > 0) $errParts[] = $missingClaim . ' 个兑换码未找到领取记录';
|
||
if ($noStock > 0) $errParts[] = '库存不足,无法补发';
|
||
$msg = '<div class="alert alert-danger">' . implode(';', $errParts) . ',工单无法处理</div>';
|
||
} else {
|
||
$replaced = 0;
|
||
foreach ($codes as $code) {
|
||
$code = trim($code);
|
||
if ($code === '') continue;
|
||
|
||
$stmt = $pdo->prepare("SELECT * FROM claim_records WHERE code = ? AND user_id = ? AND product_id = ?");
|
||
$stmt->execute([$code, $order['creator_id'], $order['product_id']]);
|
||
$cr = $stmt->fetch();
|
||
if (!$cr) continue;
|
||
|
||
$stmt = $pdo->prepare("UPDATE claim_records SET status = 3, used_at = COALESCE(used_at, NOW()), remark = '该码已过期,新码已补发' WHERE id = ?");
|
||
$stmt->execute([$cr['id']]);
|
||
|
||
$stmt = $pdo->prepare("SELECT * FROM redemption_codes WHERE value = ? AND status = 1 AND (expired_at IS NULL OR expired_at > NOW()) AND product_id = ? LIMIT 1 FOR UPDATE");
|
||
$stmt->execute([$cr['value'], $order['product_id']]);
|
||
$newCode = $stmt->fetch();
|
||
|
||
$stmt = $pdo->prepare("UPDATE redemption_codes SET status = 2, claim_user_id = ?, claimed_at = NOW() WHERE id = ?");
|
||
$stmt->execute([$order['creator_id'], $newCode['id']]);
|
||
|
||
$stmt = $pdo->prepare("INSERT INTO claim_records (product_id, created_at, user_id, code_name, batch_no, code_type, code, value, status, expired_at, price_tier1, price_tier2, claim_user, claimed_at, remark) VALUES (?, NOW(), ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, NOW(), '系统补发')");
|
||
$stmt->execute([
|
||
$newCode['product_id'],
|
||
$order['creator_id'],
|
||
$newCode['name'],
|
||
$newCode['batch_no'],
|
||
$newCode['type'],
|
||
$newCode['code'],
|
||
$newCode['value'],
|
||
$newCode['expired_at'],
|
||
$newCode['price_tier1'],
|
||
$newCode['price_tier2'],
|
||
$cr['claim_user']
|
||
]);
|
||
$replaced++;
|
||
}
|
||
|
||
$stmt = $pdo->prepare("UPDATE work_orders SET status = '已处理', processed_at = NOW(), processor_id = ? WHERE id = ?");
|
||
$stmt->execute([getCurrentUserId(), $id]);
|
||
$pdo->commit();
|
||
|
||
$_SESSION['flash_msg'] = '工单已处理,' . $replaced . ' 个兑换码已过期并补发新码';
|
||
$_SESSION['flash_type'] = 'success';
|
||
header('Location: work_order_manage.php');
|
||
exit;
|
||
}
|
||
}
|
||
} catch (Exception $e) {
|
||
$pdo->rollBack();
|
||
$msg = '<div class="alert alert-danger">处理失败,请重试</div>';
|
||
}
|
||
} elseif ($_POST['action'] === 'reject') {
|
||
$stmt = $pdo->prepare("SELECT attachment FROM work_orders WHERE id = ?");
|
||
$stmt->execute([$id]);
|
||
$order = $stmt->fetch();
|
||
$stmt = $pdo->prepare("UPDATE work_orders SET status = '已驳回', processed_at = NOW(), processor_id = ? WHERE id = ? AND status = '未处理'");
|
||
$stmt->execute([getCurrentUserId(), $id]);
|
||
if ($stmt->rowCount()) {
|
||
if ($order && $order['attachment'] && file_exists(__DIR__ . '/' . $order['attachment'])) {
|
||
unlink(__DIR__ . '/' . $order['attachment']);
|
||
}
|
||
$_SESSION['flash_msg'] = '工单已驳回';
|
||
$_SESSION['flash_type'] = 'warning';
|
||
header('Location: work_order_manage.php');
|
||
exit;
|
||
} else {
|
||
$msg = '<div class="alert alert-danger">工单状态已变更,无法驳回</div>';
|
||
}
|
||
} elseif ($_POST['action'] === 'delete') {
|
||
$stmt = $pdo->prepare("SELECT attachment, status FROM work_orders WHERE id = ?");
|
||
$stmt->execute([$id]);
|
||
$order = $stmt->fetch();
|
||
if (!$order) {
|
||
$msg = '<div class="alert alert-danger">工单不存在</div>';
|
||
} elseif ($order['status'] === '已处理') {
|
||
$msg = '<div class="alert alert-danger">已处理的工单不能删除</div>';
|
||
} else {
|
||
if ($order['attachment'] && file_exists(__DIR__ . '/' . $order['attachment'])) {
|
||
unlink(__DIR__ . '/' . $order['attachment']);
|
||
}
|
||
$pdo->prepare('DELETE FROM work_orders WHERE id = ?')->execute([$id]);
|
||
$_SESSION['flash_msg'] = '工单已删除';
|
||
$_SESSION['flash_type'] = 'success';
|
||
header('Location: work_order_manage.php');
|
||
exit;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 分页
|
||
$page = max(1, (int)($_GET['p'] ?? 1));
|
||
$perPage = 20;
|
||
$offset = ($page - 1) * $perPage;
|
||
|
||
$stmt = $pdo->prepare('SELECT COUNT(*) FROM work_orders WHERE product_id = ?');
|
||
$stmt->execute([$pid]);
|
||
$total = (int)$stmt->fetchColumn();
|
||
$totalPages = max(1, ceil($total / $perPage));
|
||
|
||
$stmt = $pdo->prepare('SELECT w.*, c.username AS creator_name, p.username AS processor_name FROM work_orders w LEFT JOIN users c ON w.creator_id = c.id LEFT JOIN users p ON w.processor_id = p.id WHERE w.product_id = ? ORDER BY w.created_at DESC LIMIT ? OFFSET ?');
|
||
$stmt->execute([$pid, $perPage, $offset]);
|
||
$orders = $stmt->fetchAll();
|
||
?>
|
||
<div class="card">
|
||
<h2>工单管理</h2>
|
||
<?php if (isset($_SESSION['flash_msg'])): ?>
|
||
<div class="alert alert-<?= h($_SESSION['flash_type'] ?? 'success') ?>"><?= h($_SESSION['flash_msg']) ?></div>
|
||
<?php unset($_SESSION['flash_msg'], $_SESSION['flash_type']); ?>
|
||
<?php endif; ?>
|
||
<?= $msg ?>
|
||
<div class="table-wrapper">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>内容</th>
|
||
<th>兑换码</th>
|
||
<th>附件</th>
|
||
<th>发起人</th>
|
||
<th>发起时间</th>
|
||
<th>处理时间</th>
|
||
<th>处理人</th>
|
||
<th>状态</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($orders as $o): ?>
|
||
<tr>
|
||
<td><?= $o['id'] ?></td>
|
||
<td style="max-width:200px;white-space:pre-wrap;word-break:break-all;"><?= h(mb_substr($o['content'], 0, 100)) ?><?= mb_strlen($o['content']) > 100 ? '...' : '' ?></td>
|
||
<td style="white-space:pre-wrap;"><?php if ($o['code']): $lines = explode("\n", $o['code']); echo h(implode("\n", $lines)); else: ?>-<?php endif; ?></td>
|
||
<td>
|
||
<?php if ($o['attachment']): ?>
|
||
<a href="<?= h($o['attachment']) ?>" target="_blank" class="btn btn-info btn-sm">查看</a>
|
||
<?php else: ?>
|
||
-
|
||
<?php endif; ?>
|
||
</td>
|
||
<td><?= h($o['creator_name'] ?? '-') ?></td>
|
||
<td><?= formatDateTime($o['created_at']) ?></td>
|
||
<td><?= formatDateTime($o['processed_at']) ?></td>
|
||
<td><?= h($o['processor_name'] ?? '-') ?></td>
|
||
<td><?= workOrderStatusBadge($o['status']) ?></td>
|
||
<td>
|
||
<div class="action-group">
|
||
<?php if ($o['status'] === '未处理'): ?>
|
||
<form method="post" style="display:inline;">
|
||
<input type="hidden" name="action" value="set_done">
|
||
<input type="hidden" name="id" value="<?= $o['id'] ?>">
|
||
<input type="hidden" name="csrf_token" value="<?= csrfToken() ?>">
|
||
<button type="submit" class="btn btn-success btn-sm">设为已处理</button>
|
||
</form>
|
||
<form method="post" style="display:inline;">
|
||
<input type="hidden" name="action" value="reject">
|
||
<input type="hidden" name="id" value="<?= $o['id'] ?>">
|
||
<input type="hidden" name="csrf_token" value="<?= csrfToken() ?>">
|
||
<button type="submit" class="btn btn-warning btn-sm" onclick="return confirm('确定驳回此工单?驳回后数据保留但不可操作')">驳回</button>
|
||
</form>
|
||
<form method="post" style="display:inline;" onsubmit="return confirm('确定删除此工单?')">
|
||
<input type="hidden" name="action" value="delete">
|
||
<input type="hidden" name="id" value="<?= $o['id'] ?>">
|
||
<input type="hidden" name="csrf_token" value="<?= csrfToken() ?>">
|
||
<button type="submit" class="btn btn-danger btn-sm">删除</button>
|
||
</form>
|
||
<?php elseif ($o['status'] === '已驳回'): ?>
|
||
<span style="color:#999;font-size:12px;">已驳回,不可操作</span>
|
||
<?php endif; ?>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php if (empty($orders)): ?>
|
||
<tr><td colspan="10" style="text-align:center;color:#999;">暂无工单</td></tr>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php renderPagination($page, $totalPages); ?>
|
||
</div>
|
||
<?php require __DIR__ . '/includes/footer.php'; ?>
|