SOFTWARE / 源码交付 · 支持小程序
JAVA 无人自助拼豆工坊系统
SpringBoot + uni-app + Vue 三端源码:材料与工具按 SKU 卖,图纸按像素算,桌位按时段排
拼豆这门生意,正在从「周末带孩子做手工」变成城市里的轻娱乐:约一张桌子坐两小时,照着图纸一颗颗往拼板上码,熨完带走。它有三个特点同时存在——耗材按颗算、工具可卖可借、到店要占座。
三者混在一起,靠人盯很容易乱:色号报错、拼板借了没还、两张桌子被同一时段约了两次。这套系统的思路不是「把店里的事都搬上手机」,而是把三件事拆开:材料按 SKU 卖、图纸按像素算、桌位按时段排。地址、订单、门店、动态,都是围绕这三件事的配套。
下面按顾客从进店到复购的链路,逐个说清每个模块的输入、状态变化和输出;技术部分给了 7 段可以直接对照的代码。
OVERVIEW / 功能总览
六个模块,各自解决一件具体的事
先给结论:拼豆店的系统难点不在「功能多」,而在「三种计量单位别混」——材料按颗、桌位按半小时、图纸按像素格。下面每个模块,都对着一件会被算错的事。
地址管理
配送要地址、自提要门店,解决「地址填错、默认地址打架」。
拼豆材料
色号、克重、颗数写进规格,解决「材料按什么卖」。
拼豆工具
镊子、拼板、熨斗可卖可借,解决「工具算耗材还是资产」。
拼豆成品
作品上架就是商品,解决「手工品怎么定价、作者怎么记」。
创意拼豆
AI 出图纸 + 图库,解决「顾客不会画、店员来不及画」。
到店预约
桌位 × 日期 × 时段,解决「无人店怎么排期、怎么防重复」。
01地址管理:把「送到哪」写成可校验的字段
输入是收货人、手机号、区域、详细门牌。这里有个常被忽略的点:省市不是让用户输入的文本,而是选择器产出的编码,否则「南京」「南京市」「江苏南京」会变成三个不同的配送区域,运费规则和门店归属全乱。
状态上有两个约束:一是默认地址唯一,新增时若勾了「设为默认」,要在同一事务里把旧默认清掉;二是地址条数上限(通常 20 条),防止脚本刷库。输出是下单页可选的地址列表,以及每条地址对应的配送方式——同城配送、到店自提、快递,由门店的服务范围决定。
手机号和详细地址在库里都是密文,列表页只解密到「省市区 + 门牌号前两位」,详情页才解全;后台客服看到的是 138****0000。这一条不是洁癖:地址表是最容易被拖库的一张表。
02拼豆材料:色号、克重、颗数,三件不同的事
输入是材料档案:色号(H23、R13 这类)、单颗尺寸、包装规格(100 颗/袋、500 颗/盒)、适用拼板。同一色号在不同品牌下色差明显,所以色号必须和品牌绑在一起,不能只存一个「红色」。
状态是库存与规格的组合:材料按袋卖、按盒卖、也按「一整套 24 色」卖,三种 SKU 共享同一个色号池,但库存各自独立。补货时按色号统计销量,才知道哪种颜色永远不够、哪种永远压仓。输出是下单时可选的材料清单,以及给图纸用的「色号 → 颗数」换算表。
截图里的商城首页把这层关系摆在最上面:材料、工具、珠子、成品、地址管理五个入口并排,用户第一眼就知道这里卖的不是一种东西,购物车再按商品类型分组结算。
【拼豆工坊】实拍 01|商城五类入口、我的页订单与作品、购物车按规格结算;截图数据为演示数据
03拼豆工具:哪些是耗材,哪些是要还的资产
输入是工具档案和它的「归属类型」:售卖型(镊子、备用拼板、熨烫纸,卖出即消耗)与借用型(大号拼板、熨斗、灯箱,到店免费用、离店归还)。同一种商品在不同门店可以是不同类型,由门店自己配置,不写死在代码里。
状态因此分成两条线:售卖型走「库存 -1」,借用型走「借出 → 归还 / 逾期」的状态机,且必须挂在预约单上——谁在哪个时段借了哪块拼板。输出是结算页的工具清单,以及闭店时的一张「未归还工具」列表,无人店最怕的就是这张表对不上。
这条线还顺手解决了另一个问题:工具丢失的赔付。借用型工具在预约时冻结一笔小额押金,归还即释放,逾期按小时扣——金额和规则写在门店配置里,避免店员口头承诺。
04拼豆成品:作品上架,作者和工费都要能追
输入是一张成品档案:作品图、尺寸、颗数、耗时、作者。作者可能是店员,也可能是顾客——顾客做的成品放在店里寄卖,是很自然的复购设计。
状态是「制作中 → 已上架 → 已售出 / 已下架」,再加一个作者归属字段:自营、寄卖、顾客带走。寄卖的成品售出后要生成一笔作者分成,这条记录必须和订单绑定,否则月底对账只能靠回忆。输出是成品商详页、作者主页的作品墙,以及一张按作者汇总的结算表。
成品和材料共用商品表,但多两个字段:是否唯一(unique_flag)和作者 ID(author_id)。手工品大多是孤品,下单即锁定,不能像材料那样「库存 100 件随便卖」。
05创意拼豆:把一张图变成能照着摆的图纸
输入是一张图片加四组参数:拼板规格(52×52 / 78×78 / 104×104)、风格化(动漫 / Q 版 / 甜美 / 森系)、细节程度(弱 / 适中 / 简约)、背景处理(保留原色 / 白底 / 去背景)。参数不是装饰,它直接决定成品要买多少颗。
状态是一条异步任务链:排队 → 出图 → 完成 / 失败。出图要几秒到几十秒,接口不能同步等,所以提交后立刻返回图纸 ID,前端轮询或订阅消息。输出是三个东西:带坐标轴的像素图、色卡用量清单(H23 436 颗、R13 317 颗)、按用量生成的「一键加购」材料包。
这一步是整套系统里最能带来转化的地方:图纸即购物车。用户看到「共 2028 颗、24 色」,下一步就是把这 24 个色号一次加进购物车,转化路径极短。
【拼豆工坊】实拍 03|图纸参数与拼板规格、图纸图库分类、详情页色号用量;截图数据为演示数据
06到店预约:无人值守的桌位要靠约束,不靠自觉
输入是门店、桌位、日期、时段(半小时一格,一天 16 格)、套餐(1 小时 / 3 小时 / 8 小时)。套餐本质上是「连续占用几格」,所以下单时要把它展开成具体的格子。状态只有三种:空闲、已约、已核销;到店扫码核销是唯一入口,避免店员手动改状态。
输出是可预约时段列表和预约单。这里的关键不是界面,而是约束:(desk_id, book_date, slot) 建唯一索引,两个请求同时抢同一格,数据库只会让一个成功——这是无人店最不能出错的地方。
门店主页那屏还挂了三个入口:连 WiFi、到店指引、联系客服。无人店没有店员可问,这三件事必须在到店前就能自助解决,否则顾客站在门口的那三分钟就是差评的来源。
【拼豆工坊】实拍 02|社区动态与三个创作入口、预约桌位与套餐时段、门店主页与座位状态;截图数据为演示数据
ARCHITECTURE / 三端分工
什么必须放在服务端,什么可以交给前端
判断标准只有一句话:凡是涉及钱、库存、名额、状态的判断,一律在服务端;凡是纯展示的排序、筛选、格式化,放前端。
端
负责什么
不该做什么
用户端
uni-app
商城浏览、下单、预约、图纸参数提交、订单与作品展示;本地缓存购物车与最近浏览。
不自己算价格、不自己判断库存和余位,不把 token 明文缓存。
管理后台
Vue + ElementUI
商品与材料档案、桌位与时段配置、订单核销、图纸审核、数据看板。
不绕过服务端直接改库存和状态,不把权限判断只写在前端路由。
服务端
SpringBoot
价格与优惠计算、库存扣减、时段占用、幂等去重、权限与数据范围、出图调度。
不把出图这种长任务压在请求线程里,不在事务里调第三方接口。
数据库用一张商品表承载材料、工具、成品三类,靠 category 区分;差异字段(色号、尺寸、作者)进 spec_json,不做三张结构相似的宽表——小店最怕的是加一个品类就要改一次表结构。
CODE / 数据库与核心代码
七段代码,对应七个真会被算错的地方
下面七段构成一套最小可跑的骨架,字段与表结构严格对得上。每段都是先说清它解决什么问题,再给代码,最后注明生产环境还要补什么。代码是按手机端 50 列宽排的,直接照着敲不会被折行打断。
01表结构:五张表撑起一条链路
用户、地址、商品、图纸、预约,五张表。设计上有三个刻意选择:
一是材料、工具、成品共用 t_goods,靠 category 分,差异字段进 spec_json——三个品类的下单、库存、退款流程是一样的,拆成三张表只会让改一次活动要动三处代码。
二是预约表上直接建 (desk_id, book_date, slot) 唯一索引,把「别重复占座」这条业务规则交给数据库执行,而不是交给代码里的 if。
三是图纸的色号用量存 MEDIUMTEXT,不拆成「图纸-颜色」明细表:它永远跟着图纸一起读,从不单独查询,拆表只是多一次 join。
SQL
— 0. 用户:出图配额放在用户维度,条件更新扣减
CREATE TABLE t_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
nickname VARCHAR(32) NOT NULL,
open_id VARCHAR(64) NOT NULL,
quota INT NOT NULL DEFAULT 5, — 每日次数
UNIQUE KEY uk_open (open_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
— 1. 收货地址:手机号与详细地址都存密文
CREATE TABLE t_address (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
receiver VARCHAR(32) NOT NULL,
phone VARCHAR(128) NOT NULL, — AES 密文
region_code VARCHAR(12) NOT NULL, — 区划码
detail VARCHAR(255) NOT NULL, — AES 密文
is_default TINYINT NOT NULL DEFAULT 0,
version INT NOT NULL DEFAULT 0,
KEY idx_user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
— 2. 商品:拼豆材料 / 工具 / 成品共用一张表
CREATE TABLE t_goods (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
shop_id BIGINT NOT NULL,
category TINYINT NOT NULL, — 1材料 2工具 3成品
title VARCHAR(64) NOT NULL,
cover VARCHAR(255) NOT NULL,
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL DEFAULT 0,
spec_json VARCHAR(512) DEFAULT NULL, — 规格
status TINYINT NOT NULL DEFAULT 1, — 1售0下
version INT NOT NULL DEFAULT 0,
author_id BIGINT DEFAULT NULL, — 成品作者
unique_flag TINYINT DEFAULT 0, — 孤品锁单
KEY idx_cat (shop_id, category, status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
— 3. 图纸:像素参数 + 色卡用量清单
CREATE TABLE t_pattern (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
cols INT NOT NULL,
rows INT NOT NULL,
color_num INT NOT NULL,
bead_num INT NOT NULL,
palette_json MEDIUMTEXT NOT NULL, — 色号用量
status TINYINT NOT NULL DEFAULT 0, — 0排队
KEY idx_user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
— 4. 预约:桌位 + 日期 + 时段唯一,天然防重复占座
CREATE TABLE t_booking (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
shop_id BIGINT NOT NULL,
desk_id BIGINT NOT NULL,
book_date DATE NOT NULL,
slot TINYINT NOT NULL, — 0..15,半小时一格
user_id BIGINT NOT NULL,
status TINYINT NOT NULL DEFAULT 1,
request_no VARCHAR(64) NOT NULL,
UNIQUE KEY uk_slot (desk_id, book_date, slot),
UNIQUE KEY uk_req (request_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;金额一律 DECIMAL,不用 float;预约表的唯一索引不要省,它是防重复占座的最后一道闸。
02地址:落库即密文,默认只能有一个
这段解决两个问题:「用户有两条默认地址,下单时随机命中一条」和「地址表被拖库,几万条真实门牌直接外泄」。
做法是把「清旧默认」和「置新默认」放进同一个事务,两步要么都成要么都不成;markDefault 的 where 里带 user_id,是为了防越权——不传这个条件,改一个 id 就能把别人的地址设成默认。返回 0 行说明这条地址不存在或不属于当前用户,直接抛异常,不要静默放过。
JAVA
@Transactional(rollbackFor = Exception.class)
public void save(Long uid, AddressDTO dto) {
Address a = new Address();
a.setUserId(uid);
a.setReceiver(dto.getReceiver());
// 手机号、详细地址落库前统一加密
a.setPhone(aes.encrypt(dto.getPhone()));
a.setDetail(aes.encrypt(dto.getDetail()));
a.setRegionCode(dto.getRegionCode());
a.setIsDefault(0);
addressMapper.insert(a);
if (Boolean.TRUE.equals(dto.getIsDefault())) {
setDefault(uid, a.getId());
}
}
@Transactional(rollbackFor = Exception.class)
public void setDefault(Long uid, Long id) {
// 先清旧默认,再置新默认,同一事务
addressMapper.clearDefault(uid);
int ok = addressMapper.markDefault(uid, id);
if (ok == 0) {
throw new BizException(“地址不存在”);
}
}
/*
clearDefault: UPDATE t_address SET is_default=0
WHERE user_id=? AND is_default=1
markDefault : UPDATE t_address
SET is_default=1, version=version+1
WHERE id=? AND user_id=?
*/
示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;密钥交给 KMS 或配置中心,不要写在代码里,也不要把密文和密钥放在同一台机器上。
03库存:把判断写进 where,扣不到再查原因
这段解决「超卖」和「扣失败了,但不知道是没货还是已下架」。
关键是不要先 select 再 update:查的时候库存还在,写的时候已经被别人抢走了,中间那段窗口就是超卖的来源。把 status=1 和 stock >= #{qty} 一起塞进 where,影响行数为 1 才算扣成功;为 0 时再查一次,把「已下架」和「库存不足」区分开给用户看——这两种情况用户的下一步动作完全不同,提示含糊等于没有提示。
JAVA
@Transactional(rollbackFor = Exception.class)
public void deduct(Long gid, int qty) {
if (qty <= 0) {
throw new BizException(“数量不合法”);
}
// 库存够才扣,判断与扣减在同一条 SQL
int ok = goodsMapper.deductStock(gid, qty);
if (ok == 0) {
// 再查一次,区分已下架和库存不足
Goods g = goodsMapper.selectById(gid);
if (g == null || g.getStatus() == 0) {
throw new BizException(“商品已下架”);
}
throw new BizException(“库存不足”);
}
}
/*
deductStock: UPDATE t_goods
SET stock=stock-#{qty},
version=version+1
WHERE id=#{gid} AND status=1
AND stock >= #{qty}
*/
示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;材料按袋卖、按颗补货会同时扣多个 SKU,批量扣减要做成「全部成功或全部失败」,不要半途 commit。
04预约:唯一索引才是裁判,缓存只是保安
这段解决「用户连点两次生成两张预约单」和「两个人同时抢同一格桌子」。
三层防护,各管一段:先按 request_no 查历史,重放直接返回原单号;再用 Redis setIfAbsent 挡住连点;最后由数据库唯一键兜底。
一个容易漏的细节:捕获 DuplicateKeyException 之后必须再查一次 byReqNo,才能分清撞的是哪个唯一键——撞 uk_req 是同一请求重放,应返回原单号;撞 uk_slot 是这一格已被占,应提示换时段。混着处理,用户重试会收到「该时段已被预约」,但他其实什么都没订成。
JAVA
public Long book(BookDTO dto, String rq) {
// 1) 同一 request_no 直接返回上次结果
Booking old = bookingMapper.byReqNo(rq);
if (old != null) {
return old.getId();
}
// 2) 缓存预检,挡住 99% 的连点
String key = “book:” + rq;
Boolean first = redis.opsForValue()
.setIfAbsent(key, “1”, TTL_5MIN);
if (!Boolean.TRUE.equals(first)) {
throw new BizException(“请求处理中”);
}
try {
return doBook(dto, rq);
} catch (DuplicateKeyException e) {
// 3) 唯一键兜底:uk_req / uk_slot
Booking b = bookingMapper.byReqNo(rq);
if (b != null) {
return b.getId();
}
throw new BizException(“该时段已被预约”);
}
}
示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;套餐(1/3/8 小时)要展开成连续格子逐格插入,同事务内完成,任何一格冲突就整单回滚。
05出图:配额先扣,任务后跑,结果回写带状态
这段解决「出图把请求线程占死」和「配额被刷」。
顺序是:条件更新扣配额(WHERE quota > 0)→ 落一条 status=0 的图纸 → 推一条消息进队列 → 立刻返回图纸 ID。前端拿着 ID 轮询,用户可以退出页面,出图不会断。
回写时 finish 的 where 带 status=0:同一条消息被重复消费时,不会把已经完成的结果覆盖成一份新的。失败分支记得把配额退回去,否则用户那一次白扣。
JAVA
public Long create(Long uid, PatternDTO dto) {
// 每人每天 5 张,条件更新扣配额
int ok = userMapper.useQuota(uid, 1);
if (ok == 0) {
throw new BizException(“今日次数已用完”);
}
Pattern p = new Pattern();
p.setUserId(uid);
p.setCols(dto.getCols());
p.setRows(dto.getRows());
p.setStatus(0); // 0 排队 1 完成 2 失败
patternMapper.insert(p);
// 出图要几秒到几十秒,交给异步队列
queue.push(p.getId(), dto.getStyleJson());
return p.getId();
}
// 出图完成后回写尺寸、色数与用量清单
public void finish(Long id, PatternResult r) {
int ok = patternMapper.finish(id, r.getCols(),
r.getRows(), r.getColorNum(),
r.getBeadNum(), r.getPaletteJson());
if (ok == 0) {
log.warn(“pattern {} done”, id);
}
}
/*
useQuota: UPDATE t_user SET quota=quota-1
WHERE id=#{uid} AND quota > 0
finish : UPDATE t_pattern
SET cols=#{c}, rows=#{r},
color_num=#{cn}, bead_num=#{bn},
palette_json=#{pj},
status=1
WHERE id=#{id} AND status=0
*/
示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;队列要配重试次数与死信队列,出图服务本身也要限并发,一张 104×104 的图纸很吃 CPU。
06多端请求:一个封装管住 token 与请求号
这段解决「每个页面各写一遍登录过期处理」。uni-app 里把请求收进一个函数:统一拼域名、统一带 token、统一在 401 时清缓存回登录页、统一给写请求挂 X-Request-No。
页面只调 request(‘/order/create’, data),不关心这些。重试时把上次的 reqNo 传回来,服务端就认得这是同一笔,不会再下一单。
JAVASCRIPT
const BASE = ‘https://api.example.com‘;
export function request(url, data = {}, o = {}) {
return new Promise((resolve, reject) => {
const header = {
‘Content-Type’: ‘application/json’,
// 同一 reqNo 重试也不会重复下单
‘X-Request-No’: o.reqNo || genNo(),
};
const token = uni.getStorageSync(‘token’);
if (token) {
header.Authorization = ‘Bearer ‘ + token;
}
uni.request({
url: BASE + url,
method: o.method || ‘POST’,
data,
header,
timeout: 10000,
success: (res) => {
const body = res.data || {};
if (res.statusCode === 401) {
uni.removeStorageSync(‘token’);
uni.reLaunch({ url: ‘/pages/login’ });
reject(new Error(‘未登录’));
return;
}
if (body.code === 0) {
resolve(body.data);
return;
}
uni.showToast({
title: body.msg || ‘请求失败’,
icon: ‘none’,
});
reject(new Error(body.msg || ‘fail’));
},
fail: reject,
});
});
}
function genNo() {
const r = Math.random().toString().slice(2, 8);
return Date.now() + r;
}
示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;小程序要先把 request 合法域名配进后台,否则请求会被直接拦掉。
07后台表格:一张表管三类商品
这是运营每天都要开的一屏:筛选 + 表格 + 分页 + 上下架。因为三类商品共用一张表,后台也就只有一个表格,分类用下拉框给三个固定值,避免运营手打出「珠子」「豆子」「拼豆珠」三个新分类。
状态用 el-tag 分色,操作列只做一件事:在售的给「下架」,下架的给「上架」。看起来简陋,但它把「误删商品」这个高频事故直接消灭了——商品从来不该被删,只该被下架。
VUE
示例实现,生产环境需补充参数校验、权限、日志、监控、限流、支付验签等;数据量大时把筛选条件下沉到 SQL,别在前端全量过滤后再分页。
COMPLIANCE / 合规与上线
手工店有三条别人没有的线
拼豆店踩的线和普通工具类产品不一样:孩子多、有押金、有 AI 生成内容。技术能做的部分有限,但做与不做差别很大。下面几条建议在开发阶段就写进需求,不要等审核被拒再补。
个人信息。手机号与详细地址加密存储,列表页脱敏到 138****0000,完整信息只在配送环节解密并留痕。位置只到门店或区域,不持续采集。隐私政策写清收集范围、用途与保存期限,注销入口放在二级页面以内。
未成年人。这是拼豆生意绕不开的一条:核心客群里就有小学生。不满十四周岁儿童的个人信息属于敏感个人信息,需取得监护人单独同意,且不得默认勾选;到店预约建议在页面上标注「未满 X 岁需监护人陪同」,无人时段不向未成年人单独开放。做得好,这条是信任;做不好,它是投诉的起点。
押金与预付。借用型工具的押金要原路退回、写明退还时限,逾期扣款规则在支付前展示而不是藏在协议里。如果做会员储值,性质上属于单用途商业预付卡,按规定记名卡单张不超过 5000 元、不记名卡不超过 1000 元,且需按当地要求备案——这条建议直接问当地商务部门,别照抄同行的做法。
AI 出图。创意拼豆的图纸生成属于生成式 AI 服务,面向公众提供需按《生成式人工智能服务管理暂行办法》履行相应手续,生成内容要有可识别的标识。用户上传的图片要提示「请确认你有权使用」,人像与知名动漫形象尤其要拦——把别人的 IP 做成图纸在店里卖,是这个小行业里最常见的侵权来源。
内容审核。社区动态、作品图、图纸标题都要过机审加人审。关键词库后台可维护,命中先进人工队列,不要直接发布。
门店与安全。经营范围要覆盖手工体验与零售;熨斗是大功率电器,电路与消防按门店实际负荷报装;无人时段要有监控并张贴告知,紧急联系入口放在门店页首屏。
资金与留痕。收款走持牌支付机构,寄卖分成用分账能力,平台不做资金池、不做二次清算。退款、改价、核销、权限变更都落操作日志,导出限权限频并带水印。
上线前建议逐项打勾
· 小程序类目(电商平台 / 到店服务)与资质已提交通过
· 隐私政策、用户协议、寄卖分成协议已上线
· 手机号、详细地址已加密,列表页已脱敏
· 儿童个人信息单独同意弹窗已接入
· 预约、下单、退款三处均有幂等与条件更新
· 押金退还与逾期扣款规则在支付前展示
· 未归还工具每日生成对账清单
· AI 出图已按生成式 AI 要求履行手续并标识
· 门店监控告示、紧急联系入口已就位
FIT / 适合谁
五种人用得上,三种人不必买
城市轻娱乐门店。拼豆、簇绒、流体熊、奶油胶,这一类的共同点是「按时段占桌、按材料收费」,把商品表里的品类换个名字就能用,桌位与时段那一层完全不用改。
手工材料电商。按色号卖散珠的店铺,最痛的是「一个订单 40 个 SKU、每个 3 袋」。这套的多规格与按色号统计销量正好对上,补货时能看出哪个色号永远缺。
文创市集与寄卖。作者把作品放在店里寄卖,月底按作者结一次账。作者 ID 与分成记录都挂在订单上,不用再靠微信群截图对账。
想加自助时段的现有门店。白天有人守、晚上十点后无人。不用整套换系统,先上预约与核销两块,跑一个月看夜间时段的真实占用,再决定要不要加无人门禁。
做行业 SaaS 或接二开的技术团队。表结构与接口按可扩展写的:换一套 UI 就是另一个客户,核心链路不用重写;多门店改造主要动 shop_id 与数据权限那一层。
哪三种不必买:只有一张桌子、全靠老板微信接单的(先把手头的单管好);只想做线上卖材料、永远不到店的(用现成商城更省);没有固定门店、纯做市集快闪的(按场次收钱,用不着排期)。
FAQ / 常见问题
五个被问得最多的问题
Q1:无人店真的可以一个人都不留吗?
不建议完全无人。能无人化的是「预约、开门、计时、结算」这四段,不能无人化的是「第一次来的顾客」和「熨烫这一步」——熨斗有烫伤风险,儿童尤其需要有人看着。常见做法是白天有人、夜间无人,系统按门店配置切换可预约时段。
Q2:出图要等几十秒,用户会不会跑?
所以要异步。提交即返回图纸 ID,页面显示进度并允许退出,出完推送一条消息。实话说,等待的关键不是速度而是「知道还要多久」,进度条比快 5 秒更有用。
Q3:拼板、熨斗借出去不还怎么办?
三道措施:借用必须挂在预约单上(谁、哪个时段、借了什么)、押金在归还时释放、闭店后自动生成一张未归还清单。真丢了就按清单扣款,有记录才有依据。
Q4:材料按颗卖太碎,能按袋、按套装卖吗?
能。同一色号可以挂多个 SKU(100 颗/袋、500 颗/盒、24 色套装),库存各自独立,色号池共用。图纸生成的材料包则按「颗数向上取整到袋」,不会出现「要 436 颗、店里只有 100 颗一袋」的尴尬。
Q5:支持二次开发吗?改得多不多?
支持。代码按 Controller / Service / Mapper 分层,表结构与接口文档随源码给。常见的四类定制是:加品类、改计价规则、换 UI 配色、接自家支付,都不需要动核心链路。
END / 写在最后
拼豆工坊这套系统说到底只做三件事:把材料算准、把桌子排开、把图纸画出来。这三件事做扎实,比加十个营销玩法都有用;反过来,靠手写记账、靠微信群排期、靠店员现场画图,客人一多就会散架。
如果你正在做手工体验店、拼豆材料电商或文创寄卖,或者想基于 SpringBoot + uni-app 做一套可二开的门店系统,欢迎找我领取演示资料与部署说明。
本文截图为演示环境,数据均为演示数据;文中不涉及任何收益承诺与效果保证。