挑战需求A 澄清问答记录 - xiaochu-dev/online-shopping-group5 GitHub Wiki
挑战需求 A · 澄清问答记录
- 项目:在线购物系统(第 5 组 · 胡成溯组)
- 记录人:周到(PM)
- 记录日期:2026-10-20
- 范围:挑战需求 A.1(商品管理升级 6 条)+ A.2(客户升级 6 条),共 12 条;另含补充问答 4 条
- 回答来源:产品经理与助教访谈
- 效力:本记录为组内唯一权威口径,与基线需求冲突处一律以本记录为准
- 版本说明:2026-10-21 并入原《挑战需求A-产品口径-1020》中仍然有效的部分(决策原则、表结构改动总表、两处认知更正、6 条实现默认),中间稿已删除
〇、决策原则(所有冲突的裁决依据)
- A 与基线需求冲突 → 以 A 为准(例:基线"仅一件在售"作废;基线"客户无需注册"改为下单必须登录)。
- A 未提及、基线已有的功能 → 继续有效,不因 A 而删除(例:口令码撤销接口保留为存量兜底)。
- A 内部措辞优先于实现便利(例:A 原文写"库存数量",故库存必须支持 N > 1)。
一、A.1 商品管理升级
Q1|多商品发布后,基线"同一时间在售仅一件"是否作废?口令码还留吗?
A:作废。可以同时挂多个商品,"只能卖一件"那套不成立。 口令码用不上了——客户能自己注册登录后,下单身份就是登录账号,不需要再靠口令码认领意向。
Q2|库存与冻结的颗粒度?
A:没有 SKU 的概念,一个商品对应一个库存数(非负整数),商品就是最小单位。 冻结是针对「商品」的状态——一件商品进入交易就整件冻住,不碰库存、不减库存;库存要等交易真正成交后才动。
说明:整件冻结 = 同一件商品在同一时刻只允许存在一笔进行中的交易(线下交易,卖家一次只能见一个人);成交并解冻后,剩余库存继续接受下一位买家的意向。因此「整件冻结」与「库存 N 件、可分 N 次交易」并不冲突。
Q3|库存扣减发生在哪一步?取消/失败是否回补?
A:扣减在最后一步——线下交易完成后,卖家在后台标「交易成功」那一刻才减 1。 标「失败」的话库存从头到尾没动过,商品恢复在售即可,谈不上回补。 没有单独的"取消"这档:取消就是「交易失败」。
Q4|库存归零自动下架是哪种"下架"?补货后能否重新上架?已有队列怎么办?
A:归零是状态直接变成「已下架」,不只是前台藏起来——买家端不展示,也不接新意向。 卖家手动改库存补货后,商品可以重新上架。 已经排着的队列:库存都没了,排着的人自然买不到;归零时把该商品所有排队中的意向批量置为失败(原因:库存不足),买家在历史下单里能看到失败结果,不做主动通知。
Q5|二级类别由谁维护、商品挂到哪一级?
A:类别由卖家(本人)在后台维护,增删改都行,层级固定两级。 商品必须挂到二级(叶子级)类别下,挂类别是必填的。 旧分类迁移不用操心——旧的没有分类,分类是本次新增。
Q6|多图与富媒体的边界?
A:图片最多 9 张,JPG/PNG,单张不超过 5MB;商品图不支持视频,想放视频就放进描述里嵌。 上传方式为网页上选文件上传。 富媒体 = 富文本,描述里可以图文混排,嵌图片或视频。
二、A.2 客户升级
Q7|注册是否强制?客户注册后还需要口令码吗?
A:浏览商品不用登录;但要下单(提交购买意向)必须先注册并登录。 登录后身份就是账号,口令码自然用不上。
Q8|注册字段与密码要求?要不要找回密码、改资料?
A:注册字段就四个——用户名、密码、电话(必填)、默认交易地点(选填)。 密码要加密存、不能明文,强度要求跟卖家账号一样。 找回密码、修改资料暂时没有要求,不做。
Q9|历史下单要看到什么程度?
A:每条至少能看到:商品名、下单时间、交易结果(成功/失败)、交易时间;按时间倒序,能翻页就行。
Q10|排队顺序、位次展示、客户自己取消?
A:排队按先到先得,各商品的队列各排各的。 不向买家展示"你排第几";客户不能自己取消意向;取消/重排由卖家在后台确认。
Q11|商家能看客户的哪些字段?要不要脱敏?
A:能看到用户名、电话、默认交易地点、注册时间。不做脱敏,电话本来就是拿来联系的。 "购买历史" = 该客户的下单记录带交易结果,成功的、失败的都在里面。
Q12|存量匿名订单怎么并入账号?同一客户能重复提交吗?
A(PM 裁定):
- 存量匿名单不并入任何客户账号,直接清库重建;如需保留旧商品则保留
product、清空intent。理由:注册不验证手机号,按电话"认领"会把别人的单挂到错误账号下。 - 重复提交限制:同一客户 + 同一商品,同时最多 1 条未结束(排队中/交易中)的意向;已成功/已失败的允许再次提交(支持复购)。
三、补充问答:需求文本未覆盖的行为空白
S1|交易失败或取消后,原来排队的人还算数吗?
A:还算数。队列按商品独立存,交易失败后商品恢复「在售」,后面排着的人接着按先到先得往下走。 受影响的只是这次交易里的那个买家——他的意向是作废、还是塞回队列重新排,由卖家在后台确认。 (补充:本体系里"取消"就是「交易失败」这一档,没有独立的"取消"状态。)
S2|客户的历史下单里,未完成的单(排队中、交易中)显不显示?
A:不显示。历史下单记录只放已经有结果的单子——交易成功、交易失败的都在里面。 还在排队、还处于交易中(冻结)的单子,还没出结果,不算"历史",不放进这个列表;它们的状态跟着各自的意向队列走。
S3|卖家能否手动下架"还有库存、正在在售"的商品?
A:能。下架有三种情形:①交易成功(卖完最后一件);②库存归零自动下架;③卖家手动下架。 手动下架跟库存无关——只要想下,正在卖、还有库存的商品照样能手动下架,随时让它退出在售。
S4|已发布商品能否编辑(价格/名称/图片)、能否删除?
A:改和删都没开这个口子。 改:目前只要求能改库存数量(补货、调库存);价格、名称、图片能否改没有要求,暂不做。 删:没有"删除商品"功能。卖完了、库存归零就走下架、进历史记录,用不着单独删——所以也不存在"有订单的商品能不能删"这回事。
四、由以上回答引出的实现裁定(团队内部)
| # | 事项 | 裁定 |
|---|---|---|
| R1 | 商品「交易中」怎么表示 | 不复用计数锁,直接 product.status = 'frozen':交易中整件不可下单;成交后 stock - 1 并回 on_sale;失败则回 on_sale 且库存不变 |
| R2 | 意向状态机 | pending → trading → success / failed;卖家侧"取消" = 置 failed,不引入独立 cancelled(cancelled 仅保留给旧口令码撤销的存量数据) |
| R3 | 失败买家的两种处置 | 卖家在后台二选一:作废(置 failed)或 塞回队列(置回 pending 并刷新排队时间为队尾)。需在卖家端提供该分支按钮 |
| R4 | 归零时队列善后 | 库存归零那一刻,把该商品所有 pending 批量置 failed,fail_reason = '库存不足' |
| R5 | 客户历史列表 | 只查 status IN ('success','failed') 的意向,按 finished_at 倒序分页;排队中/交易中不进列表 |
| R6 | 下架原因 | delisted 需区分「卖光下架 / 手动下架」,前台/后台文案不同(可用 trade_result 或新增 delist_reason 区分) |
| R7 | 商品「我的商品」列表 | 因无删除功能,已下架商品会长期堆积,列表必须提供状态筛选(在售/已下架/交易中) |
五、实现默认(低风险,不认同再改)
| # | 事项 | 默认处理 |
|---|---|---|
| 1 | S3 的"交易成功"是否等于"卖光最后一件才下架" | 按 stock - 1 后归零才下架处理;库存未归零则继续在售(与 S4"卖完了、库存归零就走下架"一致) |
| 2 | "塞回队列"是回原位还是排到队尾 | 用户表述为"重新排",按排到队尾处理(刷新 created_at,并按新时间参与先到先得) |
| 3 | 客户下单后无任何可见反馈 | 排队中/交易中不展示;仅在下单成功页给一句「已提交,等待卖家确认」,不展示位次 |
| 4 | 价格/名称写错无法修改 | 只能手动下架原商品后重新发布;发布页需加二次确认 |
| 5 | 列表页封面用哪张图 | sort_no 最小的一张;发布时第一张即封面(product.image_path 降级为封面字段) |
| 6 | 搜索范围与分页 | 商品名 + 描述,可叠加分类筛选;每页 10 条 |
| 7 | 用户名 / 电话的唯一性 | 用户名唯一(注册时提示"已被占用");电话不唯一(不验证手机号,允许多人填同一号码) |
| 8 | 客户与卖家的登录态 | 两套独立 cookie(CUSTOMER_SESSION / SELLER_SESSION),同一浏览器可同时登录后台与买家端,互不踢 |
| 9 | 下单时的交易地点 | 默认带出客户的默认交易地点,允许本单临时改写,落 intent.trade_address 快照 |
| 10 | 富文本描述的安全策略 | 卖家输入 HTML 原样存、买家端渲染,不做标签白名单(单卖家可信,课程内可接受,但需写入风险说明) |
六、表结构改动总表
以 R1「整件冻结」为准,不设
locked计数列(早期草案里的locked已作废)。
| 表 | 改动 |
|---|---|
product |
+ stock INTEGER NOT NULL DEFAULT 1(非负);category_id 由可空改必填;status 增加 frozen(交易中);delisted 需可区分卖光/手动(见 R6);不加 locked |
product_image |
新建:id / product_id / path / sort_no,一个商品最多 9 行;product.image_path 降级为封面 |
category |
+ parent_id(自引用,NULL = 一级);业务上限制最多两级;name 的唯一约束由「全表唯一」改为 UNIQUE(parent_id, name) |
customer |
新建(A.2):username(UNIQUE) / password_hash / salt / phone(必填) / default_address(可空) / created_at;密码复用 seller 的 sha256(salt + 明文) + 8 字节盐,不引入 BCrypt |
intent |
+ buyer_id(NOT NULL,关联 customer)/ trade_address / finished_at / trade_result(success·failed) / fail_reason;token_code 改可空(新单不生成,仅存量兜底);buyer_name / phone 保留为下单时快照 |
seller |
不动(仍单卖家;A.2 里的"商家"= 该账号) |
两处必须知道的现状更正
① 分类不是"从零做",已有半套。 category 表与 5 条种子数据(数码电子 / 图书教材 / 服饰鞋包 / 生活百货 / 运动户外)已存在,买家列表页也已有单级分类筛选。A1-4 实际要做的是:加 parent_id → 5 条种子改造成两级 → category_id 改必填 → 卖家后台分类增删改页面。
② 9 张 × 5MB 不能进 SQLite。 单商品最多 45MB 图片,塞进库会让打包与复制直接崩溃。必须新建 product_image 表、图片存文件系统 static/uploads/{productId}/、上传时校验扩展名 + MIME + 大小并重命名。
SQLite 迁移注意:SQLite 不支持删除约束,
token_code放开非空、category.name改唯一约束都只能重建表。课程环境最省事的做法是备份shop.db→ 删库 → 改schema.sql→ 重启(种子数据自动重灌)。