Files
freeshop/admin/orders.php
T
2026-08-14 10:55:06 +08:00

307 lines
16 KiB
PHP
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 __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(); ?>