当前位置: 首页 > news >正文

分享一套锋哥原创的基于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>
http://www.jsqmd.com/news/1176937/

相关文章:

  • 2026 龙州黄金回收实地走访:3 大收割套路深度拆解,三大 30 年零差评老牌全城全覆盖免费上门 - 福金阁黄金回收
  • LinkSwift:八大网盘直链下载助手的完整开发者指南
  • 【信息科学与工程学】【通信工程】第七十七篇 城域网+FTTR/FTTB/FTTC/FTTO的数学分析01
  • NVIDIA多模态嵌入模型架构揭秘:Llama 3.2 1B + SigLip2 400M的完美结合
  • 2026年6月成都装修公司排行榜|权威实测评测(覆盖成都全域) - 推荐官
  • 【计算机工具类-协作工具Skills】cal-com-automation 技能
  • unipp中 @tap 运行到小程序,不生效
  • MiniMax-M2.5-NVFP4推理优化技巧:让你的AMD GPU推理速度提升300%
  • 地球物理大地测量学计算系列之十广义Stokes/Hotine数值积分外部高程异常计算
  • MiniMax-M2.7-NVFP4模型配置详解:理解3072隐藏层与256专家的设计原理
  • ChatDocs完全指南:如何快速搭建本地文档智能问答系统
  • 2026 年长武靠谱的2507不锈钢管平台深度剖析,扔掉304吧!这种双相钢才是化工厂真正的省钱利器 - 行业推荐【认证官】
  • Docker容器化部署:AMD MiniMax-M2.1-MXFP4生产环境最佳实践
  • 内存狂飙到32G导致OOM?我用C# IMemoryOwner手搓“零拷贝”引擎,把国产库驱动的GC压力按在地上摩擦!
  • 宝珀中国官方售后服务中心|服务热线及门店详细地址权威信息声明(2026年7月最新) - 宝珀官方售后服务中心
  • 让AI读懂你的文档:Langchain-Chatchat本地知识库构建实战
  • Redux-cycles vs Redux-thunk:为什么函数式响应式编程是更好的异步解决方案
  • 揭秘 websocket-client 内部架构:核心组件与设计模式解析
  • Fullmoon条件验证与表单处理:构建安全Web应用的完整指南
  • Emacs-wgrep最佳实践:10年经验用户分享的高效工作流程
  • 3. 【Java】Hello, World!:第一行 Java 代码
  • 江诗丹顿中国官方售后服务中心|网点地址与客服电话权威信息声明(2026年7月最新) - 江诗丹顿官方服务中心
  • 多 Agent 协作skills首发
  • AI 写达梦/金仓代码疯狂翻车?我手撸了一套“信创专属 AI 生成引擎”,把大模型的方言幻觉按在地上摩擦!
  • 29-杨逢昌:制造业车间工装器具散乱损耗解决方案——器具三权标准化管控体系,无效找物时间缩减80%
  • Human-Level Reasoning: A Comparative Study of Large Language Models on Logical and Abstract Reaso...
  • 南开大学2021创新实践课程:如何配置GRUB引导LFS系统
  • 护眼钢化膜和普通钢化膜的区别:为什么贴了膜屏幕反而更累眼?
  • 千问怎么生成表格?各类文档导出方式优劣解析,AI 导出鸭一站式化解各类导出难题
  • 2024年必备广告拦截伴侣:Nano Defender让你告别烦人的反广告拦截弹窗