coupon/claim_records.php

368 lines
17 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
require_once __DIR__ . '/includes/auth.php';
require_once __DIR__ . '/includes/functions.php';
requireLogin();
// 导出 CSV
if (isset($_GET['export'])) {
$isAdmin = isAdmin();
$pid = getCurrentProductId();
$where = 'WHERE r.product_id = ?';
$params = [$pid];
if (!$isAdmin) {
$where .= ' AND r.user_id = ?';
$params[] = getCurrentUserId();
}
if (!empty($_GET['search'])) {
$s = '%' . $_GET['search'] . '%';
$where .= ' AND (r.code LIKE ? OR r.batch_no LIKE ? OR r.code_name LIKE ?)';
$params = array_merge($params, [$s, $s, $s]);
}
$stmt = $pdo->prepare("SELECT r.id, r.code_name, r.batch_no, r.code, r.value, r.status, r.expired_at FROM claim_records r $where ORDER BY r.id DESC");
$stmt->execute($params);
$rows = $stmt->fetchAll();
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=claim_records.csv');
echo "\xEF\xBB\xBF";
$f = fopen('php://output', 'w');
fputcsv($f, ['ID', '兑换码名称', '批次号', '兑换码', '面值', '状态', '过期时间']);
foreach ($rows as $r) {
$statusMap = [1 => '未使用', 2 => '已使用', 3 => '已过期'];
fputcsv($f, [
$r['id'],
$r['code_name'],
$r['batch_no'],
(int)$r['status'] === 3 ? maskCode($r['code']) : $r['code'],
$r['value'],
$statusMap[(int)$r['status']] ?? '未知',
$r['expired_at'] ? date('Y-m-d H:i:s', strtotime($r['expired_at'])) : '',
]);
}
fclose($f);
exit;
}
$pageTitle = '兑换码领取记录';
require __DIR__ . '/includes/header.php';
$isAdmin = isAdmin();
$msg = '';
// 管理员:修改状态
if ($isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if (!verifyCsrf($_POST['csrf_token'] ?? '')) { die('CSRF token无效'); }
$id = (int)($_POST['id'] ?? 0);
if ($_POST['action'] === 'edit_status') {
$newStatus = (int)($_POST['status'] ?? 0);
$usedAt = ($_POST['used_at'] ?? '') !== '' ? str_replace('T', ' ', $_POST['used_at']) . ':00' : '';
$userId = (int)($_POST['user_id'] ?? 0);
$usedUserId = trim($_POST['used_user_id'] ?? '');
if ($newStatus < 1 || $newStatus > 3) {
$msg = '<div class="alert alert-danger">无效的状态值</div>';
} elseif ($newStatus === 2 && $usedUserId === '') {
$msg = '<div class="alert alert-danger">状态设为已使用时必须填写会员ID</div>';
} else {
$update = ['status = ?'];
$params = [$newStatus];
if ($usedAt !== '') { $update[] = 'used_at = ?'; $params[] = $usedAt; }
if ($userId > 0) { $update[] = 'user_id = ?'; $params[] = $userId; }
if ($usedUserId !== '') { $update[] = 'used_user_id = ?'; $params[] = $usedUserId; }
$params[] = $id;
$stmt = $pdo->prepare('UPDATE claim_records SET ' . implode(', ', $update) . ' WHERE id = ?');
$stmt->execute($params);
$_SESSION['flash_msg'] = '状态已更新';
$_SESSION['flash_type'] = 'success';
header('Location: claim_records.php');
exit;
}
} elseif ($_POST['action'] === 'manual_add') {
$codeName = trim($_POST['code_name'] ?? '');
$batchNo = trim($_POST['batch_no'] ?? '');
$codeType = (int)($_POST['code_type'] ?? 1);
$code = trim($_POST['code'] ?? '');
$value = trim($_POST['value'] ?? '');
$expiredAt = $_POST['expired_at'] ?: null;
$userId = (int)($_POST['user_id'] ?? 0);
$claimedAt = ($_POST['claimed_at'] ?? '') !== '' ? str_replace('T', ' ', $_POST['claimed_at']) . ':00' : null;
$price1 = $_POST['price_tier1'] !== '' ? (float)$_POST['price_tier1'] : null;
$price2 = $_POST['price_tier2'] !== '' ? (float)$_POST['price_tier2'] : null;
$recStatus = (int)($_POST['rec_status'] ?? 1);
if (!in_array($recStatus, [1, 2, 3])) $recStatus = 1;
$usedUserId = trim($_POST['used_user_id'] ?? '');
$usedAt = ($_POST['used_at'] ?? '') !== '' ? str_replace('T', ' ', $_POST['used_at']) . ':00' : null;
if (!$codeName || !$batchNo || !$code) {
$msg = '<div class="alert alert-danger">兑换码名称、批次号、兑换码为必填项</div>';
} else {
$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, claimed_at, used_user_id, used_at, remark) VALUES (?, NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
$stmt->execute([getCurrentProductId(), $userId ?: null, $codeName, $batchNo, $codeType, $code, $value, $recStatus, $expiredAt, $price1, $price2, $claimedAt, $usedUserId ?: null, $usedAt, '手动添加']);
$_SESSION['flash_msg'] = '手动添加成功';
$_SESSION['flash_type'] = 'success';
header('Location: claim_records.php');
exit;
}
}
}
// 分页 + 条件
$page = max(1, (int)($_GET['p'] ?? 1));
$perPage = 20;
$offset = ($page - 1) * $perPage;
$pid = getCurrentProductId();
$where = 'WHERE r.product_id = ?';
$searchParams = [$pid];
if (!$isAdmin) {
$where .= ' AND r.user_id = ?';
$searchParams[] = getCurrentUserId();
}
if (!empty($_GET['search'])) {
$search = '%' . $_GET['search'] . '%';
$where .= ' AND (r.code LIKE ? OR r.batch_no LIKE ? OR r.code_name LIKE ?)';
$searchParams = array_merge($searchParams, [$search, $search, $search]);
}
$stmt = $pdo->prepare("SELECT COUNT(*) FROM claim_records r $where");
$stmt->execute($searchParams);
$total = (int)$stmt->fetchColumn();
$totalPages = max(1, ceil($total / $perPage));
$stmt = $pdo->prepare("SELECT r.*, u.username FROM claim_records r LEFT JOIN users u ON r.user_id = u.id $where ORDER BY r.id DESC LIMIT ? OFFSET ?");
$stmt->execute(array_merge($searchParams, [$perPage, $offset]));
$records = $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 style="margin-bottom:16px;display:flex;flex-wrap:wrap;gap:8px;">
<a href="claim_code.php" class="btn btn-info btn-sm">兑换码领取</a>
<?php if ($isAdmin): ?>
<a href="javascript:void(0)" class="btn btn-success btn-sm" onclick="showModal('addModal')">+ 手动添加</a>
<?php endif; ?>
<a href="?export=1<?= !empty($_GET['search']) ? '&search=' . urlencode($_GET['search']) : '' ?>" class="btn btn-primary btn-sm">下载 CSV</a>
<form method="get" style="display:flex;gap:8px;margin-left:auto;">
<input type="text" name="search" class="form-control" placeholder="搜索兑换码/批次号/名称" value="<?= h($_GET['search'] ?? '') ?>" style="max-width:300px;">
<button type="submit" class="btn btn-primary btn-sm">搜索</button>
<?php if (!empty($_GET['search'])): ?>
<a href="claim_records.php" class="btn btn-sm" style="background:#999;color:#fff;">清空</a>
<?php endif; ?>
</form>
</div>
<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>
<th>会员ID</th>
<th>用户</th>
<th>领取时间</th>
<?php if ($isAdmin): ?>
<th>出货价一档</th>
<th>出货价二挡</th>
<?php endif; ?>
<th>备注</th>
<?php if ($isAdmin): ?><th>操作</th><?php endif; ?>
</tr>
</thead>
<tbody>
<?php foreach ($records as $r): ?>
<tr>
<td><?= $r['id'] ?></td>
<td><?= formatDateTime($r['created_at']) ?></td>
<td><?= h($r['code_name']) ?></td>
<td><?= h($r['batch_no']) ?></td>
<td><?= h($r['code_type']) ?></td>
<td><?php if ((int)$r['status'] === 3): ?><code><?= h(maskCode($r['code'])) ?></code><?php else: ?><?= h($r['code']) ?><?php endif; ?></td>
<td><?= h($r['value']) ?></td>
<td><?= statusBadge((int)$r['status']) ?></td>
<td><?= formatDateTime($r['expired_at']) ?></td>
<td><?= formatDateTime($r['used_at']) ?></td>
<td><?= h($r['used_user_id'] ?? '-') ?></td>
<td><?= h($r['username'] ?? $r['user_id'] ?? '-') ?></td>
<td><?= formatDateTime($r['claimed_at']) ?></td>
<?php if ($isAdmin): ?>
<td><?= $r['price_tier1'] !== null ? h($r['price_tier1']) : '-' ?></td>
<td><?= $r['price_tier2'] !== null ? h($r['price_tier2']) : '-' ?></td>
<?php endif; ?>
<td><?= h($r['remark'] ?? '') ?></td>
<?php if ($isAdmin): ?>
<td>
<a href="javascript:void(0)" class="btn btn-warning btn-sm" onclick="editStatus(<?= $r['id'] ?>, <?= $r['status'] ?>, '<?= h($r['used_at'] ?? '') ?>', <?= (int)($r['user_id'] ?? 0) ?>, '<?= h($r['used_user_id'] ?? '') ?>')">修改状态</a>
</td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
<?php if (empty($records)): ?>
<tr><td colspan="<?= $isAdmin ? 17 : 14 ?>" style="text-align:center;color:#999;">暂无数据</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php renderPagination($page, $totalPages, !empty($_GET['search']) ? ['search' => $_GET['search']] : []); ?>
</div>
<?php
$allUsers = $pdo->query('SELECT id, username FROM users ORDER BY id ASC')->fetchAll();
if ($isAdmin):
?>
<!-- 修改状态弹窗 -->
<div class="modal" id="editModal">
<div class="modal-content">
<span class="modal-close" onclick="hideModal('editModal')">&times;</span>
<h3>修改状态</h3>
<form method="post">
<input type="hidden" name="action" value="edit_status">
<input type="hidden" name="csrf_token" value="<?= csrfToken() ?>">
<input type="hidden" name="id" id="edit_id">
<div class="form-group">
<label>状态</label>
<select name="status" class="form-control" id="edit_status">
<option value="1">未使用</option>
<option value="2">已使用</option>
<option value="3">已过期</option>
</select>
</div>
<div class="form-group">
<label>使用时间</label>
<input type="datetime-local" name="used_at" class="form-control" id="edit_used_at">
</div>
<div class="form-group">
<label>用户</label>
<select name="user_id" class="form-control" id="edit_user_id">
<option value="">-- 不关联 --</option>
<?php foreach ($allUsers as $u): ?>
<option value="<?= $u['id'] ?>"><?= h($u['username']) ?> (ID: <?= $u['id'] ?>)</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group" id="edit_used_user_group">
<label>会员ID <span id="edit_used_user_required" style="color:red;display:none;">*</span></label>
<input type="text" name="used_user_id" class="form-control" id="edit_used_user_id" placeholder="使用该兑换码的用户标识">
</div>
<button type="submit" class="btn btn-primary">保存</button>
</form>
</div>
</div>
<!-- 手动添加弹窗 -->
<div class="modal" id="addModal">
<div class="modal-content">
<span class="modal-close" onclick="hideModal('addModal')">&times;</span>
<h3>手动添加记录</h3>
<form method="post">
<input type="hidden" name="action" value="manual_add">
<input type="hidden" name="csrf_token" value="<?= csrfToken() ?>">
<div class="form-group">
<label>兑换码名称 <span style="color:red">*</span></label>
<input type="text" name="code_name" class="form-control" required>
</div>
<div class="form-group">
<label>批次号 <span style="color:red">*</span></label>
<input type="text" name="batch_no" class="form-control" required>
</div>
<div class="form-group">
<label>兑换码类型</label>
<input type="number" name="code_type" class="form-control" value="1" readonly>
</div>
<div class="form-group">
<label>兑换码 <span style="color:red">*</span></label>
<input type="text" name="code" class="form-control" required>
</div>
<div class="form-group">
<label>面值</label>
<input type="text" name="value" class="form-control" placeholder="如: 100元">
</div>
<div class="form-group">
<label>状态</label>
<select name="rec_status" class="form-control">
<option value="1">未使用</option>
<option value="2">已使用</option>
<option value="3">已过期</option>
</select>
</div>
<div class="form-group">
<label>使用时间</label>
<input type="datetime-local" name="used_at" class="form-control">
</div>
<div class="form-group">
<label>过期时间</label>
<input type="text" name="expired_at" class="form-control" placeholder="2027-06-01 12:26:53">
</div>
<div class="form-group">
<label>会员ID</label>
<input type="text" name="used_user_id" class="form-control" placeholder="使用该兑换码的用户标识">
</div>
<div class="form-group">
<label>用户</label>
<select name="user_id" class="form-control">
<option value="">-- 不关联 --</option>
<?php foreach ($allUsers as $u): ?>
<option value="<?= $u['id'] ?>"><?= h($u['username']) ?> (ID: <?= $u['id'] ?>)</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label>领取时间</label>
<input type="datetime-local" name="claimed_at" class="form-control">
</div>
<div class="form-group">
<label>出货价一档</label>
<input type="number" step="0.01" name="price_tier1" class="form-control">
</div>
<div class="form-group">
<label>出货价二挡</label>
<input type="number" step="0.01" name="price_tier2" class="form-control">
</div>
<button type="submit" class="btn btn-success">添加</button>
</form>
</div>
</div>
<script>
function showModal(id) { document.getElementById(id).classList.add('active'); }
function hideModal(id) { document.getElementById(id).classList.remove('active'); }
function editStatus(id, status, usedAt, userId, usedUserId) {
document.getElementById('edit_id').value = id;
document.getElementById('edit_status').value = status;
document.getElementById('edit_used_at').value = usedAt ? usedAt.replace(' ', 'T').substring(0, 16) : '';
document.getElementById('edit_user_id').value = userId;
document.getElementById('edit_used_user_id').value = usedUserId;
toggleUsedUserRequired();
showModal('editModal');
}
function toggleUsedUserRequired() {
var status = document.getElementById('edit_status').value;
var indicator = document.getElementById('edit_used_user_required');
var input = document.getElementById('edit_used_user_id');
if (status === '2') {
indicator.style.display = 'inline';
input.required = true;
} else {
indicator.style.display = 'none';
input.required = false;
}
}
document.getElementById('edit_status').addEventListener('change', toggleUsedUserRequired);
document.querySelectorAll('.modal').forEach(function(m) {
m.addEventListener('click', function(e) { if (e.target === this) this.classList.remove('active'); });
});
</script>
<?php endif; ?>
<?php require __DIR__ . '/includes/footer.php'; ?>