Web虚拟卡销售店铺实现方案
一、项目概述
1.1 项目背景
随着数字经济的快速发展,虚拟卡(如礼品卡、会员卡、游戏点卡等)的市场需求呈现爆发式增长。本项目旨在构建一个完整的Web虚拟卡销售平台,包含前端销售系统、后端管理系统和移动端H5支付功能,采用PHP作为后端技术栈,ThinkPHP作为开发框架,并集成微信支付、支付宝等主流支付方式。
1.2 系统架构
系统采用前后端分离架构:
- 前端:Vue.js + Element UI(管理端)+ Vant(移动端)
- 后端:ThinkPHP + MySQL
- 数据库:MySQL
- 缓存:Redis
- 支付:微信支付H5 API + 支付宝支付API
二、技术选型与环境搭建
2.1 后端技术栈
ThinkPHP框架:作为国内使用率极高的MVC架构PHP框架,提供了丰富的内置功能,如数据库抽象层、路由机制、模板引擎、验证器、日志系统等,极大提升了开发效率。
主要依赖:
{
"require": {
"php": ">=7.4",
"topthink/framework": "^6.0",
"topthink/think-migration": "^3.0",
"topthink/think-captcha": "^3.0",
"topthink/think-jwt": "^1.0",
"alibabacloud/sdk": "^2.0",
"overtrue/wechat": "^6.0"
}
}
2.2 前端技术栈
用户端:
- Vue.js 3.x
- Vant 4.x(移动端UI组件库)
- Axios(HTTP客户端)
- Vue Router(路由管理)
- Vuex(状态管理)
管理端:
- Vue.js 3.x
- Element Plus(PC端UI组件库)
- ECharts(数据可视化)
2.3 开发环境配置
服务器环境:
- Nginx 1.22.1
- MySQL 5.7+
- PHP 7.4+
- Redis 6.0+
开发工具:
- PHPStorm / VS Code
- Git版本控制
- Composer包管理
- Docker(可选)
三、数据库设计
3.1 数据库ER图
系统主要实体包括:用户(User)、虚拟卡产品(CardProduct)、卡密库存(CardSecret)、订单(Order)、支付记录(Payment)、管理员(Admin)。
3.2 数据表设计
用户表:
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(255) NOT NULL COMMENT '密码',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:0-禁用,1-正常',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
UNIQUE KEY `uk_email` (`email`),
UNIQUE KEY `uk_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
虚拟卡产品表:
CREATE TABLE `card_product` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`product_name` varchar(100) NOT NULL COMMENT '产品名称',
`product_type` varchar(50) NOT NULL COMMENT '产品类型',
`face_value` decimal(10,2) NOT NULL COMMENT '面值',
`selling_price` decimal(10,2) NOT NULL COMMENT '售价',
`description` text COMMENT '产品描述',
`cover_image` varchar(255) DEFAULT NULL COMMENT '封面图',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:0-下架,1-上架',
`sort` int(11) DEFAULT '0' COMMENT '排序',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='虚拟卡产品表';
卡密库存表:
CREATE TABLE `card_secret` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`product_id` bigint(20) NOT NULL COMMENT '产品ID',
`card_no` varchar(100) NOT NULL COMMENT '卡号',
`card_password` varchar(100) NOT NULL COMMENT '卡密',
`status` tinyint(1) DEFAULT '0' COMMENT '状态:0-未售出,1-已售出',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_card_no` (`card_no`),
KEY `idx_product_id` (`product_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='卡密库存表';
订单表:
CREATE TABLE `order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`order_no` varchar(50) NOT NULL COMMENT '订单号',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`product_id` bigint(20) NOT NULL COMMENT '产品ID',
`product_name` varchar(100) NOT NULL COMMENT '产品名称',
`face_value` decimal(10,2) NOT NULL COMMENT '面值',
`selling_price` decimal(10,2) NOT NULL COMMENT '售价',
`quantity` int(11) NOT NULL DEFAULT '1' COMMENT '购买数量',
`total_amount` decimal(10,2) NOT NULL COMMENT '总金额',
`payment_method` varchar(20) DEFAULT NULL COMMENT '支付方式',
`payment_status` tinyint(1) DEFAULT '0' COMMENT '支付状态:0-未支付,1-已支付',
`order_status` tinyint(1) DEFAULT '0' COMMENT '订单状态:0-待支付,1-已支付,2-已发货,3-已完成,4-已取消',
`card_no` varchar(100) DEFAULT NULL COMMENT '卡号',
`card_password` varchar(100) DEFAULT NULL COMMENT '卡密',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_user_id` (`user_id`),
KEY `idx_product_id` (`product_id`),
KEY `idx_payment_status` (`payment_status`),
KEY `idx_order_status` (`order_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
支付记录表:
CREATE TABLE `payment` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`order_no` varchar(50) NOT NULL COMMENT '订单号',
`payment_no` varchar(50) NOT NULL COMMENT '支付流水号',
`payment_method` varchar(20) NOT NULL COMMENT '支付方式',
`payment_amount` decimal(10,2) NOT NULL COMMENT '支付金额',
`payment_status` tinyint(1) NOT NULL COMMENT '支付状态:0-未支付,1-已支付',
`payment_time` datetime DEFAULT NULL COMMENT '支付时间',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_payment_no` (`payment_no`),
KEY `idx_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付记录表';
系统日志表:
CREATE TABLE `sys_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',
`username` varchar(50) DEFAULT NULL COMMENT '用户名',
`operation` varchar(50) DEFAULT NULL COMMENT '用户操作',
`method` varchar(200) DEFAULT NULL COMMENT '请求方法',
`params` text COMMENT '请求参数',
`time` bigint(20) DEFAULT NULL COMMENT '执行时长(毫秒)',
`ip` varchar(64) DEFAULT NULL COMMENT 'IP地址',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统日志表';
四、后端实现
4.1 ThinkPHP项目结构
src/
├── app/ # 应用目录
│ ├── controller/ # 控制器
│ │ ├── api/ # API接口控制器
│ │ │ ├── AuthController.php
│ │ │ ├── CardController.php
│ │ │ ├── OrderController.php
│ │ │ └── PaymentController.php
│ │ └── admin/ # 管理端控制器
│ │ ├── AdminAuthController.php
│ │ ├── AdminCardController.php
│ │ └── AdminOrderController.php
│ ├── model/ # 模型
│ │ ├── User.php
│ │ ├── CardProduct.php
│ │ ├── CardSecret.php
│ │ ├── Order.php
│ │ └── Payment.php
│ ├── service/ # 服务层
│ │ ├── AuthService.php
│ │ ├── CardService.php
│ │ ├── OrderService.php
│ │ └── PaymentService.php
│ ├── validate/ # 验证器
│ │ ├── UserValidate.php
│ │ ├── CardValidate.php
│ │ └── OrderValidate.php
│ └── common.php # 公共函数
├── config/ # 配置文件
│ ├── app.php
│ ├── database.php
│ ├── cache.php
│ ├── jwt.php
│ └── wechat.php
├── route/ # 路由定义
│ └── app.php
├── public/ # 入口文件
│ └── index.php
└── vendor/ # 依赖包
4.2 核心功能实现
4.2.1 用户认证与授权
JWT认证实现:
<?php
namespace app\service;
use think\facade\Config;
use think\facade\Cache;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class AuthService
{
/**
* 生成JWT Token
* @param array $userInfo 用户信息
* @return string
*/
public static function generateToken($userInfo)
{
$config = Config::get('jwt');
$payload = [
'iss' => $config['iss'],
'iat' => time(),
'exp' => time() + $config['expire'],
'user_id' => $userInfo['id'],
'username' => $userInfo['username']
];
return JWT::encode($payload, $config['key'], $config['alg']);
}
/**
* 验证JWT Token
* @param string $token
* @return array|false
*/
public static function verifyToken($token)
{
try {
$config = Config::get('jwt');
$decoded = JWT::decode($token, new Key($config['key'], $config['alg']));
return (array)$decoded;
} catch (\Exception $e) {
return false;
}
}
/**
* 用户登录
* @param string $username
* @param string $password
* @return array|false
*/
public static function login($username, $password)
{
$user = User::where('username', $username)
->where('status', 1)
->find();
if (!$user || !password_verify($password, $user->password)) {
return false;
}
$token = self::generateToken($user->toArray());
// 记录登录日志
LogService::addLoginLog($user->id, $user->username);
return [
'token' => $token,
'user_info' => [
'id' => $user->id,
'username' => $user->username,
'email' => $user->email,
'phone' => $user->phone,
'avatar' => $user->avatar
]
];
}
}
?>
4.2.2 虚拟卡管理
卡密生成算法:
<?php
namespace app\service;
use think\facade\Db;
use think\facade\Cache;
class CardService
{
/**
* 生成卡密
* @param int $productId 产品ID
* @param int $quantity 生成数量
* @param string $prefix 卡号前缀
* @return bool
*/
public static function generateCardSecret($productId, $quantity, $prefix = '')
{
Db::startTrans();
try {
for ($i = 0; $i < $quantity; $i++) {
$cardNo = self::generateCardNo($prefix);
$cardPassword = self::generateCardPassword();
$data = [
'product_id' => $productId,
'card_no' => $cardNo,
'card_password' => $cardPassword,
'status' => 0,
'create_time' => date('Y-m-d H:i:s')
];
Db::name('card_secret')->insert($data);
}
Db::commit();
return true;
} catch (\Exception $e) {
Db::rollback();
return false;
}
}
/**
* 生成卡号
* @param string $prefix
* @return string
*/
private static function generateCardNo($prefix = '')
{
$prefix = $prefix ?: date('Ymd');
$random = mt_rand(100000, 999999);
return $prefix . $random;
}
/**
* 生成卡密
* @return string
*/
private static function generateCardPassword()
{
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$password = '';
for ($i = 0; $i < 12; $i++) {
$password .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $password;
}
/**
* 获取可用卡密
* @param int $productId
* @return array|false
*/
public static function getAvailableCard($productId)
{
$card = Db::name('card_secret')
->where('product_id', $productId)
->where('status', 0)
->order('id ASC')
->find();
if (!$card) {
return false;
}
// 更新卡密状态为已售出
Db::name('card_secret')
->where('id', $card['id'])
->update(['status' => 1, 'update_time' => date('Y-m-d H:i:s')]);
return $card;
}
}
?>
4.2.3 订单服务
订单创建与处理:
<?php
namespace app\service;
use think\facade\Db;
use think\facade\Cache;
use app\model\Order;
class OrderService
{
/**
* 创建订单
* @param int $userId
* @param int $productId
* @param int $quantity
* @return array|false
*/
public static function createOrder($userId, $productId, $quantity = 1)
{
// 获取产品信息
$product = Db::name('card_product')
->where('id', $productId)
->where('status', 1)
->find();
if (!$product) {
return false;
}
// 检查库存
$availableStock = self::getAvailableStock($productId);
if ($availableStock < $quantity) {
return false;
}
Db::startTrans();
try {
// 生成订单号
$orderNo = self::generateOrderNo();
// 计算总金额
$totalAmount = bcmul($product['selling_price'], $quantity, 2);
// 创建订单
$orderData = [
'order_no' => $orderNo,
'user_id' => $userId,
'product_id' => $productId,
'product_name' => $product['product_name'],
'face_value' => $product['face_value'],
'selling_price' => $product['selling_price'],
'quantity' => $quantity,
'total_amount' => $totalAmount,
'payment_status' => 0,
'order_status' => 0,
'create_time' => date('Y-m-d H:i:s')
];
$orderId = Db::name('order')->insertGetId($orderData);
Db::commit();
return [
'order_id' => $orderId,
'order_no' => $orderNo,
'total_amount' => $totalAmount,
'product_name' => $product['product_name']
];
} catch (\Exception $e) {
Db::rollback();
return false;
}
}
/**
* 生成订单号
* @return string
*/
private static function generateOrderNo()
{
return date('YmdHis') . mt_rand(1000, 9999);
}
/**
* 获取可用库存
* @param int $productId
* @return int
*/
public static function getAvailableStock($productId)
{
return Db::name('card_secret')
->where('product_id', $productId)
->where('status', 0)
->count();
}
/**
* 支付成功回调
* @param string $orderNo
* @param array $paymentData
* @return bool
*/
public static function paymentSuccess($orderNo, $paymentData)
{
Db::startTrans();
try {
// 更新订单状态
$order = Db::name('order')
->where('order_no', $orderNo)
->where('payment_status', 0)
->lock(true)
->find();
if (!$order) {
return false;
}
// 获取卡密
$card = CardService::getAvailableCard($order['product_id']);
if (!$card) {
return false;
}
// 更新订单信息
$updateData = [
'payment_status' => 1,
'order_status' => 2, // 已发货
'payment_method' => $paymentData['payment_method'],
'card_no' => $card['card_no'],
'card_password' => $card['card_password'],
'update_time' => date('Y-m-d H:i:s')
];
Db::name('order')
->where('order_no', $orderNo)
->update($updateData);
// 添加支付记录
$paymentData = [
'order_no' => $orderNo,
'payment_no' => $paymentData['payment_no'],
'payment_method' => $paymentData['payment_method'],
'payment_amount' => $order['total_amount'],
'payment_status' => 1,
'payment_time' => date('Y-m-d H:i:s')
];
Db::name('payment')->insert($paymentData);
Db::commit();
// 发送卡密邮件或短信
self::sendCardInfo($order['user_id'], $card['card_no'], $card['card_password']);
return true;
} catch (\Exception $e) {
Db::rollback();
return false;
}
}
/**
* 发送卡密信息
* @param int $userId
* @param string $cardNo
* @param string $cardPassword
* @return bool
*/
private static function sendCardInfo($userId, $cardNo, $cardPassword)
{
$user = Db::name('user')->where('id', $userId)->find();
if (!$user) {
return false;
}
// 发送邮件
$emailService = new EmailService();
$subject = '您的虚拟卡购买成功';
$content = "尊敬的{$user['username']},您购买的虚拟卡已发货:\n卡号:{$cardNo}\n密码:{$cardPassword}\n请妥善保管,请勿泄露给他人。";
return $emailService->send($user['email'], $subject, $content);
}
}
?>
4.2.4 微信支付集成
微信支付配置:
<?php
namespace app\service;
use think\facade\Config;
use EasyWeChat\Factory;
class WeChatPayService
{
/**
* 获取微信支付实例
* @return \EasyWeChat\Payment\Application
*/
public static function getPayment()
{
$config = Config::get('wechat.payment');
return Factory::payment($config);
}
/**
* 创建支付订单
* @param string $orderNo
* @param float $amount
* @param string $body
* @param string $openid
* @return array|false
*/
public static function createOrder($orderNo, $amount, $body, $openid = null)
{
try {
$payment = self::getPayment();
$data = [
'body' => $body,
'out_trade_no' => $orderNo,
'total_fee' => intval($amount * 100), // 单位:分
'notify_url' => url('/api/payment/wechat/notify', [], true, true),
'trade_type' => $openid ? 'JSAPI' : 'NATIVE',
];
if ($openid) {
$data['openid'] = $openid;
}
$result = $payment->order->unify($data);
if ($result['return_code'] === 'SUCCESS' && $result['result_code'] === 'SUCCESS') {
return $result;
}
return false;
} catch (\Exception $e) {
return false;
}
}
/**
* 处理支付回调
* @return array|false
*/
public static function handleNotify()
{
try {
$payment = self::getPayment();
$response = $payment->handlePaidNotify(function($message, $fail){
// 验证订单
$order = Db::name('order')
->where('order_no', $message['out_trade_no'])
->find();
if (!$order) {
return $fail('订单不存在');
}
if ($order['payment_status'] == 1) {
return true;
}
// 验证金额
$totalFee = intval($order['total_amount'] * 100);
if ($message['total_fee'] != $totalFee) {
return $fail('金额不一致');
}
// 更新订单状态
$paymentData = [
'payment_no' => $message['transaction_id'],
'payment_method' => 'wechat',
'payment_status' => 1,
'payment_time' => date('Y-m-d H:i:s')
];
$result = OrderService::paymentSuccess($order['order_no'], $paymentData);
return $result ? true : $fail('订单处理失败');
});
return $response;
} catch (\Exception $e) {
return false;
}
}
}
?>
五、前端实现
5.1 用户端前端实现
5.1.1 项目结构
src/
├── api/ # API接口
│ ├── auth.js # 认证相关接口
│ ├── card.js # 虚拟卡相关接口
│ ├── order.js # 订单相关接口
│ └── payment.js # 支付相关接口
├── components/ # 公共组件
│ ├── Header.vue # 头部组件
│ ├── Footer.vue # 底部组件
│ ├── Loading.vue # 加载组件
│ └── CardItem.vue # 卡产品项组件
├── pages/ # 页面组件
│ ├── Home.vue # 首页
│ ├── Login.vue # 登录页
│ ├── Register.vue # 注册页
│ ├── ProductList.vue # 产品列表页
│ ├── ProductDetail.vue # 产品详情页
│ ├── Order.vue # 订单页
│ ├── Payment.vue # 支付页
│ ├── OrderList.vue # 订单列表页
│ └── OrderDetail.vue # 订单详情页
├── router/ # 路由配置
│ └── index.js
├── store/ # Vuex状态管理
│ ├── index.js
│ └── modules/
│ ├── auth.js # 认证模块
│ ├── card.js # 虚拟卡模块
│ └── order.js # 订单模块
├── utils/ # 工具函数
│ ├── request.js # 请求封装
│ ├── auth.js # 认证工具
│ └── common.js # 公共工具
└── App.vue # 根组件
5.1.2 核心页面实现
首页(Home.vue):
<template>
<div class="home">
<!-- 轮播图 -->
<van-swipe class="banner-swipe" :autoplay="3000">
<van-swipe-item v-for="item in banners" :key="item.id">
<img :src="item.image" class="banner-image" />
</van-swipe-item>
</van-swipe>
<!-- 产品分类 -->
<div class="category-section">
<div class="section-title">热门分类</div>
<div class="category-list">
<div v-for="category in categories" :key="category.id" class="category-item">
<img :src="category.icon" class="category-icon" />
<div class="category-name">{{ category.name }}</div>
</div>
</div>
</div>
<!-- 推荐产品 -->
<div class="product-section">
<div class="section-title">推荐产品</div>
<div class="product-list">
<CardItem
v-for="product in products"
:key="product.id"
:product="product"
@click="goToDetail(product.id)"
/>
</div>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { getBanners, getCategories, getProducts } from '@/api/card'
import CardItem from '@/components/CardItem.vue'
export default {
name: 'Home',
components: {
CardItem
},
setup() {
const router = useRouter()
const banners = ref([])
const categories = ref([])
const products = ref([])
const loadData = async () => {
try {
const [bannerRes, categoryRes, productRes] = await Promise.all([
getBanners(),
getCategories(),
getProducts({ page: 1, limit: 8 })
])
banners.value = bannerRes.data
categories.value = categoryRes.data
products.value = productRes.data.list
} catch (error) {
console.error('加载数据失败:', error)
}
}
const goToDetail = (productId) => {
router.push(`/product/${productId}`)
}
onMounted(() => {
loadData()
})
return {
banners,
categories,
products,
goToDetail
}
}
}
</script>
产品详情页(ProductDetail.vue):
<template>
<div class="product-detail">
<!-- 产品图片 -->
<div class="product-image">
<img :src="product.cover_image" />
</div>
<!-- 产品信息 -->
<div class="product-info">
<div class="product-name">{{ product.product_name }}</div>
<div class="product-price">
<span class="current-price">¥{{ product.selling_price }}</span>
<span class="face-value">面值:¥{{ product.face_value }}</span>
</div>
<div class="product-description">{{ product.description }}</div>
</div>
<!-- 购买按钮 -->
<div class="buy-action">
<van-button type="primary" block @click="handleBuy">立即购买</van-button>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { getProductDetail } from '@/api/card'
import { createOrder } from '@/api/order'
import { showToast } from 'vant'
export default {
name: 'ProductDetail',
setup() {
const route = useRoute()
const router = useRouter()
const product = ref({})
const loading = ref(false)
const loadProductDetail = async () => {
try {
const res = await getProductDetail(route.params.id)
product.value = res.data
} catch (error) {
console.error('加载产品详情失败:', error)
}
}
const handleBuy = async () => {
if (loading.value) return
loading.value = true
try {
const res = await createOrder(product.value.id, 1)
if (res.code === 0) {
router.push(`/payment/${res.data.order_no}`)
} else {
showToast(res.msg || '创建订单失败')
}
} catch (error) {
console.error('创建订单失败:', error)
showToast('创建订单失败')
} finally {
loading.value = false
}
}
onMounted(() => {
loadProductDetail()
})
return {
product,
handleBuy
}
}
}
</script>
5.2 管理端前端实现
5.2.1 项目结构
src/
├── api/ # API接口
│ ├── admin/
│ │ ├── auth.js # 管理端认证接口
│ │ ├── card.js # 管理端虚拟卡接口
│ │ ├── order.js # 管理端订单接口
│ │ └── user.js # 管理端用户接口
│ └── index.js
├── components/ # 公共组件
│ ├── Layout.vue # 布局组件
│ ├── Sidebar.vue # 侧边栏
│ ├── Header.vue # 头部
│ └── Table.vue # 表格组件
├── views/ # 页面组件
│ ├── Login.vue # 登录页
│ ├── Dashboard.vue # 仪表盘
│ ├── CardList.vue # 产品列表
│ ├── CardAdd.vue # 添加产品
│ ├── CardEdit.vue # 编辑产品
│ ├── CardSecret.vue # 卡密管理
│ ├── OrderList.vue # 订单列表
│ ├── OrderDetail.vue # 订单详情
│ ├── UserList.vue # 用户列表
│ └── System.vue # 系统设置
├── router/ # 路由配置
│ └── index.js
├── store/ # Vuex状态管理
│ ├── index.js
│ └── modules/
│ ├── auth.js # 认证模块
│ ├── card.js # 虚拟卡模块
│ └── order.js # 订单模块
├── utils/ # 工具函数
│ ├── request.js # 请求封装
│ ├── auth.js # 认证工具
│ └── common.js # 公共工具
└── App.vue # 根组件
5.2.2 核心页面实现
产品管理页(CardList.vue):
<template>
<div class="card-list">
<div class="header">
<el-button type="primary" @click="handleAdd">添加产品</el-button>
<el-button @click="handleImport">批量导入卡密</el-button>
</div>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="id" label="ID" width="60" />
<el-table-column prop="product_name" label="产品名称" />
<el-table-column prop="product_type" label="产品类型" />
<el-table-column prop="face_value" label="面值" />
<el-table-column prop="selling_price" label="售价" />
<el-table-column prop="stock" label="库存" />
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-tag :type="scope.row.status ? 'success' : 'danger'">
{{ scope.row.status ? '上架' : '下架' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="create_time" label="创建时间" />
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { getCardList, deleteCard } from '@/api/admin/card'
import { ElMessage, ElMessageBox } from 'element-plus'
export default {
name: 'CardList',
setup() {
const router = useRouter()
const tableData = ref([])
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
const loadData = async () => {
try {
const res = await getCardList({
page: currentPage.value,
limit: pageSize.value
})
tableData.value = res.data.list
total.value = res.data.total
} catch (error) {
console.error('加载数据失败:', error)
}
}
const handleAdd = () => {
router.push('/admin/card/add')
}
const handleEdit = (row) => {
router.push(`/admin/card/edit/${row.id}`)
}
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm('确定要删除该产品吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await deleteCard(row.id)
ElMessage.success('删除成功')
loadData()
} catch (error) {
console.error('删除失败:', error)
}
}
const handleSizeChange = (val) => {
pageSize.value = val
loadData()
}
const handleCurrentChange = (val) => {
currentPage.value = val
loadData()
}
onMounted(() => {
loadData()
})
return {
tableData,
currentPage,
pageSize,
total,
handleAdd,
handleEdit,
handleDelete,
handleSizeChange,
handleCurrentChange
}
}
}
</script>
六、微信H5支付集成
6.1 微信支付配置
微信支付配置文件(config/wechat.php):
<?php
return [
'payment' => [
'app_id' => env('WECHAT_APP_ID', ''),
'mch_id' => env('WECHAT_MCH_ID', ''),
'key' => env('WECHAT_KEY', ''),
'cert_path' => env('WECHAT_CERT_PATH', ''),
'key_path' => env('WECHAT_KEY_PATH', ''),
'notify_url' => env('WECHAT_NOTIFY_URL', ''),
'sandbox' => env('WECHAT_SANDBOX', false),
],
];
6.2 支付流程实现
支付控制器(PaymentController.php):
<?php
namespace app\controller\api;
use think\facade\Db;
use think\facade\Config;
use app\BaseController;
use app\service\WeChatPayService;
use app\service\OrderService;
class PaymentController extends BaseController
{
/**
* 创建支付订单
* @return \think\Response
*/
public function create()
{
$orderNo = $this->request->param('order_no');
$openid = $this->request->param('openid');
// 获取订单信息
$order = Db::name('order')
->where('order_no', $orderNo)
->where('payment_status', 0)
->find();
if (!$order) {
return json(['code' => 1, 'msg' => '订单不存在或已支付']);
}
// 创建微信支付订单
$result = WeChatPayService::createOrder(
$orderNo,
$order['total_amount'],
$order['product_name'],
$openid
);
if (!$result) {
return json(['code' => 1, 'msg' => '创建支付订单失败']);
}
return json(['code' => 0, 'data' => $result]);
}
/**
* 微信支付回调
* @return mixed
*/
public function wechatNotify()
{
return WeChatPayService::handleNotify();
}
/**
* 查询支付状态
* @return \think\Response
*/
public function query()
{
$orderNo = $this->request->param('order_no');
$order = Db::name('order')
->where('order_no', $orderNo)
->find();
if (!$order) {
return json(['code' => 1, 'msg' => '订单不存在']);
}
return json([
'code' => 0,
'data' => [
'payment_status' => $order['payment_status'],
'order_status' => $order['order_status'],
'card_no' => $order['card_no'],
'card_password' => $order['card_password']
]
]);
}
}
?>
七、系统安全与性能优化
7.1 安全防护措施
数据加密:
- 用户密码采用bcrypt加密存储
- 卡密信息采用AES-256加密存储
- 敏感数据传输使用HTTPS加密
防SQL注入:
- 使用ThinkPHP的预处理语句
- 对用户输入进行严格过滤和验证
防XSS攻击:
- 对用户输入进行HTML转义
- 使用Content Security Policy(CSP)
防CSRF攻击:
- 使用Token验证机制
- 验证请求来源
7.2 性能优化
缓存策略:
- 使用Redis缓存热点数据(如产品信息、用户信息)
- 设置合理的缓存过期时间
数据库优化:
- 建立合适的索引
- 分库分表(大流量场景)
- 读写分离
静态资源优化:
- CDN加速静态资源
- 图片懒加载
- 资源压缩
八、部署与运维
8.1 环境部署
服务器环境要求:
- Linux服务器(推荐CentOS 7+)
- Nginx 1.18+
- PHP 7.4+
- MySQL 5.7+
- Redis 6.0+
一键安装脚本:
#!/bin/bash
# 安装Nginx
yum install -y nginx
# 安装PHP
yum install -y php php-fpm php-mysql php-redis php-gd php-mbstring php-xml
# 安装MySQL
yum install -y mysql-server
# 安装Redis
yum install -y redis
# 配置防火墙
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
# 启动服务
systemctl start nginx
systemctl start php-fpm
systemctl start mysqld
systemctl start redis
systemctl enable nginx
systemctl enable php-fpm
systemctl enable mysqld
systemctl enable redis
8.2 监控与告警
系统监控:
- 使用Prometheus监控服务器性能
- 使用Grafana展示监控数据
- 设置告警规则
日志管理:
- 使用ELK(Elasticsearch + Logstash + Kibana)收集和分析日志
- 设置日志轮转策略
8.3 备份与恢复
数据库备份:
#!/bin/bash # 数据库备份脚本 DATE=$(date +%Y%m%d%H%M%S) BACKUP_DIR="/data/backup/mysql" MYSQL_USER="root" MYSQL_PASSWORD="password" DATABASE="virtual_card" mysqldump -u$MYSQL_USER -p$MYSQL_PASSWORD $DATABASE > $BACKUP_DIR/$DATABASE-$DATE.sql # 保留最近7天的备份 find $BACKUP_DIR -name "*.sql" -mtime +7 -delete
若内容若侵犯到您的权益,请发送邮件至:platform_service@jienda.com我们将第一时间处理!
所有资源仅限于参考和学习,版权归JienDa作者所有,更多请访问JienDa首页。
