284 lines
13 KiB
PHP
284 lines
13 KiB
PHP
<?php
|
||
require_once __DIR__ . '/includes/auth.php';
|
||
require_once __DIR__ . '/includes/functions.php';
|
||
requireLogin();
|
||
|
||
$pageTitle = '账单管理';
|
||
require __DIR__ . '/includes/header.php';
|
||
|
||
$isAdmin = isAdmin();
|
||
$userId = $isAdmin ? (int)($_GET['user_id'] ?? 0) : getCurrentUserId();
|
||
$detailDate = $_GET['date'] ?? '';
|
||
$detailUserId = (int)($_GET['duid'] ?? $userId);
|
||
$startDate = $_GET['start_date'] ?? '';
|
||
$endDate = $_GET['end_date'] ?? '';
|
||
|
||
// 管理员:修改账单状态
|
||
$statusMsg = '';
|
||
if ($isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||
if (!verifyCsrf($_POST['csrf_token'] ?? '')) { die('CSRF token无效'); }
|
||
if ($_POST['action'] === 'set_bill_status') {
|
||
$sDate = $_POST['bill_date'] ?? '';
|
||
$sUserId = (int)($_POST['user_id'] ?? 0);
|
||
$sStatus = $_POST['status'] ?? '';
|
||
$sStartDate = $_POST['start_date'] ?? '';
|
||
$sEndDate = $_POST['end_date'] ?? '';
|
||
if ($sDate && $sUserId > 0 && in_array($sStatus, ['未结算', '已结算', '已作废'])) {
|
||
$stmt = $pdo->prepare('INSERT INTO bill_status (bill_date, user_id, status, updated_at, updated_by) VALUES (?, ?, ?, NOW(), ?) ON DUPLICATE KEY UPDATE status = ?, updated_at = NOW(), updated_by = ?');
|
||
$stmt->execute([$sDate, $sUserId, $sStatus, getCurrentUserId(), $sStatus, getCurrentUserId()]);
|
||
$_SESSION['flash_msg'] = '账单状态已更新';
|
||
$_SESSION['flash_type'] = 'success';
|
||
$queryRedirect = [];
|
||
$filterUserId = $userId; // 保留原始筛选条件
|
||
if ($filterUserId > 0) $queryRedirect[] = 'user_id=' . $filterUserId;
|
||
if ($sStartDate) $queryRedirect[] = 'start_date=' . $sStartDate;
|
||
if ($sEndDate) $queryRedirect[] = 'end_date=' . $sEndDate;
|
||
header('Location: bill_records.php' . ($queryRedirect ? '?' . implode('&', $queryRedirect) : ''));
|
||
exit;
|
||
} else {
|
||
$statusMsg = '<div class="alert alert-danger">参数错误</div>';
|
||
}
|
||
}
|
||
}
|
||
|
||
// 详情模式
|
||
if ($detailDate) {
|
||
$pid = getCurrentProductId();
|
||
$where = 'WHERE r.status = 2 AND r.product_id = ? AND DATE(r.used_at) = ?';
|
||
$params = [$pid, $detailDate];
|
||
if ($detailUserId > 0) {
|
||
$where .= ' AND r.user_id = ?';
|
||
$params[] = $detailUserId;
|
||
}
|
||
|
||
$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 ASC");
|
||
$stmt->execute($params);
|
||
$details = $stmt->fetchAll();
|
||
|
||
// 读取该日该用户的账单状态
|
||
$stmt = $pdo->prepare("SELECT status FROM bill_status WHERE bill_date = ? AND user_id = ?");
|
||
$stmt->execute([$detailDate, $detailUserId]);
|
||
$billStatus = $stmt->fetchColumn() ?: '未结算';
|
||
?>
|
||
<div class="card">
|
||
<h2>
|
||
账单明细 - <?= h($detailDate) ?>
|
||
<?php if ($detailUserId > 0 && isset($details[0]['username'])): ?>
|
||
(<?= h($details[0]['username']) ?>)
|
||
<?php endif; ?>
|
||
<span style="font-size:14px;font-weight:normal;margin-left:12px;">状态:<?= billStatusBadge($billStatus) ?></span>
|
||
</h2>
|
||
<div style="margin-bottom:16px;">
|
||
<a href="bill_records.php<?= $userId > 0 ? '?user_id=' . $userId : '' ?><?= $startDate ? '&start_date=' . $startDate : '' ?><?= $endDate ? '&end_date=' . $endDate : '' ?>" class="btn btn-primary btn-sm">← 返回</a>
|
||
</div>
|
||
<div class="table-wrapper">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>兑换码名称</th>
|
||
<th>批次号</th>
|
||
<th>兑换码</th>
|
||
<th>面值</th>
|
||
<th>使用时间</th>
|
||
<th>会员ID</th>
|
||
<th>用户</th>
|
||
<th>出货价一档</th>
|
||
<th>出货价二挡</th>
|
||
<th>备注</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($details as $r): ?>
|
||
<tr>
|
||
<td><?= $r['id'] ?></td>
|
||
<td><?= h($r['code_name']) ?></td>
|
||
<td><?= h($r['batch_no']) ?></td>
|
||
<td><?= h($r['code']) ?></td>
|
||
<td><?= h($r['value']) ?></td>
|
||
<td><?= formatDateTime($r['used_at']) ?></td>
|
||
<td><?= h($r['used_user_id'] ?? '-') ?></td>
|
||
<td><?= h($r['username'] ?? '-') ?></td>
|
||
<td><?= $r['price_tier1'] !== null ? h($r['price_tier1']) : '-' ?></td>
|
||
<td><?= $r['price_tier2'] !== null ? h($r['price_tier2']) : '-' ?></td>
|
||
<td><?= h($r['remark'] ?? '') ?></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php if (empty($details)): ?>
|
||
<tr><td colspan="11" style="text-align:center;color:#999;">暂无数据</td></tr>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
require __DIR__ . '/includes/footer.php';
|
||
exit;
|
||
}
|
||
|
||
// 汇总模式
|
||
$page = max(1, (int)($_GET['p'] ?? 1));
|
||
$perPage = 30;
|
||
$offset = ($page - 1) * $perPage;
|
||
|
||
$pid = getCurrentProductId();
|
||
$where = 'WHERE r.status = 2 AND r.product_id = ?';
|
||
$params = [$pid];
|
||
if ($userId > 0) {
|
||
$where .= ' AND r.user_id = ?';
|
||
$params[] = $userId;
|
||
}
|
||
if ($startDate) {
|
||
$where .= ' AND DATE(r.used_at) >= ?';
|
||
$params[] = $startDate;
|
||
}
|
||
if ($endDate) {
|
||
$where .= ' AND DATE(r.used_at) <= ?';
|
||
$params[] = $endDate;
|
||
}
|
||
|
||
$stmt = $pdo->prepare("SELECT COUNT(DISTINCT DATE(r.used_at)" . ($userId > 0 ? "" : ", r.user_id") . ") FROM claim_records r $where");
|
||
$stmt->execute($params);
|
||
$total = (int)$stmt->fetchColumn();
|
||
$totalPages = max(1, ceil($total / $perPage));
|
||
|
||
$groupBy = $userId > 0 ? 'DATE(r.used_at)' : 'DATE(r.used_at), r.user_id';
|
||
$selectCols = $userId > 0
|
||
? "DATE(r.used_at) AS bill_date, r.user_id AS uid, COUNT(*) AS total, SUM(COALESCE(r.price_tier1, 0)) AS total_price1, SUM(COALESCE(r.price_tier2, 0)) AS total_price2"
|
||
: "DATE(r.used_at) AS bill_date, r.user_id AS uid, u.username, COUNT(*) AS total, SUM(COALESCE(r.price_tier1, 0)) AS total_price1, SUM(COALESCE(r.price_tier2, 0)) AS total_price2";
|
||
|
||
$stmt = $pdo->prepare("SELECT $selectCols FROM claim_records r LEFT JOIN users u ON r.user_id = u.id $where GROUP BY $groupBy ORDER BY bill_date DESC" . ($userId > 0 ? '' : ', u.username ASC') . " LIMIT ? OFFSET ?");
|
||
$stmt->execute(array_merge($params, [$perPage, $offset]));
|
||
$records = $stmt->fetchAll();
|
||
|
||
// 批量查询账单状态
|
||
$statusMap = [];
|
||
if (!empty($records)) {
|
||
$cases = [];
|
||
foreach ($records as $r) {
|
||
$uid = $userId > 0 ? $userId : $r['uid'];
|
||
$cases[] = "('" . $r['bill_date'] . "', " . (int)$uid . ")";
|
||
}
|
||
if (!empty($cases)) {
|
||
$stmt = $pdo->query("SELECT CONCAT(bill_date, '_', user_id) AS k, status FROM bill_status WHERE (bill_date, user_id) IN (" . implode(',', $cases) . ")");
|
||
while ($row = $stmt->fetch()) {
|
||
$statusMap[$row['k']] = $row['status'];
|
||
}
|
||
}
|
||
}
|
||
|
||
function billStatusBadge(string $status): string {
|
||
switch ($status) {
|
||
case '未结算': return '<span class="badge badge-warning">未结算</span>';
|
||
case '已结算': return '<span class="badge badge-success">已结算</span>';
|
||
case '已作废': return '<span class="badge badge-danger">已作废</span>';
|
||
default: return '<span class="badge">未知</span>';
|
||
}
|
||
}
|
||
|
||
$allUsers = $isAdmin ? $pdo->query('SELECT id, username FROM users ORDER BY id ASC')->fetchAll() : [];
|
||
|
||
// 构建筛选条件 URL 参数
|
||
$queryParams = [];
|
||
if ($userId > 0) $queryParams['user_id'] = $userId;
|
||
if ($startDate) $queryParams['start_date'] = $startDate;
|
||
if ($endDate) $queryParams['end_date'] = $endDate;
|
||
?>
|
||
<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; ?>
|
||
<?= $statusMsg ?>
|
||
<?php if ($isAdmin): ?>
|
||
<form method="get" style="margin-bottom:12px;display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
|
||
<label>用户:</label>
|
||
<select name="user_id" class="form-control" style="max-width:160px;" onchange="this.form.submit()">
|
||
<option value="">全部</option>
|
||
<?php foreach ($allUsers as $u): ?>
|
||
<option value="<?= $u['id'] ?>" <?= $userId === (int)$u['id'] ? 'selected' : '' ?>><?= h($u['username']) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<label>开始日期:</label>
|
||
<input type="date" name="start_date" class="form-control" style="max-width:160px;" value="<?= h($startDate) ?>">
|
||
<label>结束日期:</label>
|
||
<input type="date" name="end_date" class="form-control" style="max-width:160px;" value="<?= h($endDate) ?>">
|
||
<button type="submit" class="btn btn-primary btn-sm">查询</button>
|
||
<?php if ($startDate || $endDate || $userId > 0): ?>
|
||
<a href="bill_records.php" class="btn btn-sm" style="background:#999;color:#fff;">清空</a>
|
||
<?php endif; ?>
|
||
</form>
|
||
<?php else: ?>
|
||
<form method="get" style="margin-bottom:12px;display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
|
||
<label>开始日期:</label>
|
||
<input type="date" name="start_date" class="form-control" style="max-width:160px;" value="<?= h($startDate) ?>">
|
||
<label>结束日期:</label>
|
||
<input type="date" name="end_date" class="form-control" style="max-width:160px;" value="<?= h($endDate) ?>">
|
||
<button type="submit" class="btn btn-primary btn-sm">查询</button>
|
||
<?php if ($startDate || $endDate): ?>
|
||
<a href="bill_records.php" class="btn btn-sm" style="background:#999;color:#fff;">清空</a>
|
||
<?php endif; ?>
|
||
</form>
|
||
<?php endif; ?>
|
||
<div class="table-wrapper">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>日期</th>
|
||
<?php if (!$userId): ?><th>用户</th><?php endif; ?>
|
||
<th>使用数量</th>
|
||
<th>出货价一档合计</th>
|
||
<th>出货价二挡合计</th>
|
||
<th>状态</th>
|
||
<?php if ($isAdmin): ?><th>操作</th><?php endif; ?>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($records as $r): ?>
|
||
<?php
|
||
$uid = $userId > 0 ? $userId : $r['uid'];
|
||
$statusKey = $r['bill_date'] . '_' . $uid;
|
||
$billStatus = $statusMap[$statusKey] ?? '未结算';
|
||
?>
|
||
<tr>
|
||
<td><?= h($r['bill_date']) ?></td>
|
||
<?php if (!$userId): ?><td><?= h($r['username'] ?? '-') ?></td><?php endif; ?>
|
||
<td><a href="bill_records.php?date=<?= h($r['bill_date']) ?>&duid=<?= $uid ?><?= $startDate ? '&start_date=' . $startDate : '' ?><?= $endDate ? '&end_date=' . $endDate : '' ?>" class="btn btn-info btn-sm"><?= (int)$r['total'] ?></a></td>
|
||
<td><?= $r['total_price1'] > 0 ? number_format($r['total_price1'], 2) : '-' ?></td>
|
||
<td><?= $r['total_price2'] > 0 ? number_format($r['total_price2'], 2) : '-' ?></td>
|
||
<td><?= billStatusBadge($billStatus) ?></td>
|
||
<?php if ($isAdmin): ?>
|
||
<td>
|
||
<form method="post" style="display:inline;">
|
||
<input type="hidden" name="action" value="set_bill_status">
|
||
<input type="hidden" name="csrf_token" value="<?= csrfToken() ?>">
|
||
<input type="hidden" name="bill_date" value="<?= h($r['bill_date']) ?>">
|
||
<input type="hidden" name="user_id" value="<?= $uid ?>">
|
||
<?php if ($startDate): ?><input type="hidden" name="start_date" value="<?= h($startDate) ?>"><?php endif; ?>
|
||
<?php if ($endDate): ?><input type="hidden" name="end_date" value="<?= h($endDate) ?>"><?php endif; ?>
|
||
<select name="status" class="form-control" style="display:inline-block;width:auto;max-width:100px;padding:4px 6px;font-size:12px;" onchange="this.form.submit()">
|
||
<option value="未结算" <?= $billStatus === '未结算' ? 'selected' : '' ?>>未结算</option>
|
||
<option value="已结算" <?= $billStatus === '已结算' ? 'selected' : '' ?>>已结算</option>
|
||
<option value="已作废" <?= $billStatus === '已作废' ? 'selected' : '' ?>>已作废</option>
|
||
</select>
|
||
</form>
|
||
</td>
|
||
<?php endif; ?>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php if (empty($records)): ?>
|
||
<tr><td colspan="<?= $userId ? 5 : 6 ?><?= $isAdmin ? 1 : 0 ?>" style="text-align:center;color:#999;">暂无数据</td></tr>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php
|
||
$extra = [];
|
||
if ($userId > 0) $extra['user_id'] = $userId;
|
||
if ($startDate) $extra['start_date'] = $startDate;
|
||
if ($endDate) $extra['end_date'] = $endDate;
|
||
renderPagination($page, $totalPages, $extra);
|
||
?>
|
||
</div>
|
||
<?php require __DIR__ . '/includes/footer.php'; ?>
|