资讯动态

微信小程序销售管理系统全栈开发:从uniapp到PHP订单闭环

发布时间:2026/9/15 3:26:59 来源:尧图企业网站定制
简介微信小程序彩票销售与管理系统是一套面向小程序开发者与后端工程师的完整前后端项目涵盖双色球、大乐透、七星彩的选球、合买、开奖查询、账户资金管理等核心业务逻辑并包含基于CodeIgniter的PHP后端接口、微信登录与短信验证码交互流程以及云端部署配置。压缩包共1407个文件以php后端代码为主另有json数据、wxml/wxss前端页面、js逻辑脚本、html静态页及少量图片与配置文件整包仅2.84MB目录分层清晰便于快速导入运行。已有534人学习参考适合希望掌握彩票类小程序全栈开发、微信鉴权及资金账务处理的初中级开发者。资源中能看到完整的项目目录结构、双色球/大乐透/七星彩的开奖与合买数据模型、期号/奖池/销量等字段设计以及面向腾讯云SDK的安装脚本和辅助工具能帮助读者理解真实业务系统的前后端协作、数据交互与部署思路。1. 微信小程序销售与管理系统跑不起来的地方从来不在首页做这类销售与管理系统最容易被卡住的不是业务代码而是三个看不见的环节用户登录态怎么延续、支付回调怎么验签、管理后台的数据从哪张表来。网上能找到的源码包里首页和列表页通常都写得不错但一跑到“下单”就断一跑“支付回调”就乱一跑“管理端统计”又缺字段。这套系统真正的价值不是展示商品而是把客户、订单、库存、业绩四条线串在同一套数据结构里让前台小程序和管理后台读的是同一份数据。本文按最稳妥的路线走一遍先用 uniapp 搭前台再用 PHP 接口做业务层同时把管理端看板和导出补上。面向的读者是能写原生小程序的开发者想从“会写页面”跨到“能交付一套完整系统”本文的重点在数据结构、登录态和订单闭环。2. 微信小程序销售管理系统的整体架构与技术选型2.1 前端、管理端与服务端的分层边界常见的做法是三端分离微信小程序端负责展示和交易管理后台负责商品、订单、会员管理服务端提供统一 API。三端共用同一套数据库服务端是唯一的数据出口。第一层是 C 端小程序。它只做交互不直接读写数据库。所有请求通过 uni.request 发出带上 token拿到 JSON 后渲染。第二层是管理后台。可以用 Vue 或原生 HTML 搭建功能围绕“商品上下架、订单发货、会员积分调整、销售统计”。多数源码包里管理后台做成了 Web 版访问路径是/admin。第三层是服务端 API。推荐 PHP 或 Node.js 实现职责只有三个校验身份、校验参数、操作数据库。边界不清晰的典型表现是订单金额在小程序端计算后直接传给后端存储。正确做法是后端根据商品表的单价重新计算因为小程序端的计算逻辑可以被篡改。同理库存扣减必须在后端事务里完成而不能依赖前端传“购买数量”后自行扣减。2.2 为什么用 uniapp 而不是原生微信小程序结合热搜词里大量出现的 uniapp 相关需求这套系统用 uniapp 更合适。原因有三第一管理后台如果需要打包成 App 或 H5uniapp 可以复用同一套前端代码第二uniapp 的语法接近 Vue 2/Vue 3组件生态比原生小程序丰富尤其是下拉刷新、上拉加载、侧滑删除这类常见交互第三HBuilderX 直接支持运行到微信开发者工具开发调试路径短。原生微信小程序适合页面交互复杂、需要深度调用微信 API 的项目。但销售管理系统的页面模式相对固定列表、详情、购物车、订单、个人中心。这类页面用 uniapp 的uni-ui组件可以快速搭建。需要说明的是uniapp 编译到微信小程序后最终产物仍然是原生小程序性能上没有额外损耗。技术选型对照表模块建议方案替代方案C 端小程序uniapp Vue 3原生微信小程序管理后台Vue 3 Element Plus服务端渲染模板服务端PHP ThinkPHPNode.js Express数据库MySQL 8.0MariaDB缓存与登录态Redis可选数据库 session 表提示如果团队只熟悉 PHP就用 PHP 做服务端如果前端团队熟悉 Node则用 Express 或 NestJS。不要为了“技术新”而引入团队不熟悉的栈。2.3 数据权限与核心表结构设计销售系统最关键的是一张订单表和一张商品表。订单表必须冗余商品名称与单价快照因为商品价格会调整订单历史不能被后续改价影响。会员表要冗余累计消费金额和积分余额避免每次统计都去扫订单表。商品表结构参考CREATE TABLE product ( id int(11) NOT NULL AUTO_INCREMENT, title varchar(255) NOT NULL COMMENT 商品名称, cover varchar(500) NOT NULL COMMENT 封面图, price decimal(10,2) NOT NULL COMMENT 销售价元, stock int(11) NOT NULL DEFAULT 0 COMMENT 库存, status tinyint(1) NOT NULL DEFAULT 1 COMMENT 1上架 0下架, sort int(11) NOT NULL DEFAULT 0 COMMENT 排序权重, create_time datetime NOT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表;订单表设计时注意order_no必须唯一status使用数字状态机而非字符串。支付金额存total_fee单位是分还是元要全局统一。如果混用支付回调对账时会反复出问题。3. 用 uniapp 搭出销售端核心页面与交互细节3.1 用 HBuilderX 创建项目并配置微信小程序运行环境在 HBuilderX 中新建项目时选“uni-app”模板Vue 版本选 Vue 3。创建后找到manifest.json在“微信小程序配置”里填入 AppID。没有 AppID 时选择测试号但测试号无法调用支付和获取手机号接口。打开 HBuilderX文件 新建 项目选择 uni-app 模板。修改 manifest.json 中的微信小程序 AppID。点击“运行到小程序模拟器 微信开发者工具”。微信开发者工具中需要开启“服务端口”否则 HBuilderX 无法自动推送代码。运行后最常遇到的报错是“app.json 未找到”此时在 HBuilderX 中重新编译一次并确认项目根目录存在main.js和App.vue。编译产物在dist/dev/mp-weixin目录下该目录才是微信开发者工具实际加载的代码。3.2 首页推荐位与商品列表的上拉加载首页采用“顶部轮播 金刚区 推荐列表”结构。轮播数据和商品列表分别调两个接口。列表必须做分页常见的参数是page和pageSize后端返回total总数。// pages/index/index.vue import { getProductList } from /api/product.js; export default { data() { return { page: 1, pageSize: 10, list: [], loading: false, finished: false }; }, onLoad() { this.loadList(true); }, onReachBottom() { if (this.finished || this.loading) return; this.loadList(false); }, methods: { async loadList(reset false) { if (this.loading) return; this.loading true; if (reset) { this.page 1; this.finished false; } try { const res await getProductList({ page: this.page, pageSize: this.pageSize }); const rows res.data.list || []; this.list reset ? rows : this.list.concat(rows); this.finished this.list.length res.data.total; if (!this.finished) this.page 1; } finally { this.loading false; } } } };这段代码的关键在于reset参数。下拉刷新时reset true上拉加载更多时reset false。finished用来阻止重复请求下一页。loading是并发锁防止用户在网络延迟时连续触发多个请求。如果漏掉loading判断列表在弱网环境下会出现重复数据。3.3 商品详情页的数量选择与单选交互详情页的核心交互是选择商品规格和数量。规格选择使用单选组件数量加减需要处理边界值数量不能小于 1不能大于库存。如果项目里需要让用户选择配送时间或积分兑换档位同样用单选框实现注意 uniapp 的radio-group和原生小程序的checkbox-group的样式差异。数量选择区域代码view classstepper view classbtn clickchangeQty(-1)-/view view classnum{{ qty }}/view view classbtn clickchangeQty(1)/view /viewmethods: { changeQty(delta) { const next this.qty delta; if (next 1 || next this.product.stock) { uni.showToast({ title: next 1 ? 数量至少为1 : 超过库存, icon: none }); return; } this.qty next; } }选择数量后“立即购买”按钮直接把productId和qty传给订单确认页。加入购物车则先写入本地缓存uni.setStorageSync(cart, ...)购物车页面读取缓存渲染。这里建议缓存结构为数组对象[{ productId, qty, selected }]selected字段标识是否勾选结算。3.4 rpx 适配与顶部导航栏高度问题rpx 是微信小程序的响应式单位设计稿宽度按 750rpx 计算。但在 iPhone 刘海屏上顶部导航栏高度会动态变化。如果页面使用自定义导航栏需要获取状态栏高度和菜单按钮位置const systemInfo uni.getSystemInfoSync(); const menuButton uni.getMenuButtonBoundingClientRect(); const navBarHeight (menuButton.top - systemInfo.statusBarHeight) * 2 menuButton.height;navBarHeight是自定义导航栏的总高度。部分项目在安卓和 iOS 上不一致原因是getMenuButtonBoundingClientRect()在安卓某些机型上返回的top值包含状态栏或不包含需要以真机调试为准开发者工具里的模拟数据不可作为最终适配依据。4. 微信小程序登录态、订单状态机与会员积分的闭环4.1 封装 request.js 统一处理 token 与会话过期小程序端所有接口需要携带登录凭证。常见做法是登录后后端返回token前端存到 storage每次请求放在 header 的Authorization字段里。不要在每个页面单独写uni.request应该封装一个公共请求模块。// utils/request.js const BASE_URL https://api.example.com; export function request(options) { return new Promise((resolve, reject) { const token uni.getStorageSync(token); uni.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: token ? Bearer ${token} : }, success: (res) { if (res.statusCode 401) { uni.removeStorageSync(token); uni.navigateTo({ url: /pages/login/login }); reject(res); return; } if (res.statusCode 200 res.statusCode 300) { resolve(res.data); } else { uni.showToast({ title: res.data.message || 请求失败, icon: none }); reject(res); } }, fail: (err) { uni.showToast({ title: 网络异常, icon: none }); reject(err); } }); }); }封装后业务页面调用request({ url: /product/list, method: GET })即可。统一处理 401 的好处是会话过期时自动跳转登录页而不是在每个业务页面重复判断。Bearer前缀与后端解析逻辑要一致PHP 端通过getAuthorizationHeader()获取后去掉Bearer前缀。4.2 uni.login 获取 code 与服务端会话建立微信小程序登录的标准流程是前端调用uni.login获取临时code传给后端后端用code调用微信接口换取openid再生成自定义token返回前端。// pages/login/login.vue uni.login({ provider: weixin, success: async (loginRes) { const res await request({ url: /user/login, method: POST, data: { code: loginRes.code } }); uni.setStorageSync(token, res.data.token); uni.setStorageSync(userInfo, res.data.userInfo); uni.navigateBack(); } });后端 PHP 接收code后需调用https://api.weixin.qq.com/sns/jscode2session换取openid。注意该接口需要appid和secret且secret只能保存在服务端不能出现在小程序代码里。提示开发阶段如果不想真调微信接口可以在后端写一个 mock 分支当appid test时直接根据 code 生成固定 openid。这样便于前端调试页面跳转逻辑。4.3 订单状态机与后端幂等校验订单状态建议用数字表示0 待付款、1 已付款待发货、2 已发货、3 已完成、4 已取消、5 退款中。前端根据状态码渲染不同按钮组后端根据当前状态判断是否允许状态流转。比如只有status 1的订单才能发货只有status 0的订单才能取消。创建订单时后端必须做幂等控制前端提交一个requestId后端在订单表中增加唯一索引重复提交时捕获索引冲突并返回已存在的订单号。否则用户双击下单按钮会产生两笔订单。正确做法是订单号由后端生成生成规则建议日期 随机数例如202506011230001234。4.4 动态字段更新的常见误区在小程序中更新对象属性不能直接写this.setData({ userInfo.nickname: value })。正确的写法是使用方括号或先取再放this.setData({ userInfo.nickname: value });在 uniapp 的 Vue 3 语法里对应的写法是userInfo.value.nickname value。很多从 Vue 2 转过来的开发者在这里踩坑直接改对象属性不触发视图更新需要配合reactive或ref并整体赋值。5. 管理后台的数据看板、Excel 导出与 Charles 联调排错5.1 管理后台数据看板与 echarts 图表管理后台的销售数据看板按日、周、月展示订单量和销售额。实现上直接在 Web 管理端用echarts后端提供一个聚合接口SELECT DATE(create_time) AS day, COUNT(*) AS order_count, SUM(total_fee) AS amount FROM orders WHERE create_time DATE_SUB(NOW(), INTERVAL 30 DAY) GROUP BY DATE(create_time)后端返回 30 天的[{ day, order_count, amount }]前端一次性渲染折线图。注意日期缺失时前端要补 0否则折线图会跳过空日期导致横轴不对齐。前端可以在拿到数据后生成完整日期序列再映射。管理端权限要区分管理员和普通员工员工只能看订单不能看财务汇总这个判断最好在服务端做不要只控制菜单显隐。5.2 在小程序端导出 Excel 的实现方案管理后台导出 Excel 通常用 PHP 的phpoffice/phpspreadsheet在服务端生成文件返回下载链接。但如果需要在微信小程序端直接导出可以借助xlsx库在前端生成文件再用wx.saveFile保存到本地。import * as XLSX from xlsx; function exportExcel(rows, fileName) { const sheet XLSX.utils.json_to_sheet(rows); const workbook XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, sheet, Sheet1); const wbout XLSX.write(workbook, { bookType: xlsx, type: array }); const fs wx.getFileSystemManager(); const filePath ${wx.env.USER_DATA_PATH}/${fileName}.xlsx; fs.writeFileSync(filePath, wbout, binary); wx.shareFileMessage({ filePath: filePath }); }注意wx.env.USER_DATA_PATH是每个小程序独立的本地目录写入后可以分享给好友或转发到聊天窗口。wx.shareFileMessage要求小程序基础库 2.16.1 以上。低版本基础库可以用wx.openDocument直接打开预览但不支持转发文件。5.3 用 Charles 抓取电脑端微信小程序的 HTTPS 请求小程序发起的接口请求无法直接在控制台看到完整的请求头和响应体排查接口问题需要抓包。Charles 抓电脑端微信小程序的步骤电脑安装 Charles开启 SSL Proxying并安装证书。微信开发者工具中配置 HTTP 代理为127.0.0.1:8888。开发者工具右上角“详情 本地设置”勾选“不校验合法域名”。重新编译小程序Charles 中即可看到接口请求列表。注意从 2023 年起微信开发者工具对代理抓包做了调整需在工具设置中把“安全”里的代理设置与 Charles 一致。真机抓包则需要手机与电脑在同一局域网将手机代理指向电脑 IP。抓包主要看三个信息请求 URL 是否正确、header 是否带上了 token、返回 JSON 结构与前端解析代码是否一致。多数联调问题不是后端接口写错而是前端把res.data.list写成了res.list或者后端 key 用了order_no前端读的是orderNo。5.4 保存附件到 wx.env.user_data_path 的适用场景wx.env.USER_DATA_PATH在小程序重启后仍然保留适合保存临时文件和服务端下发的附件。如果管理系统需要展示用户签署的协议或下载电子回执常见做法是后端返回文件流前端写入wx.env.USER_DATA_PATH后用wx.openDocument打开。uni.downloadFile({ url: https://api.example.com/receipt/20250601.pdf, success(res) { const fs wx.getFileSystemManager(); const filePath ${wx.env.USER_DATA_PATH}/receipt.pdf; fs.saveFile({ tempFilePath: res.tempFilePath, filePath: filePath, success() { wx.openDocument({ filePath: filePath, fileType: pdf }); } }); } });filePath不能自定义成带子目录的形式只能直接写在根路径下。部分开发者想写成${wx.env.USER_DATA_PATH}/files/xxx.pdf会报“目录不存在”错误需要先fs.mkdir创建目录。6. 从生成到触发的全流程避坑weixin://dl/businessweixin://dl/business是微信官方的跳转链接协议常见用途是在 App 内或浏览器中拉起小程序指定页面。生成链路与触发条件有多个坑我在联调中按这个顺序排查基本一次过。微信官方后台的“工具 生成 URL Scheme”和“静态 URL Link”是两种入口。URL Scheme 适合 App 内通过location.href跳转URL Link 适合短信、邮件等场景但两者生成的链接都不是weixin://dl/business格式。真实逻辑是拿到https://wxaurl.cn/xxx形式的链接后在微信内打开微信会自动拦截并转成weixin://dl/business?txxx。所以在浏览器或 App 里手动拼weixin://dl/business无效必须由微信客户端解析。如果要在自己的 App 中直接拉起小程序不经过链接跳转应该使用微信开放平台的WXLaunchMiniProgram接口传入userName原始 ID。在 uniapp 中调用uni.navigateToMiniProgram即可uni.navigateToMiniProgram({ appId: wxxxxxxxxxxxxxxxxx, path: pages/index/index?fromapp, success() {}, fail(err) { console.log(err); } });这里的坑有三个。第一appId必须是目标小程序的 AppID不是原始 ID第二path不能以/开头但可以带 query第三正式版小程序必须与 App 关联同一开放平台账号测试时可在开发者工具中勾选“不校验合法域名”来临时跳转。如果生产环境中跳转失败优先排查目标小程序是否发布上线。开发者工具中打开时跳转只对“体验版”和“正式版”生效开发版通常无法被外部唤起。另一个坑是基础库版本navigateToMiniProgram在 iOS 上要求微信版本不低于 7.0.14低于时需要在 fail 回调里引导用户升级微信。关于 URL Link 的有效期默认 30 天可续期。如果业务要求长期有效需要调用生成接口时传入expire_type1表示长期有效。这里注意长期有效的 URL Link 生成数量有限制个人主体小程序无法生成永久链接只能先测试临时链接再根据实际情况调用expire_interval参数做续期。调试建议按三步走先在微信内直接打开生成的 URL Link确认能进入小程序再用uni.navigateToMiniProgram测试 App 拉起最后才接业务参数。参数传递用 query 拼接目标页面在onLoad(options)里读取。如果发现跳转后参数丢失检查链接的path是否带?以及 encodeURIComponent 是否处理了中文。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价