01分层结构

五个包各司其职、依赖单向:controller 只做协议转换,service 承载全部业务决策,mapper 隔离存储,pojo 按「入参 / 出参 / 实体」三界分型——类型即边界。

controller5 个 · 协议转换 service8 个 · 业务决策 mapper数据访问隔离 config5 个 · 装配 调 调 pojo / entityDiagnosticTaskEntityextends BaseEntity pojo / ipo(入参)ClaimIPO · InsertIPO …请求体 ⇄ 服务入参 pojo / vo(出参)MineVO · DetailVO …服务出参 ⇄ 响应体 读写出参入参,永不跨层直取实体 yue-library 基础设施 R / Result 统一响应 · BaseEntity 审计 · MyBatis 数据层 · 全局异常
依赖单向向下 · 入参出参与实体三界分离 · 基础设施统一兜底

02四个关键设计决策

选四个「问题真实存在、决策可被讨论、结果有测试守住」的样本——好设计不是玄学,是把一类问题挡在一层里。

1上游错误归一化:错误语义不能被上游污染问题:网关 / 模型上游的错误形态五花八门,透传即事故

问题:上游错误体可能带真实模型名、路由细节,且同一故障在不同上游表现不同(网关瞬断返回 400、欠费伪装成 401)——原样透传,轻则吓到用户,重则把「该重试的」标成「立即失败」。

决策:服务端设一道归一化层——上游错误一律先分类归一为有限的语义集合(请求错误 / 欠费 / 认证 / 限流 / 上游不可用),再按「可重试 / 不可重试」两级分发给调用方;上游原文只进日志、永不进响应。

守住它的测试:真实套件 44 个用例、8 个嵌套分组,把每一类伪装形态(欠费返回 401、限流返回 429、超限返回 413……)逐一断言归一结果。

2安全扫描指纹动态派生:零硬编码的防线问题:怎么在流式输出里拦住「模型逐字复述内部规则」

问题:内部规则与提示词不允许逐字流到用户侧,但规则会持续更新——把拦截特征写死在代码里,每次规则变更都要改代码发版。

决策:拦截指纹完全从当前规则文本动态派生——服务启动时扫描全部规则生成指纹集,规则更新重启即自动重建,代码里没有一个写死的指纹;同时以「正常对话零误杀」为硬约束选窗口参数(参数取值见源码,此处不展开)。

守住它的测试:真实套件 29 个用例——复述命中即截断、正常对话零误杀、跨数据块边界同样命中。

3失败降级不丢数据:外部依赖的每一种坏法都有出路问题:自动分类依赖模型服务,模型服务会坏

问题:反馈提交后自动分类依赖外部模型服务——超时、返回 HTML 错误页、返回非法结构、未携带凭证,四种坏法任何一种都不该让用户的反馈消失。

决策:分类永远是「尽力而为」——失败一律降级为未分类、任务保留可人工补选;降级路径与正常路径同级对待,都有显式测试用例(真实套件 7 用例对应四种坏法全覆盖)。同款决策也用在更新策略上:外部策略文件损坏时告警并回落内置默认(5 用例守住)。

4状态由服务端裁决:前端按钮只是第一道遮挡问题:多人协作的工单不能靠界面约束状态

问题:反馈工单在多人之间流转(提交 → 认领 → 解决)——若状态合法性只靠前端按钮显隐,绕过界面的调用(脚本、并发双开)会把状态改乱。

决策:全部流转在服务层裁决——未认领不可解决、重复认领冲突拒绝、解决时一次性落处理说明与时间;客户端拿到的永远是裁决后的结果。数据库枚举与服务端枚举逐值对齐,非法值写入即被库层拦截。

守住它的测试:服务层 16 用例 + 全链路集成 9 用例(真实起服务跑完整流转)。

03可读性细节

设计落到日常,是每处代码都直读意图(真实代码节选):

// 常量名即文档:为什么是这个值,写在名字与注释里
private static final int MAX_TRANSCRIPT_SIZE = 15 * 1024 * 1024;  // 源会话转写上限 15MB

// 测试名即场景:失败时看名字就知道坏了什么
@DisplayName("上游 5xx → 归一为可重试")
这套代码设计的全部样本都可通过前几格复核:规约扫描(格一)看合规面、测试报告(格二)看守护链、数据库设计(格三)看存储层决策——设计、实现、验证三位一体。