PHP项目怎样实现账单查询功能?

wen PHP项目 10

本文目录导读:

PHP项目怎样实现账单查询功能?

  1. 数据库设计(MySQL)
  2. 查询功能实现(PHP + PDO)
  3. 前端展示(分页 + 统计)
  4. 扩展高级功能
  5. 完整代码结构示例

实现PHP账单查询功能,核心在于数据库设计查询逻辑(按时间、类型、关键词等筛选)以及前端展示(分页、排序),以下是分步实现指南。


数据库设计(MySQL)

假设数据表 bills 用于存储账单记录:

CREATE TABLE `bills` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `user_id` INT(11) NOT NULL COMMENT '用户ID',
    `category` VARCHAR(50) NOT NULL COMMENT '分类(餐饮/交通/购物等)',
    `type` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '0支出 1收入',
    `amount` DECIMAL(10,2) NOT NULL COMMENT '金额',
    `note` VARCHAR(255) DEFAULT NULL COMMENT '备注',
    `billing_date` DATE NOT NULL COMMENT '账单日期',
    `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_user_date` (`user_id`, `billing_date`)  -- 复合索引加速查询
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

关键点:

  • 使用 billing_date 而非 created_at 作为账单日期,方便按自然日查询。
  • 建立 (user_id, billing_date) 复合索引,这是查询最常用的条件。
  • 金额字段使用 DECIMAL(10,2) 而非 FLOAT,避免精度问题。

查询功能实现(PHP + PDO)

1 查询表单(HTML)

放在 index.phpsearch.php

<form method="get" action="query_bills.php">
    <input type="text" name="keyword" placeholder="备注关键词" value="<?= htmlspecialchars($_GET['keyword'] ?? '') ?>">
    <select name="category">
        <option value="">所有分类</option>
        <option value="餐饮" <?= ($_GET['category'] ?? '') == '餐饮' ? 'selected' : '' ?>>餐饮</option>
        <option value="交通" <?= ($_GET['category'] ?? '') == '交通' ? 'selected' : '' ?>>交通</option>
        <option value="购物" <?= ($_GET['category'] ?? '') == '购物' ? 'selected' : '' ?>>购物</option>
    </select>
    <input type="date" name="start_date" value="<?= htmlspecialchars($_GET['start_date'] ?? '') ?>">
    <input type="date" name="end_date" value="<?= htmlspecialchars($_GET['end_date'] ?? '') ?>">
    <button type="submit">查询</button>
</form>

2 后台查询逻辑(query_bills.php)

<?php
// 1. 数据库连接(以 PDO 为例)
$pdo = new PDO('mysql:host=localhost;dbname=your_db;charset=utf8mb4', 'username', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 2. 安全获取查询参数
$params = [];
$conditions = ['user_id = :user_id'];      // 假设当前登录用户ID = 1
$params[':user_id'] = 1;
// --- 按日期范围筛选 ---
if (!empty($_GET['start_date'])) {
    $conditions[] = 'billing_date >= :start_date';
    $params[':start_date'] = $_GET['start_date'];
}
if (!empty($_GET['end_date'])) {
    $conditions[] = 'billing_date <= :end_date';
    $params[':end_date'] = $_GET['end_date'];
}
// --- 按分类筛选 ---
if (!empty($_GET['category'])) {
    $conditions[] = 'category = :category';
    $params[':category'] = $_GET['category'];
}
// --- 按备注关键词模糊搜索 ---
if (!empty($_GET['keyword'])) {
    $conditions[] = 'note LIKE :keyword';
    $params[':keyword'] = '%' . $_GET['keyword'] . '%';
}
// --- 按类型筛选(收入/支出)---
if (isset($_GET['type']) && $_GET['type'] !== '') {
    $conditions[] = 'type = :type';
    $params[':type'] = (int)$_GET['type'];
}
// 3. 组装SQL并执行
$sql = 'SELECT * FROM bills WHERE ' . implode(' AND ', $conditions);
$sql .= ' ORDER BY billing_date DESC, created_at DESC'; // 默认按日期倒序
// 分页支持(可选)
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = 20;
$offset = ($page - 1) * $perPage;
// 先查询总数用于分页
$countSql = str_replace('SELECT *', 'SELECT COUNT(*)', $sql);
$stmt = $pdo->prepare($countSql);
$stmt->execute($params);
$total = $stmt->fetchColumn();
// 再查询具体数据
$sql .= " LIMIT $perPage OFFSET $offset";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$bills = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 4. 处理结果(示例:计算总收入和总支出)
$totalIncome = $totalExpense = 0;
foreach ($bills as $bill) {
    if ($bill['type'] == 1) $totalIncome += $bill['amount'];
    else $totalExpense += $bill['amount'];
}
// 5. 输出结果(可放到HTML模板中)
?>

前端展示(分页 + 统计)

<?php if (!empty($bills)): ?>
    <p>共 <?= $total ?> 条记录,收入总额:<?= $totalIncome ?>,支出总额:<?= $totalExpense ?></p>
    <table>
        <tr><th>日期</th><th>分类</th><th>类型</th><th>金额</th><th>备注</th></tr>
        <?php foreach ($bills as $bill): ?>
        <tr>
            <td><?= htmlspecialchars($bill['billing_date']) ?></td>
            <td><?= htmlspecialchars($bill['category']) ?></td>
            <td><?= $bill['type'] == 1 ? '收入' : '支出' ?></td>
            <td style="color:<?= $bill['type'] == 1 ? 'green' : 'red' ?>"><?= number_format($bill['amount'], 2) ?></td>
            <td><?= htmlspecialchars($bill['note'] ?? '') ?></td>
        </tr>
        <?php endforeach; ?>
    </table>
    <!-- 简单分页(保持当前查询条件) -->
    <?php
    $totalPages = ceil($total / $perPage);
    $queryParams = $_GET;
    for ($i = 1; $i <= $totalPages; $i++):
        $queryParams['page'] = $i;
        $queryString = http_build_query($queryParams);
    ?>
        <a href="?<?= $queryString ?>" <?= $i == $page ? 'style="font-weight:bold"' : '' ?>><?= $i ?></a>
    <?php endfor; ?>
<?php else: ?>
    <p>暂无账单记录</p>
<?php endif; ?>

扩展高级功能

  1. 分组统计:按分类统计金额

    SELECT category, SUM(amount) AS total 
    FROM bills 
    WHERE ... 
    GROUP BY category
    ORDER BY total DESC
  2. 月度对比:按月统计收支

    SELECT DATE_FORMAT(billing_date, '%Y-%m') AS month,
           SUM(CASE WHEN type=1 THEN amount ELSE 0 END) AS income,
           SUM(CASE WHEN type=0 THEN amount ELSE 0 END) AS expense
    FROM bills
    WHERE ...
    GROUP BY month
    ORDER BY month DESC
  3. 导出CSV

    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="bills.csv"');
    $output = fopen('php://output', 'w');
    fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF)); // BOM for Excel
    fputcsv($output, ['日期','分类','金额','备注']);
    foreach ($bills as $row) {
        fputcsv($output, [$row['billing_date'], $row['category'], $row['amount'], $row['note']]);
    }
    fclose($output);
  4. 安全建议

    • 使用 预处理语句(Prepared Statement) 防止SQL注入(上面已使用)。
    • 用户ID必须从Session获取,不能从客户端提交
    • 对输出做 htmlspecialchars() 防止XSS。
    • 如果数据量大(>10万条),建议加上 分页合理索引,避免全表扫描。

完整代码结构示例

project/
├── index.php              # 表单输入页
├── query_bills.php        # 查询逻辑 + 结果展示(或分离为两个文件)
├── db.php                 # 数据库连接配置
└── style.css              # 样式(可选)

如果希望分离逻辑,query_bills.php 仅输出JSON,前端用JavaScript(Vue/React)渲染,但小项目使用服务端渲染更简单直接。

如果需要更具体的实现(如ThinkPHP/Laravel框架版本、微信小程序对接、图表统计等),可以进一步说明。

抱歉,评论功能暂时关闭!