307 lines
16 KiB
PHP
307 lines
16 KiB
PHP
<?php
|
||
// 后台订单管理:列表 + 状态流转 + 查看详情 + 发货信息填写
|
||
require __DIR__ . '/../includes/init.php';
|
||
requireStaff();
|
||
require '_common.php';
|
||
$isAdmin = isAdminStaff();
|
||
|
||
$db = db();
|
||
$msg = '';
|
||
$err = '';
|
||
// 客服账号仅可查看,禁止写操作
|
||
if (!$isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
$err = '客服账号仅可查看,如需操作请联系管理员。';
|
||
}
|
||
$allowed = ['pending', 'paid', 'shipped', 'completed', 'cancelled'];
|
||
|
||
// 行内状态变更
|
||
if (isset($_POST['update_status'])) {
|
||
verifyCsrf();
|
||
$id = (int) ($_POST['id'] ?? 0);
|
||
$status = $_POST['status'] ?? '';
|
||
if (!in_array($status, $allowed, true)) {
|
||
$err = '非法的订单状态';
|
||
} else {
|
||
$db->prepare('UPDATE ' . tn('orders') . ' SET status = ? WHERE id = ?')->execute([$status, $id]);
|
||
$msg = '订单 #' . $id . ' 状态已更新为「' . orderStatusLabel($status) . '」';
|
||
}
|
||
}
|
||
|
||
// 发货:填写连接/登录信息
|
||
if (isset($_POST['ship'])) {
|
||
verifyCsrf();
|
||
$id = (int) ($_POST['id'] ?? 0);
|
||
$serverIp = trim($_POST['server_ip'] ?? '');
|
||
$connAddr = trim($_POST['conn_addr'] ?? '');
|
||
$loginUser = trim($_POST['login_user'] ?? '');
|
||
$loginPass = trim($_POST['login_pass'] ?? '');
|
||
$remark = trim($_POST['remark'] ?? '');
|
||
$activated = isset($_POST['activated']) ? 1 : 0;
|
||
$productInfo = trim($_POST['product_info'] ?? '');
|
||
|
||
$chk = $db->prepare('SELECT id, status FROM ' . tn('orders') . ' WHERE id = ?');
|
||
$chk->execute([$id]);
|
||
$ord = $chk->fetch();
|
||
if (!$ord) {
|
||
$err = '订单不存在';
|
||
} else {
|
||
// 已存在发货记录则更新,否则插入
|
||
$ex = $db->prepare('SELECT id FROM ' . tn('order_deliveries') . ' WHERE order_id = ? ORDER BY id DESC LIMIT 1');
|
||
$ex->execute([$id]);
|
||
$exId = $ex->fetchColumn();
|
||
if ($exId) {
|
||
$db->prepare('UPDATE ' . tn('order_deliveries') . ' SET server_ip=?, conn_addr=?, login_user=?, login_pass=?, remark=?, activated=?, product_info=? WHERE id=?')
|
||
->execute([$serverIp, $connAddr, $loginUser, $loginPass, $remark, $activated, $productInfo, $exId]);
|
||
} else {
|
||
$db->prepare('INSERT INTO ' . tn('order_deliveries') . ' (order_id, server_ip, conn_addr, login_user, login_pass, remark, activated, product_info, created_at) VALUES (?,?,?,?,?,?,?,?,NOW())')
|
||
->execute([$id, $serverIp, $connAddr, $loginUser, $loginPass, $remark, $activated, $productInfo]);
|
||
}
|
||
// 由「已付款」发货后自动转为「已发货」
|
||
if ($ord['status'] === 'paid') {
|
||
$db->prepare('UPDATE ' . tn('orders') . ' SET status = ? WHERE id = ?')->execute(['shipped', $id]);
|
||
}
|
||
$msg = '发货信息已保存(订单 #' . $id . ')。';
|
||
// 通过 SMTP 通知客户(失败不影响保存)
|
||
$u = $db->prepare('SELECT u.email, u.username FROM ' . tn('orders') . ' o LEFT JOIN ' . tn('users') . ' u ON u.id = o.user_id WHERE o.id = ?');
|
||
$u->execute([$id]);
|
||
$urow = $u->fetch();
|
||
if ($urow && !empty($urow['email'])) {
|
||
$subject = '[' . SITE_NAME . '] 你的订单 #' . $id . ' 已发货';
|
||
$body = '<div style="font-family:sans-serif;max-width:560px;margin:auto">'
|
||
. '<h2 style="color:#1a73e8">订单已发货</h2>'
|
||
. '<p>你好 ' . h($urow['username']) . ',你购买的以下服务/商品已开通:</p>'
|
||
. '<div style="background:#f5f7fb;border-left:4px solid #1a73e8;padding:12px 16px;margin:10px 0;">'
|
||
. '<p style="margin:4px 0;">连接地址:' . h($connAddr) . '</p>'
|
||
. '<p style="margin:4px 0;">登录账户:' . h($loginUser) . '</p>'
|
||
. '<p style="margin:4px 0;">可在「我的订单 → 详情」中查看完整登录凭据。</p>'
|
||
. '</div>'
|
||
. '<p class="muted">如有问题请到「我的工单」联系客服。</p></div>';
|
||
fnwSendMail($urow['email'], $subject, $body);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 修改到期时间(后台)
|
||
if (isset($_POST['save_expire'])) {
|
||
verifyCsrf();
|
||
$id = (int) ($_POST['id'] ?? 0);
|
||
$clear = isset($_POST['clear_expire']);
|
||
$expRaw = trim($_POST['expires_at'] ?? '');
|
||
$newExp = null;
|
||
if (!$clear) {
|
||
if ($expRaw === '') {
|
||
$err = '请填写新的到期时间,或勾选「清除到期时间」';
|
||
} else {
|
||
$t = strtotime(str_replace('T', ' ', $expRaw));
|
||
if ($t === false) {
|
||
$err = '到期时间格式不正确';
|
||
} else {
|
||
$newExp = date('Y-m-d H:i:s', $t);
|
||
}
|
||
}
|
||
}
|
||
if ($err === '') {
|
||
$db->prepare('UPDATE ' . tn('orders') . ' SET expires_at = ?, expire_notify_sent = 0 WHERE id = ?')
|
||
->execute([$newExp, $id]);
|
||
$msg = '订单 #' . $id . ' 的到期时间已更新' . ($newExp ? '为 ' . $newExp : '(已清除)') . ',到期提醒将重新计算。';
|
||
}
|
||
}
|
||
|
||
// 删除订单(含明细与发货信息)
|
||
if (isset($_POST['delete'])) {
|
||
verifyCsrf();
|
||
$id = (int) ($_POST['delete'] ?? 0);
|
||
$db->prepare('DELETE FROM ' . tn('order_deliveries') . ' WHERE order_id = ?')->execute([$id]);
|
||
$db->prepare('DELETE FROM ' . tn('order_items') . ' WHERE order_id = ?')->execute([$id]);
|
||
$db->prepare('DELETE FROM ' . tn('orders') . ' WHERE id = ?')->execute([$id]);
|
||
$msg = '订单 #' . $id . ' 已删除';
|
||
}
|
||
|
||
$filter = trim($_GET['status'] ?? '');
|
||
$where = $filter !== '' ? 'WHERE o.status = ?' : '';
|
||
$params = $filter !== '' ? [$filter] : [];
|
||
$stmt = $db->prepare(
|
||
'SELECT o.*, u.username, u.email AS u_email FROM ' . tn('orders') . ' o
|
||
LEFT JOIN ' . tn('users') . ' u ON u.id = o.user_id
|
||
' . $where . ' ORDER BY o.created_at DESC'
|
||
);
|
||
$stmt->execute($params);
|
||
$list = $stmt->fetchAll();
|
||
|
||
// 预取商品明细
|
||
$itemsOf = [];
|
||
foreach ($list as $o) {
|
||
$it = $db->prepare('SELECT name, qty, period_days FROM ' . tn('order_items') . ' WHERE order_id = ?');
|
||
$it->execute([$o['id']]);
|
||
$itemsOf[$o['id']] = $it->fetchAll();
|
||
}
|
||
|
||
// 查看单条 + 发货
|
||
$view = isset($_GET['view']) ? (int) $_GET['view'] : 0;
|
||
$order = $delivery = null;
|
||
if ($view > 0) {
|
||
$ostmt = $db->prepare('SELECT o.*, u.username, u.email AS u_email FROM ' . tn('orders') . ' o LEFT JOIN ' . tn('users') . ' u ON u.id = o.user_id WHERE o.id = ?');
|
||
$ostmt->execute([$view]);
|
||
$order = $ostmt->fetch();
|
||
if ($order) {
|
||
$istmt = $db->prepare('SELECT * FROM ' . tn('order_items') . ' WHERE order_id = ?');
|
||
$istmt->execute([$order['id']]);
|
||
$order['items'] = $istmt->fetchAll();
|
||
$dstmt = $db->prepare('SELECT * FROM ' . tn('order_deliveries') . ' WHERE order_id = ? ORDER BY id DESC LIMIT 1');
|
||
$dstmt->execute([$order['id']]);
|
||
$delivery = $dstmt->fetch();
|
||
}
|
||
}
|
||
|
||
adminHeader('订单管理', 'orders');
|
||
?>
|
||
|
||
<h1 class="page-title"><i class="fas fa-receipt"></i> 订单管理</h1>
|
||
|
||
<?php if ($msg): ?><p class="banner-ok"><i class="fas fa-check-circle"></i> <?= h($msg) ?></p><?php endif; ?>
|
||
<?php if ($err): ?><p class="form-err"><?= h($err) ?></p><?php endif; ?>
|
||
|
||
<div class="cat-chips">
|
||
<a href="orders.php" class="chip <?= $filter===''?'active':'' ?>">全部</a>
|
||
<?php foreach ($allowed as $s): ?>
|
||
<a href="orders.php?status=<?= $s ?>" class="chip <?= $filter===$s?'active':'' ?>"><?= orderStatusLabel($s) ?></a>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
|
||
<?php if ($order): ?>
|
||
<div class="panel">
|
||
<div class="od-head">
|
||
<div>
|
||
<div class="od-no">订单号:<?= h($order['order_no']) ?></div>
|
||
<div class="od-time"><?= date('Y-m-d H:i', strtotime($order['created_at'])) ?></div>
|
||
</div>
|
||
<span class="badge-status status-<?= h($order['status']) ?>"><?= orderStatusLabel($order['status']) ?></span>
|
||
</div>
|
||
|
||
<div class="od-info">
|
||
<div class="od-block">
|
||
<div class="od-label">订单发起人</div>
|
||
<div class="od-line"><?= h($order['username'] ?? '未知') ?></div>
|
||
<div class="od-line">账号邮箱:<?= h($order['u_email'] ?? '未填写') ?></div>
|
||
</div>
|
||
<div class="od-block">
|
||
<div class="od-label">联系信息</div>
|
||
<div class="od-line">联系人:<?= $order['contact'] !== '' ? h($order['contact']) : '—' ?></div>
|
||
<div class="od-line">联系邮箱:<?= $order['contact_email'] !== '' ? h($order['contact_email']) : '—' ?></div>
|
||
<div class="od-line">收货地址:<?= $order['address'] !== '' ? h($order['address']) : '(未填写)' ?></div>
|
||
</div>
|
||
<div class="od-block">
|
||
<div class="od-label">周期 / 到期</div>
|
||
<div class="od-line">周期:<?= periodLabel($order['period_days']) ?></div>
|
||
<div class="od-line">到期时间:<?= expiryBadge($order['expires_at']) ?></div>
|
||
<?php if (!empty($order['expire_notify_sent'])): ?><div class="od-line muted">已发送「到期前提醒」邮件</div><?php endif; ?>
|
||
<form method="post" action="" class="expire-edit">
|
||
<?= csrfField() ?>
|
||
<input type="hidden" name="id" value="<?= (int) $order['id'] ?>">
|
||
<label class="expire-field">修改到期时间
|
||
<input type="datetime-local" name="expires_at" value="<?= $order['expires_at'] ? date('Y-m-d\TH:i', strtotime($order['expires_at'])) : '' ?>">
|
||
</label>
|
||
<label class="expire-clear"><input type="checkbox" name="clear_expire" value="1"> 清除到期时间</label>
|
||
<button type="submit" name="save_expire" class="btn btn-primary btn-sm"><i class="fas fa-save"></i> 保存到期时间</button>
|
||
</form>
|
||
</div>
|
||
<div class="od-block">
|
||
<div class="od-label">备注</div>
|
||
<div class="od-line"><?= $order['note'] !== '' ? nl2br(h($order['note'])) : '(无)' ?></div>
|
||
</div>
|
||
<div class="od-block">
|
||
<div class="od-label">支付方式</div>
|
||
<div class="od-line"><?= payTypeLabel($order['pay_type']) ?><?= $order['pay_type']==='points' ? '(消耗 ' . (int)$order['points_used'] . ' 积分)' : '' ?></div>
|
||
</div>
|
||
</div>
|
||
|
||
<h3>商品明细</h3>
|
||
<table class="data-table">
|
||
<thead><tr><th>商品</th><th>单价</th><th>数量</th><th>小计</th><th>周期</th></tr></thead>
|
||
<tbody>
|
||
<?php foreach (($order['items'] ?? []) as $it): ?>
|
||
<tr>
|
||
<td><?= h($it['name']) ?></td>
|
||
<td><?= money($it['price']) ?></td>
|
||
<td><?= (int) $it['qty'] ?></td>
|
||
<td><?= money($it['subtotal']) ?></td>
|
||
<td><?= periodLabel($it['period_days']) ?></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3><i class="fas fa-truck"></i> 发货信息(连接地址 / 登录凭据 / 商品信息)</h3>
|
||
<?php if ($delivery): ?>
|
||
<div class="ship-existing">
|
||
<p class="muted">已填写,当前内容如下(可重新提交覆盖):</p>
|
||
<div class="od-line">激活状态:<span class="badge-<?= !empty($delivery['activated']) ? 'on' : 'off' ?>"><?= !empty($delivery['activated']) ? '已激活' : '未激活' ?></span></div>
|
||
<div class="od-line">服务器 IP:<?= h($delivery['server_ip']) ?: '—' ?></div>
|
||
<div class="od-line">连接地址:<?= h($delivery['conn_addr']) ?: '—' ?></div>
|
||
<div class="od-line">登录账户:<?= h($delivery['login_user']) ?: '—' ?></div>
|
||
<div class="od-line">登录密码:<?= $delivery['login_pass'] !== '' ? h($delivery['login_pass']) : '—' ?></div>
|
||
<?php if ($delivery['product_info'] !== ''): ?><div class="od-line">商品信息:<?= nl2br(h($delivery['product_info'])) ?></div><?php endif; ?>
|
||
<?php if ($delivery['remark'] !== ''): ?><div class="od-line">备注:<?= nl2br(h($delivery['remark'])) ?></div><?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<form method="post" action="" class="grid-form ship-form">
|
||
<?= csrfField() ?>
|
||
<input type="hidden" name="id" value="<?= (int) $order['id'] ?>">
|
||
<label>服务器 IP<input type="text" name="server_ip" value="<?= $delivery ? h($delivery['server_ip']) : '' ?>" placeholder="如 1.2.3.4"></label>
|
||
<label>连接地址 / 域名<input type="text" name="conn_addr" value="<?= $delivery ? h($delivery['conn_addr']) : '' ?>" placeholder="如 mc.example.com:25565"></label>
|
||
<label>登录账户<input type="text" name="login_user" value="<?= $delivery ? h($delivery['login_user']) : '' ?>" placeholder="如 player01"></label>
|
||
<label>登录密码<input type="text" name="login_pass" value="<?= $delivery ? h($delivery['login_pass']) : '' ?>" placeholder="如 ********"></label>
|
||
<label>激活状态
|
||
<select name="activated">
|
||
<option value="0" <?= $delivery && empty($delivery['activated']) ? 'selected' : '' ?>>未激活</option>
|
||
<option value="1" <?= $delivery && !empty($delivery['activated']) ? 'selected' : '' ?>>已激活</option>
|
||
</select>
|
||
</label>
|
||
<label class="span2">商品信息(展示给用户)<textarea name="product_info" rows="3" placeholder="选填,将展示给该用户,如开通说明、使用须知"><?= $delivery ? h($delivery['product_info']) : '' ?></textarea></label>
|
||
<label class="span2">备注<textarea name="remark" rows="2" placeholder="选填,仅内部可见"><?= $delivery ? h($delivery['remark']) : '' ?></textarea></label>
|
||
<div class="form-actions span2">
|
||
<button type="submit" name="ship" class="btn btn-primary"><i class="fas fa-paper-plane"></i> 保存发货信息<?= $delivery ? '(覆盖)' : '' ?></button>
|
||
<a href="orders.php" class="btn btn-ghost">返回列表</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="panel">
|
||
<?php if (empty($list)): ?>
|
||
<p class="empty">没有符合条件的订单。</p>
|
||
<?php else: ?>
|
||
<table class="data-table">
|
||
<thead><tr><th>订单号</th><th>用户</th><th>商品</th><th>金额</th><th>周期</th><th>支付</th><th>状态</th><th>时间</th><th>操作</th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($list as $o): ?>
|
||
<tr>
|
||
<td><?= h($o['order_no']) ?></td>
|
||
<td><?= h($o['username'] ?? '游客') ?></td>
|
||
<td class="cell-items">
|
||
<?php foreach (($itemsOf[$o['id']] ?? []) as $it): ?>
|
||
<div><?= h($it['name']) ?> ×<?= (int)$it['qty'] ?></div>
|
||
<?php endforeach; ?>
|
||
</td>
|
||
<td><?= money($o['total']) ?></td>
|
||
<td><?= periodLabel($o['period_days']) ?></td>
|
||
<td><span class="pay-<?= h($o['pay_type']) ?>"><?= payTypeLabel($o['pay_type']) ?><?= $o['pay_type']==='points' ? ' · ' . (int)$o['points_used'] . '分' : '' ?></span></td>
|
||
<td><span class="badge-status status-<?= h($o['status']) ?>"><?= orderStatusLabel($o['status']) ?></span></td>
|
||
<td><?= date('m-d H:i', strtotime($o['created_at'])) ?></td>
|
||
<td class="row-actions">
|
||
<a href="orders.php?view=<?= (int)$o['id'] ?>" class="mini-btn" title="查看 / 发货"><i class="fas fa-eye"></i></a>
|
||
<form method="post" action="" style="display:inline" onsubmit="return confirm('确定删除该订单?');">
|
||
<?= csrfField() ?>
|
||
<button type="submit" name="delete" value="<?= (int)$o['id'] ?>" class="mini-btn danger" title="删除订单"><i class="fas fa-trash"></i></button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php adminFooter(); ?>
|