功能结构是一棵树:每个叶子节点最后都对应「一个窗体 + 一条 SQL」。 画不出来的地方就是「还没想清楚」的地方——比如「换房」算新单还是改原单。 「这个功能不做,业务能不能跑?」能跑就先不做。这条比任何架构原则都实用。
18.1 定了表,这一节定「界面上有哪些功能」——以及它怎么反过来影响表。 教材 18.2 分三小节:系统目标、系统功能结构、系统预览。 「系统预览」那一节是让学生先看见成品, 这里我改成「一屏必须看到什么」——那个问题比「界面怎么美化」重要得多。 界面部分沙箱里画不出来,所以这一段用缩进的树 + 表格把结构表达出来, 重点是「模块和表的对应关系」——那才是写代码时真正要用的东西。
一、功能结构:一棵树,不是一张表
酒店管理系统
├─ 登录
├─ 客房管理
│ ├─ 房态看板
│ └─ 房价维护
├─ 入住登记
│ ├─ 开房
│ └─ 换房
├─ 退房结账
│ ├─ 算账
│ └─ 打印账单
└─ 报表
├─ 今日入住
└─ 月度营收
每个叶子节点,最后都对应「一个窗体 + 一条 SQL」——结构图画完,模块划分其实就定了。 画不出来的地方,往往就是「还没想清楚」的地方。比如「换房」到底算新开一张单、还是改原来的单? 这件事画图时就要定,不能留到写代码。
二、模块和表的关系:谁读谁写
| 模块 | 主要读 | 主要写 |
|---|---|---|
| 房态看板 | tb_room |
— |
| 开房 | tb_room、tb_guest |
tb_checkin、tb_room.state |
| 退房结账 | tb_checkin、tb_room |
tb_bill、两条 state |
| 报表 | tb_checkin、tb_bill |
— |
看「主要写」这一列就能发现:tb_room.state 被两个模块写。
这是典型的需要事务保护的地方——开房要同时改房态和插入住单(18.7 实测)。
这张表还有个用处: 出问题时能快速圈定「谁可能改坏了这张表」——不用把整个系统翻一遍。
这就是「把设计写下来」的实际回报,不是为了交文档。
顺带一条经验: 一张表被三个以上模块写,基本就该拆了,
或者抽一个「服务层」专门管它——否则每个模块都改一点,最后没人说得清。
三、哪些功能其实不该做成模块
| 想法 | 为什么不该单独做 |
|---|---|
| 「自动提醒明天到期的客人」 | 没有通知渠道,做了也只是个列表,不如放进报表 |
| 「客户黑名单」 | 一期没有这个需求,先不做 |
| 「房间图片管理」 | 跟前台业务没关系,属于另一套东西 |
功能结构图画的是「一期要做完的东西」。 把所有想到的都画上去,结果就是永远做不完。 判断标准:这个功能不做,业务能不能跑? 能跑就先不做。 这条在项目里比任何架构原则都实用。 反过来也要小心另一种极端: 一期做得太少,业务跑不起来,系统就没人用。 「能不能跑」这个标准的意思是「核心闭环能不能走通」—— 开房、退房、结账这条链断了,其他都是白搭。
四、系统预览:先看「一屏该有什么」
| 窗体 | 一屏必须看到的东西 |
|---|---|
| 登录 | 账号、密码、登录按钮(就三样,别加多余按钮) |
| 主窗体 | 菜单树 + 当前操作员 + 房态概览 |
| 房态看板 | 房号、类型、价格、状态(一行一间,颜色标状态) |
| 退房结账 | 住单信息、房费、其他消费、合计、结账按钮 |
「一屏必须看到什么」这个问题,比「界面怎么美化」重要得多。 因为前台一天要操作几十次,多一次翻页就多几十次。 还有一个常见的界面错误: 把「查询条件」和「结果列表」放在两个窗体里。 用户改个条件就得关窗口、重开、再输入——正确做法是「条件在上、结果在下」,一屏之内改完。
五、动手 Demo:结构树、模块与表的对应、以及不该做的功能
这一段把功能结构打成树、把「模块 ↔ 表」列成表,最后过一遍「哪些想法该砍掉」。 界面在这里用缩进和表格代替——但「模块要读写哪些表」这件事,跟界面长什么样无关。
tree = u"""酒店管理系统
├─ 登录
├─ 客房管理
│ ├─ 房态看板
│ └─ 房价维护
├─ 入住登记
│ ├─ 开房
│ └─ 换房
├─ 退房结账
│ ├─ 算账
│ └─ 打印账单
└─ 报表
├─ 今日入住
└─ 月度营收"""
# 模块 -> (读的表, 写的表)
mods = {
u"房态看板": ([u"tb_room"], []),
u"开房": ([u"tb_room", u"tb_guest"], [u"tb_checkin", u"tb_room.state"]),
u"退房结账": ([u"tb_checkin", u"tb_room"], [u"tb_bill"]),
}
实跑输出:
--------------------------------------------------------------------
系统设计:功能结构图决定模块划分
--------------------------------------------------------------------
① 功能结构:一棵树,不是一张表
--------------------------------------------------------------------
酒店管理系统
├─ 登录
├─ 客房管理
│ ├─ 房态看板
│ └─ 房价维护
├─ 入住登记
│ ├─ 开房
│ └─ 换房
├─ 退房结账
│ ├─ 算账
│ └─ 打印账单
└─ 报表
├─ 今日入住
└─ 月度营收
每个叶子节点,最后都对应「一个窗体 + 一条 SQL」——结构图画完,模块划分其实就定了。
画不出来的地方,往往就是「还没想清楚」的地方。比如「换房」到底算新开一张单还是改原来的单?
这件事画图时就要定,不能留到写代码。
② 模块和表的关系:谁读谁写
--------------------------------------------------------------------
| 模块 | 主要读 | 主要写 |
|---|---|---|
| 房态看板 | tb_room | — |
| 开房 | tb_room、tb_guest | tb_checkin、tb_room.state |
| 退房结账 | tb_checkin、tb_room | tb_bill、tb_checkin.state、tb_room.state |
| 报表 | tb_checkin、tb_bill | — |
看「主要写」这一列就能发现:tb_room.state 被两个模块写。
这是典型的需要事务保护的地方(开房要同时改房态和插入住单)。
这张表还有个用处: 出问题时能快速圈定「谁可能改坏了这张表」——不用把整个系统翻一遍。
③ 哪些功能其实不该做成模块
--------------------------------------------------------------------
| 想法 | 为什么不该单独做 |
|---|---|
| 「自动提醒明天到期的客人」 | 没有通知渠道,做了也只是个列表,不如放进报表 |
| 「客户黑名单」 | 一期没有这个需求,先不做 |
| 「房间图片管理」 | 跟前台业务没关系,属于另一套东西 |
功能结构图画的是「一期要做完的东西」。把所有想到的都画上去,结果就是永远做不完。
判断标准:这个功能不做,业务能不能跑? 能跑就先不做。这条在项目里比任何架构原则都实用。
④ 系统预览:先看「一屏该有什么」
--------------------------------------------------------------------
| 窗体 | 一屏必须看到的东西 |
|---|---|
| 登录 | 账号、密码、登录按钮(就三样,别加多余按钮) |
| 主窗体 | 菜单树 + 当前操作员 + 房态概览 |
| 房态看板 | 房号、类型、价格、状态(一行一间,颜色标状态) |
| 退房结账 | 住单信息、房费、其他消费、合计、结账按钮 |
「一屏必须看到什么」这个问题,比「界面怎么美化」重要得多。
因为前台一天要操作几十次,多一次翻页就多几十次。
树打出来是给人看的,mods 那个字典才是给代码用的。
它一眼就能看出「谁写同一张表」——那就是事务该划在哪。
画树的时候最容易漏的是「换房」这种「半个功能」。
它不是「开房」也不是「退房」,是两者之间的一次状态变化。
这种功能最容易被当成「顺手改一下」,结果改出一堆脏数据。
「不该做的功能」那三行,是这一节最省钱的部分。
每个项目都会有人提「顺便加个 XX」——判断标准只有一个:不做它,核心闭环能不能走通。
最后一句提醒: 结构图不是画给客户看的,是画给自己看的。
它的作用是「在写代码之前,先把「有什么」想完」——漏掉一个模块,代价是返工一周。
30 秒实验
- 给树加一个「夜审」功能(每天凌晨把没退的房间自动算一天房费),想清楚它读写哪些表。
它会写
tb_bill和tb_checkin.days——这就又是一个「需要一个事务」的地方。 - 把「报表」拆成「今日入住」「月度营收」「入住率」三个叶子,看它们共用哪些表。
三个都读
tb_checkin——这就是「读多写少」的典型,适合加索引。 - 想一想:如果客户说「我们要能在手机上查房态」,结构图要怎么变? 多一个「接口层」——业务逻辑不变,但要多一层给手机用。 这也是为什么「分层」比「画界面」重要。
常见误区
- 「功能列得越全越专业」。 列全了但做不完,项目就是失败的。 一期的目标是「核心闭环能跑」,不是「什么都做」。
- 「结构图是文档,写完就放一边」。 它真正的用处是「分模块、定事务、圈影响范围」。 写代码时还在看它,才说明它有用。
- 「界面先随便做,后面再美化」。 「一屏该看到什么」是设计问题,不是美化问题。 位置错了、翻页多了,美化救不回来。
自测三问(点开对答案)
1. 功能结构图的叶子节点,最后对应什么? —— **一个窗体 + 一条(或几条)SQL。** 所以图画完,模块划分和 SQL 清单基本都有了。 2. 「模块 ↔ 表」这张对应表有什么用? —— **能看出「谁在写同一张表」——那就是事务该划在哪。** **出问题时也能快速圈定「谁可能改坏了这张表」。** 3. 一个功能该不该在一期做,怎么判断? —— **不做它,核心业务闭环能不能走通?** 能走通就先不做。 **「核心闭环」指的是开房—退房—结账这条主链。**下一篇 18.3 讲数据库设计:五张表的具体结构、约束压在哪、表间关系怎么验证,
以及「为什么住单里存 room_id 而不是房号」。
- ☐ (本篇完,下一篇 18.3 数据库设计)