网站后期的维护延安网站建设公司电话
2026/1/15 10:19:01 网站建设 项目流程
网站后期的维护,延安网站建设公司电话,黄页企业查询app,百度推广app下载官方一次从同步到异步的华丽转身 ✨ #x1f4cb; 目录 背景问题解决方案技术实现踩坑记录经验总结 #x1f3ac; 背景问题 问题场景 #x1f631; 长耗时接口#xff08;30-60秒#xff09;遇到网关超时#xff08;30秒#xff09;的问题#xff1a; 接口调用需要 30-…一次从同步到异步的华丽转身 ✨ 目录背景问题解决方案技术实现踩坑记录经验总结 背景问题问题场景 长耗时接口30-60秒遇到网关超时30秒的问题接口调用需要30-60秒才能返回网关设置了30秒超时重试结果接口超时 → 网关重试 → 重复调用 → 用户体验差 解决方案 异步处理 Redis缓存 完美解决用户发起请求 → 立即返回处理中 → 后台异步处理处理完成后 → Redis存储结果 → 发送通知用户点击通知链接 → 自动打开页面 → 显示处理结果 核心改动后端改动1️⃣ 新增 Redis Key// constant/redis.jsOP_TASK_AI:task_ai// 用于存储异步任务状态2️⃣ 新增查询路由// router/xxx.jsrouter.get(/api/task/result_by_key,controller.task.getTaskResult,)3️⃣ Controller层两个接口的职责分离接口用途参数返回create_task创建任务input,type,related_id处理结果 or{status: processing}get_task_result查询结果taskKeyRedis中的完整数据 ornull关键代码// 创建任务接口主动asynccreateTask(ctx){const{input,type,related_id}ctx.queryconstresultawaitthis.ctx.service.task.createTask(input,type,related_id)// ...}// 查询结果接口被动asyncgetTaskResult(ctx){const{taskKey}ctx.queryconstresultawaitthis.ctx.service.task.getTaskResult(taskKey)// ...}4️⃣ Service层核心方法createTask- 创建异步任务asynccreateTask(input,type,related_id){// 1. 检查Redis是否有结果// 2. 如果有 → 直接返回// 3. 如果没有 → 创建任务异步处理// 4. 返回 { status: processing }}processTaskAsync- 异步处理任务asyncprocessTaskAsync(input,type,redisKey,related_id){// 1. 调用长耗时接口// 2. 处理成功 → 更新Redis状态为 completed// 3. 发送通知包含跳转链接}getTaskResult- 查询任务结果asyncgetTaskResult(taskKey){// 直接从Redis查询不创建任务constredisKey${REDIS_KEYS.OP_TASK_AI}:${taskKey}constredisValueawaitthis.ctx.getRedisValue(redisKey)returnredisValue?JSON.parse(redisValue):null}Redis数据结构// processing 状态{status:processing,type:task_type,input:input_data,related_id:123,createdAt:1234567890}// completed 状态{status:completed,result:处理结果,type:task_type,input:input_data,related_id:123,completedAt:1234567890}前端改动1️⃣ Service层新增查询接口// service/xxx/index.tsasyncfunctiongetTaskResult(params:{taskKey:string}){const{data}awaithttpGet(/api/task/result_by_key,params)returndata}2️⃣ 列表页URL参数处理// 解析URL参数const{relatedId}querystring.parse(window.location.search)// 通过URL打开详情constopenDetailByUrlasync(){if(relatedId){constdetaildata.list.find((item)${item.id}relatedId)if(detail){dispatchUpdate(detail)}}}// 监听数据加载完成自动打开useEffect((){if(data.list.length0){openDetailByUrl()}},[relatedId,data])关闭时清除URL参数consthandleClose(){dispatchClose()onReset()// 重置搜索参数URL会自动更新}3️⃣ 详情页从URL查询结果// 解析URL参数const{taskKey}querystring.parse(window.location.search)as{taskKey:string}// 查询Redis并显示结果constloadTaskResultasync(){if(taskKey){consttaskResultawaitgetTaskResult({taskKey})if(taskResult){const{status,result,type}taskResult// 设置类型if(type){setTaskType(type)}// 根据状态显示结果if(statuscompletedresult){setShowResult(true)setResultValue(result)}elseif(statusprocessing){message.info(处理中请稍候...)}elseif(statusfailed){message.warning(处理失败请重试)}}}}// 页面打开时自动查询useEffect((){if(taskKeyvisible){loadTaskResult()}},[taskKey,visible])重要Form和State的区别// ❌ 错误组件不在Form.Item中不能用form管理form.setFieldsValue({field:value})// 没用// ✅ 正确直接用state管理const[field,setField]useStatestring()Select value{field}// 绑定stateonChange{setField}// 更新state️ 技术实现Redis Key设计规则${REDIS_KEYS.OP_TASK_AI}:${taskKey}为什么只用单个唯一标识✅ 简单不需要组合多个参数✅ 唯一taskKey本身就是唯一的✅ 易查前端只需要提取唯一标识示例input: https://example.com/file/1765423642667_169.jpeg taskKey: 1765423642667_169.jpeg redisKey: task_ai:1765423642667_169.jpeg异步处理流程用户点击识别 ↓ 检查Redis是否有结果 ↓ 有结果 → 直接返回 ↓ 无结果 → 设置Redis状态为 processing ↓ 异步调用 processRecognitionTask ↓ 调用AI识别接口30-60秒 ↓ 识别成功 → 更新Redis状态为 completed ↓ 发送通知包含跳转链接通知链接格式https://example.com/page/detail?relatedId${related_id}taskKey${taskKey}为什么这样设计relatedId用于获取关联数据详情打开页面taskKey用于查询Redis任务结果URL参数处理前端解析importquerystringfromquery-string// 解析URL参数const{relatedId,taskKey}querystring.parse(window.location.search)// ⚠️ 注意querystring.parse 返回的类型可能是 string | string[] | null// 需要类型断言或类型守卫const{taskKey}querystring.parse(window.location.search)as{taskKey:string}清除URL参数// 方法1重置defaultParams推荐onReset()// 会设置所有参数为默认值空值会被过滤// 方法2手动清除特定参数constclearUrlExtraParams(){constcurrentParamsquerystring.parse(location.search)const{relatedId,taskKey,...restParams}currentParams// 只保留 restParams} 踩坑记录坑1Select组件不在Form.Item中问题// Select不在Form.Item中Select onChange{onVendorTypeChange}{/* ... */}/Select// 但是代码中用了form.setFieldsValueform.setFieldsValue({vendorType:value})// ❌ 没用原因Form只能管理Form.Item中的字段解决// ✅ 直接用state管理const[vendorType,setVendorType]useStatestring()constonVendorTypeChange(value:string){setVendorType(value)// 只更新state}Select value{vendorType}onChange{onVendorTypeChange}坑2URL参数被useSyncParams重置问题URL中有额外参数如relatedId,taskKey但是useSyncParamshook 会重写URL只保留defaultParams中的参数结果额外参数被清除了 原因// useSyncParams 的 onParamSync 会重写URLconstonParamSync(){history.push(${pathname}?${querystring.stringify(params,{sort:false})})// params 只包含 defaultParams 中的字段}解决// 方案1在defaultParams中添加这些字段推荐constdefaultParams{// ...其他字段taskKey:,relatedId:,}// 方案2修改useSyncParams hook保留额外参数// 但可能影响其他页面需谨慎坑3后端获取域名的方式问题前端用window.location.host后端用什么答案// 方式1配置文件当前使用constbaseUrlthis.app.config.baseUrl||// 方式2从请求获取更灵活constbaseUrl${this.ctx.request.protocol}://${this.ctx.request.host}// 或constbaseUrlthis.ctx.request.origin坑4异步处理中的this上下文问题异步函数中this可能为undefined解决// ✅ 项目风格直接调用不捕获thisthis.processRecognitionTask(imageUrl,vendorType,redisKey,maintain_id)// 如果出错会在processRecognitionTask内部用this.ctx.throwValidateWarn处理 经验总结后端经验Redis Key设计要简单✅ 只用单个唯一标识如taskKey不要组合多个参数✅ 便于前端查询不需要复杂的key生成逻辑✅ 从输入中提取唯一标识如文件名、ID等异步处理要fire and forget✅ 不等待异步任务完成✅ 错误处理在异步函数内部完成✅ 符合项目现有代码风格两个接口职责要清晰create_task创建任务主动会创建新任务get_task_result查询结果被动只查询不创建Redis数据结构要完整存储任务相关的完整信息type、input、related_id等便于前端直接使用不需要额外查询包含状态、结果、时间戳等元数据前端经验URL参数处理要统一✅ 使用querystring.parse(window.location.search)✅ 注意类型处理可能是string | string[] | null✅ 使用类型断言或类型守卫Form和State要分清✅ Form只能管理Form.Item中的字段✅ 不在Form.Item中的组件用state管理✅ 不要混用避免无效操作useSyncParams要小心✅ 会重写URL只保留defaultParams中的参数✅ 额外参数需要在defaultParams中声明即使为空✅ 避免URL参数被意外清除页面打开时机要准确✅ 等数据加载完成后再打开✅ 通过useEffect监听数据变化✅ 避免在数据未加载时操作代码清理很重要✅ 删除无意义的form操作✅ 删除console.log和调试代码✅ 保持代码整洁和可维护通用经验异步改造的核心立即返回 → 后台处理 → 结果存储 → 通知用户Redis的妙用临时存储任务状态跨请求共享数据设置TTL自动清理URL参数传递简单直接便于分享刷新页面不丢失状态但要处理好清除逻辑代码风格一致性参考项目现有代码保持风格统一便于团队协作 最终效果用户体验提升发起请求→ 立即返回处理中可以关闭页面处理完成→ 收到通知点击链接自动打开→ 页面自动打开处理结果自动填充完美✨技术指标✅ 接口响应时间从30-60秒 →1秒✅ 用户体验从等待 →异步处理✅ 代码质量逻辑清晰职责分明✅ 可维护性高符合项目风格 适用场景什么时候用这个模式✅适合长耗时接口10秒网关有超时限制用户不需要立即看到结果可以通过通知告知用户❌不适合短耗时接口5秒需要实时反馈用户必须等待结果 核心要点总结两个接口分离创建任务 vs 查询结果Redis存储状态processing → completed/failed异步处理fire and forget模式URL参数传递通过链接跳转并自动加载结果前端状态管理Form vs State要分清Happy Coding!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询