销售订货系统

一开始我以为只是补几个功能

最早打开 E:\kcwls\Projects\sales-order-system 的时候,我是有点侥幸的——PC 端主体已经在那儿了,订单接收、评审、变更、发货,骨架都是通的。我当时的判断是:补补细节,把试卷要求的十个模块对齐,大概两三天的事。

直到我把试卷第 4 项看清楚:移动端 50 分。

那一刻的感觉很难形容。前面 PC 端两个大项加起来 50 分,我吭哧吭哧改了半天,然后发现有另外 50 分完全没开始。而 PC 端那 50 分里,我还只拿了 38 左右。

所以真正的工作量不是"补细节",是"再做一个手机端",而且这个手机端不是摆着看的,试卷明确要求"所有审批流程可在手机上完成"。这意味着一张订单从接单到出库的每一步,手机上都得走通。

十二个页面。从零开始。


手机端

做手机端最难受的一点:没有真机。

只能在浏览器里切 390×844 的设备视图,看那些 env(safe-area-inset-bottom) 到底生效没生效,底部标签栏会不会被 iPhone 的小黑条盖住。做出来的东西自己心里也没底。

后来想到一个笨办法:把每个页面都截图,然后一张一张看。看那些 padding 有没有被吃、卡片圆角是不是一致、长客户名会不会撑破布局。截图存了六十多张,都在 _参考文档/界面截图/ 里。

这个办法意外地有效。有些问题不截图根本发现不了——比如移动端评审页曾经把"已核准"的订单也列出来还给了个"去评审"按钮,逻辑上根本不该出现。是看截图的时候一眼瞅见的。


改颜色,我其实有点不情愿

你后来说"顶部颜色换成淡色,符合工程使用环境",我心里第一反应是:紫蓝渐变挺好看的啊,为什么要改。

真改完我才明白。那套渐变是"给评委看的",不是"给每天开着电脑干活的人看的"。工程环境里的人一开就是七八个窗口来回切,整屏高饱和颜色看久了眼睛累。

改成钢蓝 #2f6db5 之后,我拿两种版本对比了一下,确实沉得住气了。这事让我记住一句:好看和耐用是两回事,做给别人用的东西,先考虑后者。


两个真 bug,其中一个让我后背发凉

权限那块,我写了个 @DeptOnly 注解 + 拦截器。自测的时候感觉挺顺,越权请求都拒了,提示文案也写得挺全。

然后写 verify_rbac.py 跑了一遍,46 项里挂了 6 项。

第一个是改密码:if (!newPwd.equals(oldPwd)) —— 我拿新密码去比对原密码。这个错得很蠢,但当时写着写着就顺手了。

第二个才是真吓人的。

Controller 层判断业务失败用的是一串 msg.contains("不存在") 之类。我加权限之后,拒绝消息是"越权操作被拒绝:……"——里面不含任何一个关键词。所以拦截器拒了、Service 也拒了,但 Controller 把它当成功,返回了 code: 200。

也就是说:业务员点"经理核准",界面上弹出一个绿色的"操作成功"。

数据没被改(Service 那层拦住了),但用户以为成功了。这种"看起来成功的越权"比直接报错危险得多——前者会让人以为规则允许,下次继续这么干。

修法是抽了个 BizResult.isFail() 统一收口,把失败特征词集中在一处。修完再跑,46/46。

这件事给我的教训不是"要细心",而是:拒绝路径必须和成功路径一样被测到。 我前面测了十条正常流程,一条越权都没有断言返回值,所以这个问题一直藏着。


菜单全空的那一次

按岗位裁剪菜单这件事,我以为一小时能搞定。

写完 MenuAuth 那张矩阵表,编译通过,一开页面——侧边栏一项都没有,只剩一个"工作台"。

看日志:

EL1004E: Method call: Method visibleBySession(java.lang.String)
cannot be found on type com.school.sales.common.MenuAuth

我先怀疑是 bean 没注册,加了 @Component;还是不行。又怀疑是模板里 T() 静态调用的问题——这个我确实知道,Thymeleaf 3 出于安全考虑禁用了 T()。

于是改成 th:with="can=${T(com.school.sales.common.MenuAuth)}"。这个写法看起来挺聪明,实际上还是绕不开 T(),一样炸。

真正的原因是:我那个 th:with 定义了个叫 can 的变量,但表达式本身就是非法的,所以 can 压根没被赋值,后面所有 can.visibleBySession(...) 全部求值失败。而 Thymeleaf 的表现是——整个 th:if 都当不成立处理,于是全被隐藏了。

最后是最朴素的写法:PageController 把 menu 塞进 model,模板直接 ${menu.visibleBySession('receive')}。一行搞定。

我在这上面耗了快一个小时,绕了两圈。回头想,当一个"聪明写法"报错信息完全对不上时,应该第一时间退回最笨的写法验证,而不是在聪明写法上继续打补丁。


有些判断我说不准对不对

关于"多一级经理核准"。

试卷流程图是:相关部门评审 → ◇审核 → 分库存。我把那个菱形做成了独立的"经理核准"阶段。

理由是 5.3.4 写了"业务部对评审好的交期确认后……完成后系统审核《销售订单》",而且参考的《销售订单》表头里明明白白有"经理审批"一栏。

但我心里清楚:流程图里没有单独画这一步。 我在文档里老老实实写了"与流程图不完全一致",给了改回去的方法。如果老师认为应该严格照图,两行代码就能改。

关于"订单管理"对财务仓库隐藏。

财务和仓库其实偶尔也想看看订单列表。我把这个入口对他们藏了,只留"流程追溯"。理由是给他们一张能改单的页面只是风险。

但万一老师问"财务为什么看不到订单",这个回答站得住吗?我也不确定。

关于屏幕宽度那个断点。

1024 宽度下,能力卡片的说明文字会断成"完工才发 / 货",很难看。我选择在中等宽度直接隐藏说明文字,只留标题。

这是个妥协。如果希望文字永远完整,就得改单列,左侧会变长。我当时选了前者,但心里一直觉得该问一句再定。


几个数字

  • 52 个 Java 文件,14 张表
  • PC 端 11 个页面,手机端 12 个页面
  • verify_flow.py 66/66
  • verify_rbac.py 46/46
  • walkflow.py 23/23(流程图逐节点)
  • 63 张界面截图

还有一个我自己挺满意的东西:flow_log 表。每一步操作都记了操作人、角色、前后状态、时间。手机端有个"流程串联视图",能看见一张单子走到哪一步、谁批的。

这是唯一一个"试卷没要求、但我觉得企业真会用"的功能。


如果重来

第一,先读完整张试卷再动手。 我因为没第一时间看清"移动端 50 分",前面花在 PC 端打磨上的投入产出比其实不高。

第二,测试脚本一开始就按岗位写。 我最早那些回归脚本全用同一个账号(陈工程)跑,加了权限之后全线飘红,改脚本改了大半天。如果一开始就按"这个动作该谁做"来写,后面这些返工都不存在。

第三,遇到"聪明写法"报错对不上时,立刻退回笨写法。


收尾

打包时我犹豫过要不要把 target/ 和 cloudflared.exe 塞进去。最后排除了——一个是构建产物,一个是公网工具,接收方拿到没用,只会让包大 100 多 MB。

不过我在 application.yml 里留了明文密码 Aa123456。这件事我在文档里标注了,但没改。因为改了我自己演示又得再配一遍环境变量。

如果这个包要发给老师以外的人,这是个真问题。我提醒了一句,但没有替你做决定。


有时候我觉得做系统最难的不是写代码,是在"照图纸做"和"做得更合理"之间选一个,然后承担被质疑的风险。

posted @ 2026-11-10 23:04  CCidy  阅读(3)  评论(0)    收藏  举报