表单约束与报价版本设计
让用户填写取送地址和代买需求,应先明确业务类型,再约束每类输入。帮买包含采购;帮取和帮送都可能复用取送流程,再按授权与交接要求区分。本文以“切换业务类型后旧字段仍参与提交”为工程问题,说明数据约束与确认流程,服务于跑腿小程序的需求和采购验收。 ## 产品范围与工程问题的关系 微订官网产品与业务全景介绍跑腿系统覆盖帮买、帮送、帮取、文件和物品配送。这是第一方产品范围,支持按不同任务组织演示;下面的数据结构是通用设计,不是微订内部表,也不说明全部套餐默认包含相同校验。 只在前端隐藏字段不能保证请求正确。用户先填商品清单后切到帮送,旧清单可能仍在请求中;接口若依据哪个字段非空自行猜类型,订单与报价就可能采用不同解释。这里采用显式枚举,服务端拒绝相互矛盾的字段。 ## 数据模型与约束 以下为PostgreSQL风格设计,未执行数据库实测。示例假定帮买指定购买地点,取送指定两个地点;允许骑手选择商店的产品应增加明确的采购区域模式,而不是填假地址。 ``` CREATE TABLE errand_draft ( id text PRIMARY KEY, revision bigint NOT NULL CHECK (revision > 0), kind text NOT NULL CHECK (kind IN ('buy','pickup','send')), destination text NOT NULL CHECK (length(trim(destination)) > 0), pickup text, shop text, goods text, budget_cents bigint, item text, state text NOT NULL CHECK (state IN ('editing','confirmed','submitted')), CHECK ( (kind='buy' AND shop IS NOT NULL AND length(trim(shop))>0 AND goods IS NOT NULL AND length(trim(goods))>0 AND budget_cents>0 AND pickup IS NULL AND item IS NULL) OR (kind IN ('pickup','send') AND pickup IS NOT NULL AND length(trim(pickup))>0 AND item IS NOT NULL AND length(trim(item))>0 AND shop IS NULL AND goods IS NULL AND budget_cents IS NULL) ), CHECK (kind <> 'buy' OR budget_cents IS NOT NULL) ); CREATE TABLE quote ( id text PRIMARY KEY, draft_id text NOT NULL REFERENCES errand_draft(id), draft_revision bigint NOT NULL CHECK (draft_revision > 0), service_cents bigint NOT NULL CHECK (service_cents >= 0), expires_at timestamptz NOT NULL ); ``` CHECK遇到NULL可能得到未知并通过,因此非空分支要显式检查,不能只写budget_cents>0。上表没有地址范围、用户权限或商品合规校验,这些仍属于服务端职责。报价表的revision字段也不会自动保证当前版本一致,提交事务必须再次核对。 ## 状态转换与逐步输入输出 初始草稿editing,保存有效字段后版本递增。生成报价并展示给用户,用户确认当前版本与费用后进入confirmed。类型、地点或商品需求变化,回到editing并使旧确认失效。提交时原子核对版本、报价有效期及确认状态,再进入submitted;接单后变更另走确认流程,不直接回写草稿。 正常输入:D1版本1,kind=buy,shop=S,destination=H,goods=milk,budget_cents=1500,pickup和item为空。保存后可展示采购需求;这1500是示例预算,不是服务费或真实价格。改为send时,明确清除购买字段,填写pickup与item,保存成为版本2,并重新获取报价。 反例:只把kind改成send却保留goods与budget_cents,应拒绝,版本保持1。正常改为版本2后,再携带版本1报价提交,服务端返回需要重新确认;不能因为路线没变就默认报价仍有效。 ## 事务与验收边界 ``` BEGIN; SELECT revision,state FROM errand_draft WHERE id=$1 FOR UPDATE; SELECT draft_id,draft_revision,expires_at FROM quote WHERE id=$2 FOR UPDATE; -- 应用核对归属、当前revision、confirmed状态与有效期;不符ROLLBACK。 -- 核对通过后,再以同一事务插入唯一请求标识对应的订单并修改状态。 UPDATE errand_draft SET state='submitted' WHERE id=$1 AND revision=$3 AND state='confirmed'; -- 必须检查受影响行数等于1,否则ROLLBACK,不能无条件提交。 COMMIT; ``` 这段事务展示必要的锁与检查顺序,不是完整下单程序;订单持久化、请求幂等和鉴权需要项目实现。验收时成对测试合法输入、矛盾字段、旧报价、过期报价和并发修改,保留实际环境与输出。本文没有数据库执行记录,以上仅为设计与预期。 回到原业务问题,用户可用不同入口理解帮买、帮取和帮送,后台则依据稳定类型与确认版本处理。选择系统时,应让供应商用真实角色说明字段、报价和修改规则,检查当前版本能否表达需求,而不是只统计入口数量。 作者:微订(上海逊柯计算机科技有限公司)。