ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

旅游小程序源码四端打通:机构自助发线路与库存扣减实战

旅游小程序源码四端打通:机构自助发线路与库存扣减实战 简介这是一套基于Uniapp与ThinkPHP开发的旅游系统源码面向旅游平台运营商、旅游机构及中小型创业团队解决多角色协同与在线票务管理问题。系统覆盖消费者端与机构工作人员手机端、机构端PC及平台管理端PC支持旅游线路与景点项目发布、成人价与儿童价双票价、在线购票支付、工作人员现场扫码核销以及二级分销推广。压缩包共216个文件约20.84MB以68个vue组件、21个js脚本、13个scss样式、18个json配置及64个png图片资源为主另含搭建教程与前后端源码便于快速部署与二次开发。已有230人学习下载。读者可获得完整的多端项目结构、后台与数据库文件、跨平台前端代码及票务核销与分销实现思路适合作为旅游类小程序与管理系统开发的学习参考。1. 四端打通的旅游小程序源码为什么机构能自己发线路才是分水岭做过旅游类系统的同行大多踩过同一个坑消费者端的小程序页面做得漂漂亮亮线路列表、详情、下单流程都跑通了结果一上线发现运营根本转不动——每上一条新线路都要找开发改数据、改配置机构那边想自己调个价格、换个景点图都得排队等排期。这套「旅游小程序源码 旅游系统」真正要解决的不是前端好不好看而是把消费者端手机端、机构工作人员手机端、机构端PC、平台管理端PC四个角色拆清楚让机构能自助发布旅游线路和景点项目平台只在后台做审核和抽成。它适合正在接旅游 SaaS 外包的团队、想自建线路分销系统的旅行社技术负责人以及手里有一堆景区资源想做平台化整合的创业者。四端里最容易被低估的是机构工作人员那端的手机端——导游和计调在现场改库存、确认订单全靠它。2. 四端角色怎么切从消费者下单到机构发线路的完整链路2.1 先把四个端的职责边界画清楚很多团队一上来就写代码写到一半发现订单状态在四个端里各说各话。我的习惯是先拿一张纸把角色和数据流画出来再动手建表。消费者端手机端只做三件事浏览线路/景点、下单支付、查看订单和行程。机构工作人员手机端是移动办公入口核心是订单核销、库存调整、现场签到权限比机构端窄。机构端PC是机构的主战场发布旅游线路、维护景点项目、设置价格日历、处理退款、看经营报表都在这里。平台管理端PC管的是机构入驻审核、线路内容审核、佣金规则、资金结算和全局数据。这四端共用一套后端但接口权限必须按角色隔离。常见做法是用一套 RBAC 模型角色表里区分consumer、org_staff、org_admin、platform_admin每个接口在网关层校验角色和数据归属机构只能操作自己的线路。这里最容易翻车的是数据归属校验——只校验了角色没校验机构 IDA 机构能改 B 机构的线路这是血泪教训级别的漏洞。2.2 数据库核心表设计线路、景点、价格日历旅游系统的数据模型和普通电商最大的区别在于「线路」是组合商品一条线路包含多个景点项目还有按日期浮动的价格和库存。下面是我一般会用的核心表结构用 MySQL 举例。-- 机构表一个机构对应一个 org_admin 和若干 org_staff CREATE TABLE org ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL COMMENT 机构名称, contact VARCHAR(50) COMMENT 联系人, status TINYINT DEFAULT 0 COMMENT 0待审核 1正常 2禁用, commission_rate DECIMAL(5,4) DEFAULT 0.0500 COMMENT 平台抽成比例, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 景点项目表机构可独立维护被线路引用 CREATE TABLE scenic_spot ( id BIGINT PRIMARY KEY AUTO_INCREMENT, org_id BIGINT NOT NULL COMMENT 归属机构, name VARCHAR(100) NOT NULL, cover VARCHAR(255) COMMENT 封面图, address VARCHAR(255), longitude DECIMAL(10,6), latitude DECIMAL(10,6), status TINYINT DEFAULT 1 COMMENT 1上架 0下架, INDEX idx_org (org_id) ); -- 旅游线路表一条线路挂多个景点 CREATE TABLE tour_route ( id BIGINT PRIMARY KEY AUTO_INCREMENT, org_id BIGINT NOT NULL, title VARCHAR(150) NOT NULL, days TINYINT COMMENT 行程天数, spot_ids VARCHAR(500) COMMENT 关联景点ID逗号分隔或另建关联表, base_price DECIMAL(10,2) COMMENT 基础价, status TINYINT DEFAULT 0 COMMENT 0待审核 1上架 2下架 3驳回, audit_remark VARCHAR(255) COMMENT 审核意见, INDEX idx_org_status (org_id, status) ); -- 价格日历按日期控制价格和库存旅游系统的命脉 CREATE TABLE price_calendar ( id BIGINT PRIMARY KEY AUTO_INCREMENT, route_id BIGINT NOT NULL, travel_date DATE NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, UNIQUE KEY uk_route_date (route_id, travel_date) );逻辑说明org表用commission_rate把平台抽成下沉到机构维度避免写死在代码里。scenic_spot和tour_route都带org_id这是数据隔离的根。price_calendar用(route_id, travel_date)做唯一键防止同一天重复录入价格导致下单时取到脏数据。参数上days用 TINYINT 够用旅游线路极少超过 127 天stock用 INT 而非无符号方便后续做超卖补偿时出现负数告警。2.3 机构发布线路的接口链路机构端 PC 发布一条线路后端要串起「保存线路 → 关联景点 → 生成价格日历 → 提交审核」四步。下面是一个典型的发布接口骨架用 PythonFastAPI 风格示意重点是事务和状态机。app.post(/org/route/publish) def publish_route(payload: RoutePublishIn, userDepends(require_role(org_admin))): # 1. 校验机构归属防止越权 org get_org(user.org_id) if org.status ! 1: raise BizError(机构未通过审核无法发布线路) with db.transaction(): # 2. 落线路主表初始状态待审核 route_id insert_route( org_iduser.org_id, titlepayload.title, dayspayload.days, spot_ids,.join(map(str, payload.spot_ids)), base_pricepayload.base_price, status0 # 0 待审核 ) # 3. 批量写价格日历日期区间由前端传入 for day in payload.calendar: upsert_price_calendar(route_id, day.date, day.price, day.stock) # 4. 写审核流水平台端据此拉取待审列表 insert_audit_log(route_id, org_iduser.org_id, actionsubmit) return {route_id: route_id, status: 0}逻辑说明整个发布动作必须在一个事务里否则线路写进去了、日历没写成功用户下单时查不到价格直接报错。require_role(org_admin)在网关层拦截机构工作人员手机端没有发布权限只能改库存。参数上status0是待审核平台审核通过后才置 1消费者端只查status1的线路。这里有个细节spot_ids用逗号分隔是图省事的做法如果景点需要排序或记录游玩时长建议单独建route_spot关联表别偷懒。3. 消费者端小程序下单与库存扣减别让超卖毁掉口碑3.1 下单流程的状态机设计消费者端小程序下单看着简单实际是四端里最容易出并发问题的地方。订单状态我一般定义成待支付 → 已支付 → 已确认 → 已出行 → 已完成中间穿插已取消、退款中、已退款。状态流转必须由后端控制前端传什么状态都不认。下单时先锁库存再创建订单支付回调里再确认扣减未支付超时释放库存。app.post(/consumer/order/create) def create_order(payload: OrderCreateIn, userDepends(require_role(consumer))): # 1. 行级锁锁定价格日历防止并发超卖 cal db.query_one( SELECT * FROM price_calendar WHERE route_id%s AND travel_date%s FOR UPDATE, (payload.route_id, payload.travel_date) ) if not cal or cal.stock payload.quantity: raise BizError(库存不足) # 2. 扣减库存 db.execute(UPDATE price_calendar SET stockstock-%s WHERE id%s, (payload.quantity, cal.id)) # 3. 创建订单状态待支付带超时时间 order_id insert_order( user_iduser.id, route_idpayload.route_id, travel_datepayload.travel_date, quantitypayload.quantity, amountcal.price * payload.quantity, statusPENDING_PAY, expire_atnow() timedelta(minutes15) ) return {order_id: order_id, expire_in: 900}逻辑说明FOR UPDATE是关键没有它两个用户同时下单会读到相同库存然后双双扣减超卖就发生了。参数上expire_at给 15 分钟是行业常见值太短用户来不及付太长库存被占死。支付回调里要把订单从PENDING_PAY推到PAID同时通知机构端有新订单。超时未支付用一个定时任务扫expire_at now()的订单回滚库存并置为已取消。3.2 小程序端的接口约定与分页消费者端小程序和普通 Web 端不同网络抖动多、用户耐心低接口要尽量合并。线路列表接口一次返回封面、标题、价格区间、已售数量别让前端再发三次请求。分页用游标cursor而不是 offset旅游线路数据量大时 offset 翻页会越来越慢。// 小程序端调用线路列表游标分页 wx.request({ url: ${BASE}/consumer/route/list, data: { cursor: lastId || 0, // 上一页最后一条的 id size: 10, keyword: , // 搜索词可选 spotId: 0 // 按景点筛选可选 }, success(res) { // res.data.list 为线路数组res.data.nextCursor 为下一页游标 this.setData({ list: this.data.list.concat(res.data.list) }); } });逻辑说明游标分页用id cursor ORDER BY id LIMIT size避免深分页性能塌方。参数size建议不超过 20小程序渲染长列表会卡。keyword和spotId是可选筛选后端用动态 SQL 拼接注意参数化查询防注入。4. 平台管理端审核与结算把抽成和资金流算明白4.1 线路审核的状态流转平台管理端PC最核心的两个功能是内容审核和资金结算。机构提交线路后状态是待审核平台运营在后台看到待审列表点通过或驳回并填意见。驳回后机构端能收到通知并修改重提。审核这块逻辑不复杂但要注意审核记录要留痕谁在什么时间审的、意见是什么全部落audit_log表出纠纷时这是后悔药。-- 审核流水表 CREATE TABLE audit_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, biz_type VARCHAR(20) COMMENT route/org/refund, biz_id BIGINT NOT NULL, org_id BIGINT, action VARCHAR(20) COMMENT submit/approve/reject, operator_id BIGINT COMMENT 平台运营ID, remark VARCHAR(255), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_biz (biz_type, biz_id) );逻辑说明biz_type区分审核对象线路、机构入驻、退款都走这张表避免建一堆结构相同的日志表。operator_id记录平台运营责任到人。4.2 佣金结算的两种口径结算是最容易和机构扯皮的地方。常见两种口径按订单支付金额抽成或按实际出行核销金额抽成。前者平台回款快但机构有意见用户没出行也抽后者公平但平台要等出行后才结算。我一般做成可配置机构入驻时约定好org.commission_rate存比例结算时按口径算。def settle_order(order): org get_org(order.org_id) if org.settle_mode ON_PAY: base order.amount else: # ON_TRAVEL按核销金额 base order.verified_amount or 0 commission round(base * float(org.commission_rate), 2) org_income base - commission insert_settlement(order.id, org.id, base, commission, org_income)逻辑说明settle_mode存在机构表里避免硬编码。commission用 Decimal 计算后 round 两位别用 float 直接乘金额精度问题会累积成大坑。结算记录单独成表平台端和机构端都能查对账时以这张表为准。5. 部署与联调避坑四端并行开发最容易踩的五个雷5.1 接口字段命名不统一导致联调返工现象小程序端拿到的字段是routeName机构端 PC 期望的是title同一个接口两套命名联调时互相甩锅。原因四端由不同人开发没有先定接口契约。解决动手前用一份 OpenAPI/Swagger 文档把字段名、类型、必填项定死前后端都以文档为准改字段必须同步改文档并通知四端。5.2 库存扣减没加锁导致超卖现象促销线路放出 100 个库存实际卖出 130 单用户到现场没位置。原因下单接口用了「先查库存再更新」的非原子操作并发下读到相同库存。解决用SELECT ... FOR UPDATE行锁或直接用UPDATE ... SET stockstock-1 WHERE stock1判断影响行数后者更轻量。5.3 机构越权操作别家线路现象A 机构的运营登录后能编辑 B 机构的线路数据串了。原因接口只校验了角色是org_admin没校验线路的org_id是否等于当前用户的org_id。解决所有涉及机构数据的接口查询和更新都必须带org_id条件最好在 ORM 层做全局过滤别指望每个开发都记得写。5.4 价格日历日期时区错乱现象用户选 3 月 1 日出行下单后订单显示 2 月 28 日。原因前端传的是本地时间字符串后端按 UTC 解析跨时区差了一天。解决日期统一用YYYY-MM-DD字符串传输后端按业务时区东八区解析别用时间戳传日期。数据库travel_date用 DATE 类型而非 DATETIME。5.5 支付回调重复触发导致重复扣库存现象用户付一次款订单被确认两次库存扣了两份。原因支付平台回调可能重试接口没做幂等。解决回调入口用订单号做唯一约束或 Redis 分布式锁处理前先查订单状态已是PAID直接返回成功不重复处理。6. 用一套种子数据把四端跑通本地验证的最小闭环6.1 造数据的思路四端联调最痛苦的是没数据——机构端登录进去空空如也消费者端搜不到线路。我的习惯是写一个 seed 脚本一次性造出「一个平台管理员 两个机构 每机构三条线路 未来 30 天价格日历 若干测试订单」这样四端一启动就有东西可点。下面是一个精简版。def seed(): # 平台管理员 create_user(admin, roleplatform_admin, org_idNone) # 两个机构各带一个管理员 org_a create_org(山水旅行社, commission_rate0.06, status1) org_b create_org(远方假期, commission_rate0.05, status1) create_user(org_a_admin, roleorg_admin, org_idorg_a.id) create_user(org_b_admin, roleorg_admin, org_idorg_b.id) # 每机构造 3 条线路 30 天价格日历 for org in (org_a, org_b): for i in range(3): route_id insert_route( org_idorg.id, titlef{org.name}线路{i1}, days3, base_price999 i * 100, status1 ) for d in range(30): upsert_price_calendar( route_id, date.today() timedelta(daysd), price999 i * 100, stock50 ) print(seed done)逻辑说明status1让线路直接上架省去审核步骤方便调试。价格日历造 30 天够联调用stock50方便测并发。参数上机构抽成故意设成不同值验证结算逻辑是否按机构区分。6.2 验证清单四端各点一遍数据造好后按这个顺序验证消费者端小程序搜线路、下单、支付用沙箱、查订单机构工作人员手机端登录看新订单、核销机构端 PC 看经营数据、改库存、看结算平台管理端 PC 看机构列表、审核流水、结算记录。哪一环断了基本能定位到对应模块。6.3 一个我常留的后门习惯开发阶段我会在平台管理端留一个「重置测试数据」的按钮只有platform_admin能点一键清空订单和价格日历重新 seed。上线前务必删掉或加环境判断否则就是灾难。这个习惯帮我省了无数次手动清库的时间但也提醒一句任何后门都要有开关别让它跟着代码上生产。希望帮到你。本文还有配套的精品资源点击获取
返回列表