Skip to main content

使用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/ 中进行渲染就可以得到流程图

md流程图渲染图

三、逻辑细节了解#

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;}