如何用PHP项目实现交易流水?

wen java案例 3

本文目录导读:

如何用PHP项目实现交易流水?

  1. 数据库设计
  2. PHP核心实现
  3. 控制器示例
  4. 最佳实践建议

我来详细介绍几种在PHP项目中实现交易流水的方法和最佳实践。

数据库设计

基础流水表结构

CREATE TABLE `transaction_flow` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `flow_no` varchar(32) NOT NULL COMMENT '流水号',
    `user_id` int(11) NOT NULL COMMENT '用户ID',
    `order_no` varchar(32) DEFAULT NULL COMMENT '关联订单号',
    `transaction_type` tinyint(4) NOT NULL COMMENT '交易类型:1-充值 2-消费 3-退款 4-提现 5-转账',
    `amount` decimal(12,2) NOT NULL COMMENT '交易金额',
    `before_balance` decimal(12,2) NOT NULL COMMENT '交易前余额',
    `after_balance` decimal(12,2) NOT NULL COMMENT '交易后余额',
    `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:0-失败 1-成功 2-处理中',
    `remark` varchar(255) DEFAULT NULL COMMENT '备注说明',
    `payment_method` varchar(32) DEFAULT NULL COMMENT '支付方式:wechat/alipay/bank',
    `channel_trade_no` varchar(64) DEFAULT NULL COMMENT '第三方交易号',
    `ip_address` varchar(15) DEFAULT NULL COMMENT '客户端IP',
    `extra_info` text DEFAULT NULL COMMENT '额外信息(JSON)',
    `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_flow_no` (`flow_no`),
    KEY `idx_user_id` (`user_id`),
    KEY `idx_order_no` (`order_no`),
    KEY `idx_created_at` (`created_at`),
    KEY `idx_type_status` (`transaction_type`,`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='交易流水表';

PHP核心实现

流水号生成器

<?php
class FlowNumberGenerator
{
    /**
     * 生成唯一流水号
     * 格式:前缀 + 时间戳(14位) + 随机数(6位) + 序列号(4位)
     */
    public static function generate($prefix = 'FLOW')
    {
        $dateTime = date('YmdHis');
        $random = str_pad(mt_rand(0, 999999), 6, '0', STR_PAD_LEFT);
        $sequence = str_pad(mt_rand(0, 9999), 4, '0', STR_PAD_LEFT);
        return sprintf('%s%s%s%s', $prefix, $dateTime, $random, $sequence);
    }
    /**
     * 基于Redis生成更可靠的流水号
     */
    public static function generateWithRedis($redis, $prefix = 'FLOW')
    {
        $dateTime = date('YmdHis');
        $key = "flow_seq:{$dateTime}";
        $sequence = $redis->incr($key);
        $redis->expire($key, 2); // 2秒过期
        return sprintf('%s%s%04d', $prefix, $dateTime, $sequence);
    }
}

交易流水管理器

<?php
class TransactionManager
{
    private $db;
    private $redis;
    public function __construct($db, $redis = null)
    {
        $this->db = $db;
        $this->redis = $redis;
    }
    /**
     * 创建交易记录
     */
    public function createTransaction(array $data)
    {
        try {
            $this->db->beginTransaction();
            // 锁定用户账户
            $user = $this->lockUserAccount($data['user_id']);
            // 验证并计算余额
            $balanceResult = $this->calculateBalance($user, $data);
            if (!$balanceResult['success']) {
                throw new \Exception($balanceResult['message']);
            }
            // 生成流水号
            $flowNo = $this->redis ? 
                FlowNumberGenerator::generateWithRedis($this->redis) :
                FlowNumberGenerator::generate();
            // 插入流水记录
            $flowData = [
                'flow_no' => $flowNo,
                'user_id' => $data['user_id'],
                'order_no' => $data['order_no'] ?? null,
                'transaction_type' => $data['transaction_type'],
                'amount' => $data['amount'],
                'before_balance' => $balanceResult['before_balance'],
                'after_balance' => $balanceResult['after_balance'],
                'status' => 1, // 初始状态
                'remark' => $data['remark'] ?? '',
                'payment_method' => $data['payment_method'] ?? '',
                'ip_address' => $data['ip_address'] ?? '',
                'extra_info' => json_encode($data['extra_info'] ?? [])
            ];
            $this->insertTransactionFlow($flowData);
            // 更新用户余额
            $this->updateUserBalance($data['user_id'], $balanceResult['after_balance']);
            $this->db->commit();
            // 记录日志
            $this->logTransaction($flowNo, 'success');
            return [
                'success' => true,
                'flow_no' => $flowNo,
                'message' => '交易成功'
            ];
        } catch (\Exception $e) {
            $this->db->rollBack();
            $this->logTransaction($flowNo ?? 'unknown', 'failed', $e->getMessage());
            return [
                'success' => false,
                'message' => $e->getMessage()
            ];
        }
    }
    /**
     * 锁定用户账户
     */
    private function lockUserAccount($userId)
    {
        $sql = "SELECT * FROM users WHERE id = ? FOR UPDATE";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$userId]);
        return $stmt->fetch(\PDO::FETCH_ASSOC);
    }
    /**
     * 计算余额
     */
    private function calculateBalance($user, $data)
    {
        $currentBalance = (float)$user['balance'];
        $amount = (float)$data['amount'];
        switch ($data['transaction_type']) {
            case 1: // 充值
            case 5: // 转入
                $newBalance = $currentBalance + $amount;
                break;
            case 2: // 消费
            case 4: // 提现
                if ($currentBalance < $amount) {
                    return ['success' => false, 'message' => '余额不足'];
                }
                $newBalance = $currentBalance - $amount;
                break;
            case 3: // 退款
                $newBalance = $currentBalance + $amount;
                break;
            default:
                return ['success' => false, 'message' => '无效的交易类型'];
        }
        return [
            'success' => true,
            'before_balance' => $currentBalance,
            'after_balance' => $newBalance
        ];
    }
    /**
     * 查询交易流水
     */
    public function queryTransactions($userId, $filters = [], $page = 1, $pageSize = 20)
    {
        $where = ['user_id = ?'];
        $params = [$userId];
        // 添加筛选条件
        if (!empty($filters['type'])) {
            $where[] = 'transaction_type = ?';
            $params[] = (int)$filters['type'];
        }
        if (!empty($filters['start_time'])) {
            $where[] = 'created_at >= ?';
            $params[] = $filters['start_time'];
        }
        if (!empty($filters['end_time'])) {
            $where[] = 'created_at <= ?';
            $params[] = $filters['end_time'];
        }
        if (isset($filters['status'])) {
            $where[] = 'status = ?';
            $params[] = (int)$filters['status'];
        }
        $whereClause = implode(' AND ', $where);
        $offset = ($page - 1) * $pageSize;
        // 查询总数
        $countSql = "SELECT COUNT(*) FROM transaction_flow WHERE {$whereClause}";
        $countStmt = $this->db->prepare($countSql);
        $countStmt->execute($params);
        $total = $countStmt->fetchColumn();
        // 查询记录
        $sql = "SELECT * FROM transaction_flow WHERE {$whereClause} 
                ORDER BY created_at DESC LIMIT ? OFFSET ?";
        $params[] = $pageSize;
        $params[] = $offset;
        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        $list = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        return [
            'total' => $total,
            'page' => $page,
            'pageSize' => $pageSize,
            'list' => $list
        ];
    }
    /**
     * 日志记录
     */
    private function logTransaction($flowNo, $status, $message = '')
    {
        // 实现日志记录
        error_log("Transaction: {$flowNo}, Status: {$status}, Message: {$message}");
    }
}

控制器示例

<?php
class TransactionController
{
    private $transactionManager;
    public function __construct($db, $redis)
    {
        $this->transactionManager = new TransactionManager($db, $redis);
    }
    /**
     * 创建充值交易
     */
    public function recharge(Request $request)
    {
        // 验证参数
        $validator = new Validator($request->all(), [
            'user_id' => 'required|integer',
            'amount' => 'required|numeric|min:0.01',
            'payment_method' => 'required|in:wechat,alipay,bank'
        ]);
        if ($validator->fails()) {
            return response()->json(['error' => $validator->errors()], 400);
        }
        // 创建交易
        $result = $this->transactionManager->createTransaction([
            'user_id' => $request->input('user_id'),
            'amount' => $request->input('amount'),
            'transaction_type' => 1,
            'payment_method' => $request->input('payment_method'),
            'remark' => '用户充值',
            'ip_address' => $request->getClientIp(),
            'order_no' => $request->input('order_no')
        ]);
        return response()->json($result);
    }
    /**
     * 查询交易记录
     */
    public function query(Request $request)
    {
        $result = $this->transactionManager->queryTransactions(
            $request->input('user_id'),
            $request->only(['type', 'start_time', 'end_time', 'status']),
            $request->input('page', 1),
            $request->input('page_size', 20)
        );
        return response()->json($result);
    }
}

最佳实践建议

安全性考虑

  • 使用数据库事务保证数据一致性
  • 添加行级锁避免并发问题
  • 重要操作记录操作日志
  • 敏感数据加密存储

性能优化

  • 建立合适的索引
  • 使用分表策略(按月或按用户ID)
  • 定期归档历史数据
  • 使用缓存减少数据库查询

扩展功能

// 添加交易对账功能
trait ReconciliationTrait
{
    public function reconcile($date)
    {
        // 与第三方支付对账
        // 核对流水一致性
        // 处理异常记录
    }
}
// 添加统计分析
trait StatisticsTrait
{
    public function getStatistics($userId, $startTime, $endTime)
    {
        // 计算总交易额
        // 统计交易类型分布
        // 分析交易趋势
    }
}

这个实现方案包含了完整的交易流水处理逻辑,可以根据具体业务需求进行调整和扩展,记得在实际部署时要做好数据备份和监控告警。

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