胡成溯 第2次个人汇报 - xiaochu-dev/online-shopping-group5 GitHub Wiki

胡成溯:第2次个人汇报

  • 汇报人:胡成溯
  • 报告期次:第2次
  • 报告日期:2026-10-20
  • 项目:第5组在线购物系统
  • 依据:《工作任务表 v4.0》第四节 —— 账号与客户模块,T2-01 ~ T2-07,13 个故事点

本次迭代我负责 T2-01 ~ T2-07 共 7 项任务、13 个故事点,落在「账号与客户」这一个完整模块里: 买家账号(注册、登录、会话)、卖家侧的客户管理(列表、详情、购买历史),以及为了让三个人真正互不干扰而做的一次模块化重构。

对应课程需求是挑战需求 A.2.1(允许客户自由注册)与A.2.3(商家可查看注册客户信息及购买历史)。 补充说明:本次迭代按「一人一模块」重排分工——商品中心全归黄程宇,买家门户与交易全归周到, 我的模块不碰他们的包与表,跨模块只走 Service 接口。

一、我完成了什么

任务 我的具体工作 成果
T2-01 模块化重构 把 Entity / Mapper 从 common/ 迁入各模块 domain/ 包(Seller→auth/,Product/Category→product/,Intent→portal/,Buyer→buyer/),common/ 只留 api / exception / util / constant / config 并本迭代冻结;把原来的单个 schema.sql 拆成 00-base / 01-buyer / 02-product / 03-intent 四份,按序加载 auth/domain、schema/
T2-02 buyer 表与注册 建 buyer 表;注册校验顺序「用户名 → 查重 → 密码 ≥8 → 电话必填 → 地点 ≤100」;密码复用卖家的 sha256(salt+明文) + 8 字节盐,不引入 BCrypt;电话按澄清结论必填但不唯一、不校验格式 01-buyer.sql、BuyerAuthService
T2-03 买家登录与会话 买家登录态改用独立签名 cookie BUYER_SESSION(buyerId:过期:HMAC-SHA256),与卖家的 SHOP_SESSION 并存互不踢号;对外只暴露 BuyerContext.current() BuyerSessionCookie、BuyerContext
T2-04 客户管理列表 /admin/customers:分页 + 用户名/电话模糊检索 + 注册时间倒序;展示用户名、电话、默认地点、注册时间,不脱敏;空结果分两种提示 CustomerAdminController
T2-05 客户详情 /admin/customers/{id}:基本信息 + 购买历史区;历史通过 IntentHistoryPort 读取(见第二节判断三) 同上
T2-06 接口契约 写《契约-账号与客户-迭代2》:8 条路由的参数与模型属性、两个跨模块出口、错误提示口径、8 项联调清单 docs/契约-账号与客户-迭代2
T2-07 自动化用例 新增 BuyerAuthTest 18 条,覆盖注册 6 / 登录与隔离 4 / 客户管理 3 / 越权与 cookie 篡改 5;全量 mvn test 41 条全绿 BuyerAuthTest

二、三个技术判断

判断一:买家登录态不能和卖家共用一个 Session

问题。 全项目只有一个 SHOP_SESSION。买家如果也往这个 session 里塞属性,一方登录就把另一方覆盖——演示时最容易撞上的场景是:刚在后台标完交易,切到买家页面下单,回来发现自己被踢下线了。

处理。 买家改用独立 cookie BUYER_SESSION,值形如 buyerId:过期毫秒:HMAC-SHA256,HttpOnly。带签名是必须的:只存 buyerId 的话,用户把 cookie 改成 1 就能冒充别的买家;签名用常量时间比较,防时序侧信道。

代价。 无状态签名没法在服务端吊销单个 cookie,退出登录只清浏览器那份;密钥每次启动随机生成,应用重启后买家要重新登录。这个取舍和 HttpSession 本身的行为一致,课程演示接受;真要做「强制下线」得改成会话表。

判断二:不先把 Entity 迁出 common,「互不交叉」是假的

问题。 迭代 1 的实体和 Mapper 全在 common/entity、common/mapper 下。只要它们还在那儿,黄程宇给 product 加一个 stock 字段、周到给 intent 加一个 buyer_id,改的都是同一批文件——所谓「一人一模块」在代码层面并不成立,合并时照样冲突。

处理。 一次性重构:实体与 Mapper 按归属迁进各模块 domain/ 包,common/ 只保留 api、exception、util、constant、config,宣布本迭代冻结(谁要加工具就加在自己模块的 util/ 下)。建表脚本同样按模块拆成四份,各写各的那一份,application.yml 按 00→01→02→03 顺序加载(表之间有外键依赖,顺序有意义)。

代价。 重构当天三人都会被我卡住,所以约定「重构期间冻结功能提交」,并且当天合入。拆 02 / 03 两份脚本时我只搬运了原有表结构,一个字段都没替他们改,把要补的变更写成文件头的 TODO,避免越权。

判断三:跨模块读数据,接口要由「消费方」定义

问题。 客户详情页要显示该买家的购买历史,但 intent 表归周到。按硬规则我不能查他的表;可他的队列服务还没实现,我这边就得干等。

处理。 在 admin 侧定义端口 IntentHistoryPort + 记录 IntentHistoryItem,portal 实现后由 Spring 自动注入,我用 ObjectProvider 取——实现到位就接上,没到位就显示「交易模块尚未接入」,两边都不阻塞。记录类型只暴露页面要的五个字段,不把 portal 的 Intent 实体抛过来。

代价。 多了一层类型转换(周要把 Intent 映射成 IntentHistoryItem)。换来的是:他改自己的实体不会波及客户详情页,我也不必为了一个历史列表去碰别人的表。

前两个迭代沿用下来的判断也仍然有效:SQLite 单写锁 → Hikari 连接池固定为 1;一套异常两种出口 → RequestUtils 判 URI,一个 @ControllerAdvice 内部分流。

三、验收方式

mvn test:41 个用例全绿(Failures 0 / Errors 0)

测试类 条数 覆盖
AuthFlowTest 15 卖家登录、未登录拦截、改密四条(重构后仍全绿)
BuyerAuthTest 18 注册 6、登录与两套会话隔离 4、客户管理 3、越权与 cookie 篡改 5
CommonLayerTest 6 实体 / Mapper / 分页 / 金额边界
SchemaIdempotencyTest 2 四份建表脚本连跑三遍:表结构不变、种子不翻倍

除用例之外,还做了一次真实启动的端到端冒烟(不是 Mock):注册 → 登录拿到 BUYER_SESSION → /buyer/me 正常; 带买家 cookie 访问 /admin/customers 被 302 拒到卖家登录页;卖家登录后能在客户列表里看到刚注册的账号。

四、界面与入口

页面 地址 说明
买家注册 /buyer/register 用户名 2~20、密码 ≥8、电话必填、地点选填
买家登录 /buyer/login 成功下发 BUYER_SESSION
买家中心 /buyer/me 未登录自动跳登录并带回跳地址
客户管理 /admin/customers 后台顶栏已加入口;检索、翻页、进详情
客户详情 /admin/customers/{id} 基本信息 + 购买历史区

界面展示

以下为 2026-10-18 在本机运行的截图(卖家登录页、后台首页、基座自检页),基座与账号链路未变,仍然有效:

卖家登录页

后台首页

基座自检页

本次新增的买家注册页、买家中心、客户管理页截图待评审材料定稿后补(页面已可用,见上表入口)。

五、协作分工与边界

本迭代按「一人一模块」重排,四条硬规则进入 PR 检查项:

  1. 一个表只有一个 owner,只有 owner 能改这张表的 DDL 与 Mapper;
  2. 跨模块只允许调 Service 接口,禁止 import 对方 Entity / Mapper;
  3. 需要对方改东西,走「提供方改接口 + 通知消费方」;
  4. common/ 本迭代冻结。

模块依赖方向(无环):admin → portal → product、portal → buyer、admin → buyer。跨模块出口只有五个, 其中两个与本模块相关:BuyerContext(我提供、周调用)、IntentHistoryPort(我定义、周实现)。

六、与基线需求的差异(已报备)

# 差异 原因
1 基线「买家不用注册」→ 本迭代下单必须登录、浏览不拦截 挑战需求 A.2.1 要求客户可注册;澄清结论确定身份以登录态为准,原「手填姓名电话」的兼容路径取消
2 买家电话必填但不唯一 澄清结论明确「电话可重复填、不校验格式」,因此不加唯一索引、不做格式强校验
3 买家账号不提供改密与找回 澄清范围未包含;如需,下个迭代按卖家改密的同一套规则补

七、我的收获

第一,模块边界不是画在文档上的,是画在 import 语句上的。 只要实体还堆在 common/,谁说「我们一人一模块」都是空话。这次重构之后,黄程宇改 product、周到改 intent,我才不会和他们撞在同一个文件里。

第二,把要求变成断言,才有资格说「做完了」。 「电话可以重复填」这种一句话需求,如果不写成用例,下一个人顺手加个唯一索引就把它破坏了,而且没人会发现。现在 18 条用例就是这条要求的护栏。

第三,设计取舍要连代价一起写。 两套 cookie 换来互不踢号,代价是重启后买家要重新登录;端口隔离换来互不阻塞,代价是多一层类型转换。评审时说清代价,比只说好处更可信。

八、教师点评与后续改进

评审后按实际记录填写,并同步到第 2 次阶段性评审报告对应任务的「教师评审意见」列。

教师点评 对应任务或问题 后续改进行动 完成与验证记录
待评审后填写 — — —

相关页面:账号与客户模块(迭代 2) | 返回项目 Wiki 首页