ARTICLE · 1351480
JAVA同城服务上门做饭系统源码支持小程序
同城私厨 · 上门做饭 · 把餐厅搬回家 JAVA同城服务上门做饭系统源码支持小程序6 大模块,打通分类选品、套餐下单、师傅上门与履约售后。 SpringBoot · MyBatis-Plus · MySQL · uni-app · Vue + ElementUI |
产品拆解 / 技术实现 / 合规边界 · 建议阅读时间 12 分钟
如果你正在寻找一套覆盖精准分类、套餐管理、限时秒杀、师傅入驻、地址管理、订单管理的 JAVA同城服务上门做饭系统源码,这篇文章会把产品逻辑、技术架构、数据库设计和核心代码一次讲清。
这套系统支持小程序:用户端用 uni-app(Vue 语法)编译到微信小程序,管理后台用 Vue + ElementUI,服务端用 SpringBoot + MyBatis-Plus + MySQL。它适合本地私厨团队、家政与餐饮服务商、同城生活平台搭建上门做饭业务,也适合软件团队做本地生活服务的二次开发。
先给结论:上门做饭不是「把菜单搬到线上」,而是把选品—下单—派单—上门—验收—售后串成一条可履约的服务链。用户买的不是一个套餐,而是「有人按约定时间到我家,把一桌菜做好」的确定性。
合规提示:上门服务涉及家庭住址、门禁、联系方式等敏感个人信息,应最小化收集、加密存储、仅对已接单师傅展示;师傅需实名与健康证审核,平台应明确服务范围、爽约与退款规则;食品经营、餐饮服务需按当地要求取得相应资质,不得宣传「绝对安全」「包满意」等无法验证的承诺。
6 功能模块 | 3 端协同 | 小程序 用户入口 | 1 套源码 |
01 上门做饭,卖的是「确定的上门履约」
外卖解决「送到」,上门做饭解决「到家里做」。这两件事的履约结构完全不同:外卖的核心是骑手与出餐,上门做饭的核心是师傅档期、食材准备、上门地址和现场服务。任何一环没接住,用户得到的都是一次糟糕体验。
这套 JAVA同城服务上门做饭系统源码把业务拆成三条主线:商品线(分类—套餐—秒杀)、供给线(师傅入驻—审核—接单)、履约线(地址—下单—派单—完成)。前端负责展示与下单,服务端负责库存、档期、归属和状态,三者不能混在一起。
02 六大功能模块:从选品到上门履约
精准分类、套餐管理、限时秒杀、师傅入驻、地址管理、订单管理,这 6 个模块不是功能列表,而是围绕「找到合适的菜—约到合适的人—送到对的地址—完成这一次服务」组织的链路。对一套 支持小程序的上门做饭系统来说,前端可以轻,库存、档期、地址归属与订单状态必须由服务端统一管理。
01 精准分类 按菜系、场景、人数快速找到套餐。 | 02 套餐管理 菜品、人数、价格、上下架统一维护。 |
03 限时秒杀 限量低价拉新,库存与限购可控。 | 04 师傅入驻 实名、健康证、菜系审核后接单。 |
05 地址管理 常用地址、门禁与备注,接单后可见。 | 06 订单管理 下单、派单、上门、验收与售后全流程。 |
02.1 精准分类:让用户三步内找到想吃的
精准分类不是把菜单做成九宫格,而是建立「菜系—场景—人数—价位」的可组合分类。用户输入是筛选条件,系统按分类树与标签召回套餐,输出是排好序的列表。分类要支持多级与排序权重,后台可随时调整,前端缓存分类树避免每次全量请求。
02.2 套餐管理:把一桌菜做成可售卖的标准品
套餐管理把「一桌菜」拆成可维护的结构:套餐标题、适用人数、菜品清单、服务内容、原价与售价、库存与上下架状态。输入是后台编辑的套餐数据,状态在「草稿—上架—下架」之间流转,输出是用户端可下单的商品。套餐一旦被订单引用,价格与菜品要做快照,避免后台改价影响历史订单。

JAVA上门做饭系统实拍 02|套餐详情、精选套餐分类与师傅详情。截图数据为演示数据。
02.3 限时秒杀:用有限库存换新增,而不是换投诉
限时秒杀解决拉新与转化:限定时间段、限定库存、限定每人购买数量。输入是活动配置(套餐、秒杀价、库存、限购、起止时间),状态从「未开始—进行中—已结束—售罄」流转,输出是秒杀订单。库存必须在服务端原子扣减,并记录每人购买次数,否则前端倒计时结束前就会被脚本刷空。
02.4 师傅入驻:供给质量决定复购
师傅入驻包含注册、资料提交、平台审核、正式接单四步。师傅提交实名信息、健康证、擅长菜系、服务区域与可接单时段;平台审核通过后才进入可派单池。输入是申请资料,状态是「待审核—通过—驳回—停用」,输出是可接单的师傅档案。资质到期要能自动预警,不能让过期健康证继续接单。

JAVA上门做饭系统实拍 03|专业私厨列表、首页入口与师傅入驻。截图数据为演示数据。
02.5 地址管理:家庭住址只给该看的人看
地址管理支持常用地址、默认地址、门禁与备注。用户下单时选择地址,订单生成时做地址快照;师傅只有在接单后才能看到完整门牌与联系方式,未接单或订单结束后自动收回。输入是地址簿,状态是「启用—停用」,输出是订单里的服务地址。地址不能明文下发到列表接口,更不能被其他用户查询。
02.6 订单管理:确认、派单、上门、验收、售后
订单管理是整条链路的收口:下单时校验套餐状态、地址归属和档期;支付后进入待派单;派单给师傅后进入待上门;完成后进入待验收;验收后结算,异常走退款或售后。输入是用户下单与支付,状态在「待支付—待派单—待上门—服务中—待验收—已完成—已取消」之间流转,输出是订单记录与结算凭据。

JAVA上门做饭系统实拍 01|订单详情、个人中心与确认订单。截图数据为演示数据。
03 技术架构:三端分工与边界
这套 JAVA同城服务上门做饭系统源码采用经典分层:小程序负责浏览、下单、查看进度;管理后台负责套餐、秒杀、师傅审核与派单;SpringBoot 服务端负责库存、档期、地址可见性与订单状态。
必须放在服务端的:秒杀库存、每人限购、师傅档期、地址可见性、订单状态、退款金额。可以放前端的:分类展示、套餐图文、倒计时、表单校验。前端可以「看起来快」,但不能「决定还有没有库存」。
04 数据库设计:六张核心表
上门做饭的数据关系可以归纳成四组:分类与套餐、秒杀库存、师傅档案、地址与订单。下面保留分类、套餐、秒杀活动、师傅、地址、订单六张核心表,省略了菜品明细、结算、评价等扩展表。以下为示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等。
MYSQL · 分类、套餐、秒杀、师傅、地址、订单
-- 1. 分类:多级分类树,sort 控制展示顺序CREATE TABLE `category` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `parent_id` BIGINT UNSIGNED NOT NULL DEFAULT 0, `name` VARCHAR(32) NOT NULL, `sort` INT NOT NULL DEFAULT 0, `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1启用 0停用', PRIMARY KEY (`id`), KEY `idx_parent` (`parent_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分类';-- 2. 套餐:价格、人数、菜品、上架状态CREATE TABLE `meal_package` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `category_id` BIGINT UNSIGNED NOT NULL, `title` VARCHAR(64) NOT NULL, `price` DECIMAL(10,2) NOT NULL, `original_price` DECIMAL(10,2) DEFAULT NULL, `servings` TINYINT NOT NULL COMMENT '适用人数', `dish_count` TINYINT NOT NULL COMMENT '菜品数量', `cover` VARCHAR(255) DEFAULT NULL, `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0草稿 1上架 2下架', `create_time` DATETIME NOT NULL, PRIMARY KEY (`id`), KEY `idx_category` (`category_id`), KEY `idx_status` (`status`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='套餐';-- 3. 秒杀活动:库存、限购、时间窗CREATE TABLE `seckill_activity` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `package_id` BIGINT UNSIGNED NOT NULL, `seckill_price` DECIMAL(10,2) NOT NULL, `stock` INT NOT NULL COMMENT '剩余库存', `limit_per_user` TINYINT NOT NULL DEFAULT 1, `start_time` DATETIME NOT NULL, `end_time` DATETIME NOT NULL, `version` INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0未开始 1进行中 2已结束', PRIMARY KEY (`id`), KEY `idx_package` (`package_id`), KEY `idx_time` (`start_time`, `end_time`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='秒杀活动';-- 4. 师傅:实名、健康证、菜系、接单状态CREATE TABLE `chef` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` BIGINT UNSIGNED NOT NULL, `real_name` VARCHAR(32) NOT NULL, `phone` VARCHAR(20) DEFAULT NULL COMMENT '脱敏存储', `cuisine` VARCHAR(64) DEFAULT NULL COMMENT '擅长菜系', `experience_years` TINYINT DEFAULT NULL, `health_cert_no` VARCHAR(64) DEFAULT NULL COMMENT '健康证号(加密)', `health_expire` DATE DEFAULT NULL, `audit_status` TINYINT NOT NULL DEFAULT 0 COMMENT '0待审 1通过 2驳回 3停用', `accept_status` TINYINT NOT NULL DEFAULT 0 COMMENT '0休息 1接单中', PRIMARY KEY (`id`), UNIQUE KEY `uk_user` (`user_id`), KEY `idx_audit` (`audit_status`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='师傅';-- 5. 地址:常用地址、默认标记、门禁备注CREATE TABLE `user_address` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` BIGINT UNSIGNED NOT NULL, `contact_name` VARCHAR(32) NOT NULL, `phone` VARCHAR(20) NOT NULL COMMENT '脱敏存储', `city` VARCHAR(64) NOT NULL, `detail` VARCHAR(255) NOT NULL COMMENT '门牌详情,仅接单可见', `is_default` TINYINT NOT NULL DEFAULT 0, `status` TINYINT NOT NULL DEFAULT 1, `create_time` DATETIME NOT NULL, PRIMARY KEY (`id`), KEY `idx_user` (`user_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户地址';-- 6. 订单:套餐快照、地址快照、档期与状态CREATE TABLE `service_order` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `order_no` VARCHAR(32) NOT NULL, `user_id` BIGINT UNSIGNED NOT NULL, `package_id` BIGINT UNSIGNED NOT NULL, `package_snapshot` TEXT NOT NULL COMMENT '下单时的套餐快照', `chef_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '派单师傅', `address_id` BIGINT UNSIGNED NOT NULL, `address_snapshot` TEXT NOT NULL COMMENT '下单时的地址快照', `service_date` DATE NOT NULL, `service_time` VARCHAR(16) NOT NULL, `amount` DECIMAL(10,2) NOT NULL, `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0待支付 1待派单 2待上门 3服务中 4待验收 5已完成 6已取消', `pay_status` TINYINT NOT NULL DEFAULT 0 COMMENT '0未支付 1已支付 2已退款', `idempotent_key` VARCHAR(64) NOT NULL COMMENT '客户端下单幂等键', `create_time` DATETIME NOT NULL, `update_time` DATETIME NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`), UNIQUE KEY `uk_idem` (`idempotent_key`), KEY `idx_user` (`user_id`), KEY `idx_chef` (`chef_id`), KEY `idx_status` (`status`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务订单';05 核心业务代码:事务、并发、幂等
上门做饭最容易出问题的是「秒杀下单」和「重复提交」。下面三段代码分别处理下单事务、库存原子扣减与支付回调幂等。
先看核心事务:校验套餐与地址后写订单,所有校验和写库在同一事务里,避免出现「订单生成了但套餐已下架」。
JAVA · 创建服务订单(事务 + 快照)
/** * 创建订单:校验套餐、地址归属,写套餐与地址快照。 * 示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等。 */@Servicepublic class OrderService { @Resource private MealPackageMapper packageMapper; @Resource private UserAddressMapper addressMapper; @Resource private ServiceOrderMapper orderMapper; @Transactional(rollbackFor = Exception.class) public ServiceOrder createOrder(CreateOrderDTO dto) { MealPackage pkg = packageMapper.selectById(dto.getPackageId()); if (pkg == null || pkg.getStatus() != PackageStatus.ON_SHELF) { throw new BizException("套餐已下架,请选择其他套餐"); } UserAddress address = addressMapper.selectById(dto.getAddressId()); if (address == null || !address.getUserId().equals(dto.getUserId())) { throw new BizException("地址不存在或不属于当前用户"); } ServiceOrder order = new ServiceOrder(); order.setOrderNo(IdGen.nextNo("SO")); order.setUserId(dto.getUserId()); order.setPackageId(pkg.getId()); order.setPackageSnapshot(JsonUtil.toJson(pkg)); // 价格/菜品快照 order.setAddressId(address.getId()); order.setAddressSnapshot(JsonUtil.toJson(address)); order.setServiceDate(dto.getServiceDate()); order.setServiceTime(dto.getServiceTime()); order.setAmount(pkg.getPrice()); order.setStatus(OrderStatus.PENDING_PAY.getCode()); order.setIdempotentKey(dto.getIdempotentKey()); orderMapper.insert(order); // uk_idem 防重复 return order; }}秒杀是并发扣减的典型场景。库存必须在数据库里用条件更新扣减,同时用「活动 + 用户」唯一键限制每人购买次数;扣减失败就返回售罄,不能先查后改。
JAVA · 秒杀库存原子扣减
/** * 秒杀扣库存:WHERE stock >= 1 的原子更新,防超卖。 * uidx_seckill_user 唯一索引限制每人购买次数。 * 示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等。 */@Mapperpublic interface SeckillMapper { @Update("UPDATE seckill_activity SET stock = stock - 1, version = version + 1 " + "WHERE id = #{activityId} AND stock >= 1 " + "AND status = 1 AND NOW() BETWEEN start_time AND end_time") int deductStock(@Param("activityId") Long activityId);}@Servicepublic class SeckillService { @Resource private SeckillMapper seckillMapper; @Resource private SeckillOrderMapper seckillOrderMapper; @Transactional(rollbackFor = Exception.class) public void buy(Long activityId, Long userId) { int rows = seckillMapper.deductStock(activityId); if (rows != 1) { throw new BizException("手慢了,秒杀已结束或库存不足"); } SeckillOrder record = new SeckillOrder(); record.setActivityId(activityId); record.setUserId(userId); seckillOrderMapper.insert(record); // 唯一键防重复购买 }}支付回调可能重复到达,必须做幂等:用订单号或支付流水号唯一索引兜底,重复通知只更新一次状态,不重复派单、不重复记账。
JAVA · 支付回调幂等处理
/** * 支付回调:按 order_no 条件更新,已支付则直接返回。 * 示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等。 */@Servicepublic class PayCallbackService { @Resource private ServiceOrderMapper orderMapper; @Transactional(rollbackFor = Exception.class) public void onPaid(String orderNo, String tradeNo) { int rows = orderMapper.markPaidIfUnpaid(orderNo, tradeNo); if (rows == 0) { return; // 已处理或订单不存在,幂等返回 }// 事务内推进到待派单,后续由派单服务消费 orderMapper.updateStatus(orderNo, OrderStatus.PENDING_DISPATCH.getCode()); }}06 权限与多端请求:地址与订单归属
家庭地址是最敏感的数据。每次查看订单或地址,都要在服务端确认:订单属于本人、师傅已接单、或管理员。下面用服务层校验,师傅只能看到自己接单的订单地址。
JAVA · 订单地址查看权限
/** * 订单地址权限:本人、接单师傅、管理员可见完整地址。 * 示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等。 */@Servicepublic class OrderPermissionService { @Resource private ServiceOrderMapper orderMapper; public ServiceOrder assertCanView(Long orderId, LoginUser current) { ServiceOrder order = orderMapper.selectById(orderId); if (order == null) { throw new BizException(404, "订单不存在"); } if (current.isAdmin() || order.getUserId().equals(current.getUserId())) { return order; } if (current.isChef() && current.getUserId().equals(order.getChefId())) { return order; } throw new BizException(403, "无权查看该订单"); }}用户端是 uni-app(Vue 语法),统一请求封装把 token、401 跳登录和错误提示收口。地址是否完整、订单能否操作,都以服务端返回为准。以下为示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等。
JAVASCRIPT · uni-app 统一请求封装
// utils/request.js —— 用户端 uni-app(Vue 语法)统一请求封装// 小程序通过 uni.request 访问 SpringBoot 接口,自动携带 tokenconst BASE_URL = 'https://api.example.com'; // 生产替换为 HTTPS 正式域名export function request(options) { return new Promise((resolve, reject) => { uni.request({ url: BASE_URL + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + uni.getStorageSync('token'), }, success: (res) => { if (res.statusCode === 200 && res.data.code === 0) { resolve(res.data.data); } else if (res.statusCode === 401) { uni.navigateTo({ url: '/pages/login/login' }); reject(new Error('登录已过期')); } else { uni.showToast({ title: res.data.msg || '请求失败', icon: 'none' }); reject(new Error(res.data.msg)); } }, fail: reject, }); });}07 管理后台:师傅入驻审核表格
管理后台用 Vue + ElementUI 构建,师傅入驻审核是供给质量的第一道关。列表展示实名、菜系、健康证状态,审核人员直接通过或驳回;分页、状态标签、操作按钮都放在表格里。以下为示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等。
VUE · ElementUI 师傅入驻审核表格
<template> <div class="chef-list"> <el-table :data="list" v-loading="loading" border stripe> <el-table-column prop="chefId" label="师傅ID" width="90" /> <el-table-column prop="realName" label="姓名" width="100" /> <el-table-column prop="cuisine" label="擅长菜系" min-width="120" /> <el-table-column prop="experienceYears" label="从业年限" width="100"> <template #default="{ row }">{{ row.experienceYears }} 年</template> </el-table-column> <el-table-column label="健康证" width="120"> <template #default="{ row }"> <el-tag :type="row.healthValid ? 'success' : 'danger'" size="small"> {{ row.healthValid ? '有效' : '过期/缺失' }} </el-tag> </template> </el-table-column> <el-table-column label="状态" width="100"> <template #default="{ row }"> <el-tag :type="statusType(row.auditStatus)" size="small">{{ statusText(row.auditStatus) }}</el-tag> </template> </el-table-column> <el-table-column label="操作" width="180" fixed="right"> <template #default="{ row }"> <el-button size="mini" type="success" @click="audit(row, 1)">通过</el-button> <el-button size="mini" type="danger" @click="audit(row, 2)">驳回</el-button> </template> </el-table-column> </el-table> <el-pagination style="margin-top:16px;text-align:right;" :current-page="page" :page-size="size" :total="total" layout="total, prev, pager, next" @current-change="load" /> </div></template><script>export default { data() { return { list: [], page: 1, size: 10, total: 0, loading: false }; }, methods: { async load() { this.loading = true; try { const { data } = await this.$http.get('/api/admin/chef/page', { params: { page: this.page, size: this.size }, }); this.list = data.records; this.total = data.total; } finally { this.loading = false; } }, statusText(s) { return ['待审核', '已通过', '已驳回', '已停用'][s] || '未知'; }, statusType(s) { return ['warning', 'success', 'danger', 'info'][s] || 'info'; }, audit(row, status) { this.$http.post('/api/admin/chef/audit', { id: row.chefId, status }).then(() => { this.$message.success('操作成功'); this.load(); }); }, }, mounted() { this.load(); },};</script>08 合规与上线检查:上门服务的安全底线
上门服务进入的是用户家里,安全与隐私要求比普通外卖更高。上线前建议逐项核对:
另外,上门服务要提前设计「异常路径」:师傅临时取消、用户改约、地址错误、现场无法进入。系统不能只有「正常完成」一种状态,否则所有异常都会落到客服手工处理。
09 这套源码适合谁
本地私厨团队 把师傅档期、套餐和上门订单搬到小程序。 | 家政 / 餐饮服务商 增加上门做饭品类,复用现有师傅与客服体系。 |
同城生活平台 用一套系统做区域化私厨频道,叠加本地运营。 | Java / Vue 学习者 学习库存、秒杀、事务、幂等与多端联调。 |
10 常见问题
Q1:这套系统支持小程序吗?
支持。用户端用 uni-app(Vue 语法)开发,可编译到微信小程序;管理后台独立用 Vue + ElementUI,服务端用 SpringBoot + MyBatis-Plus + MySQL。
Q2:秒杀会不会超卖?
不会用「先查再改」的写法。库存用条件更新原子扣减,配合「活动 + 用户」唯一键限制重复购买,扣减失败直接返回售罄。
Q3:师傅能看到用户完整地址吗?
只有在接单后、订单进行中才能看到完整地址与联系信息;未接单、订单结束或取消后自动收回,后台可配置查看窗口。
Q4:能直接上线商用吗?
作为项目起点可以,但上线前需补充参数校验、权限、日志、监控、限流、支付验签,以及师傅资质审核、食品合规、隐私授权、退款规则与售后流程。
结语:把「一桌菜」变成可履约的服务
这套 JAVA同城服务上门做饭系统源码用精准分类、套餐管理、限时秒杀、师傅入驻、地址管理、订单管理六条线,把选品、供给与履约串成闭环:套餐可维护、秒杀可控制、师傅可审核、地址可保护、订单可追踪。它 支持小程序,适合把本地私厨服务真正搬到线上。
如果你正在做本地私厨、家政上门或同城生活服务,或想基于 SpringBoot + uni-app + Vue 技术栈做一套可二开的上门做饭系统,欢迎找我领取演示资料和部署说明。















































































