uni-app 优惠券系统
章节介绍
优惠券系统是现代应用中常见的营销工具,通过优惠券的发放、领取和使用,可以有效提升用户转化率和销售额。在 uni-app 中实现优惠券系统需要考虑跨端适配、数据同步、规则管理等多个方面。本教程将详细介绍 uni-app 优惠券系统的核心知识点和实现方法,帮助开发者快速构建功能完善的优惠券体系。
核心知识点
优惠券系统架构设计
优惠券系统通常包含以下核心组件:
- 优惠券管理模块:处理优惠券的创建、更新和管理
- 规则引擎模块:定义和管理优惠券规则
- 发放模块:管理优惠券的发放方式
- 领取模块:处理用户领取优惠券
- 使用模块:管理优惠券的使用场景
- 核销模块:处理优惠券的核销逻辑
- 数据分析模块:分析优惠券的使用效果和用户行为
优惠券类型设计
常见的优惠券类型包括:
- 满减券:满足一定金额条件后减免
- 折扣券:按照一定比例折扣
- 代金券:直接抵扣固定金额
- 免邮券:免除运费
- 赠品券:赠送指定商品
- 组合券:多种优惠组合
优惠券规则设计
优惠券规则是优惠券系统的核心,需要考虑以下因素:
- 使用条件:优惠券的使用条件,如最低消费金额
- 使用范围:优惠券的适用范围,如特定商品、品类或全场
- 使用限制:优惠券的使用限制,如每人限领、限用次数
- 有效期:优惠券的有效时间
- 叠加规则:优惠券是否可以与其他优惠叠加使用
- 发放规则:优惠券的发放规则,如发放数量、发放对象
优惠券发放方式
常见的优惠券发放方式包括:
- 主动发放:系统主动向用户发放优惠券
- 活动领取:用户通过参与活动领取优惠券
- 积分兑换:用户使用积分兑换优惠券
- 分享领取:用户分享或邀请好友领取优惠券
- 购买获得:用户购买特定商品获得优惠券
- 会员专享:特定等级会员专享优惠券
优惠券使用场景
常见的优惠券使用场景包括:
- 线上购物:在应用内购物时使用优惠券
- 线下消费:在线下门店消费时使用优惠券
- 服务预约:预约服务时使用优惠券
- 续费优惠:会员续费时使用优惠券
- 特定活动:在特定活动中使用优惠券
优惠券数据分析
优惠券数据分析需要关注以下指标:
- 发放量:系统发放的优惠券数量
- 领取率:优惠券的领取比例
- 使用率:优惠券的使用比例
- 核销率:优惠券的核销比例
- 转化率:优惠券带来的转化效果
- ROI:优惠券的投资回报率
- 用户行为:用户在优惠券使用过程中的行为路径
实用案例分析
案例:实现完整的优惠券系统
功能需求
- 优惠券创建:管理员创建不同类型的优惠券
- 优惠券发放:系统向用户发放优惠券
- 优惠券领取:用户领取优惠券
- 优惠券使用:用户在购物时使用优惠券
- 优惠券核销:系统核销使用的优惠券
- 数据分析:分析优惠券的使用效果
实现步骤
- 设计优惠券数据结构:定义优惠券相关的数据模型
- 实现优惠券管理 API:开发优惠券相关的接口
- 构建优惠券中心页面:展示用户的优惠券
- 开发优惠券发放功能:实现各种优惠券发放方式
- 实现优惠券领取功能:开发用户领取优惠券的功能
- 开发优惠券使用功能:实现优惠券的使用和核销逻辑
- 集成数据分析功能:分析优惠券的使用效果
代码示例
优惠券数据结构设计
// 优惠券模板数据模型
const couponTemplateSchema = {
id: String, // 模板ID
name: String, // 优惠券名称
type: String, // 优惠券类型(满减、折扣、代金等)
value: Number, // 优惠金额或折扣比例
minSpend: Number, // 最低消费金额
maxDiscount: Number, // 最大优惠金额
usageScope: String, // 使用范围(全场、特定商品、特定品类)
applicableItems: Array, // 适用商品或品类ID
usageLimit: Number, // 每人使用限制
totalLimit: Number, // 总发放数量
issuedCount: Number, // 已发放数量
usedCount: Number, // 已使用数量
startTime: Date, // 开始时间
endTime: Date, // 结束时间
status: String, // 状态(未开始、进行中、已结束)
description: String, // 优惠券描述
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
};
// 用户优惠券数据模型
const userCouponSchema = {
id: String, // 优惠券ID
userId: String, // 用户ID
templateId: String, // 优惠券模板ID
couponCode: String, // 优惠券码
value: Number, // 优惠金额或折扣比例
minSpend: Number, // 最低消费金额
maxDiscount: Number, // 最大优惠金额
usageScope: String, // 使用范围
applicableItems: Array, // 适用商品或品类ID
status: String, // 状态(未使用、已使用、已过期)
startTime: Date, // 开始时间
endTime: Date, // 结束时间
obtainTime: Date, // 领取时间
usedTime: Date, // 使用时间
orderId: String, // 使用的订单ID
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
};
// 优惠券发放记录数据模型
const couponIssueRecordSchema = {
id: String, // 记录ID
templateId: String, // 优惠券模板ID
userId: String, // 用户ID
issueType: String, // 发放类型(主动发放、活动领取、积分兑换等)
issueTime: Date, // 发放时间
status: String, // 状态(成功、失败)
reason: String, // 发放原因或失败原因
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
};
// 优惠券使用记录数据模型
const couponUseRecordSchema = {
id: String, // 记录ID
couponId: String, // 优惠券ID
userId: String, // 用户ID
orderId: String, // 订单ID
useTime: Date, // 使用时间
originalAmount: Number, // 原始金额
discountAmount: Number, // 优惠金额
actualAmount: Number, // 实际支付金额
status: String, // 状态(成功、失败)
reason: String, // 使用原因或失败原因
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
};优惠券管理API
// api/coupon.js
import request from './request';
export const couponApi = {
// 获取用户优惠券列表
getUserCoupons(params) {
return request({
url: '/api/coupon/user/list',
method: 'GET',
params
});
},
// 领取优惠券
receiveCoupon(templateId) {
return request({
url: '/api/coupon/receive',
method: 'POST',
data: { templateId }
});
},
// 兑换优惠券
exchangeCoupon(exchangeCode) {
return request({
url: '/api/coupon/exchange',
method: 'POST',
data: { exchangeCode }
});
},
// 获取可用优惠券
getAvailableCoupons(orderInfo) {
return request({
url: '/api/coupon/available',
method: 'POST',
data: { orderInfo }
});
},
// 使用优惠券
useCoupon(couponId, orderId) {
return request({
url: '/api/coupon/use',
method: 'POST',
data: { couponId, orderId }
});
},
// 取消使用优惠券
cancelUseCoupon(couponId, orderId) {
return request({
url: '/api/coupon/cancel',
method: 'POST',
data: { couponId, orderId }
});
},
// 获取优惠券模板列表
getCouponTemplates(params) {
return request({
url: '/api/coupon/template/list',
method: 'GET',
params
});
}
};优惠券中心组件
<template>
<view class="coupon-center">
<!-- 优惠券分类标签 -->
<view class="coupon-tabs">
<view
v-for="tab in tabs"
:key="tab.key"
:class="['tab-item', { 'active': activeTab === tab.key }]"
@click="switchTab(tab.key)"
>
<text class="tab-text">{{ tab.name }}</text>
</view>
</view>
<!-- 可领取优惠券 -->
<view v-if="activeTab === 'available'" class="available-coupons">
<view class="section-header">
<text class="section-title">可领取优惠券</text>
</view>
<view class="coupon-list">
<view v-for="template in availableTemplates" :key="template.id" class="coupon-template">
<view class="template-content">
<view class="template-value">
<text v-if="template.type === 'discount'" class="value-text">
{{ template.value }}折
</text>
<text v-else class="value-text">
¥{{ template.value }}
</text>
<text v-if="template.minSpend" class="min-spend">
满{{ template.minSpend }}可用
</text>
</view>
<view class="template-info">
<text class="template-name">{{ template.name }}</text>
<text class="template-desc">{{ template.description }}</text>
<text class="template-time">
{{ formatTime(template.startTime) }}至{{ formatTime(template.endTime) }}
</text>
<text class="template-count">
剩余{{ template.totalLimit - template.issuedCount }}张
</text>
</view>
<button @click="receiveCoupon(template.id)" class="receive-btn">
立即领取
</button>
</view>
</view>
</view>
</view>
<!-- 已领取优惠券 -->
<view v-else class="my-coupons">
<view class="section-header">
<text class="section-title">我的优惠券</text>
</view>
<view class="coupon-list">
<view v-for="coupon in userCoupons" :key="coupon.id" class="coupon-item" :class="coupon.status">
<view class="coupon-content">
<view class="coupon-value">
<text v-if="coupon.type === 'discount'" class="value-text">
{{ coupon.value }}折
</text>
<text v-else class="value-text">
¥{{ coupon.value }}
</text>
<text v-if="coupon.minSpend" class="min-spend">
满{{ coupon.minSpend }}可用
</text>
</view>
<view class="coupon-info">
<text class="coupon-name">{{ coupon.name }}</text>
<text class="coupon-desc">{{ coupon.description }}</text>
<text class="coupon-time">
{{ formatTime(coupon.startTime) }}至{{ formatTime(coupon.endTime) }}
</text>
<text class="coupon-scope">{{ getScopeText(coupon.usageScope) }}</text>
</view>
<view class="coupon-status">
<text v-if="coupon.status === 'unused'" class="status-text">
未使用
</text>
<text v-else-if="coupon.status === 'used'" class="status-text">
已使用
</text>
<text v-else-if="coupon.status === 'expired'" class="status-text">
已过期
</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
activeTab: 'available',
tabs: [
{ key: 'available', name: '可领取' },
{ key: 'unused', name: '未使用' },
{ key: 'used', name: '已使用' },
{ key: 'expired', name: '已过期' }
],
availableTemplates: [],
userCoupons: []
};
},
mounted() {
this.loadData();
},
methods: {
async loadData() {
try {
// 获取可领取优惠券模板
const templatesResponse = await this.$api.coupon.getCouponTemplates({ status: 'ongoing' });
this.availableTemplates = templatesResponse.data;
// 获取用户优惠券
const couponsResponse = await this.$api.coupon.getUserCoupons({ status: this.activeTab });
this.userCoupons = couponsResponse.data;
} catch (error) {
console.error('获取优惠券数据失败', error);
}
},
switchTab(tabKey) {
this.activeTab = tabKey;
this.loadData();
},
async receiveCoupon(templateId) {
try {
await this.$api.coupon.receiveCoupon(templateId);
uni.showToast({ title: '领取成功', icon: 'success' });
// 刷新数据
this.loadData();
} catch (error) {
uni.showToast({ title: error.message || '领取失败', icon: 'none' });
}
},
formatTime(time) {
const date = new Date(time);
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
},
getScopeText(scope) {
const scopeMap = {
all: '全场通用',
category: '指定品类',
product: '指定商品'
};
return scopeMap[scope] || '未知范围';
}
}
};
</script>
<style scoped>
.coupon-center {
background-color: #f5f5f5;
min-height: 100vh;
}
.coupon-tabs {
display: flex;
background-color: #fff;
padding: 10px 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
position: sticky;
top: 0;
z-index: 10;
}
.tab-item {
flex: 1;
text-align: center;
padding: 10px 0;
}
.tab-item.active {
border-bottom: 2px solid #1890ff;
}
.tab-text {
font-size: 14px;
color: #333;
}
.tab-item.active .tab-text {
color: #1890ff;
font-weight: bold;
}
.section-header {
padding: 15px;
background-color: #f5f5f5;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
}
.coupon-list {
padding: 10px;
}
.coupon-template {
background-color: #fff;
border-radius: 8px;
margin-bottom: 15px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.template-content {
display: flex;
padding: 15px;
align-items: center;
}
.template-value {
flex: 0 0 100px;
text-align: center;
border-right: 1px dashed #e0e0e0;
padding-right: 15px;
}
.value-text {
font-size: 24px;
font-weight: bold;
color: #ff4d4f;
display: block;
margin-bottom: 5px;
}
.min-spend {
font-size: 12px;
color: #999;
display: block;
}
.template-info {
flex: 1;
padding: 0 15px;
}
.template-name {
font-size: 14px;
font-weight: bold;
color: #333;
display: block;
margin-bottom: 5px;
}
.template-desc {
font-size: 12px;
color: #666;
display: block;
margin-bottom: 5px;
}
.template-time {
font-size: 12px;
color: #999;
display: block;
margin-bottom: 5px;
}
.template-count {
font-size: 12px;
color: #999;
display: block;
}
.receive-btn {
background-color: #1890ff;
color: #fff;
border: none;
padding: 8px 16px;
border-radius: 4px;
font-size: 14px;
}
.coupon-item {
background-color: #fff;
border-radius: 8px;
margin-bottom: 15px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
position: relative;
}
.coupon-item.used,
.coupon-item.expired {
opacity: 0.6;
}
.coupon-item.used::after,
.coupon-item.expired::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(45deg, transparent 49%, #999 49%, #999 51%, transparent 51%);
background-size: 10px 10px;
pointer-events: none;
border-radius: 8px;
}
.coupon-content {
display: flex;
padding: 15px;
align-items: center;
}
.coupon-value {
flex: 0 0 100px;
text-align: center;
border-right: 1px dashed #e0e0e0;
padding-right: 15px;
}
.coupon-info {
flex: 1;
padding: 0 15px;
}
.coupon-name {
font-size: 14px;
font-weight: bold;
color: #333;
display: block;
margin-bottom: 5px;
}
.coupon-desc {
font-size: 12px;
color: #666;
display: block;
margin-bottom: 5px;
}
.coupon-time {
font-size: 12px;
color: #999;
display: block;
margin-bottom: 5px;
}
.coupon-scope {
font-size: 12px;
color: #999;
display: block;
}
.coupon-status {
flex: 0 0 80px;
text-align: center;
}
.status-text {
font-size: 14px;
color: #999;
display: block;
}
.coupon-item.unused .status-text {
color: #1890ff;
}
.coupon-item.used .status-text {
color: #999;
}
.coupon-item.expired .status-text {
color: #999;
}
</style>优惠券规则引擎
// 优惠券规则引擎
class CouponRuleEngine {
// 初始化规则
constructor() {
this.rules = {
// 使用条件规则
usageConditions: {
minSpend: this.checkMinSpend,
usageScope: this.checkUsageScope,
validity: this.checkValidity,
usageLimit: this.checkUsageLimit
},
// 发放规则
issueConditions: {
totalLimit: this.checkTotalLimit,
userLimit: this.checkUserLimit,
eligibility: this.checkEligibility
}
};
}
// 检查优惠券使用条件
checkUsageConditions(coupon, orderInfo) {
const conditions = [];
// 检查最低消费
if (coupon.minSpend) {
conditions.push(this.rules.usageConditions.minSpend(coupon, orderInfo));
}
// 检查使用范围
conditions.push(this.rules.usageConditions.usageScope(coupon, orderInfo));
// 检查有效期
conditions.push(this.rules.usageConditions.validity(coupon));
// 检查使用限制
conditions.push(this.rules.usageConditions.usageLimit(coupon, orderInfo));
const result = conditions.every(condition => condition.allowed);
const reason = conditions.find(condition => !condition.allowed)?.reason;
return { allowed: result, reason };
}
// 检查最低消费
checkMinSpend(coupon, orderInfo) {
const orderAmount = orderInfo.totalAmount || 0;
if (orderAmount < coupon.minSpend) {
return { allowed: false, reason: `订单金额未达到最低消费${coupon.minSpend}元` };
}
return { allowed: true };
}
// 检查使用范围
checkUsageScope(coupon, orderInfo) {
if (coupon.usageScope === 'all') {
return { allowed: true };
}
if (coupon.usageScope === 'category' && coupon.applicableItems) {
const orderCategories = orderInfo.items.map(item => item.categoryId);
const hasApplicableItem = coupon.applicableItems.some(categoryId =>
orderCategories.includes(categoryId)
);
if (!hasApplicableItem) {
return { allowed: false, reason: '订单商品不在优惠券使用范围内' };
}
}
if (coupon.usageScope === 'product' && coupon.applicableItems) {
const orderProductIds = orderInfo.items.map(item => item.productId);
const hasApplicableItem = coupon.applicableItems.some(productId =>
orderProductIds.includes(productId)
);
if (!hasApplicableItem) {
return { allowed: false, reason: '订单商品不在优惠券使用范围内' };
}
}
return { allowed: true };
}
// 检查有效期
checkValidity(coupon) {
const now = new Date();
if (now < new Date(coupon.startTime)) {
return { allowed: false, reason: '优惠券尚未开始使用' };
}
if (now > new Date(coupon.endTime)) {
return { allowed: false, reason: '优惠券已过期' };
}
return { allowed: true };
}
// 检查使用限制
checkUsageLimit(coupon, orderInfo) {
// 实际项目中从数据库获取用户使用次数
const usageCount = 0;
if (coupon.usageLimit && usageCount >= coupon.usageLimit) {
return { allowed: false, reason: '优惠券使用次数已达上限' };
}
return { allowed: true };
}
// 检查优惠券发放条件
checkIssueConditions(template, user) {
const conditions = [];
// 检查总发放数量
conditions.push(this.rules.issueConditions.totalLimit(template));
// 检查用户领取限制
conditions.push(this.rules.issueConditions.userLimit(template, user));
// 检查用户资格
conditions.push(this.rules.issueConditions.eligibility(template, user));
const result = conditions.every(condition => condition.allowed);
const reason = conditions.find(condition => !condition.allowed)?.reason;
return { allowed: result, reason };
}
// 检查总发放数量
checkTotalLimit(template) {
if (template.totalLimit && template.issuedCount >= template.totalLimit) {
return { allowed: false, reason: '优惠券已发放完毕' };
}
return { allowed: true };
}
// 检查用户领取限制
checkUserLimit(template, user) {
// 实际项目中从数据库获取用户领取次数
const userIssueCount = 0;
if (template.userLimit && userIssueCount >= template.userLimit) {
return { allowed: false, reason: '您已达到领取上限' };
}
return { allowed: true };
}
// 检查用户资格
checkEligibility(template, user) {
// 检查用户等级
if (template.minUserLevel && user.level < template.minUserLevel) {
return { allowed: false, reason: '等级不足,无法领取' };
}
// 检查用户标签
if (template.targetTags && template.targetTags.length > 0) {
const userTags = user.tags || [];
const hasTag = template.targetTags.some(tag => userTags.includes(tag));
if (!hasTag) {
return { allowed: false, reason: '不符合领取条件' };
}
}
return { allowed: true };
}
// 计算优惠金额
calculateDiscount(coupon, orderInfo) {
const orderAmount = orderInfo.totalAmount || 0;
let discountAmount = 0;
switch (coupon.type) {
case '满减':
discountAmount = coupon.value;
break;
case '折扣':
discountAmount = orderAmount * (1 - coupon.value / 10);
break;
case '代金':
discountAmount = coupon.value;
break;
default:
discountAmount = 0;
}
// 检查最大优惠金额
if (coupon.maxDiscount && discountAmount > coupon.maxDiscount) {
discountAmount = coupon.maxDiscount;
}
// 优惠金额不能超过订单金额
discountAmount = Math.min(discountAmount, orderAmount);
return discountAmount;
}
// 检查优惠券是否可以叠加
checkStackable(coupon1, coupon2) {
// 这里可以实现具体的叠加规则
// 例如:代金券可以与折扣券叠加,但不能与其他代金券叠加
if (coupon1.type === coupon2.type && coupon1.type === '代金') {
return false;
}
return true;
}
}
export default new CouponRuleEngine();优惠券管理
// 优惠券管理
class CouponManager {
// 初始化
constructor(ruleEngine) {
this.ruleEngine = ruleEngine;
}
// 创建优惠券模板
async createCouponTemplate(templateData) {
try {
// 验证模板数据
this.validateTemplateData(templateData);
// 保存模板数据
const template = await this.saveCouponTemplate(templateData);
return { success: true, template };
} catch (error) {
console.error('创建优惠券模板失败', error);
throw error;
}
}
// 验证模板数据
validateTemplateData(templateData) {
if (!templateData.name) {
throw new Error('优惠券名称不能为空');
}
if (!templateData.type) {
throw new Error('优惠券类型不能为空');
}
if (templateData.value === undefined || templateData.value <= 0) {
throw new Error('优惠券价值必须大于0');
}
if (!templateData.startTime || !templateData.endTime) {
throw new Error('优惠券有效期不能为空');
}
if (new Date(templateData.endTime) < new Date(templateData.startTime)) {
throw new Error('优惠券结束时间不能早于开始时间');
}
if (templateData.totalLimit && templateData.totalLimit <= 0) {
throw new Error('优惠券发放数量必须大于0');
}
}
// 保存优惠券模板
async saveCouponTemplate(templateData) {
// 实际项目中保存到数据库
console.log('保存优惠券模板', templateData);
return { id: '1', ...templateData };
}
// 发放优惠券
async issueCoupon(templateId, userId, issueType = '主动发放') {
try {
// 获取优惠券模板
const template = await this.getCouponTemplate(templateId);
if (!template) {
throw new Error('优惠券模板不存在');
}
// 检查发放条件
const user = await this.getUserInfo(userId);
const issueCheck = this.ruleEngine.checkIssueConditions(template, user);
if (!issueCheck.allowed) {
throw new Error(issueCheck.reason);
}
// 创建用户优惠券
const coupon = await this.createUserCoupon(template, userId);
// 更新模板发放数量
await this.updateTemplateIssuedCount(templateId);
// 记录发放记录
await this.recordCouponIssue(templateId, userId, issueType);
return { success: true, coupon };
} catch (error) {
console.error('发放优惠券失败', error);
throw error;
}
}
// 获取优惠券模板
async getCouponTemplate(templateId) {
// 实际项目中从数据库获取
return {
id: templateId,
name: '测试优惠券',
type: '满减',
value: 10,
minSpend: 100,
totalLimit: 100,
issuedCount: 0,
startTime: new Date(),
endTime: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
};
}
// 获取用户信息
async getUserInfo(userId) {
// 实际项目中从数据库获取
return {
id: userId,
level: 1,
tags: []
};
}
// 创建用户优惠券
async createUserCoupon(template, userId) {
// 生成优惠券码
const couponCode = this.generateCouponCode();
const couponData = {
userId,
templateId: template.id,
couponCode,
value: template.value,
minSpend: template.minSpend,
maxDiscount: template.maxDiscount,
usageScope: template.usageScope,
applicableItems: template.applicableItems,
status: 'unused',
startTime: template.startTime,
endTime: template.endTime,
obtainTime: new Date(),
createdAt: new Date(),
updatedAt: new Date()
};
// 实际项目中保存到数据库
console.log('创建用户优惠券', couponData);
return { id: '1', ...couponData };
}
// 更新模板发放数量
async updateTemplateIssuedCount(templateId) {
// 实际项目中更新数据库
console.log('更新模板发放数量', templateId);
}
// 记录发放记录
async recordCouponIssue(templateId, userId, issueType) {
// 实际项目中保存到数据库
console.log('记录发放记录', templateId, userId, issueType);
}
// 生成优惠券码
generateCouponCode() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let code = '';
for (let i = 0; i < 12; i++) {
code += chars.charAt(Math.floor(Math.random() * chars.length));
}
return code;
}
// 用户领取优惠券
async receiveCoupon(templateId, userId) {
return this.issueCoupon(templateId, userId, '用户领取');
}
// 使用优惠券
async useCoupon(couponId, orderId, orderInfo) {
try {
// 获取优惠券
const coupon = await this.getUserCoupon(couponId, orderId);
if (!coupon) {
throw new Error('优惠券不存在');
}
if (coupon.status !== 'unused') {
throw new Error('优惠券已使用或过期');
}
// 检查使用条件
const usageCheck = this.ruleEngine.checkUsageConditions(coupon, orderInfo);
if (!usageCheck.allowed) {
throw new Error(usageCheck.reason);
}
// 计算优惠金额
const discountAmount = this.ruleEngine.calculateDiscount(coupon, orderInfo);
// 更新优惠券状态
await this.updateCouponStatus(couponId, 'used', { orderId, usedTime: new Date() });
// 记录使用记录
await this.recordCouponUse(couponId, orderId, orderInfo, discountAmount);
// 更新模板使用数量
await this.updateTemplateUsedCount(coupon.templateId);
return { success: true, discountAmount };
} catch (error) {
console.error('使用优惠券失败', error);
throw error;
}
}
// 获取用户优惠券
async getUserCoupon(couponId, userId) {
// 实际项目中从数据库获取
return {
id: couponId,
userId,
templateId: '1',
status: 'unused',
minSpend: 100,
value: 10,
type: '满减',
startTime: new Date(),
endTime: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
};
}
// 更新优惠券状态
async updateCouponStatus(couponId, status, updateData = {}) {
// 实际项目中更新数据库
console.log('更新优惠券状态', couponId, status, updateData);
}
// 记录使用记录
async recordCouponUse(couponId, orderId, orderInfo, discountAmount) {
// 实际项目中保存到数据库
console.log('记录使用记录', couponId, orderId, orderInfo, discountAmount);
}
// 更新模板使用数量
async updateTemplateUsedCount(templateId) {
// 实际项目中更新数据库
console.log('更新模板使用数量', templateId);
}
// 取消使用优惠券
async cancelUseCoupon(couponId, orderId) {
try {
// 获取优惠券
const coupon = await this.getUserCoupon(couponId, orderId);
if (!coupon) {
throw new Error('优惠券不存在');
}
if (coupon.status !== 'used' || coupon.orderId !== orderId) {
throw new Error('优惠券未使用或不属于该订单');
}
// 更新优惠券状态
await this.updateCouponStatus(couponId, 'unused', { orderId: null, usedTime: null });
// 更新模板使用数量
await this.decrementTemplateUsedCount(coupon.templateId);
return { success: true };
} catch (error) {
console.error('取消使用优惠券失败', error);
throw error;
}
}
// 减少模板使用数量
async decrementTemplateUsedCount(templateId) {
// 实际项目中更新数据库
console.log('减少模板使用数量', templateId);
}
// 处理优惠券过期
async processCouponExpiration() {
try {
// 获取过期的优惠券
const expiredCoupons = await this.getExpiredCoupons();
for (const coupon of expiredCoupons) {
// 更新优惠券状态
await this.updateCouponStatus(coupon.id, 'expired');
}
} catch (error) {
console.error('处理优惠券过期失败', error);
}
}
// 获取过期的优惠券
async getExpiredCoupons() {
// 实际项目中从数据库获取
return [];
}
// 获取用户可用优惠券
async getUserAvailableCoupons(userId, orderInfo) {
try {
// 获取用户未使用的优惠券
const unusedCoupons = await this.getUserUnusedCoupons(userId);
// 过滤可用的优惠券
const availableCoupons = [];
for (const coupon of unusedCoupons) {
const usageCheck = this.ruleEngine.checkUsageConditions(coupon, orderInfo);
if (usageCheck.allowed) {
const discountAmount = this.ruleEngine.calculateDiscount(coupon, orderInfo);
availableCoupons.push({ ...coupon, discountAmount });
}
}
// 按照优惠金额排序
availableCoupons.sort((a, b) => b.discountAmount - a.discountAmount);
return availableCoupons;
} catch (error) {
console.error('获取用户可用优惠券失败', error);
return [];
}
}
// 获取用户未使用的优惠券
async getUserUnusedCoupons(userId) {
// 实际项目中从数据库获取
return [];
}
}
export default new CouponManager();实现技巧与注意事项
跨端适配
- 数据同步:使用 uni-app 的本地存储和云存储结合,确保不同平台的数据同步
- API 适配:对于平台特定的 API,使用条件编译进行适配
- UI 适配:根据不同平台的屏幕尺寸和交互习惯,调整优惠券中心的 UI 设计
- 性能优化:考虑不同平台的性能差异,优化优惠券数据的加载和渲染
数据安全
- 敏感数据加密:对优惠券的敏感信息进行加密存储和传输
- 防作弊机制:实现防作弊机制,防止用户刷优惠券
- 数据验证:在服务端验证所有优惠券操作,防止客户端篡改
- 日志记录:记录详细的优惠券操作日志,便于排查问题
性能优化
- 数据缓存:缓存优惠券数据,减少重复请求
- 批量处理:对优惠券过期等操作使用批量处理,减少数据库压力
- 异步处理:对非关键优惠券操作使用异步处理,提升响应速度
- 索引优化:为优惠券相关的数据库表添加适当的索引,提升查询性能
可扩展性
- 模块化设计:将优惠券系统拆分为多个模块,便于维护和扩展
- 插件化架构:采用插件化架构,支持不同类型的优惠券插件
- 配置化管理:使用配置文件管理优惠券规则,便于调整
- API 标准化:设计标准化的 API 接口,便于与其他系统集成
常见问题与解决方案
问题:优惠券领取失败
解决方案:
- 检查优惠券发放数量是否已达上限
- 检查用户领取次数是否已达上限
- 检查用户是否符合领取条件
- 检查网络连接是否正常
- 记录详细的错误日志,便于排查问题
问题:优惠券使用失败
解决方案:
- 检查优惠券是否在有效期内
- 检查订单金额是否达到最低消费要求
- 检查订单商品是否在优惠券使用范围内
- 检查优惠券是否已被使用或过期
- 检查优惠券使用次数是否已达上限
问题:优惠券系统性能问题
解决方案:
- 优化数据库查询,使用适当的索引
- 实现优惠券数据的缓存机制
- 对优惠券操作进行批量处理
- 考虑使用分布式架构,提升系统处理能力
问题:用户恶意刷优惠券
解决方案:
- 实现防作弊机制,如 IP 限制、设备限制
- 对高频优惠券领取行为进行监控和限制
- 建立优惠券异常检测系统,识别和处理异常领取行为
- 制定明确的优惠券规则和惩罚机制,对作弊用户进行处理
总结
本教程详细介绍了 uni-app 优惠券系统的核心知识点和实现方法,包括优惠券系统架构设计、优惠券类型设计、优惠券规则设计、优惠券发放方式、优惠券使用场景等内容。通过实用案例和代码示例,展示了如何在 uni-app 中实现完整的优惠券功能。
优惠券系统是提升用户转化率和销售额的重要工具,合理设计和实现优惠券系统可以有效促进应用的增长。在实际开发中,需要根据应用的具体需求和用户群体,设计适合的优惠券类型和规则,同时注重系统的性能、安全性和可扩展性。
通过本教程的学习,开发者应该能够:
- 理解优惠券系统的基本架构和核心组件
- 掌握优惠券规则的设计和实现方法
- 实现各种优惠券发放和使用场景
- 解决优惠券系统开发中遇到的常见问题
- 优化优惠券系统的性能和安全性
希望本教程对开发者在 uni-app 中实现优惠券系统有所帮助,祝大家开发顺利!