+ 给前端
+ toast
+ 的
+ message
+ 只放用户能看懂的中文;动作 / 状态的内部 code、堆栈等技术细节不进
+ message
+ ,由访问日志承载。本规范是必须遵守的跨模块约定,新功能照做。
+
message 面向用户、诊断面向开发,两者分离。
+
+ 前端往往直接把后端业务异常的
+ message
+ 弹给最终用户。如果
+ message
+ 里夹着
+ complete
+ /
+ status
+ /
+ action
+ 这类内部术语,用户看不懂,会误以为系统异常或数据没保存。
+
complete
+ 动作,后端返回
+ "当前任务状态不支持动作【complete】"
+ 。用户合理的预期是看到
+ "任务已完成,请勿重复提交"
+ 这类人话。
+
+ 因此约定:
+
+ 用户看友好中文(
+ message
+ ),开发排查看访问日志(
+ infra_api_access_log
+ 里有原始 code、入参、堆栈)。
+
+ 两条信息流互不污染。
+
+ 整套方案
+ 零新表、framework 零改动
+ ,纯在
+ rdms-project
+ 域内:一个解析器组件 + 错误码文案模板改造 + service 接入。
+
StatusActionTextResolver
+
+ 位置:
+ rdms-project-boot · service/status/StatusActionTextResolver.java
+ (
+ @Component
+ )。把动作 / 状态的 code 翻成中文展示名,供错误文案使用。
+
| 方法 | +作用 | +查不到 / 空入参 | +
|---|---|---|
actionName(objectType, actionCode) |
+ 动作中文名 | +
+ 回退原
+ actionCode
+ ,不抛错
+ |
+
statusName(objectType, statusCode) |
+ 状态中文名 | +
+ 回退原
+ statusCode
+ ,不抛错
+ |
+
rdms_object_status_transition.action_name
+ ,状态名取自
+ rdms_object_status_model.status_name
+ 。运维在状态机表里配新动作 / 新状态,文案自动生效,
+ 不用改代码发版
+ 。
+ 错误码定义时,文案就留中文名占位,由 service 在抛错前用 resolver 填入。例如:
+// 个人事项 —— 正面样板
+ErrorCode PERSONAL_ITEM_STATUS_ACTION_NOT_ALLOWED =
+ new ErrorCode(1_008_008_004, "当前个人事项为「{}」状态,不支持「{}」操作");
+
+ 抛出前:
+
+ exception(PERSONAL_ITEM_STATUS_ACTION_NOT_ALLOWED, resolver.statusName(...), resolver.actionName(...))
+
+ —— 占位填的是中文名,不是裸 code。
+
状态机校验失败抛错时,先经 resolver 翻译再返回的 service:
+ProductRequirementServiceImpl
+ (产品需求)
+ PersonalItemServiceImpl
+ (个人事项)
+ ProjectTaskServiceImpl
+ (任务)
+ ProductServiceImpl
+ (产品)
+ ProjectExecutionServiceImpl
+ (执行)
+ ProjectServiceImpl
+ (项目)
+ ProjectRequirementServiceImpl
+ (项目需求)
+ StatusActionTextResolver
+ 。
+ PERSONAL_ITEM_STATUS_ACTION_NOT_ALLOWED
+ ),
+ 不要把 code 直接嵌进文案
+ 。
+ actionName / statusName
+ 把 code 翻成中文名再填占位。
+ rdms_object_status_model
+ /
+ rdms_object_status_transition
+ 配好
+ status_name
+ /
+ action_name
+ ,resolver 即自动生效。
+
+ 除"状态机动作 / 状态翻中文"外,凡
+ message
+ 会被前端直接展示,
+ 以下技术 token 一律不得出现在 message 里
+ ,只能进日志(
+ log.warn
+ /
+ infra_api_access_log
+ ):
+
| 禁止外泄 | +反例 | +正确做法 | +
|---|---|---|
| 数据库表名 / 列名 | +
+ "未在
+ system_role
+ 找到"
+ |
+ "…未配置,请联系管理员" | +
| 权限码 / 内部标记 | +
+ "操作权限【
+ project:project:update
+ 】"、"【
+ member
+ 】"
+ |
+ "您没有此项操作权限,请联系管理员" | +
| 动作 / 状态 code | +
+ "不支持动作【
+ complete
+ 】"
+ |
+ resolver 翻中文名(见 §2) | +
| 类名 / 字段名 / 堆栈 | +
+ "
+ NullPointerException at ...
+ "
+ |
+ 友好提示,异常进日志 | +
OvertimeApplicationServiceImpl
+ 已注入 resolver,文案对齐其它域「当前加班申请为「{}」状态,不支持「{}」操作」。
+ Project/ProductObjectPermissionService
+ 已去占位、权限码改
+ log.warn
+ ;错误码文案改"您没有该项目/产品的此项操作权限,请联系管理员"。
+ PRODUCT/PROJECT_INTERNAL_ROLE_NOT_CONFIGURED
+ 两条已去掉
+ system_role
+ 。
+ OvertimeApplicationServiceImplTest
+ (2 用例,
+ mvn test
+ 通过):验证状态机「动作不允许 / 缺原因」抛错时,
+ message
+ 填的是
+ StatusActionTextResolver
+ 翻出的中文名、不外泄英文动作 / 状态 code。属 Mockito 单测(mock resolver),覆盖的是 service 层「填中文名而非裸
+ code」这条契约;resolver 自身的 DB 翻译由
+ StatusActionTextResolverTest
+ 覆盖。真实 DB 状态机表是否配齐
+ status_name
+ /
+ action_name
+ 的端到端校验仍依赖运行时,不在单测范围。
+ infra_api_access_log
+ (访问日志,留原始 code / 入参 / 堆栈)。
+ CLAUDE.md · 接口语义
+ 章节留有指向本文档的红线指针。
+