04-一个请求的完整旅程

📅 发布时间:2026/8/23 22:33:29
04-一个请求的完整旅程 04-一个请求的完整旅程前面三篇讲了全景、哲学、模式。从这篇开始进入代码。以一次分页查询用户列表和一次审批通过任务为例把 HTTP 请求从浏览器到 Oracle 再回来的每一跳拆开——这一篇是后面所有引擎类文章的索引。文章目录04-一个请求的完整旅程一、例一分页查询 POST /ea/page/SYS_USER_s第1跳JwtAuthFilterplatform模块第2跳EaControllerformbuilder模块第3跳EaEnginemetadata模块第4跳返回二、例二审批通过 POST /api/workflow/process/complete三、两条链的公共部分四、系列后续怎么展开一、例一分页查询 POST /ea/page/SYS_USER_s第1跳JwtAuthFilterplatform模块browise-platform/src/main/java/com/browise/platform/auth/JwtAuthFilter.java6.0KBorder1 的原生 Filter不依赖Spring Security。① enabled检查browise.auth.enabled配置开关 ② excludePaths匹配/auth/ /login /captcha /error 放行支持/**前缀 ③ extractTokenAuthorization: Bearer xxx → 没有则查Cookie BROWISE_AT ④ JwtUtil.validateHS256验签 exp过期检查 ⑤ JwtBlacklist双查 isInvalid(psnId, iat) ← 用户级改密后iat之前的全失效 isInvalidByToken(token) ← token级登出单设备 ⑥ CacheManager.getUserProfile(psnId) → OP_PERSON SYS_USER.STATUS禁用即拒 ⑦ CurrentUser.set(profile) ← ThreadLocal业务代码随处可取 ⑧ MenuAuthProvider.canAccess(profile, /ea/page) → 用户角色 → SYS_ROLE_MENU → 菜单路径匹配 → 不匹配返回403 ⑨ finally CurrentUser.clear() ← ThreadLocal必须清否则线程池复用串用户第2跳EaControllerformbuilder模块browise-formbuilder/.../controller/EaController.java15.2KBPostMapping(/ea/page/{sqlId})publicResultpage(PathVariableStringsqlId,RequestBodyMapString,Objectparams){intpageNum...,pageSize...;ListMapString,ObjectrowseaEngine.query(sqlId,params,pageNum,pageSize);longtotaleaEngine.count(sqlId,params);returnResult.ok(PageResult.of(rows,total));}第3跳EaEnginemetadata模块browise-metadata/.../ea/core/engine/EaEngine.java549行内部五步① EaMetadataCache.get(sqlId) → ConcurrentHashMap查缓存 → 命中EaSqlMeta EaTableMeta[] EaFieldMeta[] EaConditionMeta[] → 未命中抛EA_NOT_FOUND启动时MetadataLoader已全量装载 ② SqlBuilder.buildSelect(sqlId, params, dialect) → appendColumns按EaFieldMeta.dataType(1字符/2日期/3数字/4日期时间) 选方言列表达式Oracle: to_char(birth,yyyymmdd) → appendFromEaTableMeta多表JOIN或主表 → where 11 → appendJoinAndWhere按EaConditionMeta拼条件 conditionType1固定值始终拼接动态参数null跳过 ③ dialect.paginate(sql, orderBy, start, end) → OracleDialectROWNUM三层包装第10篇专述 → MySqlDialectLIMIT ? OFFSET ? ④ EaConnectionProvider.getConnection() → BrowiseEaProvider从Spring DataSource取连接 → PreparedStatement参数化执行禁止字符串拼接 ⑤ postProcess(rows) → 遍历EaFieldMeta isEncrypted() → Sm4EncryptStrategy.decrypt()逐字段解密 isMasked() → maskRule(3,4)脱敏→320***********1234 → SlowSqlMonitor.check(sql, params, elapsedMs) 超阈值log.warn 可选文件追加第4跳返回Result { code:0, msg:success, data:{ rows:[...], total:137 } } → GlobalExceptionHandler兜底任何异常→Result.error(-1,msg) → 前端httpClient.ts响应拦截code!0抛业务错误401触发登出 → useRowSet灌入primary缓冲_t全为0 → MetaGrid渲染二、例二审批通过 POST /api/workflow/process/complete这条链更长穿platform→workflow→metadata三个模块① JwtAuthFilter同上 ② ProcessControllerbrowise-workflow/.../ProcessController.java28.9KB ├─ resolveFormSqlid(taskId) │ → WorkflowConfigService.findForm()查ACT_RE_BINDINGFORM │ → 拿到流程节点绑定的表单sqlId │ ├─ 表单数据upsert到T_DATA │ → FormDataMapper存在则update不存在则insert │ → 走EA引擎的insert/updateByFields │ └─ FlowableProcessService.completeTask(taskId, variables) │ ├─ ①前置拦截器 │ InterceptorChainService按TaskInterceptorConfig表 │ 读before拦截器id串从ApplicationContext取Bean执行 │ ├─ ②taskService.complete() │ → Flowable引擎推进到下一节点 │ ├─ ③后置拦截器 │ 同①after id串 │ └─ ④下一任务处理人通知 → WorkflowNotifyService发WorkflowMessageEventSpring事件 → TransactionalEventListener(AFTER_COMMIT)监听 → 事务提交后才WebSocket推送 ← 关键事务没提交就推送用户点待办却查不到数据三、两条链的公共部分环节组件位置认证JwtAuthFilter CurrentUser(ThreadLocal)platform鉴权菜单RBAC AuthAspect(RequirePermission/DataScope)platform数据EaEngine SqlBuilder Dialectmetadata异常GlobalExceptionHandler → Resultplatform上下文清理finally CurrentUser.clear() DataScope popplatform这就是16个模块在运行时的协作方式——不是所有请求都穿过所有模块而是按URL前缀路由到对应Controller公共能力认证/异常/EA数据访问横切所有链路。四、系列后续怎么展开这篇出现的每个环节后面各有一篇或几篇拆到底本篇环节后续文章EaEngine五步06篇引擎调度、07-08篇SqlBuilderDialect分页09-10篇8方言与ROWNUM演化postProcess加密脱敏14-15篇JwtAuthFilter九步25-26篇双令牌、黑名单CurrentUser29篇ThreadLocal上下文InterceptorChain41篇数据库配置的拦截器链事件推送43篇事务提交后WebSocket✅ 亮点用一查一改两条真实链路把16模块的运行时协作串起来每跳标注模块归属和源码文件——是整个系列的导航图。适合第一次接触多模块项目的人建立全局观。扩展方向后续每篇都是本文某一跳的代码级展开。