分享一套锋哥原创的基于Python的会员管理系统(带微信小程序会员端)(FastAPI+Vue3)优质版
大家好,我是Java1234_小锋老师,分享一套锋哥原创的基于Python的会员管理系统(带微信小程序会员端)(FastAPI+Vue3)优质版

项目介绍
随着移动互联网与数字化运营的快速发展,会员制已成为零售、餐饮、生活服务等行业提升客户黏性与复购率的重要手段。传统依赖纸质会员卡或分散表格登记的管理模式,存在信息滞后、统计困难、等级折扣难以自动执行、会员端触达不便等问题。因此,设计并实现一套前后端分离、移动端可访问的会员管理系统,对中小型商户实现精细化运营具有现实意义。
本文设计并实现了“基于Python的会员管理系统(带微信小程序端)”。系统采用B/S架构与前后端分离思想:后端基于Python语言与FastAPI框架构建RESTful接口,使用SQLAlchemy操作MySQL数据库(库名db_member),并结合JWT完成管理员与会员双端身份认证;管理后台基于Vue3、Vue Router、Pinia与Element Plus实现,首页集成ECharts数据统计图表;微信小程序端面向会员用户,提供用户名密码登录、注册、公告浏览、资产查看、头像与个人资料修改、密码修改等功能。业务上覆盖会员档案、会员等级与折扣、商品管理、充值、消费、积分获取与兑换、公告发布以及管理员与角色管理等核心场景,并实现消费折扣计算、余额扣减、积分累计与自动升级等关键业务闭环。
系统经过功能测试验证,各模块运行稳定,接口响应正常,能够满足本科毕业设计对完整性、规范性与实用性的要求。实践表明,Python与FastAPI适合快速构建高可读性的后端服务,Vue3适合构建交互友好的管理端,微信小程序则能有效降低会员使用门槛。论文同时给出了E-R图、功能结构图、时序图与数据表字段设计,并对关键功能附核心代码说明,便于复现与二次开发。
源码下载
链接: https://pan.baidu.com/s/1hbcKeOPblLjWPn4royqf3A?pwd=1234
提取码: 1234
系统展示







核心代码
"""
消费业务服务
对齐 Java ConsumeServiceImpl.doConsume
"""
from decimal import Decimal, ROUND_DOWN, ROUND_HALF_UP
from sqlalchemy.orm import Session
from app.common.exception import BusinessException
from app.models.consume import Consume
from app.models.member import Member
from app.models.member_level import MemberLevel
from app.models.points_record import PointsRecord
from app.models.product import Product
class ConsumeService:
"""消费服务类"""
@staticmethod
def do_consume(db: Session, consume: Consume) -> None:
"""
执行会员消费:按等级折扣扣费、扣库存、增加积分并记录
:param db: 数据库会话
:param consume: 消费信息
"""
member = (
db.query(Member)
.filter(Member.id == consume.member_id, Member.deleted == 0)
.first()
)
if member is None:
raise BusinessException("会员不存在")
if member.status is not None and member.status == 0:
raise BusinessException("会员已冻结,无法消费")
amount = consume.amount
# 若选择了商品,以商品价格为准并扣库存
if consume.product_id is not None:
product = (
db.query(Product)
.filter(Product.id == consume.product_id, Product.deleted == 0)
.first()
)
if product is None:
raise BusinessException("商品不存在")
if product.status is not None and product.status == 0:
raise BusinessException("商品已下架")
if product.stock is not None and product.stock <= 0:
raise BusinessException("商品库存不足")
amount = product.price
product.stock = product.stock - 1
db.add(product)
if amount is None or amount <= 0:
raise BusinessException("消费金额必须大于0")
# 按会员等级计算折扣
discount = Decimal("1")
if member.level_id is not None:
level = (
db.query(MemberLevel)
.filter(MemberLevel.id == member.level_id, MemberLevel.deleted == 0)
.first()
)
if level is not None and level.discount is not None:
discount = Decimal(str(level.discount))
real_amount = (amount * discount).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
balance = member.balance if member.balance is not None else Decimal("0")
if balance < real_amount:
raise BusinessException("会员余额不足")
new_balance = balance - real_amount
points_gained = int(real_amount.to_integral_value(rounding=ROUND_DOWN))
current_points = member.points if member.points is not None else 0
new_points = current_points + points_gained
member.balance = new_balance
member.points = new_points
ConsumeService._upgrade_level(db, member, new_points)
db.add(member)
consume.amount = amount
consume.real_amount = real_amount
consume.points_gained = points_gained
consume.balance_after = new_balance
consume.deleted = 0
db.add(consume)
# 记录积分获取
if points_gained > 0:
record = PointsRecord(
member_id=member.id,
change_points=points_gained,
type="获取",
remark="消费获得积分",
deleted=0,
)
db.add(record)
db.commit()
@staticmethod
def _upgrade_level(db: Session, member: Member, new_points: int) -> None:
"""
根据积分自动升级会员等级
:param db: 数据库会话
:param member: 会员
:param new_points: 最新积分
"""
levels = (
db.query(MemberLevel)
.filter(MemberLevel.deleted == 0)
.order_by(MemberLevel.upgrade_points.desc())
.all()
)
for level in levels:
if level.upgrade_points is not None and new_points >= level.upgrade_points:
member.level_id = level.id
break
<template>
<div class="page-card">
<div class="search-bar">
<el-select v-model="query.memberId" placeholder="选择会员" clearable filterable style="width: 200px">
<el-option v-for="m in members" :key="m.id" :label="`${m.name}(${m.cardNo})`" :value="m.id" />
</el-select>
<el-button type="primary" @click="loadData"><el-icon><Search /></el-icon>查询</el-button>
<el-button @click="query.memberId=null; query.pageNum=1; loadData()"><el-icon><Refresh /></el-icon>重置</el-button>
</div>
<div class="toolbar">
<el-button type="primary" @click="openDialog()"><el-icon><Plus /></el-icon>会员消费</el-button>
</div>
<el-table :data="tableData" stripe border style="width: 100%">
<el-table-column prop="cardNo" label="会员卡号" min-width="120" />
<el-table-column prop="memberName" label="会员姓名" min-width="100" />
<el-table-column prop="productName" label="商品" min-width="120" />
<el-table-column prop="amount" label="原价" min-width="90" />
<el-table-column prop="realAmount" label="实付" min-width="90" />
<el-table-column prop="pointsGained" label="获得积分" min-width="90" />
<el-table-column prop="balanceAfter" label="消费后余额" min-width="110" />
<el-table-column prop="operator" label="操作人" min-width="90" />
<el-table-column prop="createTime" label="消费时间" min-width="160" />
<el-table-column label="操作" min-width="90" fixed="right">
<template #default="{ row }">
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-wrap">
<el-pagination v-model:current-page="query.pageNum" v-model:page-size="query.pageSize" :total="total"
:page-sizes="[10,20,50]" layout="total, sizes, prev, pager, next" @size-change="loadData" @current-change="loadData" />
</div>
<el-dialog v-model="dialogVisible" title="会员消费" width="480px" destroy-on-close>
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px">
<el-form-item label="会员" prop="memberId">
<el-select v-model="form.memberId" filterable placeholder="请选择会员" style="width: 100%">
<el-option v-for="m in members" :key="m.id" :label="`${m.name}(${m.cardNo}) 余额:${m.balance}`" :value="m.id" />
</el-select>
</el-form-item>
<el-form-item label="商品" prop="productId">
<el-select v-model="form.productId" filterable placeholder="请选择商品" style="width: 100%">
<el-option v-for="p in products" :key="p.id" :label="`${p.productName} ¥${p.price}`" :value="p.id" />
</el-select>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.remark" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="saving" @click="handleSave">确认消费</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
/**
* 消费管理页面
*/
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import request from '@/utils/request'
const tableData = ref([])
const members = ref([])
const products = ref([])
const total = ref(0)
const dialogVisible = ref(false)
const saving = ref(false)
const formRef = ref()
const query = reactive({ pageNum: 1, pageSize: 10, memberId: null })
const form = reactive({ memberId: null, productId: null, remark: '' })
const rules = {
memberId: [{ required: true, message: '请选择会员', trigger: 'change' }],
productId: [{ required: true, message: '请选择商品', trigger: 'change' }]
}
async function loadData() {
const res = await request.get('/api/consume/list', { params: query })
tableData.value = res.data.records
total.value = res.data.total
}
async function loadOptions() {
const [m, p] = await Promise.all([
request.get('/api/member/all'),
request.get('/api/product/all')
])
members.value = m.data
products.value = p.data
}
function openDialog() {
Object.assign(form, { memberId: null, productId: null, remark: '' })
dialogVisible.value = true
}
async function handleSave() {
await formRef.value.validate()
saving.value = true
try {
await request.post('/api/consume', form)
ElMessage.success('消费成功')
dialogVisible.value = false
loadData()
loadOptions()
} finally { saving.value = false }
}
function handleDelete(row) {
ElMessageBox.confirm('确定删除该消费记录吗?', '提示', { type: 'warning' }).then(async () => {
await request.delete(`/api/consume/${row.id}`)
ElMessage.success('删除成功')
loadData()
}).catch(() => {})
}
onMounted(() => { loadOptions(); loadData() })
</script>
更多推荐




所有评论(0)