使用JoyCode快速上手历史项目开发
作为研发人员,工作重心是编写高质量、可扩展的代码,并保证代码的可维护性和可重用性。在日常工作中,我经常使用以下最佳实践,来优化我的上手、开发和重构流程,以提高团队的生产力和代码的质量。
新人加入项目团队后,必须快速掌握项目的业务和代码结构,以及项目中使用的技术和工具。若项目有完善的数据库设计文档、架构图、流程图、业务说明、设计方案,那么上手会更加方便。然而,大部分项目都缺乏这些文档,因此我们需要有效地快速上手。
一般而言,上手工作会按照"先看数据结构,再缕脉络,后看细节"的流程进行。
#
一、数据结构整理首先,需要查看项目的数据结构,这些数据通常会以关系型数据库的形式进行存储。通过了解所有表的信息,可以对整个项目有一个大体的印象,首先要对表按照模块、业务域等方式进行分类,对项目有个大体脉络,然后再通过ER图、表结构等了解细节。 我们可以通过JDH-GPT来提升这一步的效率
指令:请按照业务域对以下数据库表进行分类
表分类结果:
- 回答相关表:answer_draft_info, answer_info, answer_scale_factor, answer_sheet, ticket_info, ticket_scale_r
- 业务相关表:content_business, content_business_relation, content_disease_relation, tenant_config, tenant_info, tenant_scale, tenant_user
- 测评相关表:scale_config, scale_decision_rule, scale_factor, scale_factor_item, scale_factor_question, scale_info, scale_man_user, scale_option_validate_rule, scale_privacy_auth_record, scale_quesiton_component, scale_question_decision_rule, scale_question_eval, scale_question_group, scale_question_info, scale_question_jump_decision_rule, scale_question_option, scale_question_option_decision_rule, scale_question_questions, scale_question_validate_rule, scale_reference_document, scale_scene_config, scale_source, scale_tag
- 其他相关表:snowflake_worker_id, system_config, user_info
#
二、代码脉络查看通过选择代码 自定义指定 根据以下代码 生成md格式流程图
可以生成对应的流程图 方便我们快速了解代码的执行流程和大致逻辑,下面是我的一个使用示例,通过gpt可以快速缕清这段代码的功能点
#
源代码:public Object html(@RequestParam( value = "seoRedirectFlag",required = false )String seoRedirectFlag, ModelAndView modelAndView, HttpServletRequest httpRequest){ String requestURL = httpRequest.getRequestURL().toString(); URL url = null; try { url = new URL(requestURL); } catch (MalformedURLException e) { log.error("URL格式错误 requestUrl={}",requestURL,e); return new ResponseEntity(HttpStatus.NOT_FOUND); }
String requestPath = url.getPath(); String requestHost = url.getHost();
log.info("PageController#html requestURL={},requestPath={}",requestURL,requestPath); if(requestPath == null) { String errorMsg = "请求路径Path"+requestURL+"不存在"; return new ResponseEntity(errorMsg.getBytes(StandardCharsets.UTF_8),HttpStatus.NOT_FOUND); }
// 通过host查询对应的page信息 List<SeoPage> seoPages = getSeoPages(requestHost); if(CollectionUtils.isEmpty(seoPages)) { String errorMsg = "请求路径Path"+requestURL+"不是SEO支持到域名"; log.error(errorMsg); return new ResponseEntity(errorMsg.getBytes(StandardCharsets.UTF_8),HttpStatus.NOT_FOUND); }
// 通过url和path 获取url信息 SeoUrlInfo seoUrlInfo = getSeoUrlInfo(requestHost,requestPath); // 开始获取 seoPage SeoPage seoPage = getSeoPage(seoPages, seoUrlInfo,requestURL); if(seoPage == null) { String errorMsg = "请求路径Path "+requestURL+"无法匹配到对应到页面"; log.error(errorMsg); return new ResponseEntity(errorMsg.getBytes(StandardCharsets.UTF_8),HttpStatus.NOT_FOUND); }
String sourceUrl = null; String query = null; boolean hasParam = false; if(seoUrlInfo == null) { log.info("PageController#html 找不到 seoUrlInfo");
sourceUrl = buildUrl(seoPage.getSeoUrlMatchRegExp(), requestURL,seoPage.getSourceUrlTemplate()); if(StringUtils.isNotBlank(sourceUrl)) { try { URL target = new URL(sourceUrl); query = target.getQuery(); hasParam = StringUtils.isNotBlank(query)?true:false; } catch (MalformedURLException e) { log.error("URL格式错误 requestUrl={}",requestURL,e); return new ResponseEntity(HttpStatus.NOT_FOUND); } } } else { sourceUrl = seoUrlInfo.getSourceUrl(); hasParam = StringUtils.isNotBlank(seoUrlInfo.getSourceUrlParams()); query = seoUrlInfo.getSourceUrlParams(); } if(sourceUrl == null) { return new ResponseEntity(HttpStatus.NOT_FOUND); } // 如果开启了跳转模式 有参数 不是已经跳转过来的请求 那么302跳转 if(shouldRedirect(redirectModel,hasParam,seoRedirectFlag)) { // 拼接跳转路径 StringBuilder redirectUrlSb = new StringBuilder(); redirectUrlSb.append(requestPath); redirectUrlSb.append("?"); redirectUrlSb.append(query); redirectUrlSb.append("&seoRedirectFlag=true"); String redirectUrl =redirectUrlSb.toString(); log.info("开始跳转 redirectUrl={}",redirectUrl); modelAndView.setView(new RedirectView(redirectUrl,true,false)); return modelAndView; } else { Cookie[] cookies = httpRequest.getCookies(); String htmlContent = readHtml(sourceUrl,cookies); if(StringUtils.isNotBlank(htmlContent)) { log.info("PageController#getHtmlContent sourceUrl={},htmlContent.length={}",sourceUrl,htmlContent.length()); byte[] bytes = handlePageContentForUser(seoPage, seoUrlInfo, requestURL, htmlContent, query); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.TEXT_HTML); return new ResponseEntity(bytes,headers,HttpStatus.OK); } else { log.warn("PageController#getHtmlContent html is blank sourceUrl={}",sourceUrl); } } modelAndView.setView(new RedirectView("/",true,false)); return modelAndView; }
#
md流程图源码:flowchart TB start[开始] --> getRequestURL[获取请求URL] getRequestURL --> checkURL[检查URL格式] checkURL --> |URL格式正确| getURLInfo[获取URL信息] checkURL --> |URL格式错误| return404[返回404] getURLInfo --> checkRequestPath[检查请求路径] checkRequestPath --> |请求路径不存在| returnNotFound[返回404] checkRequestPath --> |请求路径存在| getSeoPages[获取SEO页面] getSeoPages --> |找到匹配的SEO页面| getSeoUrlInfo[获取URL的SEO信息] getSeoPages --> |找不到匹配的SEO页面| returnNotFound[返回404] getSeoUrlInfo --> |未找到SEO信息| buildSourceUrl[构建源URL] getSeoUrlInfo --> |已找到SEO信息| getSourceUrlInfo[获取源URL信息] getSourceUrlInfo --> buildSourceUrl[构建源URL] buildSourceUrl --> |构建失败| returnNotFound[返回404] buildSourceUrl --> |构建成功| readHtml[读取HTML内容] readHtml --> |成功获取HTML| handlePageContent[处理页面内容] handlePageContent --> |构建成功| returnHtml[返回HTML内容] handlePageContent --> |构建失败| returnNotFound[返回404] readHtml --> |未成功获取HTML| returnNotFound[返回404] shouldRedirect --> |需要302跳转| redirectTo[执行302跳转] shouldRedirect --> |无需302跳转| getHtmlContent[获取HTML内容] redirectTo --> |执行成功| returnRedirect[返回302跳转] redirectTo --> |执行失败| returnNotFound[返回404] getHtmlContent --> readHtml[读取HTML内容]
将上面的代码复制到 https://mermaid.live/ 中进行渲染就可以得到流程图
#
三、逻辑细节了解#
1. 解释代码具体逻辑实现可以通过 选中代码右键 解释代码 和 添加注释 功能来进行了解,下面是一个使用示例,可以看到对这种细节代码 他可以很好的给出相应的解释
#
待解释的代码:private SeoPage getSeoPage(List<SeoPage> seoPages, SeoUrlInfo seoUrlInfo,String requestURL) { if (seoUrlInfo != null) { // 如果urlInfo不为null 直接通过id获取 Long pageId = seoUrlInfo.getPageId(); return seoPages.stream().filter(item -> pageId.equals(item.getPageId())).findFirst().orElse(null); } else { // 如果urlInfo为null 需要通过url进行正则匹配 return matchPage(seoPages, requestURL); } }
#
解释结果:这是一个Java方法,它接受一个SeoUrlInfo对象、一个请求URL和一个SeoPage对象列表作为输入,并返回一个SeoPage对象。它根据提供的SeoUrlInfo或请求URL从SeoPage列表中获取匹配的SeoPage对象。
如果SeoUrlInfo不为null,则通过SeoUrlInfo中的PageId属性找到匹配的SeoPage对象。 如果SeoUrlInfo为null,则通过请求URL和SeoPage对象列表中的正则表达式进行匹配,以找到匹配的SeoPage对象。
#
2. 代码注释大家可以看到 代码解释的时候如果有注释可以更好的解释代码逻辑,如果代码缺少注释的话就可以先试用代码注释功能,然后再进行代码解释
#
待添加注释的代码:private RenderParam buildRenderParam(String url, HttpServletRequest httpRequest) { RenderParam renderParam = new RenderParam(); renderParam.setUrl(url); renderParam.setDebugModel(seoDebugModelSwitch); Cookie[] cookies = httpRequest.getCookies(); renderParam.setCookies(cookies); Enumeration<String> headerNames = httpRequest.getHeaderNames(); if(headerNames!=null) { Map<String,String> headers = new HashMap<>(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); String headerValue = httpRequest.getHeader(headerName); headers.put(headerName,headerValue); } renderParam.setHeaders(headers); } return renderParam; }
#
添加注释后的代码:/** * 构建渲染参数对象 * @param url 渲染的URL * @param httpRequest HTTP请求对象 * @return RenderParam 渲染参数对象 */private RenderParam buildRenderParam(String url, HttpServletRequest httpRequest) { //创建渲染参数对象 RenderParam renderParam = new RenderParam(); //设置渲染的URL renderParam.setUrl(url); //设置调试模式开关 renderParam.setDebugModel(seoDebugModelSwitch); //获取请求中的Cookie数组 Cookie[] cookies = httpRequest.getCookies(); //设置Cookie数组到渲染参数对象中 renderParam.setCookies(cookies); //获取请求中的Header名称枚举 Enumeration<String> headerNames = httpRequest.getHeaderNames(); //如果存在Header名称枚举 if(headerNames!=null) { //创建Header Map对象 Map<String,String> headers = new HashMap<>(); //循环遍历Header名称枚举 while (headerNames.hasMoreElements()) { //获取下一个Header名称 String headerName = headerNames.nextElement(); //获取当前Header名称对应的值 String headerValue = httpRequest.getHeader(headerName); //将Header名称和值放入Header Map对象中 headers.put(headerName,headerValue); } //将Header Map对象设置到渲染参数对象中 renderParam.setHeaders(headers); } //返回渲染参数对象 return renderParam;}